AlertReceiver.java revision ea388edb0fa1588732c98bdaeced1357a2bee82a
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 17package com.android.calendar.alerts; 18 19import com.android.calendar.R; 20import com.android.calendar.Utils; 21 22import android.app.Notification; 23import android.app.Notification.Builder; 24import android.app.PendingIntent; 25import android.app.Service; 26import android.content.BroadcastReceiver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.res.Resources; 30import android.net.Uri; 31import android.os.PowerManager; 32import android.text.TextUtils; 33import android.text.format.DateFormat; 34import android.text.format.DateUtils; 35import android.text.format.Time; 36import android.util.Log; 37 38import java.util.Locale; 39import java.util.TimeZone; 40 41/** 42 * Receives android.intent.action.EVENT_REMINDER intents and handles 43 * event reminders. The intent URI specifies an alert id in the 44 * CalendarAlerts database table. This class also receives the 45 * BOOT_COMPLETED intent so that it can add a status bar notification 46 * if there are Calendar event alarms that have not been dismissed. 47 * It also receives the TIME_CHANGED action so that it can fire off 48 * snoozed alarms that have become ready. The real work is done in 49 * the AlertService class. 50 * 51 * To trigger this code after pushing the apk to device: 52 * adb shell am broadcast -a "android.intent.action.EVENT_REMINDER" 53 * -n "com.android.calendar/.alerts.AlertReceiver" 54 */ 55public class AlertReceiver extends BroadcastReceiver { 56 private static final String TAG = "AlertReceiver"; 57 58 private static final String DELETE_ACTION = "delete"; 59 60 static final Object mStartingServiceSync = new Object(); 61 static PowerManager.WakeLock mStartingService; 62 63 @Override 64 public void onReceive(Context context, Intent intent) { 65 if (AlertService.DEBUG) { 66 Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); 67 } 68 69 if (DELETE_ACTION.equals(intent.getAction())) { 70 71 /* The user has clicked the "Clear All Notifications" 72 * buttons so dismiss all Calendar alerts. 73 */ 74 // TODO Grab a wake lock here? 75 Intent serviceIntent = new Intent(context, DismissAllAlarmsService.class); 76 context.startService(serviceIntent); 77 } else { 78 Intent i = new Intent(); 79 i.setClass(context, AlertService.class); 80 i.putExtras(intent); 81 i.putExtra("action", intent.getAction()); 82 Uri uri = intent.getData(); 83 84 // This intent might be a BOOT_COMPLETED so it might not have a Uri. 85 if (uri != null) { 86 i.putExtra("uri", uri.toString()); 87 } 88 beginStartingService(context, i); 89 } 90 } 91 92 /** 93 * Start the service to process the current event notifications, acquiring 94 * the wake lock before returning to ensure that the service will run. 95 */ 96 public static void beginStartingService(Context context, Intent intent) { 97 synchronized (mStartingServiceSync) { 98 if (mStartingService == null) { 99 PowerManager pm = 100 (PowerManager)context.getSystemService(Context.POWER_SERVICE); 101 mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 102 "StartingAlertService"); 103 mStartingService.setReferenceCounted(false); 104 } 105 mStartingService.acquire(); 106 context.startService(intent); 107 } 108 } 109 110 /** 111 * Called back by the service when it has finished processing notifications, 112 * releasing the wake lock if the service is now stopping. 113 */ 114 public static void finishStartingService(Service service, int startId) { 115 synchronized (mStartingServiceSync) { 116 if (mStartingService != null) { 117 if (service.stopSelfResult(startId)) { 118 mStartingService.release(); 119 } 120 } 121 } 122 } 123 124 /** 125 * Creates an alert notification. If high priority, this will set 126 * FLAG_HIGH_PRIORITY on the resulting notification and attach the a pending 127 * intent. Otherwise, it creates a standard notification. 128 */ 129 public static Notification makeNewAlertNotification(Context context, 130 String title, String location, int numReminders, 131 boolean highPriority, long startMillis, boolean allDay) { 132 Resources res = context.getResources(); 133 134 // Create an intent triggered by clicking on the status icon. 135 Intent clickIntent = new Intent(); 136 clickIntent.setClass(context, AlertActivity.class); 137 clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 138 139 // Create an intent triggered by clicking on the "Clear All Notifications" button 140 Intent deleteIntent = new Intent(); 141 deleteIntent.setClass(context, AlertReceiver.class); 142 deleteIntent.setAction(DELETE_ACTION); 143 144 if (title == null || title.length() == 0) { 145 title = res.getString(R.string.no_title_label); 146 } 147 148 Builder bob = new Notification.Builder(context); 149 bob.setContentTitle(title); 150 bob.setSmallIcon(R.drawable.stat_notify_calendar); 151 152 PendingIntent pendingClickIntent = PendingIntent.getActivity( 153 context, 0, clickIntent, 0); 154 bob.setContentIntent(pendingClickIntent); 155 bob.setDeleteIntent(PendingIntent.getBroadcast(context, 0, deleteIntent, 0)); 156 if (highPriority) { 157 bob.setFullScreenIntent(pendingClickIntent, true); 158 } 159 160 if (numReminders > 1) { 161 bob.setNumber(numReminders); 162 } 163 164 // Format the second line which shows time and location. 165 // 166 // 1) Show time only for non-all day events 167 // 2) No date for today 168 // 3) Show "tomorrow" for tomorrow 169 // 4) Show date for days beyond that 170 171 String tz = Utils.getTimeZone(context, null); 172 Time time = new Time(tz); 173 time.setToNow(); 174 int today = Time.getJulianDay(time.toMillis(false), time.gmtoff); 175 time.set(startMillis); 176 int eventDay = Time.getJulianDay(time.toMillis(false), time.gmtoff); 177 178 int flags = DateUtils.FORMAT_ABBREV_ALL; 179 if (!allDay) { 180 flags |= DateUtils.FORMAT_SHOW_TIME; 181 if (DateFormat.is24HourFormat(context)) { 182 flags |= DateUtils.FORMAT_24HOUR; 183 } 184 } else { 185 tz = Time.TIMEZONE_UTC; 186 } 187 188 if (eventDay > today + 1) { 189 flags |= DateUtils.FORMAT_SHOW_DATE; 190 } 191 192 StringBuilder sb = new StringBuilder(Utils.formatDateRange(context, startMillis, 193 startMillis, flags)); 194 195 if (!allDay && tz != Time.getCurrentTimezone()) { 196 // Assumes time was set to the current tz 197 time.set(startMillis); 198 boolean isDST = time.isDst != 0; 199 sb.append(" ").append(TimeZone.getTimeZone(tz).getDisplayName( 200 isDST, TimeZone.SHORT, Locale.getDefault())); 201 } 202 203 if (eventDay == today + 1) { 204 // Tomorrow 205 sb.append(", "); 206 sb.append(context.getString(R.string.tomorrow)); 207 } 208 209 String loc; 210 if (location != null && !TextUtils.isEmpty(loc = location.trim())) { 211 sb.append(", "); 212 sb.append(loc); 213 } 214 bob.setContentText(sb.toString()); 215 216 return bob.getNotification(); 217 } 218} 219 220