summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSara Ting <sarating@google.com>2012-09-11 16:16:27 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-09-11 16:16:27 -0700
commite472146a2a1fa8663455ef396ac23dfd19045f6a (patch)
treee8e9c17e6b0896e91a6c205277159aa07afac725 /src
parenta498f6ed3628bd36576949acd8e0afd71fca8b02 (diff)
parentd1fa3c0e0a8f45538c867c4f56bb629e1219ed23 (diff)
downloadandroid_packages_apps_Calendar-e472146a2a1fa8663455ef396ac23dfd19045f6a.tar.gz
android_packages_apps_Calendar-e472146a2a1fa8663455ef396ac23dfd19045f6a.tar.bz2
android_packages_apps_Calendar-e472146a2a1fa8663455ef396ac23dfd19045f6a.zip
Merge "Stored recently fired alerts in SharedPrefs so multiple calendar apps can coexist without eating each other's alerts." into ics-ub-calendar-aqua
Diffstat (limited to 'src')
-rw-r--r--src/com/android/calendar/alerts/AlertReceiver.java2
-rw-r--r--src/com/android/calendar/alerts/AlertService.java47
-rw-r--r--src/com/android/calendar/alerts/AlertUtils.java125
3 files changed, 168 insertions, 6 deletions
diff --git a/src/com/android/calendar/alerts/AlertReceiver.java b/src/com/android/calendar/alerts/AlertReceiver.java
index 16c42b50..b8c38770 100644
--- a/src/com/android/calendar/alerts/AlertReceiver.java
+++ b/src/com/android/calendar/alerts/AlertReceiver.java
@@ -256,7 +256,7 @@ public class AlertReceiver extends BroadcastReceiver {
// Turn off timestamp.
notificationBuilder.setWhen(0);
-
+
if (Utils.isJellybeanOrLater()) {
// Setting to a higher priority will encourage notification manager to expand the
// notification.
diff --git a/src/com/android/calendar/alerts/AlertService.java b/src/com/android/calendar/alerts/AlertService.java
index 9851e19a..ac1ca26d 100644
--- a/src/com/android/calendar/alerts/AlertService.java
+++ b/src/com/android/calendar/alerts/AlertService.java
@@ -362,6 +362,9 @@ public class AlertService extends Service {
Log.e(TAG, "Illegal state: next notification refresh time found to be in the past.");
}
+ // Flushes old fired alerts from internal storage, if needed.
+ AlertUtils.flushOldAlertsFromInternalStorage(context);
+
return true;
}
@@ -490,11 +493,37 @@ public class AlertService extends Service {
int state = alertCursor.getInt(ALERT_INDEX_STATE);
final boolean allDay = alertCursor.getInt(ALERT_INDEX_ALL_DAY) != 0;
+ // Use app local storage to keep track of fired alerts to fix problem of multiple
+ // installed calendar apps potentially causing missed alarms.
+ boolean newAlertOverride = false;
+ String alertIdStr = Long.toString(alertId);
+ if (AlertUtils.BYPASS_DB && ((currentTime - alarmTime) / MINUTE_MS < 1)) {
+ // To avoid re-firing alerts, only fire if alarmTime is very recent. Otherwise
+ // we can get refires for non-dismissed alerts after app installation, or if the
+ // SharedPrefs was cleared too early. This means alerts that were timed while
+ // the phone was off may show up silently in the notification bar.
+ boolean alreadyFired = AlertUtils.hasAlertFiredInSharedPrefs(context, eventId,
+ beginTime, alarmTime);
+ if (!alreadyFired) {
+ newAlertOverride = true;
+ }
+ }
+
if (DEBUG) {
- Log.d(TAG, "alertCursor result: alarmTime:" + alarmTime + " alertId:" + alertId
- + " eventId:" + eventId + " state: " + state + " minutes:" + minutes
- + " declined:" + declined + " beginTime:" + beginTime
- + " endTime:" + endTime + " allDay:" + allDay);
+ StringBuilder msgBuilder = new StringBuilder();
+ msgBuilder.append("alertCursor result: alarmTime:").append(alarmTime)
+ .append(" alertId:").append(alertId)
+ .append(" eventId:").append(eventId)
+ .append(" state: ").append(state)
+ .append(" minutes:").append(minutes)
+ .append(" declined:").append(declined)
+ .append(" beginTime:").append(beginTime)
+ .append(" endTime:").append(endTime)
+ .append(" allDay:").append(allDay);
+ if (AlertUtils.BYPASS_DB) {
+ msgBuilder.append(" newAlertOverride: " + newAlertOverride);
+ }
+ Log.d(TAG, msgBuilder.toString());
}
ContentValues values = new ContentValues();
@@ -510,7 +539,7 @@ public class AlertService extends Service {
// Remove declined events
if (!declined) {
- if (state == CalendarAlerts.STATE_SCHEDULED) {
+ if (state == CalendarAlerts.STATE_SCHEDULED || newAlertOverride) {
newState = CalendarAlerts.STATE_FIRED;
numFired++;
newAlert = true;
@@ -528,6 +557,11 @@ public class AlertService extends Service {
if (newState != -1) {
values.put(CalendarAlerts.STATE, newState);
state = newState;
+
+ if (AlertUtils.BYPASS_DB) {
+ AlertUtils.setAlertFiredInSharedPrefs(context, eventId, beginTime,
+ alarmTime);
+ }
}
if (state == CalendarAlerts.STATE_FIRED) {
@@ -930,6 +964,9 @@ public class AlertService extends Service {
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
+
+ // Flushes old fired alerts from internal storage, if needed.
+ AlertUtils.flushOldAlertsFromInternalStorage(getApplication());
}
@Override
diff --git a/src/com/android/calendar/alerts/AlertUtils.java b/src/com/android/calendar/alerts/AlertUtils.java
index 1777a2d4..9f160c6b 100644
--- a/src/com/android/calendar/alerts/AlertUtils.java
+++ b/src/com/android/calendar/alerts/AlertUtils.java
@@ -22,6 +22,7 @@ import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.net.Uri;
import android.provider.CalendarContract;
import android.provider.CalendarContract.CalendarAlerts;
@@ -29,15 +30,19 @@ import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.format.Time;
+import android.util.Log;
import com.android.calendar.EventInfoActivity;
import com.android.calendar.R;
import com.android.calendar.Utils;
import java.util.Locale;
+import java.util.Map;
import java.util.TimeZone;
public class AlertUtils {
+ private static final String TAG = "AlertUtils";
+ static final boolean DEBUG = true;
public static final long SNOOZE_DELAY = 5 * 60 * 1000L;
@@ -52,6 +57,28 @@ public class AlertUtils {
public static final String NOTIFICATION_ID_KEY = "notificationid";
public static final String EVENT_IDS_KEY = "eventids";
+ // A flag for using local storage to save alert state instead of the alerts DB table.
+ // This allows the unbundled app to run alongside other calendar apps without eating
+ // alerts from other apps.
+ static boolean BYPASS_DB = true;
+
+ // SharedPrefs table name for storing fired alerts. This prevents other installed
+ // Calendar apps from eating the alerts.
+ private static final String ALERTS_SHARED_PREFS_NAME = "calendar_alerts";
+
+ // Keyname prefix for the alerts data in SharedPrefs. The key will contain a combo
+ // of event ID, begin time, and alarm time. The value will be the fired time.
+ private static final String KEY_FIRED_ALERT_PREFIX = "preference_alert_";
+
+ // The last time the SharedPrefs was scanned and flushed of old alerts data.
+ private static final String KEY_LAST_FLUSH_TIME_MS = "preference_flushTimeMs";
+
+ // The # of days to save alert states in the shared prefs table, before flushing. This
+ // can be any value, since AlertService will also check for a recent alertTime before
+ // ringing the alert.
+ private static final int FLUSH_INTERVAL_DAYS = 1;
+ private static final int FLUSH_INTERVAL_MS = FLUSH_INTERVAL_DAYS * 24 * 60 * 60 * 1000;
+
/**
* Schedules an alarm intent with the system AlarmManager that will notify
* listeners when a reminder should be fired. The provider will keep
@@ -184,4 +211,102 @@ public class AlertUtils {
return i;
}
+ public static SharedPreferences getFiredAlertsTable(Context context) {
+ return context.getSharedPreferences(ALERTS_SHARED_PREFS_NAME, Context.MODE_PRIVATE);
+ }
+
+ private static String getFiredAlertsKey(long eventId, long beginTime,
+ long alarmTime) {
+ StringBuilder sb = new StringBuilder(KEY_FIRED_ALERT_PREFIX);
+ sb.append(eventId);
+ sb.append("_");
+ sb.append(beginTime);
+ sb.append("_");
+ sb.append(alarmTime);
+ return sb.toString();
+ }
+
+ /**
+ * Returns whether the SharedPrefs storage indicates we have fired the alert before.
+ */
+ static boolean hasAlertFiredInSharedPrefs(Context context, long eventId, long beginTime,
+ long alarmTime) {
+ SharedPreferences prefs = getFiredAlertsTable(context);
+ return prefs.contains(getFiredAlertsKey(eventId, beginTime, alarmTime));
+ }
+
+ /**
+ * Store fired alert info in the SharedPrefs.
+ */
+ static void setAlertFiredInSharedPrefs(Context context, long eventId, long beginTime,
+ long alarmTime) {
+ // Store alarm time as the value too so we don't have to parse all the keys to flush
+ // old alarms out of the table later.
+ SharedPreferences prefs = getFiredAlertsTable(context);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putLong(getFiredAlertsKey(eventId, beginTime, alarmTime), alarmTime);
+ editor.apply();
+ }
+
+ /**
+ * Scans and flushes the internal storage of old alerts. Looks up the previous flush
+ * time in SharedPrefs, and performs the flush if overdue. Otherwise, no-op.
+ */
+ static void flushOldAlertsFromInternalStorage(Context context) {
+ if (BYPASS_DB) {
+ SharedPreferences prefs = getFiredAlertsTable(context);
+
+ // Only flush if it hasn't been done in a while.
+ long nowTime = System.currentTimeMillis();
+ long lastFlushTimeMs = prefs.getLong(KEY_LAST_FLUSH_TIME_MS, 0);
+ if (nowTime - lastFlushTimeMs > FLUSH_INTERVAL_MS) {
+ if (DEBUG) {
+ Log.d(TAG, "Flushing old alerts from shared prefs table");
+ }
+
+ // Scan through all fired alert entries, removing old ones.
+ SharedPreferences.Editor editor = prefs.edit();
+ Time timeObj = new Time();
+ for (Map.Entry<String, ?> entry : prefs.getAll().entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (key.startsWith(KEY_FIRED_ALERT_PREFIX)) {
+ long alertTime;
+ if (value instanceof Long) {
+ alertTime = (Long) value;
+ } else {
+ // Should never occur.
+ Log.e(TAG,"SharedPrefs key " + key + " did not have Long value: " +
+ value);
+ continue;
+ }
+
+ if (nowTime - alertTime >= FLUSH_INTERVAL_MS) {
+ editor.remove(key);
+ if (DEBUG) {
+ int ageInDays = getIntervalInDays(alertTime, nowTime, timeObj);
+ Log.d(TAG, "SharedPrefs key " + key + ": removed (" + ageInDays +
+ " days old)");
+ }
+ } else {
+ if (DEBUG) {
+ int ageInDays = getIntervalInDays(alertTime, nowTime, timeObj);
+ Log.d(TAG, "SharedPrefs key " + key + ": keep (" + ageInDays +
+ " days old)");
+ }
+ }
+ }
+ }
+ editor.putLong(KEY_LAST_FLUSH_TIME_MS, nowTime);
+ editor.apply();
+ }
+ }
+ }
+
+ private static int getIntervalInDays(long startMillis, long endMillis, Time timeObj) {
+ timeObj.set(startMillis);
+ int startDay = Time.getJulianDay(startMillis, timeObj.gmtoff);
+ timeObj.set(endMillis);
+ return Time.getJulianDay(endMillis, timeObj.gmtoff) - startDay;
+ }
}