summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSara Ting <sarating@google.com>2012-09-11 16:17:11 -0700
committerAndroid Git Automerger <android-git-automerger@android.com>2012-09-11 16:17:11 -0700
commitc4a5fab28fcc7c3e9b2bc7cee379d2cd8a42fc9b (patch)
tree9555938f1b33b1744d7748997049bf066ac0ef04 /src
parent7a17c9530026c3b018cc0be7455858a3f806ad91 (diff)
parente472146a2a1fa8663455ef396ac23dfd19045f6a (diff)
downloadandroid_packages_apps_Calendar-c4a5fab28fcc7c3e9b2bc7cee379d2cd8a42fc9b.tar.gz
android_packages_apps_Calendar-c4a5fab28fcc7c3e9b2bc7cee379d2cd8a42fc9b.tar.bz2
android_packages_apps_Calendar-c4a5fab28fcc7c3e9b2bc7cee379d2cd8a42fc9b.zip
am e472146a: Merge "Stored recently fired alerts in SharedPrefs so multiple calendar apps can coexist without eating each other\'s alerts." into ics-ub-calendar-aqua
* commit 'e472146a2a1fa8663455ef396ac23dfd19045f6a': Stored recently fired alerts in SharedPrefs so multiple calendar apps can coexist without eating each other's alerts.
Diffstat (limited to 'src')
-rw-r--r--src/com/android/calendar/alerts/AlertService.java47
-rw-r--r--src/com/android/calendar/alerts/AlertUtils.java125
2 files changed, 167 insertions, 5 deletions
diff --git a/src/com/android/calendar/alerts/AlertService.java b/src/com/android/calendar/alerts/AlertService.java
index 9d75217b..9811dabf 100644
--- a/src/com/android/calendar/alerts/AlertService.java
+++ b/src/com/android/calendar/alerts/AlertService.java
@@ -363,6 +363,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;
}
@@ -500,11 +503,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();
@@ -520,7 +549,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;
@@ -538,6 +567,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) {
@@ -945,6 +979,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 cb5a33ca..f68f5224 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;
+
/**
* Creates an AlarmManagerInterface that wraps a real AlarmManager. The alarm code
* was abstracted to an interface to make it testable.
@@ -195,4 +222,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;
+ }
}