1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.calendar; 18 19 import android.app.Notification; 20 import android.app.NotificationManager; 21 import android.app.PendingIntent; 22 import android.app.Service; 23 import android.content.BroadcastReceiver; 24 import android.content.ContentResolver; 25 import android.content.ContentValues; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.SharedPreferences; 29 import android.content.res.Resources; 30 import android.database.Cursor; 31 import android.net.Uri; 32 import android.os.PowerManager; 33 import android.preference.PreferenceManager; 34 import android.provider.Calendar.CalendarAlerts; 35 36 /** 37 * Receives android.intent.action.EVENT_REMINDER intents and handles 38 * event reminders. The intent URI specifies an alert id in the 39 * CalendarAlerts database table. This class also receives the 40 * BOOT_COMPLETED intent so that it can add a status bar notification 41 * if there are Calendar event alarms that have not been dismissed. 42 * It also receives the TIME_CHANGED action so that it can fire off 43 * snoozed alarms that have become ready. The real work is done in 44 * the AlertService class. 45 */ 46 public class AlertReceiver extends BroadcastReceiver { 47 private static final String[] ALERT_PROJECTION = new String[] { 48 CalendarAlerts.TITLE, // 0 49 CalendarAlerts.EVENT_LOCATION, // 1 50 }; 51 private static final int ALERT_INDEX_TITLE = 0; 52 private static final int ALERT_INDEX_EVENT_LOCATION = 1; 53 54 private static final String DELETE_ACTION = "delete"; 55 56 static final Object mStartingServiceSync = new Object(); 57 static PowerManager.WakeLock mStartingService; 58 59 @Override onReceive(Context context, Intent intent)60 public void onReceive(Context context, Intent intent) { 61 if (DELETE_ACTION.equals(intent.getAction())) { 62 63 /* The user has clicked the "Clear All Notifications" 64 * buttons so dismiss all Calendar alerts. 65 */ 66 // TODO Grab a wake lock here? 67 Intent serviceIntent = new Intent(context, DismissAllAlarmsService.class); 68 context.startService(serviceIntent); 69 } else { 70 Intent i = new Intent(); 71 i.setClass(context, AlertService.class); 72 i.putExtras(intent); 73 i.putExtra("action", intent.getAction()); 74 Uri uri = intent.getData(); 75 76 // This intent might be a BOOT_COMPLETED so it might not have a Uri. 77 if (uri != null) { 78 i.putExtra("uri", uri.toString()); 79 } 80 beginStartingService(context, i); 81 } 82 } 83 84 /** 85 * Start the service to process the current event notifications, acquiring 86 * the wake lock before returning to ensure that the service will run. 87 */ beginStartingService(Context context, Intent intent)88 public static void beginStartingService(Context context, Intent intent) { 89 synchronized (mStartingServiceSync) { 90 if (mStartingService == null) { 91 PowerManager pm = 92 (PowerManager)context.getSystemService(Context.POWER_SERVICE); 93 mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 94 "StartingAlertService"); 95 mStartingService.setReferenceCounted(false); 96 } 97 mStartingService.acquire(); 98 context.startService(intent); 99 } 100 } 101 102 /** 103 * Called back by the service when it has finished processing notifications, 104 * releasing the wake lock if the service is now stopping. 105 */ finishStartingService(Service service, int startId)106 public static void finishStartingService(Service service, int startId) { 107 synchronized (mStartingServiceSync) { 108 if (mStartingService != null) { 109 if (service.stopSelfResult(startId)) { 110 mStartingService.release(); 111 } 112 } 113 } 114 } 115 updateAlertNotification(Context context)116 public static void updateAlertNotification(Context context) { 117 // This can be called regularly to synchronize the alert notification 118 // with the contents of the CalendarAlerts table. 119 120 ContentResolver cr = context.getContentResolver(); 121 122 if (cr == null) { 123 return; 124 } 125 126 String selection = CalendarAlerts.STATE + "=" + CalendarAlerts.FIRED; 127 Cursor alertCursor = CalendarAlerts.query(cr, ALERT_PROJECTION, selection, null); 128 129 NotificationManager nm = 130 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 131 132 if (alertCursor == null) { 133 nm.cancel(AlertActivity.NOTIFICATION_ID); 134 return; 135 } 136 137 if (!alertCursor.moveToFirst()) { 138 alertCursor.close(); 139 nm.cancel(AlertActivity.NOTIFICATION_ID); 140 return; 141 } 142 143 // Check the settings to see if alerts are disabled 144 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 145 String reminderType = prefs.getString(CalendarPreferenceActivity.KEY_ALERTS_TYPE, 146 CalendarPreferenceActivity.ALERT_TYPE_STATUS_BAR); 147 if (reminderType.equals(CalendarPreferenceActivity.ALERT_TYPE_OFF)) { 148 return; 149 } 150 151 String title = alertCursor.getString(ALERT_INDEX_TITLE); 152 String location = alertCursor.getString(ALERT_INDEX_EVENT_LOCATION); 153 154 Notification notification = AlertReceiver.makeNewAlertNotification(context, title, 155 location, alertCursor.getCount()); 156 alertCursor.close(); 157 158 nm.notify(0, notification); 159 } 160 makeNewAlertNotification(Context context, String title, String location, int numReminders)161 public static Notification makeNewAlertNotification(Context context, String title, 162 String location, int numReminders) { 163 Resources res = context.getResources(); 164 165 // Create an intent triggered by clicking on the status icon. 166 Intent clickIntent = new Intent(); 167 clickIntent.setClass(context, AlertActivity.class); 168 clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 169 170 // Create an intent triggered by clicking on the "Clear All Notifications" button 171 Intent deleteIntent = new Intent(); 172 deleteIntent.setClass(context, AlertReceiver.class); 173 deleteIntent.setAction(DELETE_ACTION); 174 175 if (title == null || title.length() == 0) { 176 title = res.getString(R.string.no_title_label); 177 } 178 179 String helperString; 180 if (numReminders > 1) { 181 String format; 182 if (numReminders == 2) { 183 format = res.getString(R.string.alert_missed_events_single); 184 } else { 185 format = res.getString(R.string.alert_missed_events_multiple); 186 } 187 helperString = String.format(format, numReminders - 1); 188 } else { 189 helperString = location; 190 } 191 192 Notification notification = new Notification( 193 R.drawable.stat_notify_calendar, 194 null, 195 System.currentTimeMillis()); 196 notification.setLatestEventInfo(context, 197 title, 198 helperString, 199 PendingIntent.getActivity(context, 0, clickIntent, 0)); 200 notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0); 201 202 return notification; 203 } 204 } 205 206