auto import from //branches/cupcake/...@126645
[android/platform/packages/apps/Calendar.git] / src / com / android / calendar / AlertReceiver.java
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.res.Resources;
29 import android.database.Cursor;
30 import android.net.Uri;
31 import android.os.PowerManager;
32 import android.provider.Calendar.CalendarAlerts;
33
34 /**
35  * Receives android.intent.action.EVENT_REMINDER intents and handles
36  * event reminders.  The intent URI specifies an alert id in the 
37  * CalendarAlerts database table.  This class also receives the
38  * BOOT_COMPLETED intent so that it can add a status bar notification
39  * if there are Calendar event alarms that have not been dismissed.
40  * It also receives the TIME_CHANGED action so that it can fire off
41  * snoozed alarms that have become ready.  The real work is done in
42  * the AlertService class.
43  */
44 public class AlertReceiver extends BroadcastReceiver {
45     private static final String[] ALERT_PROJECTION = new String[] { 
46         CalendarAlerts.TITLE,           // 0
47         CalendarAlerts.EVENT_LOCATION,  // 1
48     };
49     private static final int ALERT_INDEX_TITLE = 0;
50     private static final int ALERT_INDEX_EVENT_LOCATION = 1;
51     
52     private static final String DELETE_ACTION = "delete";
53     
54     private static final String[] PROJECTION = new String[] { 
55         CalendarAlerts._ID,              // 0
56         CalendarAlerts.STATE,            // 1
57     };
58     
59     public static final int INDEX_STATE = 1;
60     
61     static final Object mStartingServiceSync = new Object();
62     static PowerManager.WakeLock mStartingService;
63     
64     @Override
65     public void onReceive(Context context, Intent intent) {
66         if (DELETE_ACTION.equals(intent.getAction())) {
67             
68             /* The user has clicked the "Clear All Notifications"
69              * buttons so dismiss all Calendar alerts.
70              */
71             dismissAllEvents(context);
72         } else {
73             Intent i = new Intent();
74             i.setClass(context, AlertService.class);
75             i.putExtras(intent);
76             i.putExtra("action", intent.getAction());
77             Uri uri = intent.getData();
78             
79             // This intent might be a BOOT_COMPLETED so it might not have a Uri.
80             if (uri != null) {
81                 i.putExtra("uri", uri.toString());
82                 
83                 // Record the received time in the CalendarAlerts table.
84                 // This is useful for finding bugs that cause alarms to be
85                 // missed or delayed.
86                 ContentResolver cr = context.getContentResolver();
87                 ContentValues values = new ContentValues();
88                 long currentTime = System.currentTimeMillis();
89                 values.put(CalendarAlerts.RECEIVED_TIME, currentTime);
90                 cr.update(uri, values, null /* where */, null /* args */);
91             }
92             beginStartingService(context, i);
93         }
94     }
95     
96     private void dismissAllEvents(Context context) {
97         Uri uri = CalendarAlerts.CONTENT_URI_BY_INSTANCE;
98         String selection = CalendarAlerts.STATE + "=" + CalendarAlerts.FIRED;
99         ContentResolver resolver = context.getContentResolver();
100         Cursor cursor = resolver.query(uri, PROJECTION, selection, null, null);
101         if (cursor != null) {
102             cursor.moveToPosition(-1);
103             while (cursor.moveToNext()) {
104                 cursor.updateInt(INDEX_STATE, CalendarAlerts.DISMISSED);
105             }
106             cursor.commitUpdates();
107             cursor.close();
108         }
109     }
110
111     /**
112      * Start the service to process the current event notifications, acquiring
113      * the wake lock before returning to ensure that the service will run.
114      */
115     public static void beginStartingService(Context context, Intent intent) {
116         synchronized (mStartingServiceSync) {
117             if (mStartingService == null) {
118                 PowerManager pm =
119                     (PowerManager)context.getSystemService(Context.POWER_SERVICE);
120                 mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
121                         "StartingAlertService");
122                 mStartingService.setReferenceCounted(false);
123             }
124             mStartingService.acquire();
125             context.startService(intent);
126         }
127     }
128     
129     /**
130      * Called back by the service when it has finished processing notifications,
131      * releasing the wake lock if the service is now stopping.
132      */
133     public static void finishStartingService(Service service, int startId) {
134         synchronized (mStartingServiceSync) {
135             if (mStartingService != null) {
136                 if (service.stopSelfResult(startId)) {
137                     mStartingService.release();
138                 }
139             }
140         }
141     }
142     
143     public static void updateAlertNotification(Context context) {
144         // This can be called regularly to synchronize the alert notification
145         // with the contents of the CalendarAlerts table.
146         
147         ContentResolver cr = context.getContentResolver();
148         
149         if (cr == null) {
150             return;
151         }
152         
153         String selection = CalendarAlerts.STATE + "=" + CalendarAlerts.FIRED;
154         Cursor alertCursor = CalendarAlerts.query(cr, ALERT_PROJECTION, selection, null);
155         
156         NotificationManager nm = 
157                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
158         
159         if (alertCursor == null) {
160             nm.cancel(AlertActivity.NOTIFICATION_ID);
161             return;
162         }
163
164         if (!alertCursor.moveToFirst()) {
165             alertCursor.close();
166             nm.cancel(AlertActivity.NOTIFICATION_ID);
167             return;
168         }
169         
170         String title = alertCursor.getString(ALERT_INDEX_TITLE);
171         String location = alertCursor.getString(ALERT_INDEX_EVENT_LOCATION);
172         
173         Notification notification = AlertReceiver.makeNewAlertNotification(context, title, 
174                 location, alertCursor.getCount());
175         alertCursor.close();
176         
177         nm.notify(0, notification);
178     }
179     
180     public static Notification makeNewAlertNotification(Context context, String title, 
181             String location, int numReminders) {
182         Resources res = context.getResources();
183         
184         // Create an intent triggered by clicking on the status icon.
185         Intent clickIntent = new Intent();
186         clickIntent.setClass(context, AlertActivity.class);
187         clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
188         
189         // Create an intent triggered by clicking on the "Clear All Notifications" button
190         Intent deleteIntent = new Intent();
191         deleteIntent.setClass(context, AlertReceiver.class);
192         deleteIntent.setAction(DELETE_ACTION);
193         
194         if (title == null || title.length() == 0) {
195             title = res.getString(R.string.no_title_label);
196         }
197         
198         String helperString;
199         if (numReminders > 1) {
200             String format;
201             if (numReminders == 2) {
202                 format = res.getString(R.string.alert_missed_events_single);
203             } else {
204                 format = res.getString(R.string.alert_missed_events_multiple);
205             }
206             helperString = String.format(format, numReminders - 1);
207         } else {
208             helperString = location;
209         }
210         
211         Notification notification = new Notification(
212                 R.drawable.stat_notify_calendar,
213                 null,
214                 System.currentTimeMillis());
215         notification.setLatestEventInfo(context,
216                 title,
217                 helperString,
218                 PendingIntent.getActivity(context, 0, clickIntent, 0));
219         notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0);
220         
221         return notification;
222     }
223 }
224