AlertService.java revision 7e4c339fb171f542ff8d5d4916c649f9597aa926
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 494e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Tingimport java.util.ArrayList; 50e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chanimport java.util.HashMap; 51146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 52146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/** 53146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * This service is used to handle calendar event reminders. 54146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project */ 55146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectpublic class AlertService extends Service { 56f58faf32bbce943eb4791cef2ad6c327e3724cc7Michael Chan static final boolean DEBUG = true; 57146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final String TAG = "AlertService"; 580e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 59146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private volatile Looper mServiceLooper; 60146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private volatile ServiceHandler mServiceHandler; 610e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 620e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan private static final String[] ALERT_PROJECTION = new String[] { 63146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts._ID, // 0 64146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.EVENT_ID, // 1 65146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.STATE, // 2 66146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.TITLE, // 3 67146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.EVENT_LOCATION, // 4 68146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.SELF_ATTENDEE_STATUS, // 5 69146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.ALL_DAY, // 6 70146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.ALARM_TIME, // 7 71146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.MINUTES, // 8 72146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.BEGIN, // 9 73e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan CalendarAlerts.END, // 10 7442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting CalendarAlerts.DESCRIPTION, // 11 75146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project }; 760e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 77146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_ID = 0; 78146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_EVENT_ID = 1; 79146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_STATE = 2; 80146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_TITLE = 3; 81146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_EVENT_LOCATION = 4; 82146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_SELF_ATTENDEE_STATUS = 5; 83146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_ALL_DAY = 6; 84146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_ALARM_TIME = 7; 85146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_MINUTES = 8; 86146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_BEGIN = 9; 87e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final int ALERT_INDEX_END = 10; 8842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting private static final int ALERT_INDEX_DESCRIPTION = 11; 89146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 90e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String ACTIVE_ALERTS_SELECTION = "(" + CalendarAlerts.STATE + "=? OR " 91e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + CalendarAlerts.STATE + "=?) AND " + CalendarAlerts.ALARM_TIME + "<="; 92146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 93e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String[] ACTIVE_ALERTS_SELECTION_ARGS = new String[] { 9443ffa461b738692925a94c005c90ff60757455a7RoboErik Integer.toString(CalendarAlerts.STATE_FIRED), 9543ffa461b738692925a94c005c90ff60757455a7RoboErik Integer.toString(CalendarAlerts.STATE_SCHEDULED) 96146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project }; 970e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 98e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC"; 990e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 10043ffa461b738692925a94c005c90ff60757455a7RoboErik private static final String DISMISS_OLD_SELECTION = CalendarAlerts.END + "<? AND " 10143ffa461b738692925a94c005c90ff60757455a7RoboErik + CalendarAlerts.STATE + "=?"; 10243ffa461b738692925a94c005c90ff60757455a7RoboErik 10360edea82999b3a4d9a2c29d04c7ea611c86f4b78Sara Ting // The grace period before changing a notification's priority bucket. 10460edea82999b3a4d9a2c29d04c7ea611c86f4b78Sara Ting private static final int DEPRIORITIZE_GRACE_PERIOD_MS = 15 * 60 * 1000; 10560edea82999b3a4d9a2c29d04c7ea611c86f4b78Sara Ting 1068af2529989a9b10a0bb84736695c22fc02a17a4aThe Android Open Source Project void processMessage(Message msg) { 107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Bundle bundle = (Bundle) msg.obj; 1080e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 109146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // On reboot, update the notification bar with the contents of the 110146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // CalendarAlerts table. 111146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project String action = bundle.getString("action"); 112e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 113fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.d(TAG, bundle.getLong(android.provider.CalendarContract.CalendarAlerts.ALARM_TIME) 114e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " Action = " + action); 115e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 116e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 117146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (action.equals(Intent.ACTION_BOOT_COMPLETED) 118146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project || action.equals(Intent.ACTION_TIME_CHANGED)) { 119146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project doTimeChanged(); 120146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return; 121146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 122146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 123a27a886892fe3ec5edbc63c0b58e0a988623011aRoboErik if (!action.equals(android.provider.CalendarContract.ACTION_EVENT_REMINDER) 12443ffa461b738692925a94c005c90ff60757455a7RoboErik && !action.equals(Intent.ACTION_LOCALE_CHANGED) 12543ffa461b738692925a94c005c90ff60757455a7RoboErik && !action.equals(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)) { 126e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.w(TAG, "Invalid action: " + action); 127e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return; 128146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 12943ffa461b738692925a94c005c90ff60757455a7RoboErik if (action.equals(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)) { 13043ffa461b738692925a94c005c90ff60757455a7RoboErik dismissOldAlerts(this); 13143ffa461b738692925a94c005c90ff60757455a7RoboErik } 13242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting 13342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting if (action.equals(android.provider.CalendarContract.ACTION_EVENT_REMINDER) && 13442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting bundle.getBoolean(AlertUtils.QUIET_UPDATE_KEY)) { 13542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting updateAlertNotification(this, true); 13642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } else { 13742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting updateAlertNotification(this, false); 13842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } 139e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 140605a0901134c0840b2fcf0514b4c1f8bc10dc7e0Michael Chan 14143ffa461b738692925a94c005c90ff60757455a7RoboErik static void dismissOldAlerts(Context context) { 14243ffa461b738692925a94c005c90ff60757455a7RoboErik ContentResolver cr = context.getContentResolver(); 14343ffa461b738692925a94c005c90ff60757455a7RoboErik final long currentTime = System.currentTimeMillis(); 14443ffa461b738692925a94c005c90ff60757455a7RoboErik ContentValues vals = new ContentValues(); 14543ffa461b738692925a94c005c90ff60757455a7RoboErik vals.put(CalendarAlerts.STATE, CalendarAlerts.STATE_DISMISSED); 14643ffa461b738692925a94c005c90ff60757455a7RoboErik cr.update(CalendarAlerts.CONTENT_URI, vals, DISMISS_OLD_SELECTION, new String[] { 14743ffa461b738692925a94c005c90ff60757455a7RoboErik Long.toString(currentTime), Integer.toString(CalendarAlerts.STATE_SCHEDULED) 14843ffa461b738692925a94c005c90ff60757455a7RoboErik }); 14943ffa461b738692925a94c005c90ff60757455a7RoboErik } 15043ffa461b738692925a94c005c90ff60757455a7RoboErik 15142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting static boolean updateAlertNotification(Context context, boolean quietUpdate) { 152e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentResolver cr = context.getContentResolver(); 1534e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting NotificationManager nm = 1544e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 155e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long currentTime = System.currentTimeMillis(); 15643ffa461b738692925a94c005c90ff60757455a7RoboErik SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 15743ffa461b738692925a94c005c90ff60757455a7RoboErik 15843ffa461b738692925a94c005c90ff60757455a7RoboErik boolean doAlert = prefs.getBoolean(GeneralPreferences.KEY_ALERTS, true); 15943ffa461b738692925a94c005c90ff60757455a7RoboErik if (!doAlert) { 16043ffa461b738692925a94c005c90ff60757455a7RoboErik if (DEBUG) { 16143ffa461b738692925a94c005c90ff60757455a7RoboErik Log.d(TAG, "alert preference is OFF"); 16243ffa461b738692925a94c005c90ff60757455a7RoboErik } 16343ffa461b738692925a94c005c90ff60757455a7RoboErik 16443ffa461b738692925a94c005c90ff60757455a7RoboErik // If we shouldn't be showing notifications cancel any existing ones 16543ffa461b738692925a94c005c90ff60757455a7RoboErik // and return. 16643ffa461b738692925a94c005c90ff60757455a7RoboErik nm.cancelAll(); 16743ffa461b738692925a94c005c90ff60757455a7RoboErik return true; 16843ffa461b738692925a94c005c90ff60757455a7RoboErik } 16943ffa461b738692925a94c005c90ff60757455a7RoboErik 170fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Cursor alertCursor = cr.query(CalendarAlerts.CONTENT_URI, ALERT_PROJECTION, 171fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik (ACTIVE_ALERTS_SELECTION + currentTime), ACTIVE_ALERTS_SELECTION_ARGS, 172fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik ACTIVE_ALERTS_SORT); 1730e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 174e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor == null || alertCursor.getCount() == 0) { 175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (alertCursor != null) { 176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project alertCursor.close(); 177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 178e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 179e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) Log.d(TAG, "No fired or scheduled alerts"); 18042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting nm.cancelAll(); 181e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return false; 182146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1830e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 184e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 185e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alert count:" + alertCursor.getCount()); 186146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1870e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 18876180232681f9a3091fda763e364ee2e40765c9bSara Ting // Process the query results and bucketize events. 189d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting ArrayList<NotificationInfo> highPriorityEvents = new ArrayList<NotificationInfo>(); 190d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting ArrayList<NotificationInfo> mediumPriorityEvents = new ArrayList<NotificationInfo>(); 19142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting ArrayList<NotificationInfo> expiredEvents = new ArrayList<NotificationInfo>(); 19276180232681f9a3091fda763e364ee2e40765c9bSara Ting StringBuilder expiredDigestTitleBuilder = new StringBuilder(); 193d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting int numFired = processQuery(alertCursor, cr, currentTime, highPriorityEvents, 194d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting mediumPriorityEvents, expiredEvents, expiredDigestTitleBuilder); 19576180232681f9a3091fda763e364ee2e40765c9bSara Ting String expiredDigestTitle = expiredDigestTitleBuilder.toString(); 19676180232681f9a3091fda763e364ee2e40765c9bSara Ting 197d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting if (highPriorityEvents.size() + mediumPriorityEvents.size() + expiredEvents.size() == 0) { 19876180232681f9a3091fda763e364ee2e40765c9bSara Ting nm.cancelAll(); 19976180232681f9a3091fda763e364ee2e40765c9bSara Ting return true; 20076180232681f9a3091fda763e364ee2e40765c9bSara Ting } 20176180232681f9a3091fda763e364ee2e40765c9bSara Ting 20276180232681f9a3091fda763e364ee2e40765c9bSara Ting quietUpdate = quietUpdate || (numFired == 0); 20376180232681f9a3091fda763e364ee2e40765c9bSara Ting boolean doPopup = numFired > 0 && 20476180232681f9a3091fda763e364ee2e40765c9bSara Ting prefs.getBoolean(GeneralPreferences.KEY_ALERTS_POPUP, false); 20576180232681f9a3091fda763e364ee2e40765c9bSara Ting boolean defaultVibrate = shouldUseDefaultVibrate(context, prefs); 20676180232681f9a3091fda763e364ee2e40765c9bSara Ting String ringtone = quietUpdate ? null : prefs.getString( 20776180232681f9a3091fda763e364ee2e40765c9bSara Ting GeneralPreferences.KEY_ALERTS_RINGTONE, null); 2087e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting boolean notificationPosted = false; 20976180232681f9a3091fda763e364ee2e40765c9bSara Ting long nextRefreshTime = Long.MAX_VALUE; 21076180232681f9a3091fda763e364ee2e40765c9bSara Ting 211d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting // Post the individual higher priority events (future and recently started 212d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting // concurrent events). Order these so that earlier start times appear higher in 213d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting // the notification list. 214d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting for (NotificationInfo info : highPriorityEvents) { 21576180232681f9a3091fda763e364ee2e40765c9bSara Ting String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, 21676180232681f9a3091fda763e364ee2e40765c9bSara Ting info.allDay, info.location); 21776180232681f9a3091fda763e364ee2e40765c9bSara Ting postNotification(info, summaryText, context, quietUpdate, doPopup, defaultVibrate, 2187e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting ringtone, true, notificationPosted, nm); 2197e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting notificationPosted = true; 22076180232681f9a3091fda763e364ee2e40765c9bSara Ting } 22176180232681f9a3091fda763e364ee2e40765c9bSara Ting 222d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting // Post the medium priority events (concurrent events that started a while ago). 223d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting // Order these so more recent start times appear higher in the notification list. 224d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting for (int i = mediumPriorityEvents.size() - 1; i >= 0; i--) { 225d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting NotificationInfo info = mediumPriorityEvents.get(i); 22676180232681f9a3091fda763e364ee2e40765c9bSara Ting // TODO: Change to a relative time description like: "Started 40 minutes ago". 22776180232681f9a3091fda763e364ee2e40765c9bSara Ting // This requires constant refreshing to the message as time goes. 22876180232681f9a3091fda763e364ee2e40765c9bSara Ting String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, 22976180232681f9a3091fda763e364ee2e40765c9bSara Ting info.allDay, info.location); 23076180232681f9a3091fda763e364ee2e40765c9bSara Ting 231d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting // Refresh when concurrent event ends so it will drop into the expired digest. 23276180232681f9a3091fda763e364ee2e40765c9bSara Ting nextRefreshTime = Math.min(nextRefreshTime, info.endMillis); 23376180232681f9a3091fda763e364ee2e40765c9bSara Ting 234d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting postNotification(info, summaryText, context, quietUpdate, false, defaultVibrate, 2357e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting ringtone, false, notificationPosted, nm); 2367e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting notificationPosted = true; 23776180232681f9a3091fda763e364ee2e40765c9bSara Ting } 23876180232681f9a3091fda763e364ee2e40765c9bSara Ting 23976180232681f9a3091fda763e364ee2e40765c9bSara Ting // Post the expired events as 1 combined notification. 24076180232681f9a3091fda763e364ee2e40765c9bSara Ting int numExpired = expiredEvents.size(); 24176180232681f9a3091fda763e364ee2e40765c9bSara Ting if (numExpired > 0) { 24276180232681f9a3091fda763e364ee2e40765c9bSara Ting Notification notification; 24376180232681f9a3091fda763e364ee2e40765c9bSara Ting if (numExpired == 1) { 24476180232681f9a3091fda763e364ee2e40765c9bSara Ting // If only 1 expired event, display an "old-style" basic alert. 24576180232681f9a3091fda763e364ee2e40765c9bSara Ting NotificationInfo info = expiredEvents.get(0); 24676180232681f9a3091fda763e364ee2e40765c9bSara Ting String summaryText = AlertUtils.formatTimeLocation(context, info.startMillis, 24776180232681f9a3091fda763e364ee2e40765c9bSara Ting info.allDay, info.location); 24876180232681f9a3091fda763e364ee2e40765c9bSara Ting notification = AlertReceiver.makeBasicNotification(context, info.eventName, 24976180232681f9a3091fda763e364ee2e40765c9bSara Ting summaryText, info.startMillis, info.endMillis, info.eventId, 25076180232681f9a3091fda763e364ee2e40765c9bSara Ting info.notificationId, false); 25176180232681f9a3091fda763e364ee2e40765c9bSara Ting } else { 25276180232681f9a3091fda763e364ee2e40765c9bSara Ting // Multiple expired events are listed in a digest. 25376180232681f9a3091fda763e364ee2e40765c9bSara Ting notification = AlertReceiver.makeDigestNotification(context, 2548748724e382ca014067a3ceb5ff4eacbd9c4021aSara Ting expiredEvents, expiredDigestTitle, false); 25576180232681f9a3091fda763e364ee2e40765c9bSara Ting } 25676180232681f9a3091fda763e364ee2e40765c9bSara Ting 25776180232681f9a3091fda763e364ee2e40765c9bSara Ting // Add options for a quiet update. 2587e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting addNotificationOptions(notification, true, expiredDigestTitle, defaultVibrate, 2597e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting ringtone, notificationPosted); 26076180232681f9a3091fda763e364ee2e40765c9bSara Ting 26176180232681f9a3091fda763e364ee2e40765c9bSara Ting // Remove any individual expired notifications before posting. 26276180232681f9a3091fda763e364ee2e40765c9bSara Ting for (NotificationInfo expiredInfo : expiredEvents) { 26376180232681f9a3091fda763e364ee2e40765c9bSara Ting nm.cancel(expiredInfo.notificationId); 26476180232681f9a3091fda763e364ee2e40765c9bSara Ting } 26576180232681f9a3091fda763e364ee2e40765c9bSara Ting 26676180232681f9a3091fda763e364ee2e40765c9bSara Ting // Post the new notification for the group. 26776180232681f9a3091fda763e364ee2e40765c9bSara Ting nm.notify(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID, notification); 2687e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting notificationPosted = true; 26976180232681f9a3091fda763e364ee2e40765c9bSara Ting 27076180232681f9a3091fda763e364ee2e40765c9bSara Ting if (DEBUG) { 27176180232681f9a3091fda763e364ee2e40765c9bSara Ting Log.d(TAG, "Posting digest alarm notification, numEvents:" + expiredEvents.size() 27276180232681f9a3091fda763e364ee2e40765c9bSara Ting + ", notificationId:" + AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID 27376180232681f9a3091fda763e364ee2e40765c9bSara Ting + (quietUpdate ? ", quiet" : ", loud")); 27476180232681f9a3091fda763e364ee2e40765c9bSara Ting } 27576180232681f9a3091fda763e364ee2e40765c9bSara Ting } else { 27676180232681f9a3091fda763e364ee2e40765c9bSara Ting nm.cancel(AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID); 27776180232681f9a3091fda763e364ee2e40765c9bSara Ting } 27876180232681f9a3091fda763e364ee2e40765c9bSara Ting 27976180232681f9a3091fda763e364ee2e40765c9bSara Ting // Schedule the next silent refresh time so notifications will change 28076180232681f9a3091fda763e364ee2e40765c9bSara Ting // buckets (eg. drop into expired digest, etc). 281d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting if (nextRefreshTime > currentTime) { 282d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting AlertUtils.scheduleNextNotificationRefresh(context, null, nextRefreshTime); 283d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting } else if (nextRefreshTime < currentTime) { 284d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting Log.e(TAG, "Illegal state: next notification refresh time found to be in the past."); 285d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting } 28676180232681f9a3091fda763e364ee2e40765c9bSara Ting 28776180232681f9a3091fda763e364ee2e40765c9bSara Ting return true; 28876180232681f9a3091fda763e364ee2e40765c9bSara Ting } 28976180232681f9a3091fda763e364ee2e40765c9bSara Ting 29076180232681f9a3091fda763e364ee2e40765c9bSara Ting /** 29176180232681f9a3091fda763e364ee2e40765c9bSara Ting * Processes the query results and bucketizes the alerts. 29276180232681f9a3091fda763e364ee2e40765c9bSara Ting * 293d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting * @param highPriorityEvents This will contain future events, and concurrent events 294d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting * that started recently (less than the interval DEPRIORITIZE_GRACE_PERIOD_MS). 295d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting * @param mediumPriorityEvents This will contain concurrent events that started 296d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting * more than DEPRIORITIZE_GRACE_PERIOD_MS ago. 297d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting * @param expiredEvents Will contain events that have ended. 29876180232681f9a3091fda763e364ee2e40765c9bSara Ting * @param expiredDigestTitle Should pass in an empty StringBuilder; this will be 29976180232681f9a3091fda763e364ee2e40765c9bSara Ting * modified to contain a title consolidating all expired event titles. 30076180232681f9a3091fda763e364ee2e40765c9bSara Ting * @return Returns the number of new alerts to fire. If this is 0, it implies 30176180232681f9a3091fda763e364ee2e40765c9bSara Ting * a quiet update. 30276180232681f9a3091fda763e364ee2e40765c9bSara Ting */ 30376180232681f9a3091fda763e364ee2e40765c9bSara Ting private static int processQuery(final Cursor alertCursor, final ContentResolver cr, 304d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting final long currentTime, ArrayList<NotificationInfo> highPriorityEvents, 305d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting ArrayList<NotificationInfo> mediumPriorityEvents, 306d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting ArrayList<NotificationInfo> expiredEvents, StringBuilder expiredDigestTitle) { 30776180232681f9a3091fda763e364ee2e40765c9bSara Ting HashMap<Long, Long> eventIds = new HashMap<Long, Long>(); 30876180232681f9a3091fda763e364ee2e40765c9bSara Ting int numFired = 0; 309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project try { 310e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan while (alertCursor.moveToNext()) { 311e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alertId = alertCursor.getLong(ALERT_INDEX_ID); 312e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long eventId = alertCursor.getLong(ALERT_INDEX_EVENT_ID); 313e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int minutes = alertCursor.getInt(ALERT_INDEX_MINUTES); 314e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String eventName = alertCursor.getString(ALERT_INDEX_TITLE); 31542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting final String description = alertCursor.getString(ALERT_INDEX_DESCRIPTION); 316e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String location = alertCursor.getString(ALERT_INDEX_EVENT_LOCATION); 317e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int status = alertCursor.getInt(ALERT_INDEX_SELF_ATTENDEE_STATUS); 318e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final boolean declined = status == Attendees.ATTENDEE_STATUS_DECLINED; 319e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long beginTime = alertCursor.getLong(ALERT_INDEX_BEGIN); 320e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long endTime = alertCursor.getLong(ALERT_INDEX_END); 321e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final Uri alertUri = ContentUris 322e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan .withAppendedId(CalendarAlerts.CONTENT_URI, alertId); 323e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alarmTime = alertCursor.getLong(ALERT_INDEX_ALARM_TIME); 324e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int state = alertCursor.getInt(ALERT_INDEX_STATE); 3257321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan final boolean allDay = alertCursor.getInt(ALERT_INDEX_ALL_DAY) != 0; 326e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 327e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 328e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alarmTime:" + alarmTime + " alertId:" + alertId 329e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " eventId:" + eventId + " state: " + state + " minutes:" + minutes 330e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " declined:" + declined + " beginTime:" + beginTime 331e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " endTime:" + endTime); 332146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3330e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 334e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentValues values = new ContentValues(); 335e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newState = -1; 336e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 337e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Uncomment for the behavior of clearing out alerts after the 338e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // events ended. b/1880369 339e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // 340e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // if (endTime < currentTime) { 341e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // newState = CalendarAlerts.DISMISSED; 342e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // } else 343e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 3443dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan // Remove declined events 3453dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan if (!declined) { 346fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state == CalendarAlerts.STATE_SCHEDULED) { 347fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik newState = CalendarAlerts.STATE_FIRED; 348e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan numFired++; 349e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 350e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the received time in the CalendarAlerts table. 351e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is useful for finding bugs that cause alarms to be 352e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // missed or delayed. 353e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.RECEIVED_TIME, currentTime); 354e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 355e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } else { 356fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik newState = CalendarAlerts.STATE_DISMISSED; 357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 359e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Update row if state changed 360e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (newState != -1) { 361e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.STATE, newState); 362e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan state = newState; 363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3640e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 365fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state == CalendarAlerts.STATE_FIRED) { 366e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the time posting to notification manager. 367e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is used for debugging missed alarms. 368e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.NOTIFY_TIME, currentTime); 369146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3700e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 371e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Write row to if anything changed 372e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (values.size() > 0) cr.update(alertUri, values, null, null); 3730e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 374fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state != CalendarAlerts.STATE_FIRED) { 375e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan continue; 376e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 377146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 378e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Pick an Event title for the notification panel by the latest 379e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // alertTime and give prefer accepted events in case of ties. 380e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newStatus; 381e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan switch (status) { 382e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_ACCEPTED: 383e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 2; 384e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 385e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_TENTATIVE: 386e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 1; 387e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 388e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan default: 389e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 0; 390e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 391e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 3924e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting // Don't count duplicate alerts for the same event 3934e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting if (eventIds.put(eventId, beginTime) == null) { 39442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting NotificationInfo notificationInfo = new NotificationInfo(eventName, location, 39557dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting description, beginTime, endTime, eventId, allDay); 39642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting 397d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting // TODO: Prioritize by "primary" calendar 398d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting long highPriorityCutoff = currentTime - DEPRIORITIZE_GRACE_PERIOD_MS; 399d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting if (beginTime > highPriorityCutoff) { 400d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting highPriorityEvents.add(notificationInfo); 401d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting } else if (endTime >= currentTime) { 402d8abe4ec4f0dd7f4edf9e9ab31766bab85cdf9bdSara Ting mediumPriorityEvents.add(notificationInfo); 40342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } else { 40457dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting expiredEvents.add(notificationInfo); 40542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting if (!TextUtils.isEmpty(eventName)) { 40676180232681f9a3091fda763e364ee2e40765c9bSara Ting if (expiredDigestTitle.length() > 0) { 40776180232681f9a3091fda763e364ee2e40765c9bSara Ting expiredDigestTitle.append(", "); 40842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } 40976180232681f9a3091fda763e364ee2e40765c9bSara Ting expiredDigestTitle.append(eventName); 41042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } 4114e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting } 4129881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson } 413146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 414146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } finally { 415e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor != null) { 416e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan alertCursor.close(); 417e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 418146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 41976180232681f9a3091fda763e364ee2e40765c9bSara Ting return numFired; 420e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 421e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 42242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting private static void postNotification(NotificationInfo info, String summaryText, 42342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting Context context, boolean quietUpdate, boolean doPopup, boolean defaultVibrate, 4247e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting String ringtone, boolean highPriority, boolean invokedNotify, 4257e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting NotificationManager notificationMgr) { 42642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting String tickerText = getTickerText(info.eventName, info.location); 42742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting Notification notification = AlertReceiver.makeExpandingNotification(context, 42842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting info.eventName, summaryText, info.description, info.startMillis, 42942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting info.endMillis, info.eventId, info.notificationId, doPopup, highPriority); 43042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting addNotificationOptions(notification, quietUpdate, tickerText, defaultVibrate, 4317e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting ringtone, invokedNotify); 43242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting notificationMgr.notify(info.notificationId, notification); 43342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting 43442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting if (DEBUG) { 43542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting Log.d(TAG, "Posting individual alarm notification, eventId:" + info.eventId 43642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting + ", notificationId:" + info.notificationId 43742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting + (quietUpdate ? ", quiet" : ", loud") 43842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting + (highPriority ? ", high-priority" : "")); 43942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } 44042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } 44142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting 44242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting private static String getTickerText(String eventName, String location) { 44342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting String tickerText = eventName; 44442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting if (!TextUtils.isEmpty(location)) { 44542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting tickerText = eventName + " - " + location; 44642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } 44742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting return tickerText; 44842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } 44942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting 4504e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting static class NotificationInfo { 4514e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting String eventName; 4524e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting String location; 45342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting String description; 4544e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting long startMillis; 4554e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting long endMillis; 4564e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting long eventId; 45742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting int notificationId; 4584e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting boolean allDay; 4594e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting 46042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting NotificationInfo(String eventName, String location, String description, long startMillis, 46157dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting long endMillis, long eventId, boolean allDay) { 4624e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting this.eventName = eventName; 4634e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting this.location = location; 46442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting this.description = description; 4654e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting this.startMillis = startMillis; 4664e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting this.endMillis = endMillis; 4674e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting this.eventId = eventId; 4684e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting this.allDay = allDay; 46957dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting this.notificationId = getNotificationId(eventId, startMillis); 47057dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting } 47142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting 47257dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting /* 47357dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting * Convert reminder into the ID for posting notifications. Use hash so we don't 47457dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting * have to worry about any limits (but handle the case of a collision with the ID 47557dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting * reserved for representing the expired notification digest). 47657dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting */ 47757dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting private static int getNotificationId(long eventId, long startMillis) { 47857dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting long result = 17; 47957dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting result = 37 * result + eventId; 48057dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting result = 37 * result + startMillis; 48157dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting int notificationId = Long.valueOf(result).hashCode(); 48242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting if (notificationId == AlertUtils.EXPIRED_GROUP_NOTIFICATION_ID) { 48357dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting notificationId = Integer.MAX_VALUE; 48442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } 48557dd943e5598d4778698d3c94a112f124aa7c0d1Sara Ting return notificationId; 486146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 4874e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting } 4880e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 4894e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting private static boolean shouldUseDefaultVibrate(Context context, SharedPreferences prefs) { 4904e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting // Find out the circumstances under which to vibrate. 4914e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting // Migrate from pre-Froyo boolean setting if necessary. 4924e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting String vibrateWhen; // "always" or "silent" or "never" 4934e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting if(prefs.contains(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN)) 4944e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting { 4954e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting // Look up Froyo setting 4964e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting vibrateWhen = 4974e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting prefs.getString(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN, null); 4984e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting } else if(prefs.contains(GeneralPreferences.KEY_ALERTS_VIBRATE)) { 4994e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting // No Froyo setting. Migrate pre-Froyo setting to new Froyo-defined value. 5004e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting boolean vibrate = 5014e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting prefs.getBoolean(GeneralPreferences.KEY_ALERTS_VIBRATE, false); 5024e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting vibrateWhen = vibrate ? 5034e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting context.getString(R.string.prefDefault_alerts_vibrate_true) : 5044e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting context.getString(R.string.prefDefault_alerts_vibrate_false); 5054e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting } else { 5064e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting // No setting. Use Froyo-defined default. 5074e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting vibrateWhen = context.getString(R.string.prefDefault_alerts_vibrateWhen); 5084e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting } 509146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 5104e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting if (vibrateWhen.equals("always")) { 5114e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting return true; 512146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 5134e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting if (!vibrateWhen.equals("silent")) { 5144e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting return false; 5154e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting } 5164e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting 5174e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting // Settings are to vibrate when silent. Return true if it is now silent. 5184e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting AudioManager audioManager = 5194e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 5204e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting return audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE; 5214e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting } 5220e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 52342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting private static void addNotificationOptions(Notification notification, boolean quietUpdate, 5247e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting String tickerText, boolean defaultVibrate, String reminderRingtone, 5257e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting boolean invokedNotify) { 526e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.defaults |= Notification.DEFAULT_LIGHTS; 527e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 528e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Quietly update notification bar. Nothing new. Maybe something just got deleted. 529e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!quietUpdate) { 530e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Flash ticker in status bar 53142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting if (!TextUtils.isEmpty(tickerText)) { 53242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting notification.tickerText = tickerText; 53342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting } 534e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 5357e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting // If we've already posted a notification, don't play any more sounds so only 5367e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting // 1 sound per group of notifications. 5377e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting if (!invokedNotify) { 5380e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 5397e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting // Generate either a pop-up dialog, status bar notification, or 5407e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting // neither. Pop-up dialog and status bar notification may include a 5417e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting // sound, an alert, or both. A status bar notification also includes 5427e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting // a toast. 5437e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting if (defaultVibrate) { 5447e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting notification.defaults |= Notification.DEFAULT_VIBRATE; 5457e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting } 5467e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting 5477e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting // Possibly generate a sound. If 'Silent' is chosen, the ringtone 5487e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting // string will be empty. 5497e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting notification.sound = TextUtils.isEmpty(reminderRingtone) ? null : Uri 5507e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting .parse(reminderRingtone); 5517e4c339fb171f542ff8d5d4916c649f9597aa926Sara Ting } 552146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 553146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 5540e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 555146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private void doTimeChanged() { 556146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project ContentResolver cr = getContentResolver(); 557146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Object service = getSystemService(Context.ALARM_SERVICE); 558146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlarmManager manager = (AlarmManager) service; 559fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // TODO Move this into Provider 560fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik rescheduleMissedAlarms(cr, this, manager); 56142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting updateAlertNotification(this, false); 562146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 5630e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 564fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik private static final String SORT_ORDER_ALARMTIME_ASC = 565fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.ALARM_TIME + " ASC"; 566fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 567fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik private static final String WHERE_RESCHEDULE_MISSED_ALARMS = 568fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.STATE 569fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + "=" 570fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.STATE_SCHEDULED 571fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 572fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.ALARM_TIME 573fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + "<?" 574fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 575fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.ALARM_TIME 576fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + ">?" 577fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 578fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.END + ">=?"; 579fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 580fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik /** 581fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * Searches the CalendarAlerts table for alarms that should have fired but 582fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * have not and then reschedules them. This method can be called at boot 583fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * time to restore alarms that may have been lost due to a phone reboot. 584fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * 585fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param cr the ContentResolver 586fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param context the Context 587fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param manager the AlarmManager 588fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik */ 589fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik public static final void rescheduleMissedAlarms(ContentResolver cr, Context context, 590fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik AlarmManager manager) { 591fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // Get all the alerts that have been scheduled but have not fired 592fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // and should have fired by now and are not too old. 593fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long now = System.currentTimeMillis(); 594fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long ancient = now - DateUtils.DAY_IN_MILLIS; 595fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik String[] projection = new String[] { 596fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.ALARM_TIME, 597fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik }; 598fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 599fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // TODO: construct an explicit SQL query so that we can add 600fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // "GROUPBY" instead of doing a sort and de-dup 601fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection, 602fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] { 603fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Long.toString(now), Long.toString(ancient), Long.toString(now) 604fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik }), SORT_ORDER_ALARMTIME_ASC); 605fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (cursor == null) { 606fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik return; 607fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 608fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 609fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (DEBUG) { 610fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.d(TAG, "missed alarms found: " + cursor.getCount()); 611fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 612fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 613fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik try { 614fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long alarmTime = -1; 615fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 616fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik while (cursor.moveToNext()) { 617fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long newAlarmTime = cursor.getLong(0); 618fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (alarmTime != newAlarmTime) { 619fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (DEBUG) { 620fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.w(TAG, "rescheduling missed alarm. alarmTime: " + newAlarmTime); 621fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 6229881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson AlertUtils.scheduleAlarm(context, manager, newAlarmTime); 623fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik alarmTime = newAlarmTime; 624fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 625fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 626fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } finally { 627fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik cursor.close(); 628fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 629fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 630fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 631146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private final class ServiceHandler extends Handler { 632146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public ServiceHandler(Looper looper) { 633146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project super(looper); 634146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 6350e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 636146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 637146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void handleMessage(Message msg) { 638146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project processMessage(msg); 639146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // NOTE: We MUST not call stopSelf() directly, since we need to 640146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // make sure the wake lock acquired by AlertReceiver is released. 641146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlertReceiver.finishStartingService(AlertService.this, msg.arg1); 6420e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan } 643e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 644146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 645146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 646146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onCreate() { 647146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project HandlerThread thread = new HandlerThread("AlertService", 648146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Process.THREAD_PRIORITY_BACKGROUND); 649146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project thread.start(); 6500e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 651146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper = thread.getLooper(); 652146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceHandler = new ServiceHandler(mServiceLooper); 653146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 654146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 655146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 656c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff public int onStartCommand(Intent intent, int flags, int startId) { 657c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff if (intent != null) { 658c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff Message msg = mServiceHandler.obtainMessage(); 659c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.arg1 = startId; 660c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.obj = intent.getExtras(); 661c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff mServiceHandler.sendMessage(msg); 662c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff } 663c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff return START_REDELIVER_INTENT; 664146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 665146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 666146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 667146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onDestroy() { 668146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper.quit(); 669146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 670146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 671146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 672146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public IBinder onBind(Intent intent) { 673146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return null; 674146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 675146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project} 676