AlertService.java revision 43ffa461b738692925a94c005c90ff60757455a7
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[] { 9143ffa461b738692925a94c005c90ff60757455a7RoboErik Integer.toString(CalendarAlerts.STATE_FIRED), 9243ffa461b738692925a94c005c90ff60757455a7RoboErik Integer.toString(CalendarAlerts.STATE_SCHEDULED) 93146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project }; 940e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 95e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC"; 960e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 9743ffa461b738692925a94c005c90ff60757455a7RoboErik private static final String DISMISS_OLD_SELECTION = CalendarAlerts.END + "<? AND " 9843ffa461b738692925a94c005c90ff60757455a7RoboErik + CalendarAlerts.STATE + "=?"; 9943ffa461b738692925a94c005c90ff60757455a7RoboErik 1008af2529989a9b10a0bb84736695c22fc02a17a4aThe Android Open Source Project void processMessage(Message msg) { 101146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Bundle bundle = (Bundle) msg.obj; 1020e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 103146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // On reboot, update the notification bar with the contents of the 104146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // CalendarAlerts table. 105146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project String action = bundle.getString("action"); 106e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 107fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.d(TAG, bundle.getLong(android.provider.CalendarContract.CalendarAlerts.ALARM_TIME) 108e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " Action = " + action); 109e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 110e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 111146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (action.equals(Intent.ACTION_BOOT_COMPLETED) 112146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project || action.equals(Intent.ACTION_TIME_CHANGED)) { 113146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project doTimeChanged(); 114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return; 115146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 116146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 117a27a886892fe3ec5edbc63c0b58e0a988623011aRoboErik if (!action.equals(android.provider.CalendarContract.ACTION_EVENT_REMINDER) 11843ffa461b738692925a94c005c90ff60757455a7RoboErik && !action.equals(Intent.ACTION_LOCALE_CHANGED) 11943ffa461b738692925a94c005c90ff60757455a7RoboErik && !action.equals(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)) { 120e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.w(TAG, "Invalid action: " + action); 121e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return; 122146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 12343ffa461b738692925a94c005c90ff60757455a7RoboErik if (action.equals(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)) { 12443ffa461b738692925a94c005c90ff60757455a7RoboErik dismissOldAlerts(this); 12543ffa461b738692925a94c005c90ff60757455a7RoboErik } 126e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan updateAlertNotification(this); 127e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 128605a0901134c0840b2fcf0514b4c1f8bc10dc7e0Michael Chan 12943ffa461b738692925a94c005c90ff60757455a7RoboErik static void dismissOldAlerts(Context context) { 13043ffa461b738692925a94c005c90ff60757455a7RoboErik ContentResolver cr = context.getContentResolver(); 13143ffa461b738692925a94c005c90ff60757455a7RoboErik final long currentTime = System.currentTimeMillis(); 13243ffa461b738692925a94c005c90ff60757455a7RoboErik ContentValues vals = new ContentValues(); 13343ffa461b738692925a94c005c90ff60757455a7RoboErik vals.put(CalendarAlerts.STATE, CalendarAlerts.STATE_DISMISSED); 13443ffa461b738692925a94c005c90ff60757455a7RoboErik cr.update(CalendarAlerts.CONTENT_URI, vals, DISMISS_OLD_SELECTION, new String[] { 13543ffa461b738692925a94c005c90ff60757455a7RoboErik Long.toString(currentTime), Integer.toString(CalendarAlerts.STATE_SCHEDULED) 13643ffa461b738692925a94c005c90ff60757455a7RoboErik }); 13743ffa461b738692925a94c005c90ff60757455a7RoboErik } 13843ffa461b738692925a94c005c90ff60757455a7RoboErik 139e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan static boolean updateAlertNotification(Context context) { 140e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentResolver cr = context.getContentResolver(); 141e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long currentTime = System.currentTimeMillis(); 1420e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 14343ffa461b738692925a94c005c90ff60757455a7RoboErik SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 14443ffa461b738692925a94c005c90ff60757455a7RoboErik 14543ffa461b738692925a94c005c90ff60757455a7RoboErik boolean doAlert = prefs.getBoolean(GeneralPreferences.KEY_ALERTS, true); 14643ffa461b738692925a94c005c90ff60757455a7RoboErik boolean doPopup = prefs.getBoolean(GeneralPreferences.KEY_ALERTS_POPUP, false); 14743ffa461b738692925a94c005c90ff60757455a7RoboErik 14843ffa461b738692925a94c005c90ff60757455a7RoboErik if (!doAlert) { 14943ffa461b738692925a94c005c90ff60757455a7RoboErik if (DEBUG) { 15043ffa461b738692925a94c005c90ff60757455a7RoboErik Log.d(TAG, "alert preference is OFF"); 15143ffa461b738692925a94c005c90ff60757455a7RoboErik } 15243ffa461b738692925a94c005c90ff60757455a7RoboErik 15343ffa461b738692925a94c005c90ff60757455a7RoboErik // If we shouldn't be showing notifications cancel any existing ones 15443ffa461b738692925a94c005c90ff60757455a7RoboErik // and return. 15543ffa461b738692925a94c005c90ff60757455a7RoboErik NotificationManager nm = (NotificationManager) context 15643ffa461b738692925a94c005c90ff60757455a7RoboErik .getSystemService(Context.NOTIFICATION_SERVICE); 15743ffa461b738692925a94c005c90ff60757455a7RoboErik nm.cancelAll(); 15843ffa461b738692925a94c005c90ff60757455a7RoboErik return true; 15943ffa461b738692925a94c005c90ff60757455a7RoboErik } 16043ffa461b738692925a94c005c90ff60757455a7RoboErik 161fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Cursor alertCursor = cr.query(CalendarAlerts.CONTENT_URI, ALERT_PROJECTION, 162fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik (ACTIVE_ALERTS_SELECTION + currentTime), ACTIVE_ALERTS_SELECTION_ARGS, 163fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik ACTIVE_ALERTS_SORT); 1640e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 165e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor == null || alertCursor.getCount() == 0) { 166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (alertCursor != null) { 167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project alertCursor.close(); 168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 169e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 170e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) Log.d(TAG, "No fired or scheduled alerts"); 171e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan NotificationManager nm = 172e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 173e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan nm.cancel(0); 174e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return false; 175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1760e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 177e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 178e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alert count:" + alertCursor.getCount()); 179146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1800e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 181e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan String notificationEventName = null; 182e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan String notificationEventLocation = null; 183e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan long notificationEventBegin = 0; 184e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int notificationEventStatus = 0; 1857321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan boolean notificationEventAllDay = true; 186e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan HashMap<Long, Long> eventIds = new HashMap<Long, Long>(); 187e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int numReminders = 0; 188e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int numFired = 0; 189146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project try { 190e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan while (alertCursor.moveToNext()) { 191e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alertId = alertCursor.getLong(ALERT_INDEX_ID); 192e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long eventId = alertCursor.getLong(ALERT_INDEX_EVENT_ID); 193e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int minutes = alertCursor.getInt(ALERT_INDEX_MINUTES); 194e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String eventName = alertCursor.getString(ALERT_INDEX_TITLE); 195e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String location = alertCursor.getString(ALERT_INDEX_EVENT_LOCATION); 196e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int status = alertCursor.getInt(ALERT_INDEX_SELF_ATTENDEE_STATUS); 197e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final boolean declined = status == Attendees.ATTENDEE_STATUS_DECLINED; 198e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long beginTime = alertCursor.getLong(ALERT_INDEX_BEGIN); 199e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long endTime = alertCursor.getLong(ALERT_INDEX_END); 200e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final Uri alertUri = ContentUris 201e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan .withAppendedId(CalendarAlerts.CONTENT_URI, alertId); 202e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alarmTime = alertCursor.getLong(ALERT_INDEX_ALARM_TIME); 203e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int state = alertCursor.getInt(ALERT_INDEX_STATE); 2047321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan final boolean allDay = alertCursor.getInt(ALERT_INDEX_ALL_DAY) != 0; 205e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 206e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 207e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alarmTime:" + alarmTime + " alertId:" + alertId 208e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " eventId:" + eventId + " state: " + state + " minutes:" + minutes 209e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " declined:" + declined + " beginTime:" + beginTime 210e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " endTime:" + endTime); 211146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2120e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 213e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentValues values = new ContentValues(); 214e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newState = -1; 215e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 216e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Uncomment for the behavior of clearing out alerts after the 217e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // events ended. b/1880369 218e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // 219e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // if (endTime < currentTime) { 220e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // newState = CalendarAlerts.DISMISSED; 221e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // } else 222e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 2233dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan // Remove declined events 2243dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan if (!declined) { 2253dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan // Don't count duplicate alerts for the same event 2263dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan if (eventIds.put(eventId, beginTime) == null) { 2273dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan numReminders++; 2283dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan } 2293dd7e40b48c91913b02cab6b06ce5a07a9654709Michael Chan 230fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state == CalendarAlerts.STATE_SCHEDULED) { 231fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik newState = CalendarAlerts.STATE_FIRED; 232e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan numFired++; 233e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 234e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the received time in the CalendarAlerts table. 235e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is useful for finding bugs that cause alarms to be 236e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // missed or delayed. 237e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.RECEIVED_TIME, currentTime); 238e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 239e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } else { 240fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik newState = CalendarAlerts.STATE_DISMISSED; 241146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 242146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 243e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Update row if state changed 244e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (newState != -1) { 245e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.STATE, newState); 246e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan state = newState; 247146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2480e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 249fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state == CalendarAlerts.STATE_FIRED) { 250e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the time posting to notification manager. 251e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is used for debugging missed alarms. 252e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.NOTIFY_TIME, currentTime); 253146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2540e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 255e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Write row to if anything changed 256e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (values.size() > 0) cr.update(alertUri, values, null, null); 2570e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 258fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (state != CalendarAlerts.STATE_FIRED) { 259e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan continue; 260e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 262e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Pick an Event title for the notification panel by the latest 263e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // alertTime and give prefer accepted events in case of ties. 264e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newStatus; 265e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan switch (status) { 266e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_ACCEPTED: 267e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 2; 268e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 269e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_TENTATIVE: 270e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 1; 271e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 272e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan default: 273e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 0; 274e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 275e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 276e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // TODO Prioritize by "primary" calendar 277e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Assumes alerts are sorted by begin time in reverse 278e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (notificationEventName == null 279e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan || (notificationEventBegin <= beginTime && 280e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventStatus < newStatus)) { 281e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventName = eventName; 282e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventLocation = location; 283e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventBegin = beginTime; 284e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventStatus = newStatus; 2857321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan notificationEventAllDay = allDay; 286146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 287146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } finally { 289e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor != null) { 290e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan alertCursor.close(); 291e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 292146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2930e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 294ccf4a12bc3b25c46d592d3f9116f52751b96010cMason Tang boolean quietUpdate = numFired == 0; 295cca9ecb23b079c47856af22f89f7a6f3dec7a492Mason Tang boolean highPriority = numFired > 0 && doPopup; 296e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan postNotification(context, prefs, notificationEventName, notificationEventLocation, 2977321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan numReminders, quietUpdate, highPriority, notificationEventBegin, 2987321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan notificationEventAllDay); 299e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 300e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return true; 301e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 302e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 303e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static void postNotification(Context context, SharedPreferences prefs, 304ccf4a12bc3b25c46d592d3f9116f52751b96010cMason Tang String eventName, String location, int numReminders, 3057321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan boolean quietUpdate, boolean highPriority, long startMillis, boolean allDay) { 306e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 307e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "###### creating new alarm notification, numReminders: " + numReminders 308c9656c9e42d9bb640688648b9dfe8d9f4e16b47dMason Tang + (quietUpdate ? " QUIET" : " loud") 309c9656c9e42d9bb640688648b9dfe8d9f4e16b47dMason Tang + (highPriority ? " high-priority" : "")); 310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3110e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 3120e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan NotificationManager nm = 313e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 314146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 315e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (numReminders == 0) { 316e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan nm.cancel(0); 317e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return; 318146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3190e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 320e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Notification notification = AlertReceiver.makeNewAlertNotification(context, eventName, 3217321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan location, numReminders, highPriority, startMillis, allDay); 322e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.defaults |= Notification.DEFAULT_LIGHTS; 323e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 324e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Quietly update notification bar. Nothing new. Maybe something just got deleted. 325e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!quietUpdate) { 326e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Flash ticker in status bar 327e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.tickerText = eventName; 328e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!TextUtils.isEmpty(location)) { 329e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.tickerText = eventName + " - " + location; 330e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 331e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 332e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Generate either a pop-up dialog, status bar notification, or 333e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // neither. Pop-up dialog and status bar notification may include a 334e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // sound, an alert, or both. A status bar notification also includes 335e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // a toast. 3361fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma 3371fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // Find out the circumstances under which to vibrate. 3381fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // Migrate from pre-Froyo boolean setting if necessary. 3391fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma String vibrateWhen; // "always" or "silent" or "never" 3404b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa if(prefs.contains(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN)) 3411fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma { 3421fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // Look up Froyo setting 3431fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma vibrateWhen = 3444b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa prefs.getString(GeneralPreferences.KEY_ALERTS_VIBRATE_WHEN, null); 3454b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa } else if(prefs.contains(GeneralPreferences.KEY_ALERTS_VIBRATE)) { 3461fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // No Froyo setting. Migrate pre-Froyo setting to new Froyo-defined value. 3471fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean vibrate = 3484b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa prefs.getBoolean(GeneralPreferences.KEY_ALERTS_VIBRATE, false); 3491fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma vibrateWhen = vibrate ? 3501fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma context.getString(R.string.prefDefault_alerts_vibrate_true) : 3511fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma context.getString(R.string.prefDefault_alerts_vibrate_false); 3521fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma } else { 3531fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma // No setting. Use Froyo-defined default. 3541fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma vibrateWhen = context.getString(R.string.prefDefault_alerts_vibrateWhen); 3551fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma } 3561fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean vibrateAlways = vibrateWhen.equals("always"); 3571fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean vibrateSilent = vibrateWhen.equals("silent"); 3581fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma AudioManager audioManager = 3591fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 3601fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma boolean nowSilent = 3611fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE; 362e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 363e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Possibly generate a vibration 3641fec2207219842a71fbbb8567cd968ab61ce3c1cJim Shuma if (vibrateAlways || (vibrateSilent && nowSilent)) { 365e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.defaults |= Notification.DEFAULT_VIBRATE; 366e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 3670e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 3680e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan // Possibly generate a sound. If 'Silent' is chosen, the ringtone 3690e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan // string will be empty. 3700e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan String reminderRingtone = prefs.getString( 3714b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa GeneralPreferences.KEY_ALERTS_RINGTONE, null); 3720e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan notification.sound = TextUtils.isEmpty(reminderRingtone) ? null : Uri 3730e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan .parse(reminderRingtone); 374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 375146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 376146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project nm.notify(0, notification); 377146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3780e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 379146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private void doTimeChanged() { 380146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project ContentResolver cr = getContentResolver(); 381146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Object service = getSystemService(Context.ALARM_SERVICE); 382146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlarmManager manager = (AlarmManager) service; 383fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // TODO Move this into Provider 384fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik rescheduleMissedAlarms(cr, this, manager); 385e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan updateAlertNotification(this); 386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3870e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 388fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik private static final String SORT_ORDER_ALARMTIME_ASC = 389fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.ALARM_TIME + " ASC"; 390fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 391fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik private static final String WHERE_RESCHEDULE_MISSED_ALARMS = 392fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.STATE 393fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + "=" 394fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.STATE_SCHEDULED 395fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 396fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.ALARM_TIME 397fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + "<?" 398fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 399fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.ALARM_TIME 400fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + ">?" 401fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + " AND " 402fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik + CalendarContract.CalendarAlerts.END + ">=?"; 403fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 404fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik /** 405fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * Searches the CalendarAlerts table for alarms that should have fired but 406fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * have not and then reschedules them. This method can be called at boot 407fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * time to restore alarms that may have been lost due to a phone reboot. 408fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * 409fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param cr the ContentResolver 410fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param context the Context 411fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik * @param manager the AlarmManager 412fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik */ 413fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik public static final void rescheduleMissedAlarms(ContentResolver cr, Context context, 414fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik AlarmManager manager) { 415fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // Get all the alerts that have been scheduled but have not fired 416fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // and should have fired by now and are not too old. 417fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long now = System.currentTimeMillis(); 418fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long ancient = now - DateUtils.DAY_IN_MILLIS; 419fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik String[] projection = new String[] { 420fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik CalendarContract.CalendarAlerts.ALARM_TIME, 421fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik }; 422fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 423fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // TODO: construct an explicit SQL query so that we can add 424fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik // "GROUPBY" instead of doing a sort and de-dup 425fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection, 426fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] { 427fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Long.toString(now), Long.toString(ancient), Long.toString(now) 428fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik }), SORT_ORDER_ALARMTIME_ASC); 429fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (cursor == null) { 430fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik return; 431fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 432fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 433fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (DEBUG) { 434fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.d(TAG, "missed alarms found: " + cursor.getCount()); 435fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 436fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 437fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik try { 438fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long alarmTime = -1; 439fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 440fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik while (cursor.moveToNext()) { 441fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik long newAlarmTime = cursor.getLong(0); 442fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (alarmTime != newAlarmTime) { 443fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik if (DEBUG) { 444fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik Log.w(TAG, "rescheduling missed alarm. alarmTime: " + newAlarmTime); 445fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 446fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik AlertActivity.scheduleAlarm(context, manager, newAlarmTime); 447fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik alarmTime = newAlarmTime; 448fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 449fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 450fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } finally { 451fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik cursor.close(); 452fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 453fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik } 454fa292a0db2a6f04255c75a57908b17ba48a96183RoboErik 455146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private final class ServiceHandler extends Handler { 456146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public ServiceHandler(Looper looper) { 457146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project super(looper); 458146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 4590e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 460146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 461146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void handleMessage(Message msg) { 462146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project processMessage(msg); 463146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // NOTE: We MUST not call stopSelf() directly, since we need to 464146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // make sure the wake lock acquired by AlertReceiver is released. 465146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlertReceiver.finishStartingService(AlertService.this, msg.arg1); 4660e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan } 467e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 468146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 469146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 470146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onCreate() { 471146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project HandlerThread thread = new HandlerThread("AlertService", 472146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Process.THREAD_PRIORITY_BACKGROUND); 473146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project thread.start(); 4740e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 475146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper = thread.getLooper(); 476146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceHandler = new ServiceHandler(mServiceLooper); 477146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 478146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 479146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 480c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff public int onStartCommand(Intent intent, int flags, int startId) { 481c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff if (intent != null) { 482c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff Message msg = mServiceHandler.obtainMessage(); 483c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.arg1 = startId; 484c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.obj = intent.getExtras(); 485c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff mServiceHandler.sendMessage(msg); 486c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff } 487c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff return START_REDELIVER_INTENT; 488146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 489146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 490146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 491146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onDestroy() { 492146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper.quit(); 493146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 494146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 495146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 496146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public IBinder onBind(Intent intent) { 497146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return null; 498146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 499146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project} 500