AlertService.java revision e2ae1ef8decfddcc4e5802483e92cab93c6fc67c
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 17146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectpackage com.android.calendar; 18146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 19146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.AlarmManager; 20146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.Notification; 21146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.NotificationManager; 22146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.Service; 23146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentResolver; 24e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chanimport android.content.ContentUris; 25146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentValues; 26146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Context; 27146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Intent; 28146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.SharedPreferences; 29146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.database.Cursor; 30146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.net.Uri; 31146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Bundle; 32146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Handler; 33146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.HandlerThread; 34146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.IBinder; 35146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Looper; 36146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Message; 37146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Process; 38146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.preference.PreferenceManager; 39146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.provider.Calendar.Attendees; 40146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.provider.Calendar.CalendarAlerts; 41146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.TextUtils; 42146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.util.Log; 43e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 44e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chanimport java.util.HashMap; 45146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 46146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/** 47146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * This service is used to handle calendar event reminders. 48146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project */ 49146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectpublic class AlertService extends Service { 50e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan static final boolean DEBUG = true; 51146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final String TAG = "AlertService"; 520e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 53146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private volatile Looper mServiceLooper; 54146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private volatile ServiceHandler mServiceHandler; 550e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 560e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan private static final String[] ALERT_PROJECTION = new String[] { 57146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts._ID, // 0 58146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.EVENT_ID, // 1 59146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.STATE, // 2 60146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.TITLE, // 3 61146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.EVENT_LOCATION, // 4 62146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.SELF_ATTENDEE_STATUS, // 5 63146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.ALL_DAY, // 6 64146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.ALARM_TIME, // 7 65146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.MINUTES, // 8 66146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.BEGIN, // 9 67e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan CalendarAlerts.END, // 10 68146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project }; 690e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 70146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_ID = 0; 71146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_EVENT_ID = 1; 72146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_STATE = 2; 73146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_TITLE = 3; 74146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_EVENT_LOCATION = 4; 75146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_SELF_ATTENDEE_STATUS = 5; 76146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_ALL_DAY = 6; 77146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_ALARM_TIME = 7; 78146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_MINUTES = 8; 79146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private static final int ALERT_INDEX_BEGIN = 9; 80e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final int ALERT_INDEX_END = 10; 81146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 82e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String ACTIVE_ALERTS_SELECTION = "(" + CalendarAlerts.STATE + "=? OR " 83e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + CalendarAlerts.STATE + "=?) AND " + CalendarAlerts.ALARM_TIME + "<="; 84146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 85e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String[] ACTIVE_ALERTS_SELECTION_ARGS = new String[] { 86e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Integer.toString(CalendarAlerts.FIRED), Integer.toString(CalendarAlerts.SCHEDULED) 87146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project }; 880e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 89e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC"; 900e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 918af2529989a9b10a0bb84736695c22fc02a17a4aThe Android Open Source Project @SuppressWarnings("deprecation") 928af2529989a9b10a0bb84736695c22fc02a17a4aThe Android Open Source Project void processMessage(Message msg) { 93146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Bundle bundle = (Bundle) msg.obj; 940e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 95146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // On reboot, update the notification bar with the contents of the 96146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // CalendarAlerts table. 97146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project String action = bundle.getString("action"); 98e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 99e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "" + bundle.getLong(android.provider.Calendar.CalendarAlerts.ALARM_TIME) 100e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " Action = " + action); 101e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 102e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 103146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (action.equals(Intent.ACTION_BOOT_COMPLETED) 104146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project || action.equals(Intent.ACTION_TIME_CHANGED)) { 105146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project doTimeChanged(); 106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return; 107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 108146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 109e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!action.equals(android.provider.Calendar.EVENT_REMINDER_ACTION) 110e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan && !action.equals(Intent.ACTION_LOCALE_CHANGED)) { 111e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.w(TAG, "Invalid action: " + action); 112e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return; 113146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 115e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan updateAlertNotification(this); 116e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 117605a0901134c0840b2fcf0514b4c1f8bc10dc7e0Michael Chan 118e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan static boolean updateAlertNotification(Context context) { 119e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentResolver cr = context.getContentResolver(); 120e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long currentTime = System.currentTimeMillis(); 1210e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 122e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Cursor alertCursor = CalendarAlerts.query(cr, ALERT_PROJECTION, ACTIVE_ALERTS_SELECTION 123e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + currentTime, ACTIVE_ALERTS_SELECTION_ARGS, ACTIVE_ALERTS_SORT); 1240e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 125e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor == null || alertCursor.getCount() == 0) { 126146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (alertCursor != null) { 127146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project alertCursor.close(); 128146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 129e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 130e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) Log.d(TAG, "No fired or scheduled alerts"); 131e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan NotificationManager nm = 132e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 133e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan nm.cancel(0); 134e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return false; 135146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1360e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 137e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 138e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alert count:" + alertCursor.getCount()); 139146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1400e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 141e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan String notificationEventName = null; 142e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan String notificationEventLocation = null; 143e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan long notificationEventBegin = 0; 144e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int notificationEventStatus = 0; 145e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan HashMap<Long, Long> eventIds = new HashMap<Long, Long>(); 146e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int numReminders = 0; 147e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int numFired = 0; 148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project try { 149e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan while (alertCursor.moveToNext()) { 150e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alertId = alertCursor.getLong(ALERT_INDEX_ID); 151e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long eventId = alertCursor.getLong(ALERT_INDEX_EVENT_ID); 152e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int minutes = alertCursor.getInt(ALERT_INDEX_MINUTES); 153e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String eventName = alertCursor.getString(ALERT_INDEX_TITLE); 154e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final String location = alertCursor.getString(ALERT_INDEX_EVENT_LOCATION); 155e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final boolean allDay = alertCursor.getInt(ALERT_INDEX_ALL_DAY) != 0; 156e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final int status = alertCursor.getInt(ALERT_INDEX_SELF_ATTENDEE_STATUS); 157e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final boolean declined = status == Attendees.ATTENDEE_STATUS_DECLINED; 158e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long beginTime = alertCursor.getLong(ALERT_INDEX_BEGIN); 159e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long endTime = alertCursor.getLong(ALERT_INDEX_END); 160e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final Uri alertUri = ContentUris 161e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan .withAppendedId(CalendarAlerts.CONTENT_URI, alertId); 162e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan final long alarmTime = alertCursor.getLong(ALERT_INDEX_ALARM_TIME); 163e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int state = alertCursor.getInt(ALERT_INDEX_STATE); 164e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 165e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 166e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "alarmTime:" + alarmTime + " alertId:" + alertId 167e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " eventId:" + eventId + " state: " + state + " minutes:" + minutes 168e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " declined:" + declined + " beginTime:" + beginTime 169e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + " endTime:" + endTime); 170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 1710e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 172e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan ContentValues values = new ContentValues(); 173e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newState = -1; 174e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 175e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Uncomment for the behavior of clearing out alerts after the 176e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // events ended. b/1880369 177e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // 178e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // if (endTime < currentTime) { 179e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // newState = CalendarAlerts.DISMISSED; 180e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // } else 181e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 182e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Remove declined events and duplicate alerts for the same event 183e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!declined && eventIds.put(eventId, beginTime) == null) { 184e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan numReminders++; 185e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (state == CalendarAlerts.SCHEDULED) { 186e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newState = CalendarAlerts.FIRED; 187e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan numFired++; 188e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 189e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the received time in the CalendarAlerts table. 190e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is useful for finding bugs that cause alarms to be 191e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // missed or delayed. 192e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.RECEIVED_TIME, currentTime); 193e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 194e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } else { 195e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newState = CalendarAlerts.DISMISSED; 196e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 197e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!declined) Log.d(TAG, "dropping dup alert for event " + eventId); 198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 199146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 200146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 201e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Update row if state changed 202e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (newState != -1) { 203e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.STATE, newState); 204e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan state = newState; 205146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2060e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 207e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (state == CalendarAlerts.FIRED) { 208e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Record the time posting to notification manager. 209e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // This is used for debugging missed alarms. 210e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan values.put(CalendarAlerts.NOTIFY_TIME, currentTime); 211146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2120e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 213e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Write row to if anything changed 214e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (values.size() > 0) cr.update(alertUri, values, null, null); 2150e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 216e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (state != CalendarAlerts.FIRED) { 217e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan continue; 218e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 220e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Pick an Event title for the notification panel by the latest 221e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // alertTime and give prefer accepted events in case of ties. 222e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan int newStatus; 223e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan switch (status) { 224e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_ACCEPTED: 225e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 2; 226e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 227e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan case Attendees.ATTENDEE_STATUS_TENTATIVE: 228e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 1; 229e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan break; 230e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan default: 231e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan newStatus = 0; 232e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 233e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 234e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // TODO Prioritize by "primary" calendar 235e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Assumes alerts are sorted by begin time in reverse 236e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (notificationEventName == null 237e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan || (notificationEventBegin <= beginTime && 238e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventStatus < newStatus)) { 239e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventName = eventName; 240e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventLocation = location; 241e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventBegin = beginTime; 242e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notificationEventStatus = newStatus; 243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 245146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } finally { 246e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (alertCursor != null) { 247e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan alertCursor.close(); 248e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2500e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 251e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 252146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project String reminderType = prefs.getString(CalendarPreferenceActivity.KEY_ALERTS_TYPE, 253146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarPreferenceActivity.ALERT_TYPE_STATUS_BAR); 2540e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 255e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // TODO check for this before adding stuff to the alerts table. 256146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project if (reminderType.equals(CalendarPreferenceActivity.ALERT_TYPE_OFF)) { 257e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 258146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Log.d(TAG, "alert preference is OFF"); 259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 260e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return true; 261e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 262e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 263e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan postNotification(context, prefs, notificationEventName, notificationEventLocation, 264e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan numReminders, numFired == 0 /* quiet update */); 265e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 266e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (numFired > 0 && reminderType.equals(CalendarPreferenceActivity.ALERT_TYPE_ALERTS)) { 267e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Intent alertIntent = new Intent(); 268e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan alertIntent.setClass(context, AlertActivity.class); 269e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan alertIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 270e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan context.startActivity(alertIntent); 271e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 272e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 273e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return true; 274e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 275e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 276e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan private static void postNotification(Context context, SharedPreferences prefs, 277e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan String eventName, String location, int numReminders, boolean quietUpdate) { 278e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (DEBUG) { 279e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Log.d(TAG, "###### creating new alarm notification, numReminders: " + numReminders 280e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan + (quietUpdate ? " QUIET" : " loud")); 281146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2820e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 2830e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan NotificationManager nm = 284e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 285146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 286e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (numReminders == 0) { 287e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan nm.cancel(0); 288e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan return; 289146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 2900e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 291e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan Notification notification = AlertReceiver.makeNewAlertNotification(context, eventName, 292e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan location, numReminders); 293e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.defaults |= Notification.DEFAULT_LIGHTS; 294e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 295e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Quietly update notification bar. Nothing new. Maybe something just got deleted. 296e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!quietUpdate) { 297e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Flash ticker in status bar 298e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.tickerText = eventName; 299e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (!TextUtils.isEmpty(location)) { 300e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.tickerText = eventName + " - " + location; 301e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 302e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 303e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Generate either a pop-up dialog, status bar notification, or 304e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // neither. Pop-up dialog and status bar notification may include a 305e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // sound, an alert, or both. A status bar notification also includes 306e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // a toast. 307e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan boolean reminderVibrate = 308e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan prefs.getBoolean(CalendarPreferenceActivity.KEY_ALERTS_VIBRATE, false); 309e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan 310e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan // Possibly generate a vibration 311e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan if (reminderVibrate) { 312e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan notification.defaults |= Notification.DEFAULT_VIBRATE; 313e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 3140e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 3150e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan // Possibly generate a sound. If 'Silent' is chosen, the ringtone 3160e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan // string will be empty. 3170e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan String reminderRingtone = prefs.getString( 3180e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan CalendarPreferenceActivity.KEY_ALERTS_RINGTONE, null); 3190e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan notification.sound = TextUtils.isEmpty(reminderRingtone) ? null : Uri 3200e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan .parse(reminderRingtone); 321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 322146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 323146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project nm.notify(0, notification); 324146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3250e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 326146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private void doTimeChanged() { 327146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project ContentResolver cr = getContentResolver(); 328146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Object service = getSystemService(Context.ALARM_SERVICE); 329146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlarmManager manager = (AlarmManager) service; 330146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project CalendarAlerts.rescheduleMissedAlarms(cr, this, manager); 331e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan updateAlertNotification(this); 332146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3330e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 334146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project private final class ServiceHandler extends Handler { 335146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public ServiceHandler(Looper looper) { 336146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project super(looper); 337146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 3380e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 339146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void handleMessage(Message msg) { 341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project processMessage(msg); 342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // NOTE: We MUST not call stopSelf() directly, since we need to 343146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project // make sure the wake lock acquired by AlertReceiver is released. 344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project AlertReceiver.finishStartingService(AlertService.this, msg.arg1); 3450e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan } 346e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan } 347146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 348146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 349146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onCreate() { 350146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project HandlerThread thread = new HandlerThread("AlertService", 351146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project Process.THREAD_PRIORITY_BACKGROUND); 352146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project thread.start(); 3530e7235b00fdf47c773592a324c4a62ef95d1dcf4Michael Chan 354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper = thread.getLooper(); 355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceHandler = new ServiceHandler(mServiceLooper); 356146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 359c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff public int onStartCommand(Intent intent, int flags, int startId) { 360c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff if (intent != null) { 361c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff Message msg = mServiceHandler.obtainMessage(); 362c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.arg1 = startId; 363c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff msg.obj = intent.getExtras(); 364c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff mServiceHandler.sendMessage(msg); 365c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff } 366c1dc950c9b5756937a1df44463cc09fdf0649420Ken Shirriff return START_REDELIVER_INTENT; 367146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 368146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 369146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 370146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public void onDestroy() { 371146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project mServiceLooper.quit(); 372146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 373146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project 374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project @Override 375146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project public IBinder onBind(Intent intent) { 376146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project return null; 377146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project } 378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project} 379