diff --git a/README.md b/README.md index 5cb2f9356c6d13743dcd7bf19218f23652eb197a..d60c0c4252917ccffd350f1474179683e4f2c182 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/app/build.gradle b/app/build.gradle index 32720802513ab0f0e0de806110feb88758ff0b5d..6ded8fe2a45451cf990e02d67d08dd4548b75b33 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,7 +84,6 @@ android { } dependencies { - // Core implementation 'androidx.core:core-ktx:1.8.0' implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') @@ -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' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 703ced6b5036fb283ea4b96d2588f9cbeb589760..e7d82796a2e88674253e6942c974715f6c57fedd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -204,7 +204,7 @@ 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. @@ -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)); @@ -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 @@ -692,41 +694,48 @@ 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) { - int priorityVal = Notification.PRIORITY_DEFAULT; - if (highPriority) { - priorityVal = Notification.PRIORITY_HIGH; - } + NotificationMgr notificationMgr) { - String tickerText = getTickerText(info.eventName, info.location); - NotificationWrapper notification = AlertReceiver.makeExpandingNotification(context, - info.eventName, summaryText, info.description, info.startMillis, - info.endMillis, info.eventId, notificationId, prefs.getDoPopup(), priorityVal); + 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 - boolean quietUpdate = true; - String ringtone = NotificationPrefs.EMPTY_RINGTONE; - if (info.newAlert) { - quietUpdate = prefs.quietUpdate; + if (!notificationMgr.isNotificationDisplayed(notificationId)) { + int priorityVal = Notification.PRIORITY_DEFAULT; + if (highPriority) { + priorityVal = Notification.PRIORITY_HIGH; + } - // If we've already played a ringtone, don't play any more sounds so only - // 1 sound per group of notifications. - ringtone = prefs.getRingtoneAndSilence(); - } - addNotificationOptions(notification, quietUpdate, tickerText, - prefs.getDefaultVibrate(), ringtone, - true); /* Show the LED for these non-expired events */ + String tickerText = getTickerText(info.eventName, info.location); + NotificationWrapper notification = AlertReceiver.makeExpandingNotification(context, + info.eventName, summaryText, info.description, info.startMillis, + info.endMillis, info.eventId, notificationId, prefs.getDoPopup(), priorityVal); - // Post the notification. - notificationMgr.notify(notificationId, notification); + boolean quietUpdate = true; + String ringtone = NotificationPrefs.EMPTY_RINGTONE; + if (info.newAlert) { + quietUpdate = prefs.quietUpdate; - if (DEBUG) { - Log.d(TAG, "Posting individual alarm notification, eventId:" + info.eventId - + ", notificationId:" + notificationId - + (TextUtils.isEmpty(ringtone) ? ", quiet" : ", LOUD") - + (highPriority ? ", high-priority" : "")); + // If we've already played a ringtone, don't play any more sounds so only + // 1 sound per group of notifications. + ringtone = prefs.getRingtoneAndSilence(); + } + addNotificationOptions(notification, quietUpdate, tickerText, + prefs.getDefaultVibrate(), ringtone, + true); /* Show the LED for these non-expired events */ + + // Post the notification. + notificationMgr.notify(notificationId, notification); + + if (DEBUG) { + Log.d(TAG, "Posting individual alarm notification, eventId:" + info.eventId + + ", notificationId:" + notificationId + + (TextUtils.isEmpty(ringtone) ? ", quiet" : ", LOUD") + + (highPriority ? ", high-priority" : "")); + } } + return notificationId; } private static String getTickerText(String eventName, String location) { @@ -921,7 +930,7 @@ public class AlertService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null) { - + Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent.getExtras(); @@ -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 notifIds) { + final StatusBarNotification[] statusBarNotifs = mNm.getActiveNotifications(); + for (StatusBarNotification statusBarNotif : statusBarNotifs) { + final int statusBarNotifId = statusBarNotif.getId(); + if (!notifIds.contains(statusBarNotifId)) { + cancel(statusBarNotifId); + } + } + } } static class NotificationInfo { diff --git a/app/src/main/java/com/android/calendar/alerts/NotificationMgr.java b/app/src/main/java/com/android/calendar/alerts/NotificationMgr.java index 718f20321768e381f9ea4e780558ac8842681b0f..00e6c1dce94a4251a91b9bcb2d00e3fca847f913 100644 --- a/app/src/main/java/com/android/calendar/alerts/NotificationMgr.java +++ b/app/src/main/java/com/android/calendar/alerts/NotificationMgr.java @@ -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. @@ -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 notifIds); } diff --git a/settings.gradle b/settings.gradle index b2b9aba9073e6211abfb0caf20ff2b263e674d13..802eaf2285820aee749ddd7676c5e83c58242feb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,7 @@ dependencyResolutionManagement { google() mavenCentral() maven { url "https://jitpack.io" } + maven { url 'https://gitlab.e.foundation/api/v4/groups/9/-/packages/maven'} } } rootProject.name = "Etar-Calendar"