Loading README.md +13 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,19 @@ git submodule update --init gradle aarGen gradle :app:assembleDebug ``` *Update on May 25th 2023:* 1. Clone the repository 2. remove "build" dir inside external/calendar, external/chips, external/colorpicker and external/timezonepicker 3. run `git submodule update --init` 4. in `build.gradle` update `minSdk` to 26 or higher, to avoid icon AAR issue 5. Run `./gradlew aarGen` 6. Run `./gradlew :app:assembleDebug` NB: we were integrating it within /e/OS from Android_prebuilt until version 9. Now it is build with the OS. ## License Copyright (c) 2005-2013, The Android Open Source Project Loading app/build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,6 @@ android { } dependencies { // Core implementation 'androidx.core:core-ktx:1.8.0' implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') Loading @@ -99,4 +98,5 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation 'foundation.e:elib:0.0.1-alpha11' } app/src/main/AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -204,7 +204,7 @@ <provider android:name="androidx.core.content.FileProvider" android:authorities="foundation.e.calendar.provider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data Loading app/src/main/java/com/android/calendar/alerts/AlertService.java +76 −46 Original line number Diff line number Diff line Loading @@ -42,11 +42,12 @@ import android.os.Process; import android.provider.CalendarContract; import android.provider.CalendarContract.Attendees; import android.provider.CalendarContract.CalendarAlerts; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; import androidx.core.app.NotificationCompat; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import com.android.calendar.Utils; Loading @@ -55,6 +56,7 @@ import com.android.calendarcommon2.Time; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.TimeZone; Loading Loading @@ -224,7 +226,6 @@ public class AlertService extends Service { } long nextRefreshTime = Long.MAX_VALUE; int currentNotificationId = 1; NotificationPrefs notificationPrefs = new NotificationPrefs(context, prefs, (numFired == 0)); Loading @@ -233,15 +234,18 @@ public class AlertService extends Service { redistributeBuckets(highPriorityEvents, mediumPriorityEvents, lowPriorityEvents, maxNotifications); final HashSet<Integer> displayedNotifIds = new HashSet<>(); // Post the individual higher priority events (future and recently started // concurrent events). Order these so that earlier start times appear higher in // the notification list. for (int i = 0; i < highPriorityEvents.size(); i++) { NotificationInfo info = highPriorityEvents.get(i); String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, for (NotificationInfo info : highPriorityEvents) { final String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, info.allDay, info.location); postNotification(info, summaryText, context, true, notificationPrefs, nm, currentNotificationId++); final int notifId = postNotification(info, summaryText, context, true, notificationPrefs, nm); displayedNotifIds.add(notifId); // Keep concurrent events high priority (to appear higher in the notification list) // until 15 minutes into the event. Loading @@ -254,13 +258,15 @@ public class AlertService extends Service { // TODO: Post these with the same notification priority level as the higher priority // events, so that all notifications will be co-located together. for (int i = mediumPriorityEvents.size() - 1; i >= 0; i--) { NotificationInfo info = mediumPriorityEvents.get(i); final NotificationInfo info = mediumPriorityEvents.get(i); // TODO: Change to a relative time description like: "Started 40 minutes ago". // This requires constant refreshing to the message as time goes. String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, final String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, info.allDay, info.location); postNotification(info, summaryText, context, false, notificationPrefs, nm, currentNotificationId++); final int notifId = postNotification(info, summaryText, context, false, notificationPrefs, nm); displayedNotifIds.add(notifId); // Refresh when concurrent event ends so it will drop into the expired digest. nextRefreshTime = Math.min(nextRefreshTime, getNextRefreshTime(info, currentTime)); Loading Loading @@ -307,12 +313,8 @@ public class AlertService extends Service { } // Remove the notifications that are hanging around from the previous refresh. if (currentNotificationId <= maxNotifications) { nm.cancelAllBetween(currentNotificationId, maxNotifications); if (DEBUG) { Log.d(TAG, "Canceling leftover notification IDs " + currentNotificationId + "-" + maxNotifications); } if (displayedNotifIds.size() <= maxNotifications) { nm.cancelAllExcept(displayedNotifIds); } // Schedule the next silent refresh time so notifications will change Loading Loading @@ -692,9 +694,14 @@ public class AlertService extends Service { return digestTitle.toString(); } private static void postNotification(NotificationInfo info, String summaryText, private static int postNotification(NotificationInfo info, String summaryText, Context context, boolean highPriority, NotificationPrefs prefs, NotificationMgr notificationMgr, int notificationId) { NotificationMgr notificationMgr) { final int notificationId = Long.valueOf(info.eventId).intValue() + summaryText.hashCode(); //final String eventIDHexa = Long.toHexString(info.eventId); //for test purpose, it would avoid overflow issue of parsing long to int if (!notificationMgr.isNotificationDisplayed(notificationId)) { int priorityVal = Notification.PRIORITY_DEFAULT; if (highPriority) { priorityVal = Notification.PRIORITY_HIGH; Loading Loading @@ -728,6 +735,8 @@ public class AlertService extends Service { + (highPriority ? ", high-priority" : "")); } } return notificationId; } private static String getTickerText(String eventName, String location) { String tickerText = eventName; Loading Loading @@ -1018,6 +1027,27 @@ public class AlertService extends Service { public void notify(int id, NotificationWrapper nw) { mNm.notify(id, nw.mNotification); } @Override public boolean isNotificationDisplayed(int notifId) { final StatusBarNotification[] statusBarNotifs = mNm.getActiveNotifications(); for (StatusBarNotification statusBarNotif : statusBarNotifs) { if (notifId == statusBarNotif.getId()) return true; } return false; } @Override public void cancelAllExcept(@NonNull HashSet<Integer> notifIds) { final StatusBarNotification[] statusBarNotifs = mNm.getActiveNotifications(); for (StatusBarNotification statusBarNotif : statusBarNotifs) { final int statusBarNotifId = statusBarNotif.getId(); if (!notifIds.contains(statusBarNotifId)) { cancel(statusBarNotifId); } } } } static class NotificationInfo { Loading app/src/main/java/com/android/calendar/alerts/NotificationMgr.java +13 −0 Original line number Diff line number Diff line Loading @@ -17,14 +17,21 @@ package com.android.calendar.alerts; import android.app.NotificationChannel; import android.service.notification.StatusBarNotification; import androidx.annotation.NonNull; import com.android.calendar.alerts.AlertService.NotificationWrapper; import java.util.HashSet; public abstract class NotificationMgr { public abstract void notify(int id, NotificationWrapper notification); public abstract void cancel(int id); public abstract void createNotificationChannel(NotificationChannel channel); public abstract boolean isNotificationDisplayed(int notifId); /** * Don't actually use the notification framework's cancelAll since the SyncAdapter * might post notifications and we don't want to affect those. Loading @@ -41,4 +48,10 @@ public abstract class NotificationMgr { cancel(i); } } /** * Cancel all notification except the one listed by Ids * @param notifIds */ public abstract void cancelAllExcept(@NonNull HashSet<Integer> notifIds); } Loading
README.md +13 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,19 @@ git submodule update --init gradle aarGen gradle :app:assembleDebug ``` *Update on May 25th 2023:* 1. Clone the repository 2. remove "build" dir inside external/calendar, external/chips, external/colorpicker and external/timezonepicker 3. run `git submodule update --init` 4. in `build.gradle` update `minSdk` to 26 or higher, to avoid icon AAR issue 5. Run `./gradlew aarGen` 6. Run `./gradlew :app:assembleDebug` NB: we were integrating it within /e/OS from Android_prebuilt until version 9. Now it is build with the OS. ## License Copyright (c) 2005-2013, The Android Open Source Project Loading
app/build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,6 @@ android { } dependencies { // Core implementation 'androidx.core:core-ktx:1.8.0' implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') Loading @@ -99,4 +98,5 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation 'foundation.e:elib:0.0.1-alpha11' }
app/src/main/AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -204,7 +204,7 @@ <provider android:name="androidx.core.content.FileProvider" android:authorities="foundation.e.calendar.provider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data Loading
app/src/main/java/com/android/calendar/alerts/AlertService.java +76 −46 Original line number Diff line number Diff line Loading @@ -42,11 +42,12 @@ import android.os.Process; import android.provider.CalendarContract; import android.provider.CalendarContract.Attendees; import android.provider.CalendarContract.CalendarAlerts; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; import androidx.core.app.NotificationCompat; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import com.android.calendar.Utils; Loading @@ -55,6 +56,7 @@ import com.android.calendarcommon2.Time; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.TimeZone; Loading Loading @@ -224,7 +226,6 @@ public class AlertService extends Service { } long nextRefreshTime = Long.MAX_VALUE; int currentNotificationId = 1; NotificationPrefs notificationPrefs = new NotificationPrefs(context, prefs, (numFired == 0)); Loading @@ -233,15 +234,18 @@ public class AlertService extends Service { redistributeBuckets(highPriorityEvents, mediumPriorityEvents, lowPriorityEvents, maxNotifications); final HashSet<Integer> displayedNotifIds = new HashSet<>(); // Post the individual higher priority events (future and recently started // concurrent events). Order these so that earlier start times appear higher in // the notification list. for (int i = 0; i < highPriorityEvents.size(); i++) { NotificationInfo info = highPriorityEvents.get(i); String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, for (NotificationInfo info : highPriorityEvents) { final String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, info.allDay, info.location); postNotification(info, summaryText, context, true, notificationPrefs, nm, currentNotificationId++); final int notifId = postNotification(info, summaryText, context, true, notificationPrefs, nm); displayedNotifIds.add(notifId); // Keep concurrent events high priority (to appear higher in the notification list) // until 15 minutes into the event. Loading @@ -254,13 +258,15 @@ public class AlertService extends Service { // TODO: Post these with the same notification priority level as the higher priority // events, so that all notifications will be co-located together. for (int i = mediumPriorityEvents.size() - 1; i >= 0; i--) { NotificationInfo info = mediumPriorityEvents.get(i); final NotificationInfo info = mediumPriorityEvents.get(i); // TODO: Change to a relative time description like: "Started 40 minutes ago". // This requires constant refreshing to the message as time goes. String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, final String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, info.allDay, info.location); postNotification(info, summaryText, context, false, notificationPrefs, nm, currentNotificationId++); final int notifId = postNotification(info, summaryText, context, false, notificationPrefs, nm); displayedNotifIds.add(notifId); // Refresh when concurrent event ends so it will drop into the expired digest. nextRefreshTime = Math.min(nextRefreshTime, getNextRefreshTime(info, currentTime)); Loading Loading @@ -307,12 +313,8 @@ public class AlertService extends Service { } // Remove the notifications that are hanging around from the previous refresh. if (currentNotificationId <= maxNotifications) { nm.cancelAllBetween(currentNotificationId, maxNotifications); if (DEBUG) { Log.d(TAG, "Canceling leftover notification IDs " + currentNotificationId + "-" + maxNotifications); } if (displayedNotifIds.size() <= maxNotifications) { nm.cancelAllExcept(displayedNotifIds); } // Schedule the next silent refresh time so notifications will change Loading Loading @@ -692,9 +694,14 @@ public class AlertService extends Service { return digestTitle.toString(); } private static void postNotification(NotificationInfo info, String summaryText, private static int postNotification(NotificationInfo info, String summaryText, Context context, boolean highPriority, NotificationPrefs prefs, NotificationMgr notificationMgr, int notificationId) { NotificationMgr notificationMgr) { final int notificationId = Long.valueOf(info.eventId).intValue() + summaryText.hashCode(); //final String eventIDHexa = Long.toHexString(info.eventId); //for test purpose, it would avoid overflow issue of parsing long to int if (!notificationMgr.isNotificationDisplayed(notificationId)) { int priorityVal = Notification.PRIORITY_DEFAULT; if (highPriority) { priorityVal = Notification.PRIORITY_HIGH; Loading Loading @@ -728,6 +735,8 @@ public class AlertService extends Service { + (highPriority ? ", high-priority" : "")); } } return notificationId; } private static String getTickerText(String eventName, String location) { String tickerText = eventName; Loading Loading @@ -1018,6 +1027,27 @@ public class AlertService extends Service { public void notify(int id, NotificationWrapper nw) { mNm.notify(id, nw.mNotification); } @Override public boolean isNotificationDisplayed(int notifId) { final StatusBarNotification[] statusBarNotifs = mNm.getActiveNotifications(); for (StatusBarNotification statusBarNotif : statusBarNotifs) { if (notifId == statusBarNotif.getId()) return true; } return false; } @Override public void cancelAllExcept(@NonNull HashSet<Integer> notifIds) { final StatusBarNotification[] statusBarNotifs = mNm.getActiveNotifications(); for (StatusBarNotification statusBarNotif : statusBarNotifs) { final int statusBarNotifId = statusBarNotif.getId(); if (!notifIds.contains(statusBarNotifId)) { cancel(statusBarNotifId); } } } } static class NotificationInfo { Loading
app/src/main/java/com/android/calendar/alerts/NotificationMgr.java +13 −0 Original line number Diff line number Diff line Loading @@ -17,14 +17,21 @@ package com.android.calendar.alerts; import android.app.NotificationChannel; import android.service.notification.StatusBarNotification; import androidx.annotation.NonNull; import com.android.calendar.alerts.AlertService.NotificationWrapper; import java.util.HashSet; public abstract class NotificationMgr { public abstract void notify(int id, NotificationWrapper notification); public abstract void cancel(int id); public abstract void createNotificationChannel(NotificationChannel channel); public abstract boolean isNotificationDisplayed(int notifId); /** * Don't actually use the notification framework's cancelAll since the SyncAdapter * might post notifications and we don't want to affect those. Loading @@ -41,4 +48,10 @@ public abstract class NotificationMgr { cancel(i); } } /** * Cancel all notification except the one listed by Ids * @param notifIds */ public abstract void cancelAllExcept(@NonNull HashSet<Integer> notifIds); }