AlertService.java revision fa292a0db2a6f04255c75a57908b17ba48a96183
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 { 55fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik static final boolean DEBUG = false; 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[] { 91fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Integer.toString(CalendarAlerts.STATE_FIRED), Integer.toString(CalendarAlerts.STATE_SCHEDULED) 92146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project }; 930e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 94e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC"; 950e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 968af2529989a9b10a0bb84736695c22fc02a17a4aThe Android Open Source Project void processMessage(Message msg) { 97146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Bundle bundle = (Bundle) msg.obj; 980e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 99146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // On reboot, update the notification bar with the contents of the 100146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // CalendarAlerts table. 101146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project String action = bundle.getString("action"); 102e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 103fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.d(TAG, bundle.getLong(android.provider.CalendarContract.CalendarAlerts.ALARM_TIME) 104e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " Action = " + action); 105e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 106e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (action.equals(Intent.ACTION_BOOT_COMPLETED) 108146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project || action.equals(Intent.ACTION_TIME_CHANGED)) { 109146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project doTimeChanged(); 110146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return; 111146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 112146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 113a27a886892fe3ec5edbc63c0b58e0a988623011aRoboErik if (!action.equals(android.provider.CalendarContract.ACTION_EVENT_REMINDER) 114e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan && !action.equals(Intent.ACTION_LOCALE_CHANGED)) { 115e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.w(TAG, "Invalid action: " + action); 116e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return; 117146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 118146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 119e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan updateAlertNotification(this); 120e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 121605a0901134c0840b2fcf0514b4c1f8bc10dc7e0Michael Chan 122e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan static boolean updateAlertNotification(Context context) { 123e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentResolver cr = context.getContentResolver(); 124e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long currentTime = System.currentTimeMillis(); 1250e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 126fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Cursor alertCursor = cr.query(CalendarAlerts.CONTENT_URI, ALERT_PROJECTION, 127fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik (ACTIVE_ALERTS_SELECTION + currentTime), ACTIVE_ALERTS_SELECTION_ARGS, 128fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik ACTIVE_ALERTS_SORT); 1290e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 130e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor == null || alertCursor.getCount() == 0) { 131146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (alertCursor != null) { 132146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project alertCursor.close(); 133146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 134e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 135e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) Log.d(TAG, "No fired or scheduled alerts"); 136e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan NotificationManager nm = 137e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 138e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan nm.cancel(0); 139e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return false; 140146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1410e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 142e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 143e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alert count:" + alertCursor.getCount()); 144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1450e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 146e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan String notificationEventName = null; 147e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan String notificationEventLocation = null; 148e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan long notificationEventBegin = 0; 149e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int notificationEventStatus = 0; 1507321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan boolean notificationEventAllDay = true; 151e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan HashMap<Long, Long> eventIds = new HashMap<Long, Long>(); 152e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int numReminders = 0; 153e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int numFired = 0; 154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project try { 155e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan while (alertCursor.moveToNext()) { 156e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alertId = alertCursor.getLong(ALERT_INDEX_ID); 157e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long eventId = alertCursor.getLong(ALERT_INDEX_EVENT_ID); 158e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int minutes = alertCursor.getInt(ALERT_INDEX_MINUTES); 159e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String eventName = alertCursor.getString(ALERT_INDEX_TITLE); 160e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String location = alertCursor.getString(ALERT_INDEX_EVENT_LOCATION); 161e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int status = alertCursor.getInt(ALERT_INDEX_SELF_ATTENDEE_STATUS); 162e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final boolean declined = status == Attendees.ATTENDEE_STATUS_DECLINED; 163e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long beginTime = alertCursor.getLong(ALERT_INDEX_BEGIN); 164e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long endTime = alertCursor.getLong(ALERT_INDEX_END); 165e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final Uri alertUri = ContentUris 166e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan .withAppendedId(CalendarAlerts.CONTENT_URI, alertId); 167e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alarmTime = alertCursor.getLong(ALERT_INDEX_ALARM_TIME); 168e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int state = alertCursor.getInt(ALERT_INDEX_STATE); 1697321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan final boolean allDay = alertCursor.getInt(ALERT_INDEX_ALL_DAY) != 0; 170e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 171e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 172e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alarmTime:" + alarmTime + " alertId:" + alertId 173e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " eventId:" + eventId + " state: " + state + " minutes:" + minutes 174e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " declined:" + declined + " beginTime:" + beginTime 175e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " endTime:" + endTime); 176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1770e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 178e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentValues values = new ContentValues(); 179e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newState = -1; 180e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 181e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Uncomment for the behavior of clearing out alerts after the 182e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // events ended. b/1880369 183e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // 184e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // if (endTime < currentTime) { 185e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // newState = CalendarAlerts.DISMISSED; 186e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // } else 187e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 1883dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan // Remove declined events 1893dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan if (!declined) { 1903dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan // Don't count duplicate alerts for the same event 1913dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan if (eventIds.put(eventId, beginTime) == null) { 1923dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan numReminders++; 1933dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan } 1943dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan 195fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state == CalendarAlerts.STATE_SCHEDULED) { 196fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik newState = CalendarAlerts.STATE_FIRED; 197e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan numFired++; 198e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 199e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the received time in the CalendarAlerts table. 200e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is useful for finding bugs that cause alarms to be 201e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // missed or delayed. 202e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.RECEIVED_TIME, currentTime); 203e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 204e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } else { 205fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik newState = CalendarAlerts.STATE_DISMISSED; 206146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 207146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 208e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Update row if state changed 209e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (newState != -1) { 210e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.STATE, newState); 211e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan state = newState; 212146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2130e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 214fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state == CalendarAlerts.STATE_FIRED) { 215e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the time posting to notification manager. 216e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is used for debugging missed alarms. 217e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.NOTIFY_TIME, currentTime); 218146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2190e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 220e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Write row to if anything changed 221e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (values.size() > 0) cr.update(alertUri, values, null, null); 2220e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 223fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state != CalendarAlerts.STATE_FIRED) { 224e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan continue; 225e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 227e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Pick an Event title for the notification panel by the latest 228e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // alertTime and give prefer accepted events in case of ties. 229e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newStatus; 230e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan switch (status) { 231e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_ACCEPTED: 232e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 2; 233e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 234e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_TENTATIVE: 235e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 1; 236e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 237e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan default: 238e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 0; 239e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 240e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 241e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // TODO Prioritize by "primary" calendar 242e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Assumes alerts are sorted by begin time in reverse 243e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (notificationEventName == null 244e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan || (notificationEventBegin <= beginTime && 245e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventStatus < newStatus)) { 246e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventName = eventName; 247e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventLocation = location; 248e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventBegin = beginTime; 249e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventStatus = newStatus; 2507321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan notificationEventAllDay = allDay; 251146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 252146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 253146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } finally { 254e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor != null) { 255e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan alertCursor.close(); 256e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 257146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2580e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 2594b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 260cca9ecb23b079c47856af22f89f7a6f3dec7a492Mason Tang 2614b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa boolean doAlert = prefs.getBoolean(GeneralPreferences.KEY_ALERTS, true); 2624b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa boolean doPopup = prefs.getBoolean(GeneralPreferences.KEY_ALERTS_POPUP, false); 2630e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 264e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // TODO check for this before adding stuff to the alerts table. 265cca9ecb23b079c47856af22f89f7a6f3dec7a492Mason Tang if (!doAlert) { 266e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Log.d(TAG, "alert preference is OFF"); 268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 269e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return true; 270e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 271e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 272ccf4a12bc3b25c46d592d3f9116f52751b96010cMason Tang boolean quietUpdate = numFired == 0; 273cca9ecb23b079c47856af22f89f7a6f3dec7a492Mason Tang boolean highPriority = numFired > 0 && doPopup; 274e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan postNotification(context, prefs, notificationEventName, notificationEventLocation, 2757321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan numReminders, quietUpdate, highPriority, notificationEventBegin, 2767321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan notificationEventAllDay); 277e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 278e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return true; 279e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 280e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 281e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static void postNotification(Context context, SharedPreferences prefs, 282ccf4a12bc3b25c46d592d3f9116f52751b96010cMason Tang String eventName, String location, int numReminders, 2837321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan boolean quietUpdate, boolean highPriority, long startMillis, boolean allDay) { 284e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 285e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "###### creating new alarm notification, numReminders: " + numReminders 286c9656c9e42d9bb640688648b9dfe8d9f4e16b47dMason Tang + (quietUpdate ? " QUIET" : " loud") 287c9656c9e42d9bb640688648b9dfe8d9f4e16b47dMason Tang + (highPriority ? " high-priority" : "")); 288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2890e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 2900e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan NotificationManager nm = 291e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 292146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 293e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (numReminders == 0) { 294e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan nm.cancel(0); 295e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return; 296146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2970e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 298e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Notification notification = AlertReceiver.makeNewAlertNotification(context, eventName, 2997321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan location, numReminders, highPriority, startMillis, allDay); 300e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.defaults |= Notification.DEFAULT_LIGHTS; 301e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 302e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Quietly update notification bar. Nothing new. Maybe something just got deleted. 303e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!quietUpdate) { 304e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Flash ticker in status bar 305e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.tickerText = eventName; 306e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!TextUtils.isEmpty(location)) { 307e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.tickerText = eventName + " - " + location; 308e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 309e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 310e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Generate either a pop-up dialog, status bar notification, or 311e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // neither. Pop-up dialog and status bar notification may include a 312e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // sound, an alert, or both. A status bar notification also includes 313e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // a toast. 3141fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma 3151fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // Find out the circumstances under which to vibrate. 3161fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // Migrate from pre-Froyo boolean setting if necessary. 3171fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma String vibrateWhen; // "always" or "silent" or "never" 3184b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa if(prefs.contains(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN)) 3191fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma { 3201fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // Look up Froyo setting 3211fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma vibrateWhen = 3224b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa prefs.getString(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN, null); 3234b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa } else if(prefs.contains(GeneralPreferences.KEY_ALERTS_VIBRATE)) { 3241fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // No Froyo setting. Migrate pre-Froyo setting to new Froyo-defined value. 3251fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean vibrate = 3264b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa prefs.getBoolean(GeneralPreferences.KEY_ALERTS_VIBRATE, false); 3271fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma vibrateWhen = vibrate ? 3281fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma context.getString(R.string.prefDefault_alerts_vibrate_true) : 3291fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma context.getString(R.string.prefDefault_alerts_vibrate_false); 3301fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma } else { 3311fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // No setting. Use Froyo-defined default. 3321fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma vibrateWhen = context.getString(R.string.prefDefault_alerts_vibrateWhen); 3331fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma } 3341fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean vibrateAlways = vibrateWhen.equals("always"); 3351fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean vibrateSilent = vibrateWhen.equals("silent"); 3361fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma AudioManager audioManager = 3371fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 3381fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean nowSilent = 3391fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE; 340e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 341e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Possibly generate a vibration 3421fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma if (vibrateAlways || (vibrateSilent && nowSilent)) { 343e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.defaults |= Notification.DEFAULT_VIBRATE; 344e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 3450e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 3460e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan // Possibly generate a sound. If 'Silent' is chosen, the ringtone 3470e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan // string will be empty. 3480e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan String reminderRingtone = prefs.getString( 3494b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa GeneralPreferences.KEY_ALERTS_RINGTONE, null); 3500e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan notification.sound = TextUtils.isEmpty(reminderRingtone) ? null : Uri 3510e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan .parse(reminderRingtone); 352146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 353146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project nm.notify(0, notification); 355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3560e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private void doTimeChanged() { 358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project ContentResolver cr = getContentResolver(); 359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Object service = getSystemService(Context.ALARM_SERVICE); 360146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlarmManager manager = (AlarmManager) service; 361fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // TODO Move this into Provider 362fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik rescheduleMissedAlarms(cr, this, manager); 363e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan updateAlertNotification(this); 364146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3650e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 366fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik private static final String SORT_ORDER_ALARMTIME_ASC = 367fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.ALARM_TIME + " ASC"; 368fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 369fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik private static final String WHERE_RESCHEDULE_MISSED_ALARMS = 370fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.STATE 371fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + "=" 372fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.STATE_SCHEDULED 373fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 374fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.ALARM_TIME 375fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + "<?" 376fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 377fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.ALARM_TIME 378fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + ">?" 379fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 380fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.END + ">=?"; 381fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 382fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik /** 383fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * Searches the CalendarAlerts table for alarms that should have fired but 384fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * have not and then reschedules them. This method can be called at boot 385fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * time to restore alarms that may have been lost due to a phone reboot. 386fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * 387fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param cr the ContentResolver 388fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param context the Context 389fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param manager the AlarmManager 390fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik */ 391fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik public static final void rescheduleMissedAlarms(ContentResolver cr, Context context, 392fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik AlarmManager manager) { 393fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // Get all the alerts that have been scheduled but have not fired 394fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // and should have fired by now and are not too old. 395fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long now = System.currentTimeMillis(); 396fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long ancient = now - DateUtils.DAY_IN_MILLIS; 397fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik String[] projection = new String[] { 398fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.ALARM_TIME, 399fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik }; 400fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 401fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // TODO: construct an explicit SQL query so that we can add 402fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // "GROUPBY" instead of doing a sort and de-dup 403fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection, 404fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] { 405fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Long.toString(now), Long.toString(ancient), Long.toString(now) 406fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik }), SORT_ORDER_ALARMTIME_ASC); 407fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (cursor == null) { 408fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik return; 409fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 410fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 411fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (DEBUG) { 412fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.d(TAG, "missed alarms found: " + cursor.getCount()); 413fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 414fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 415fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik try { 416fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long alarmTime = -1; 417fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 418fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik while (cursor.moveToNext()) { 419fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long newAlarmTime = cursor.getLong(0); 420fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (alarmTime != newAlarmTime) { 421fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (DEBUG) { 422fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.w(TAG, "rescheduling missed alarm. alarmTime: " + newAlarmTime); 423fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 424fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik AlertActivity.scheduleAlarm(context, manager, newAlarmTime); 425fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik alarmTime = newAlarmTime; 426fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 427fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 428fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } finally { 429fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik cursor.close(); 430fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 431fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 432fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 433146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private final class ServiceHandler extends Handler { 434146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public ServiceHandler(Looper looper) { 435146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project super(looper); 436146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 4370e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 438146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 439146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void handleMessage(Message msg) { 440146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project processMessage(msg); 441146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // NOTE: We MUST not call stopSelf() directly, since we need to 442146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // make sure the wake lock acquired by AlertReceiver is released. 443146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlertReceiver.finishStartingService(AlertService.this, msg.arg1); 4440e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan } 445e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 446146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 447146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 448146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onCreate() { 449146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project HandlerThread thread = new HandlerThread("AlertService", 450146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Process.THREAD_PRIORITY_BACKGROUND); 451146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project thread.start(); 4520e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 453146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper = thread.getLooper(); 454146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceHandler = new ServiceHandler(mServiceLooper); 455146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 456146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 457146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 458c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff public int onStartCommand(Intent intent, int flags, int startId) { 459c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff if (intent != null) { 460c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff Message msg = mServiceHandler.obtainMessage(); 461c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.arg1 = startId; 462c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.obj = intent.getExtras(); 463c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff mServiceHandler.sendMessage(msg); 464c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff } 465c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff return START_REDELIVER_INTENT; 466146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 467146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 468146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 469146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onDestroy() { 470146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper.quit(); 471146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 472146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 473146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 474146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public IBinder onBind(Intent intent) { 475146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return null; 476146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 477146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project} 478