AlertService.java revision 9881907c47b2658fa85954bfb339c4b1eab9fc8e
1146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/* 2146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 3146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * 4146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * you may not use this file except in compliance with the License. 6146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * You may obtain a copy of the License at 7146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * 8146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * 10146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * See the License for the specific language governing permissions and 14146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * limitations under the License. 15146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project */ 16146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 1723e7da3eacee7bceb105cdfc7b5329c7a43846d5Mason Tangpackage com.android.calendar.alerts; 1823e7da3eacee7bceb105cdfc7b5329c7a43846d5Mason Tang 194b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawaimport com.android.calendar.GeneralPreferences; 2023e7da3eacee7bceb105cdfc7b5329c7a43846d5Mason Tangimport com.android.calendar.R; 21146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 22146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.AlarmManager; 23146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.Notification; 24146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.NotificationManager; 25146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.Service; 26146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentResolver; 27e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chanimport android.content.ContentUris; 28146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentValues; 29146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Context; 30146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Intent; 31146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.SharedPreferences; 32146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.database.Cursor; 331fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shumaimport android.media.AudioManager; 34146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.net.Uri; 35146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Bundle; 36146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Handler; 37146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.HandlerThread; 38146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.IBinder; 39146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Looper; 40146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Message; 41146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Process; 42fa292a0db2a6f04255c75a57908b17ba48a96183RoboErikimport android.provider.CalendarContract; 43a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Attendees; 44a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.CalendarAlerts; 45146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.TextUtils; 46fa292a0db2a6f04255c75a57908b17ba48a96183RoboErikimport android.text.format.DateUtils; 47146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.util.Log; 48e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 49e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chanimport java.util.HashMap; 50146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 51146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/** 52146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * This service is used to handle calendar event reminders. 53146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project */ 54146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectpublic class AlertService extends Service { 55f58faf32bbce943eb4791cef2ad6c327e3724cc7Michael Chan static final boolean DEBUG = true; 56146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final String TAG = "AlertService"; 570e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 58146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private volatile Looper mServiceLooper; 59146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private volatile ServiceHandler mServiceHandler; 600e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 610e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan private static final String[] ALERT_PROJECTION = new String[] { 62146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts._ID, // 0 63146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.EVENT_ID, // 1 64146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.STATE, // 2 65146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.TITLE, // 3 66146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.EVENT_LOCATION, // 4 67146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.SELF_ATTENDEE_STATUS, // 5 68146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.ALL_DAY, // 6 69146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.ALARM_TIME, // 7 70146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.MINUTES, // 8 71146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.BEGIN, // 9 72e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan CalendarAlerts.END, // 10 73146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project }; 740e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 75146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_ID = 0; 76146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_EVENT_ID = 1; 77146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_STATE = 2; 78146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_TITLE = 3; 79146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_EVENT_LOCATION = 4; 80146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_SELF_ATTENDEE_STATUS = 5; 81146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_ALL_DAY = 6; 82146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_ALARM_TIME = 7; 83146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_MINUTES = 8; 84146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_BEGIN = 9; 85e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final int ALERT_INDEX_END = 10; 86146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 87e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String ACTIVE_ALERTS_SELECTION = "(" + CalendarAlerts.STATE + "=? OR " 88e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + CalendarAlerts.STATE + "=?) AND " + CalendarAlerts.ALARM_TIME + "<="; 89146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 90e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String[] ACTIVE_ALERTS_SELECTION_ARGS = new String[] { 9143ffa461b738692925a94c005c90ff60757455a7RoboErik Integer.toString(CalendarAlerts.STATE_FIRED), 9243ffa461b738692925a94c005c90ff60757455a7RoboErik Integer.toString(CalendarAlerts.STATE_SCHEDULED) 93146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project }; 940e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 95e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC"; 960e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 9743ffa461b738692925a94c005c90ff60757455a7RoboErik private static final String DISMISS_OLD_SELECTION = CalendarAlerts.END + "<? AND " 9843ffa461b738692925a94c005c90ff60757455a7RoboErik + CalendarAlerts.STATE + "=?"; 9943ffa461b738692925a94c005c90ff60757455a7RoboErik 1008af2529989a9b10a0bb84736695c22fc02a17a4aThe Android Open Source Project void processMessage(Message msg) { 101146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Bundle bundle = (Bundle) msg.obj; 1020e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 103146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // On reboot, update the notification bar with the contents of the 104146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // CalendarAlerts table. 105146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project String action = bundle.getString("action"); 106e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 107fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.d(TAG, bundle.getLong(android.provider.CalendarContract.CalendarAlerts.ALARM_TIME) 108e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " Action = " + action); 109e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 110e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 111146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (action.equals(Intent.ACTION_BOOT_COMPLETED) 112146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project || action.equals(Intent.ACTION_TIME_CHANGED)) { 113146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project doTimeChanged(); 114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return; 115146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 116146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 117a27a886892fe3ec5edbc63c0b58e0a988623011aRoboErik if (!action.equals(android.provider.CalendarContract.ACTION_EVENT_REMINDER) 11843ffa461b738692925a94c005c90ff60757455a7RoboErik && !action.equals(Intent.ACTION_LOCALE_CHANGED) 11943ffa461b738692925a94c005c90ff60757455a7RoboErik && !action.equals(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)) { 120e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.w(TAG, "Invalid action: " + action); 121e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return; 122146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 12343ffa461b738692925a94c005c90ff60757455a7RoboErik if (action.equals(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)) { 12443ffa461b738692925a94c005c90ff60757455a7RoboErik dismissOldAlerts(this); 12543ffa461b738692925a94c005c90ff60757455a7RoboErik } 126e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan updateAlertNotification(this); 127e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 128605a0901134c0840b2fcf0514b4c1f8bc10dc7e0Michael Chan 12943ffa461b738692925a94c005c90ff60757455a7RoboErik static void dismissOldAlerts(Context context) { 13043ffa461b738692925a94c005c90ff60757455a7RoboErik ContentResolver cr = context.getContentResolver(); 13143ffa461b738692925a94c005c90ff60757455a7RoboErik final long currentTime = System.currentTimeMillis(); 13243ffa461b738692925a94c005c90ff60757455a7RoboErik ContentValues vals = new ContentValues(); 13343ffa461b738692925a94c005c90ff60757455a7RoboErik vals.put(CalendarAlerts.STATE, CalendarAlerts.STATE_DISMISSED); 13443ffa461b738692925a94c005c90ff60757455a7RoboErik cr.update(CalendarAlerts.CONTENT_URI, vals, DISMISS_OLD_SELECTION, new String[] { 13543ffa461b738692925a94c005c90ff60757455a7RoboErik Long.toString(currentTime), Integer.toString(CalendarAlerts.STATE_SCHEDULED) 13643ffa461b738692925a94c005c90ff60757455a7RoboErik }); 13743ffa461b738692925a94c005c90ff60757455a7RoboErik } 13843ffa461b738692925a94c005c90ff60757455a7RoboErik 139e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan static boolean updateAlertNotification(Context context) { 140e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentResolver cr = context.getContentResolver(); 141e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long currentTime = System.currentTimeMillis(); 14243ffa461b738692925a94c005c90ff60757455a7RoboErik SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 14343ffa461b738692925a94c005c90ff60757455a7RoboErik 14443ffa461b738692925a94c005c90ff60757455a7RoboErik boolean doAlert = prefs.getBoolean(GeneralPreferences.KEY_ALERTS, true); 14543ffa461b738692925a94c005c90ff60757455a7RoboErik boolean doPopup = prefs.getBoolean(GeneralPreferences.KEY_ALERTS_POPUP, false); 14643ffa461b738692925a94c005c90ff60757455a7RoboErik 14743ffa461b738692925a94c005c90ff60757455a7RoboErik if (!doAlert) { 14843ffa461b738692925a94c005c90ff60757455a7RoboErik if (DEBUG) { 14943ffa461b738692925a94c005c90ff60757455a7RoboErik Log.d(TAG, "alert preference is OFF"); 15043ffa461b738692925a94c005c90ff60757455a7RoboErik } 15143ffa461b738692925a94c005c90ff60757455a7RoboErik 15243ffa461b738692925a94c005c90ff60757455a7RoboErik // If we shouldn't be showing notifications cancel any existing ones 15343ffa461b738692925a94c005c90ff60757455a7RoboErik // and return. 15443ffa461b738692925a94c005c90ff60757455a7RoboErik NotificationManager nm = (NotificationManager) context 15543ffa461b738692925a94c005c90ff60757455a7RoboErik .getSystemService(Context.NOTIFICATION_SERVICE); 15643ffa461b738692925a94c005c90ff60757455a7RoboErik nm.cancelAll(); 15743ffa461b738692925a94c005c90ff60757455a7RoboErik return true; 15843ffa461b738692925a94c005c90ff60757455a7RoboErik } 15943ffa461b738692925a94c005c90ff60757455a7RoboErik 160fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Cursor alertCursor = cr.query(CalendarAlerts.CONTENT_URI, ALERT_PROJECTION, 161fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik (ACTIVE_ALERTS_SELECTION + currentTime), ACTIVE_ALERTS_SELECTION_ARGS, 162fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik ACTIVE_ALERTS_SORT); 1630e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 164e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor == null || alertCursor.getCount() == 0) { 165146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (alertCursor != null) { 166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project alertCursor.close(); 167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 168e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 169e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) Log.d(TAG, "No fired or scheduled alerts"); 170e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan NotificationManager nm = 171e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 1729881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson nm.cancel(AlertUtils.NOTIFICATION_ID); 173e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return false; 174146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1750e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 176e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 177e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alert count:" + alertCursor.getCount()); 178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1790e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 180e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan String notificationEventName = null; 181e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan String notificationEventLocation = null; 182e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan long notificationEventBegin = 0; 1839881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson long notificationEventEnd = 0; 1849881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson long notificationEventId = -1; 185e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int notificationEventStatus = 0; 1867321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan boolean notificationEventAllDay = true; 187e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan HashMap<Long, Long> eventIds = new HashMap<Long, Long>(); 188e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int numReminders = 0; 189e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int numFired = 0; 190146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project try { 191e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan while (alertCursor.moveToNext()) { 192e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alertId = alertCursor.getLong(ALERT_INDEX_ID); 193e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long eventId = alertCursor.getLong(ALERT_INDEX_EVENT_ID); 194e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int minutes = alertCursor.getInt(ALERT_INDEX_MINUTES); 195e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String eventName = alertCursor.getString(ALERT_INDEX_TITLE); 196e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String location = alertCursor.getString(ALERT_INDEX_EVENT_LOCATION); 197e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int status = alertCursor.getInt(ALERT_INDEX_SELF_ATTENDEE_STATUS); 198e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final boolean declined = status == Attendees.ATTENDEE_STATUS_DECLINED; 199e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long beginTime = alertCursor.getLong(ALERT_INDEX_BEGIN); 200e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long endTime = alertCursor.getLong(ALERT_INDEX_END); 201e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final Uri alertUri = ContentUris 202e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan .withAppendedId(CalendarAlerts.CONTENT_URI, alertId); 203e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alarmTime = alertCursor.getLong(ALERT_INDEX_ALARM_TIME); 204e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int state = alertCursor.getInt(ALERT_INDEX_STATE); 2057321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan final boolean allDay = alertCursor.getInt(ALERT_INDEX_ALL_DAY) != 0; 206e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 207e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 208e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alarmTime:" + alarmTime + " alertId:" + alertId 209e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " eventId:" + eventId + " state: " + state + " minutes:" + minutes 210e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " declined:" + declined + " beginTime:" + beginTime 211e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " endTime:" + endTime); 212146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2130e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 214e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentValues values = new ContentValues(); 215e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newState = -1; 216e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 217e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Uncomment for the behavior of clearing out alerts after the 218e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // events ended. b/1880369 219e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // 220e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // if (endTime < currentTime) { 221e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // newState = CalendarAlerts.DISMISSED; 222e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // } else 223e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 2243dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan // Remove declined events 2253dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan if (!declined) { 2263dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan // Don't count duplicate alerts for the same event 2273dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan if (eventIds.put(eventId, beginTime) == null) { 2283dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan numReminders++; 2293dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan } 2303dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan 231fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state == CalendarAlerts.STATE_SCHEDULED) { 232fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik newState = CalendarAlerts.STATE_FIRED; 233e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan numFired++; 234e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 235e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the received time in the CalendarAlerts table. 236e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is useful for finding bugs that cause alarms to be 237e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // missed or delayed. 238e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.RECEIVED_TIME, currentTime); 239e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 240e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } else { 241fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik newState = CalendarAlerts.STATE_DISMISSED; 242146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 244e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Update row if state changed 245e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (newState != -1) { 246e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.STATE, newState); 247e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan state = newState; 248146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2490e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 250fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state == CalendarAlerts.STATE_FIRED) { 251e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the time posting to notification manager. 252e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is used for debugging missed alarms. 253e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.NOTIFY_TIME, currentTime); 254146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2550e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 256e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Write row to if anything changed 257e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (values.size() > 0) cr.update(alertUri, values, null, null); 2580e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 259fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state != CalendarAlerts.STATE_FIRED) { 260e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan continue; 261e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 263e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Pick an Event title for the notification panel by the latest 264e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // alertTime and give prefer accepted events in case of ties. 265e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newStatus; 266e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan switch (status) { 267e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_ACCEPTED: 268e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 2; 269e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 270e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_TENTATIVE: 271e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 1; 272e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 273e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan default: 274e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 0; 275e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 276e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 277e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // TODO Prioritize by "primary" calendar 278e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Assumes alerts are sorted by begin time in reverse 279e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (notificationEventName == null 280e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan || (notificationEventBegin <= beginTime && 281e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventStatus < newStatus)) { 282e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventLocation = location; 283e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventBegin = beginTime; 2849881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson notificationEventEnd = endTime; 2859881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson notificationEventId = eventId; 286e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventStatus = newStatus; 2877321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan notificationEventAllDay = allDay; 288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2899881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson if (numReminders == 1) { 2909881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson notificationEventName = eventName; 2919881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson } else { 2929881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson notificationEventName = eventName + ", " + notificationEventName; 2939881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson } 294146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 295146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } finally { 296e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor != null) { 297e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan alertCursor.close(); 298e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 299146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3000e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 301ccf4a12bc3b25c46d592d3f9116f52751b96010cMason Tang boolean quietUpdate = numFired == 0; 302cca9ecb23b079c47856af22f89f7a6f3dec7a492Mason Tang boolean highPriority = numFired > 0 && doPopup; 303e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan postNotification(context, prefs, notificationEventName, notificationEventLocation, 3047321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan numReminders, quietUpdate, highPriority, notificationEventBegin, 3059881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson notificationEventEnd, notificationEventId, notificationEventAllDay); 306e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 307e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return true; 308e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 309e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 310e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static void postNotification(Context context, SharedPreferences prefs, 3119881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson String eventName, String location, int numReminders, boolean quietUpdate, 3129881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson boolean highPriority, long startMillis, long endMillis, long id, boolean allDay) { 313e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 314e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "###### creating new alarm notification, numReminders: " + numReminders 315c9656c9e42d9bb640688648b9dfe8d9f4e16b47dMason Tang + (quietUpdate ? " QUIET" : " loud") 316c9656c9e42d9bb640688648b9dfe8d9f4e16b47dMason Tang + (highPriority ? " high-priority" : "")); 317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3180e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 3190e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan NotificationManager nm = 320e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 322e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (numReminders == 0) { 3239881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson nm.cancel(AlertUtils.NOTIFICATION_ID); 324e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return; 325146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3260e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 327e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Notification notification = AlertReceiver.makeNewAlertNotification(context, eventName, 3289881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson location, numReminders, highPriority, startMillis, endMillis, id, allDay); 329e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.defaults |= Notification.DEFAULT_LIGHTS; 330e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 331e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Quietly update notification bar. Nothing new. Maybe something just got deleted. 332e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!quietUpdate) { 333e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Flash ticker in status bar 334e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.tickerText = eventName; 335e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!TextUtils.isEmpty(location)) { 336e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.tickerText = eventName + " - " + location; 337e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 338e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 339e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Generate either a pop-up dialog, status bar notification, or 340e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // neither. Pop-up dialog and status bar notification may include a 341e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // sound, an alert, or both. A status bar notification also includes 342e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // a toast. 3431fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma 3441fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // Find out the circumstances under which to vibrate. 3451fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // Migrate from pre-Froyo boolean setting if necessary. 3461fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma String vibrateWhen; // "always" or "silent" or "never" 3474b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa if(prefs.contains(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN)) 3481fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma { 3491fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // Look up Froyo setting 3501fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma vibrateWhen = 3514b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa prefs.getString(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN, null); 3524b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa } else if(prefs.contains(GeneralPreferences.KEY_ALERTS_VIBRATE)) { 3531fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // No Froyo setting. Migrate pre-Froyo setting to new Froyo-defined value. 3541fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean vibrate = 3554b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa prefs.getBoolean(GeneralPreferences.KEY_ALERTS_VIBRATE, false); 3561fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma vibrateWhen = vibrate ? 3571fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma context.getString(R.string.prefDefault_alerts_vibrate_true) : 3581fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma context.getString(R.string.prefDefault_alerts_vibrate_false); 3591fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma } else { 3601fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // No setting. Use Froyo-defined default. 3611fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma vibrateWhen = context.getString(R.string.prefDefault_alerts_vibrateWhen); 3621fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma } 3631fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean vibrateAlways = vibrateWhen.equals("always"); 3641fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean vibrateSilent = vibrateWhen.equals("silent"); 3651fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma AudioManager audioManager = 3661fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 3671fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean nowSilent = 3681fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE; 369e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 370e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Possibly generate a vibration 3711fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma if (vibrateAlways || (vibrateSilent && nowSilent)) { 372e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.defaults |= Notification.DEFAULT_VIBRATE; 373e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 3740e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 3750e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan // Possibly generate a sound. If 'Silent' is chosen, the ringtone 3760e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan // string will be empty. 3770e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan String reminderRingtone = prefs.getString( 3784b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa GeneralPreferences.KEY_ALERTS_RINGTONE, null); 3790e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan notification.sound = TextUtils.isEmpty(reminderRingtone) ? null : Uri 3800e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan .parse(reminderRingtone); 381146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 382146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 3839881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson nm.notify(AlertUtils.NOTIFICATION_ID, notification); 384146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3850e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private void doTimeChanged() { 387146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project ContentResolver cr = getContentResolver(); 388146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Object service = getSystemService(Context.ALARM_SERVICE); 389146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlarmManager manager = (AlarmManager) service; 390fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // TODO Move this into Provider 391fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik rescheduleMissedAlarms(cr, this, manager); 392e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan updateAlertNotification(this); 393146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3940e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 395fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik private static final String SORT_ORDER_ALARMTIME_ASC = 396fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.ALARM_TIME + " ASC"; 397fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 398fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik private static final String WHERE_RESCHEDULE_MISSED_ALARMS = 399fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.STATE 400fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + "=" 401fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.STATE_SCHEDULED 402fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 403fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.ALARM_TIME 404fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + "<?" 405fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 406fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.ALARM_TIME 407fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + ">?" 408fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 409fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.END + ">=?"; 410fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 411fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik /** 412fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * Searches the CalendarAlerts table for alarms that should have fired but 413fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * have not and then reschedules them. This method can be called at boot 414fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * time to restore alarms that may have been lost due to a phone reboot. 415fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * 416fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param cr the ContentResolver 417fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param context the Context 418fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param manager the AlarmManager 419fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik */ 420fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik public static final void rescheduleMissedAlarms(ContentResolver cr, Context context, 421fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik AlarmManager manager) { 422fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // Get all the alerts that have been scheduled but have not fired 423fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // and should have fired by now and are not too old. 424fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long now = System.currentTimeMillis(); 425fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long ancient = now - DateUtils.DAY_IN_MILLIS; 426fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik String[] projection = new String[] { 427fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.ALARM_TIME, 428fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik }; 429fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 430fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // TODO: construct an explicit SQL query so that we can add 431fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // "GROUPBY" instead of doing a sort and de-dup 432fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection, 433fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] { 434fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Long.toString(now), Long.toString(ancient), Long.toString(now) 435fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik }), SORT_ORDER_ALARMTIME_ASC); 436fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (cursor == null) { 437fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik return; 438fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 439fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 440fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (DEBUG) { 441fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.d(TAG, "missed alarms found: " + cursor.getCount()); 442fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 443fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 444fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik try { 445fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long alarmTime = -1; 446fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 447fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik while (cursor.moveToNext()) { 448fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long newAlarmTime = cursor.getLong(0); 449fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (alarmTime != newAlarmTime) { 450fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (DEBUG) { 451fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.w(TAG, "rescheduling missed alarm. alarmTime: " + newAlarmTime); 452fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 4539881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson AlertUtils.scheduleAlarm(context, manager, newAlarmTime); 454fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik alarmTime = newAlarmTime; 455fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 456fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 457fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } finally { 458fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik cursor.close(); 459fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 460fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 461fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 462146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private final class ServiceHandler extends Handler { 463146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public ServiceHandler(Looper looper) { 464146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project super(looper); 465146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 4660e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 467146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 468146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void handleMessage(Message msg) { 469146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project processMessage(msg); 470146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // NOTE: We MUST not call stopSelf() directly, since we need to 471146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // make sure the wake lock acquired by AlertReceiver is released. 472146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlertReceiver.finishStartingService(AlertService.this, msg.arg1); 4730e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan } 474e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 475146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 476146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 477146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onCreate() { 478146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project HandlerThread thread = new HandlerThread("AlertService", 479146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Process.THREAD_PRIORITY_BACKGROUND); 480146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project thread.start(); 4810e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 482146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper = thread.getLooper(); 483146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceHandler = new ServiceHandler(mServiceLooper); 484146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 485146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 486146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 487c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff public int onStartCommand(Intent intent, int flags, int startId) { 488c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff if (intent != null) { 489c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff Message msg = mServiceHandler.obtainMessage(); 490c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.arg1 = startId; 491c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.obj = intent.getExtras(); 492c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff mServiceHandler.sendMessage(msg); 493c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff } 494c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff return START_REDELIVER_INTENT; 495146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 496146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 497146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 498146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onDestroy() { 499146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper.quit(); 500146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 501146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 502146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 503146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public IBinder onBind(Intent intent) { 504146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return null; 505146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 506146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project} 507