/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.calendar.alerts; import com.android.calendar.R; import com.android.calendar.Utils; import android.app.Notification; import android.app.Notification.Builder; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.net.Uri; import android.os.PowerManager; import android.text.TextUtils; import android.text.format.DateFormat; import android.text.format.DateUtils; import android.text.format.Time; import android.util.Log; import java.util.Locale; import java.util.TimeZone; /** * Receives android.intent.action.EVENT_REMINDER intents and handles * event reminders. The intent URI specifies an alert id in the * CalendarAlerts database table. This class also receives the * BOOT_COMPLETED intent so that it can add a status bar notification * if there are Calendar event alarms that have not been dismissed. * It also receives the TIME_CHANGED action so that it can fire off * snoozed alarms that have become ready. The real work is done in * the AlertService class. * * To trigger this code after pushing the apk to device: * adb shell am broadcast -a "android.intent.action.EVENT_REMINDER" * -n "com.android.calendar/.alerts.AlertReceiver" */ public class AlertReceiver extends BroadcastReceiver { private static final String TAG = "AlertReceiver"; private static final String DELETE_ACTION = "delete"; static final Object mStartingServiceSync = new Object(); static PowerManager.WakeLock mStartingService; public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders"; @Override public void onReceive(Context context, Intent intent) { if (AlertService.DEBUG) { Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); } if (DELETE_ACTION.equals(intent.getAction())) { /* The user has clicked the "Clear All Notifications" * buttons so dismiss all Calendar alerts. */ // TODO Grab a wake lock here? Intent serviceIntent = new Intent(context, DismissAllAlarmsService.class); context.startService(serviceIntent); } else { Intent i = new Intent(); i.setClass(context, AlertService.class); i.putExtras(intent); i.putExtra("action", intent.getAction()); Uri uri = intent.getData(); // This intent might be a BOOT_COMPLETED so it might not have a Uri. if (uri != null) { i.putExtra("uri", uri.toString()); } beginStartingService(context, i); } } /** * Start the service to process the current event notifications, acquiring * the wake lock before returning to ensure that the service will run. */ public static void beginStartingService(Context context, Intent intent) { synchronized (mStartingServiceSync) { if (mStartingService == null) { PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StartingAlertService"); mStartingService.setReferenceCounted(false); } mStartingService.acquire(); context.startService(intent); } } /** * Called back by the service when it has finished processing notifications, * releasing the wake lock if the service is now stopping. */ public static void finishStartingService(Service service, int startId) { synchronized (mStartingServiceSync) { if (mStartingService != null) { if (service.stopSelfResult(startId)) { mStartingService.release(); } } } } /** * Creates an alert notification. If high priority, this will set * FLAG_HIGH_PRIORITY on the resulting notification and attach the a pending * intent. Otherwise, it creates a standard notification. */ public static Notification makeNewAlertNotification(Context context, String title, String location, int numReminders, boolean highPriority, long startMillis, boolean allDay) { Resources res = context.getResources(); // Create an intent triggered by clicking on the status icon. Intent clickIntent = new Intent(); clickIntent.setClass(context, AlertActivity.class); clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Create an intent triggered by clicking on the "Clear All Notifications" button Intent deleteIntent = new Intent(); deleteIntent.setClass(context, AlertReceiver.class); deleteIntent.setAction(DELETE_ACTION); if (title == null || title.length() == 0) { title = res.getString(R.string.no_title_label); } Builder bob = new Notification.Builder(context); bob.setContentTitle(title); bob.setSmallIcon(R.drawable.stat_notify_calendar); PendingIntent pendingClickIntent = PendingIntent.getActivity( context, 0, clickIntent, 0); bob.setContentIntent(pendingClickIntent); bob.setDeleteIntent(PendingIntent.getBroadcast(context, 0, deleteIntent, 0)); if (highPriority) { bob.setFullScreenIntent(pendingClickIntent, true); } if (numReminders > 1) { bob.setNumber(numReminders); } // Format the second line which shows time and location. // // 1) Show time only for non-all day events // 2) No date for today // 3) Show "tomorrow" for tomorrow // 4) Show date for days beyond that String tz = Utils.getTimeZone(context, null); Time time = new Time(tz); time.setToNow(); int today = Time.getJulianDay(time.toMillis(false), time.gmtoff); time.set(startMillis); int eventDay = Time.getJulianDay(time.toMillis(false), time.gmtoff); int flags = DateUtils.FORMAT_ABBREV_ALL; if (!allDay) { flags |= DateUtils.FORMAT_SHOW_TIME; if (DateFormat.is24HourFormat(context)) { flags |= DateUtils.FORMAT_24HOUR; } } else { flags |= DateUtils.FORMAT_UTC; } if (eventDay > today + 1) { flags |= DateUtils.FORMAT_SHOW_DATE; } StringBuilder sb = new StringBuilder(Utils.formatDateRange(context, startMillis, startMillis, flags)); if (!allDay && tz != Time.getCurrentTimezone()) { // Assumes time was set to the current tz time.set(startMillis); boolean isDST = time.isDst != 0; sb.append(" ").append(TimeZone.getTimeZone(tz).getDisplayName( isDST, TimeZone.SHORT, Locale.getDefault())); } if (eventDay == today + 1) { // Tomorrow sb.append(", "); sb.append(context.getString(R.string.tomorrow)); } String loc; if (location != null && !TextUtils.isEmpty(loc = location.trim())) { sb.append(", "); sb.append(loc); } bob.setContentText(sb.toString()); return bob.getNotification(); } }