1146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/*
2146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Copyright (C) 2007 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
1723e7da3eacee7bceb105cdfc7b5329c7a43846d5Mason Tangpackage com.android.calendar.alerts;
1823e7da3eacee7bceb105cdfc7b5329c7a43846d5Mason Tang
19146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.Notification;
20146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.PendingIntent;
21146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.Service;
22146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.BroadcastReceiver;
2342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Tingimport android.content.ContentUris;
24146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Context;
25146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Intent;
26146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.res.Resources;
271946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Tingimport android.database.Cursor;
28146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.net.Uri;
29247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Tingimport android.os.Handler;
30247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Tingimport android.os.HandlerThread;
31146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.PowerManager;
321946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Tingimport android.provider.CalendarContract.Attendees;
331946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Tingimport android.provider.CalendarContract.Calendars;
3442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Tingimport android.provider.CalendarContract.Events;
357e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzsteinimport android.telephony.TelephonyManager;
367e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzsteinimport android.text.Spannable;
374e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Tingimport android.text.SpannableStringBuilder;
38ea388edb0fa1588732c98bdaeced1357a2bee82aMichael Chanimport android.text.TextUtils;
39f6deec2a00340052f074715ad7e95d2671aebcd7Sara Tingimport android.text.style.RelativeSizeSpan;
4042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Tingimport android.text.style.TextAppearanceSpan;
417e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzsteinimport android.text.style.URLSpan;
42e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chanimport android.util.Log;
43855078e178cbe135b76a7e8deb75e849ca97773eSara Tingimport android.view.View;
44855078e178cbe135b76a7e8deb75e849ca97773eSara Tingimport android.widget.RemoteViews;
45146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4625b09db869f377b131052447f62ad02e2505cd87Michael Chanimport com.android.calendar.R;
4725b09db869f377b131052447f62ad02e2505cd87Michael Chanimport com.android.calendar.Utils;
4825b09db869f377b131052447f62ad02e2505cd87Michael Chanimport com.android.calendar.alerts.AlertService.NotificationWrapper;
4925b09db869f377b131052447f62ad02e2505cd87Michael Chan
501946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Tingimport java.util.ArrayList;
514e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Tingimport java.util.List;
52477442a0af36c4b306e603f33d3774a5aa82eccaSara Tingimport java.util.regex.Pattern;
53a99feb99d4bab964e66f952d005ede6c998ae11bRoboErik
54146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/**
55146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Receives android.intent.action.EVENT_REMINDER intents and handles
56e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan * event reminders.  The intent URI specifies an alert id in the
57146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * CalendarAlerts database table.  This class also receives the
58146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * BOOT_COMPLETED intent so that it can add a status bar notification
59146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * if there are Calendar event alarms that have not been dismissed.
60146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * It also receives the TIME_CHANGED action so that it can fire off
61146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * snoozed alarms that have become ready.  The real work is done in
62146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * the AlertService class.
637321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan *
647321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan * To trigger this code after pushing the apk to device:
657321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan * adb shell am broadcast -a "android.intent.action.EVENT_REMINDER"
667321a0630aca3e5093d12f0e4f55da77620f53edMichael Chan *    -n "com.android.calendar/.alerts.AlertReceiver"
67146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project */
68146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectpublic class AlertReceiver extends BroadcastReceiver {
69e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan    private static final String TAG = "AlertReceiver";
70e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
717e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static final String MAP_ACTION = "com.android.calendar.MAP";
727e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static final String CALL_ACTION = "com.android.calendar.CALL";
73247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    private static final String MAIL_ACTION = "com.android.calendar.MAIL";
74247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    private static final String EXTRA_EVENT_ID = "eventid";
75e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
763a07a68da6460c36a5dbec5b8828baa4355dbe04Sara Ting    // The broadcast for notification refreshes scheduled by the app. This is to
773a07a68da6460c36a5dbec5b8828baa4355dbe04Sara Ting    // distinguish the EVENT_REMINDER broadcast sent by the provider.
783a07a68da6460c36a5dbec5b8828baa4355dbe04Sara Ting    public static final String EVENT_REMINDER_APP_ACTION =
793a07a68da6460c36a5dbec5b8828baa4355dbe04Sara Ting            "com.android.calendar.EVENT_REMINDER_APP";
803a07a68da6460c36a5dbec5b8828baa4355dbe04Sara Ting
81146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    static final Object mStartingServiceSync = new Object();
82146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    static PowerManager.WakeLock mStartingService;
83477442a0af36c4b306e603f33d3774a5aa82eccaSara Ting    private static final Pattern mBlankLinePattern = Pattern.compile("^\\s*$[\n\r]",
84477442a0af36c4b306e603f33d3774a5aa82eccaSara Ting            Pattern.MULTILINE);
85e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
8643ffa461b738692925a94c005c90ff60757455a7RoboErik    public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders";
874e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting    private static final int NOTIFICATION_DIGEST_MAX_LENGTH = 3;
8843ffa461b738692925a94c005c90ff60757455a7RoboErik
897e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static final String GEO_PREFIX = "geo:";
907e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static final String TEL_PREFIX = "tel:";
917e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static final int MAX_NOTIF_ACTIONS = 3;
927e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
93247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    private static Handler sAsyncHandler;
94247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    static {
95247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        HandlerThread thr = new HandlerThread("AlertReceiver async");
96247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        thr.start();
97247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        sAsyncHandler = new Handler(thr.getLooper());
98247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    }
99247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting
100146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
101247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    public void onReceive(final Context context, final Intent intent) {
102e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        if (AlertService.DEBUG) {
103e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan            Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString());
104e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        }
1059675253917e4207a811386ace164f5238ec0d25cPaul Sliwowski        if (MAP_ACTION.equals(intent.getAction())) {
1067e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // Try starting the map action.
1077e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // If no map location is found (something changed since the notification was originally
1087e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // fired), update the notifications to express this change.
1097e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1);
1107e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (eventId != -1) {
1117e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                URLSpan[] urlSpans = getURLSpans(context, eventId);
1127e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                Intent geoIntent = createMapActivityIntent(context, urlSpans);
1137e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                if (geoIntent != null) {
1147e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    // Location was successfully found, so dismiss the shade and start maps.
1157e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    context.startActivity(geoIntent);
1167e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    closeNotificationShade(context);
1177e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                } else {
1187e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    // No location was found, so update all notifications.
1197e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    // Our alert service does not currently allow us to specify only one
1207e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    // specific notification to refresh.
1217e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    AlertService.updateAlertNotification(context);
1227e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                }
1237e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
1247e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        } else if (CALL_ACTION.equals(intent.getAction())) {
1257e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // Try starting the call action.
1267e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // If no call location is found (something changed since the notification was originally
1277e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // fired), update the notifications to express this change.
1287e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1);
1297e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (eventId != -1) {
1307e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                URLSpan[] urlSpans = getURLSpans(context, eventId);
1317e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                Intent callIntent = createCallActivityIntent(context, urlSpans);
1327e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                if (callIntent != null) {
1337e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    // Call location was successfully found, so dismiss the shade and start dialer.
1347e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    context.startActivity(callIntent);
1357e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    closeNotificationShade(context);
1367e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                } else {
1377e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    // No call location was found, so update all notifications.
1387e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    // Our alert service does not currently allow us to specify only one
1397e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    // specific notification to refresh.
1407e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    AlertService.updateAlertNotification(context);
1417e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                }
1427e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
143247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        } else if (MAIL_ACTION.equals(intent.getAction())) {
1447e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            closeNotificationShade(context);
145247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting
146247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            // Now start the email intent.
147247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1);
148247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            if (eventId != -1) {
149e98dca7e0042f7b36788b0e5d8dc6e94048c895cMichael Chan                Intent i = new Intent(context, QuickResponseActivity.class);
150e98dca7e0042f7b36788b0e5d8dc6e94048c895cMichael Chan                i.putExtra(QuickResponseActivity.EXTRA_EVENT_ID, eventId);
151e98dca7e0042f7b36788b0e5d8dc6e94048c895cMichael Chan                i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
152e98dca7e0042f7b36788b0e5d8dc6e94048c895cMichael Chan                context.startActivity(i);
153247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            }
154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Intent i = new Intent();
156146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            i.setClass(context, AlertService.class);
157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            i.putExtras(intent);
158146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            i.putExtra("action", intent.getAction());
159146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Uri uri = intent.getData();
160e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
161146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // This intent might be a BOOT_COMPLETED so it might not have a Uri.
162146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (uri != null) {
163146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                i.putExtra("uri", uri.toString());
164146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
165146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            beginStartingService(context, i);
166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Start the service to process the current event notifications, acquiring
171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * the wake lock before returning to ensure that the service will run.
172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public static void beginStartingService(Context context, Intent intent) {
174146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        synchronized (mStartingServiceSync) {
175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mStartingService == null) {
176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                PowerManager pm =
177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    (PowerManager)context.getSystemService(Context.POWER_SERVICE);
178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
179146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        "StartingAlertService");
180146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mStartingService.setReferenceCounted(false);
181146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
182146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mStartingService.acquire();
183146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            context.startService(intent);
184146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
185146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
186e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
187146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
188146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Called back by the service when it has finished processing notifications,
189146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * releasing the wake lock if the service is now stopping.
190146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public static void finishStartingService(Service service, int startId) {
192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        synchronized (mStartingServiceSync) {
193146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mStartingService != null) {
194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (service.stopSelfResult(startId)) {
195146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mStartingService.release();
196146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
197146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
199146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
200146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
20142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    private static PendingIntent createClickEventIntent(Context context, long eventId,
20242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting            long startMillis, long endMillis, int notificationId) {
20342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        return createDismissAlarmsIntent(context, eventId, startMillis, endMillis, notificationId,
204c0e5679492faf8b3d437e658449083324d062119Paul Sliwowski                DismissAlarmsService.SHOW_ACTION);
20542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    }
20642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
20742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    private static PendingIntent createDeleteEventIntent(Context context, long eventId,
20842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting            long startMillis, long endMillis, int notificationId) {
20942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        return createDismissAlarmsIntent(context, eventId, startMillis, endMillis, notificationId,
210c0e5679492faf8b3d437e658449083324d062119Paul Sliwowski                DismissAlarmsService.DISMISS_ACTION);
21142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    }
21242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
21342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    private static PendingIntent createDismissAlarmsIntent(Context context, long eventId,
214c0e5679492faf8b3d437e658449083324d062119Paul Sliwowski            long startMillis, long endMillis, int notificationId, String action) {
21542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        Intent intent = new Intent();
21642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.setClass(context, DismissAlarmsService.class);
217c0e5679492faf8b3d437e658449083324d062119Paul Sliwowski        intent.setAction(action);
21842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.putExtra(AlertUtils.EVENT_ID_KEY, eventId);
21942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.putExtra(AlertUtils.EVENT_START_KEY, startMillis);
22042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.putExtra(AlertUtils.EVENT_END_KEY, endMillis);
22142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.putExtra(AlertUtils.NOTIFICATION_ID_KEY, notificationId);
22242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
22342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // Must set a field that affects Intent.filterEquals so that the resulting
22442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // PendingIntent will be a unique instance (the 'extras' don't achieve this).
22542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // This must be unique for the click event across all reminders (so using
22642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // event ID + startTime should be unique).  This also must be unique from
22742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // the delete event (which also uses DismissAlarmsService).
22842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        Uri.Builder builder = Events.CONTENT_URI.buildUpon();
22942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        ContentUris.appendId(builder, eventId);
23042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        ContentUris.appendId(builder, startMillis);
23142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.setData(builder.build());
23242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
23342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    }
23442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
23542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    private static PendingIntent createSnoozeIntent(Context context, long eventId,
23642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting            long startMillis, long endMillis, int notificationId) {
23742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        Intent intent = new Intent();
23842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.setClass(context, SnoozeAlarmsService.class);
23942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.putExtra(AlertUtils.EVENT_ID_KEY, eventId);
24042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.putExtra(AlertUtils.EVENT_START_KEY, startMillis);
24142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.putExtra(AlertUtils.EVENT_END_KEY, endMillis);
24242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.putExtra(AlertUtils.NOTIFICATION_ID_KEY, notificationId);
24342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
24442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        Uri.Builder builder = Events.CONTENT_URI.buildUpon();
24542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        ContentUris.appendId(builder, eventId);
24642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        ContentUris.appendId(builder, startMillis);
24742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        intent.setData(builder.build());
24842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
24942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    }
25042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
251567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting    private static PendingIntent createAlertActivityIntent(Context context) {
252567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting        Intent clickIntent = new Intent();
253567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting        clickIntent.setClass(context, AlertActivity.class);
254567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting        clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
255567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting        return PendingIntent.getActivity(context, 0, clickIntent,
256567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting                    PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
257567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting    }
258567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting
25925b09db869f377b131052447f62ad02e2505cd87Michael Chan    public static NotificationWrapper makeBasicNotification(Context context, String title,
26042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting            String summaryText, long startMillis, long endMillis, long eventId,
261487d52c2789114e0ee3e7ce85694611b8d59dd70Sara Ting            int notificationId, boolean doPopup, int priority) {
262855078e178cbe135b76a7e8deb75e849ca97773eSara Ting        Notification n = buildBasicNotification(new Notification.Builder(context),
263855078e178cbe135b76a7e8deb75e849ca97773eSara Ting                context, title, summaryText, startMillis, endMillis, eventId, notificationId,
264487d52c2789114e0ee3e7ce85694611b8d59dd70Sara Ting                doPopup, priority, false);
26525b09db869f377b131052447f62ad02e2505cd87Michael Chan        return new NotificationWrapper(n, notificationId, eventId, startMillis, endMillis, doPopup);
26642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    }
26742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
268855078e178cbe135b76a7e8deb75e849ca97773eSara Ting    private static Notification buildBasicNotification(Notification.Builder notificationBuilder,
269855078e178cbe135b76a7e8deb75e849ca97773eSara Ting            Context context, String title, String summaryText, long startMillis, long endMillis,
270487d52c2789114e0ee3e7ce85694611b8d59dd70Sara Ting            long eventId, int notificationId, boolean doPopup, int priority,
271855078e178cbe135b76a7e8deb75e849ca97773eSara Ting            boolean addActionButtons) {
27242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        Resources resources = context.getResources();
27342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        if (title == null || title.length() == 0) {
27442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting            title = resources.getString(R.string.no_title_label);
27542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        }
27642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
27742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // Create an intent triggered by clicking on the status icon, that dismisses the
27842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // notification and shows the event.
27942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        PendingIntent clickIntent = createClickEventIntent(context, eventId, startMillis,
28042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting                endMillis, notificationId);
28142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
28242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // Create a delete intent triggered by dismissing the notification.
28342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        PendingIntent deleteIntent = createDeleteEventIntent(context, eventId, startMillis,
28442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting            endMillis, notificationId);
28542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
28642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // Create the base notification.
28742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        notificationBuilder.setContentTitle(title);
28842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        notificationBuilder.setContentText(summaryText);
28942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        notificationBuilder.setSmallIcon(R.drawable.stat_notify_calendar);
29042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        notificationBuilder.setContentIntent(clickIntent);
29142ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        notificationBuilder.setDeleteIntent(deleteIntent);
29242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        if (doPopup) {
293567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting            notificationBuilder.setFullScreenIntent(createAlertActivityIntent(context), true);
29442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        }
29542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
2967e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        PendingIntent mapIntent = null, callIntent = null, snoozeIntent = null, emailIntent = null;
297855078e178cbe135b76a7e8deb75e849ca97773eSara Ting        if (addActionButtons) {
2987e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // Send map, call, and email intent back to ourself first for a couple reasons:
2997e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // 1) Workaround issue where clicking action button in notification does
3007e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            //    not automatically close the notification shade.
3017e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // 2) Event information will always be up to date.
3027e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
3037e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // Create map and/or call intents.
3047e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            URLSpan[] urlSpans = getURLSpans(context, eventId);
3057e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            mapIntent = createMapBroadcastIntent(context, urlSpans, eventId);
3067e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            callIntent = createCallBroadcastIntent(context, urlSpans, eventId);
307855078e178cbe135b76a7e8deb75e849ca97773eSara Ting
308855078e178cbe135b76a7e8deb75e849ca97773eSara Ting            // Create email intent for emailing attendees.
309af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            emailIntent = createBroadcastMailIntent(context, eventId, title);
3107e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
3117e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // Create snooze intent.  TODO: change snooze to 10 minutes.
3127e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            snoozeIntent = createSnoozeIntent(context, eventId, startMillis, endMillis,
3137e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                    notificationId);
314af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting        }
315487d52c2789114e0ee3e7ce85694611b8d59dd70Sara Ting
316fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting        if (Utils.isJellybeanOrLater()) {
317af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            // Turn off timestamp.
318af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            notificationBuilder.setWhen(0);
319855078e178cbe135b76a7e8deb75e849ca97773eSara Ting
320487d52c2789114e0ee3e7ce85694611b8d59dd70Sara Ting            // Should be one of the values in Notification (ie. Notification.PRIORITY_HIGH, etc).
321487d52c2789114e0ee3e7ce85694611b8d59dd70Sara Ting            // A higher priority will encourage notification manager to expand it.
322487d52c2789114e0ee3e7ce85694611b8d59dd70Sara Ting            notificationBuilder.setPriority(priority);
323855078e178cbe135b76a7e8deb75e849ca97773eSara Ting
3247e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // Add action buttons. Show at most three, using the following priority ordering:
3257e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // 1. Map
3267e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // 2. Call
3277e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // 3. Email
3287e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // 4. Snooze
3297e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // Actions will only be shown if they are applicable; i.e. with no location, map will
3307e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // not be shown, and with no recipients, snooze will not be shown.
3317e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            // TODO: Get icons, get strings. Maybe show preview of actual location/number?
3327e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            int numActions = 0;
3337e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (mapIntent != null && numActions < MAX_NOTIF_ACTIONS) {
3347e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                notificationBuilder.addAction(R.drawable.ic_map,
3357e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                        resources.getString(R.string.map_label), mapIntent);
3367e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                numActions++;
337af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            }
3387e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (callIntent != null && numActions < MAX_NOTIF_ACTIONS) {
3397e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                notificationBuilder.addAction(R.drawable.ic_call,
3407e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                        resources.getString(R.string.call_label), callIntent);
3417e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                numActions++;
3427e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
3437e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (emailIntent != null && numActions < MAX_NOTIF_ACTIONS) {
344af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting                notificationBuilder.addAction(R.drawable.ic_menu_email_holo_dark,
345af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting                        resources.getString(R.string.email_guests_label), emailIntent);
3467e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                numActions++;
3477e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
3487e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (snoozeIntent != null && numActions < MAX_NOTIF_ACTIONS) {
3497e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                notificationBuilder.addAction(R.drawable.ic_alarm_holo_dark,
3507e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                        resources.getString(R.string.snooze_label), snoozeIntent);
3517e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                numActions++;
352af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            }
353af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            return notificationBuilder.getNotification();
354af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting
355af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting        } else {
356af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            // Old-style notification (pre-JB).  Use custom view with buttons to provide
357af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            // JB-like functionality (snooze/email).
358af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            Notification n = notificationBuilder.getNotification();
359af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting
360af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            // Use custom view with buttons to provide JB-like functionality (snooze/email).
361af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            RemoteViews contentView = new RemoteViews(context.getPackageName(),
362af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting                    R.layout.notification);
363af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setImageViewResource(R.id.image, R.drawable.stat_notify_calendar);
364af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setTextViewText(R.id.title,  title);
365af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setTextViewText(R.id.text, summaryText);
3667e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
3677e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            int numActions = 0;
3687e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (mapIntent == null || numActions >= MAX_NOTIF_ACTIONS) {
3697e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setViewVisibility(R.id.map_button, View.GONE);
370fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            } else {
3717e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setViewVisibility(R.id.map_button, View.VISIBLE);
3727e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setOnClickPendingIntent(R.id.map_button, mapIntent);
373af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting                contentView.setViewVisibility(R.id.end_padding, View.GONE);
3747e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                numActions++;
375fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            }
3767e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (callIntent == null || numActions >= MAX_NOTIF_ACTIONS) {
3777e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setViewVisibility(R.id.call_button, View.GONE);
3787e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            } else {
3797e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setViewVisibility(R.id.call_button, View.VISIBLE);
3807e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setOnClickPendingIntent(R.id.call_button, callIntent);
3817e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setViewVisibility(R.id.end_padding, View.GONE);
3827e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                numActions++;
3837e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
3847e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (emailIntent == null || numActions >= MAX_NOTIF_ACTIONS) {
385af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting                contentView.setViewVisibility(R.id.email_button, View.GONE);
386af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            } else {
387af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting                contentView.setViewVisibility(R.id.email_button, View.VISIBLE);
388af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting                contentView.setOnClickPendingIntent(R.id.email_button, emailIntent);
389af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting                contentView.setViewVisibility(R.id.end_padding, View.GONE);
3907e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                numActions++;
391fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            }
3927e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (snoozeIntent == null || numActions >= MAX_NOTIF_ACTIONS) {
3937e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setViewVisibility(R.id.snooze_button, View.GONE);
3947e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            } else {
3957e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setViewVisibility(R.id.snooze_button, View.VISIBLE);
3967e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setOnClickPendingIntent(R.id.snooze_button, snoozeIntent);
3977e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                contentView.setViewVisibility(R.id.end_padding, View.GONE);
3987e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                numActions++;
3997e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
4007e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
401af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            n.contentView = contentView;
402af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting
403af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            return n;
40442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        }
40542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    }
40642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
40742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    /**
40842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting     * Creates an expanding notification.  The initial expanded state is decided by
40942ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting     * the notification manager based on the priority.
41042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting     */
41125b09db869f377b131052447f62ad02e2505cd87Michael Chan    public static NotificationWrapper makeExpandingNotification(Context context, String title,
41242ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting            String summaryText, String description, long startMillis, long endMillis, long eventId,
413487d52c2789114e0ee3e7ce85694611b8d59dd70Sara Ting            int notificationId, boolean doPopup, int priority) {
414855078e178cbe135b76a7e8deb75e849ca97773eSara Ting        Notification.Builder basicBuilder = new Notification.Builder(context);
415855078e178cbe135b76a7e8deb75e849ca97773eSara Ting        Notification notification = buildBasicNotification(basicBuilder, context, title,
416855078e178cbe135b76a7e8deb75e849ca97773eSara Ting                summaryText, startMillis, endMillis, eventId, notificationId, doPopup,
417487d52c2789114e0ee3e7ce85694611b8d59dd70Sara Ting                priority, true);
418fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting        if (Utils.isJellybeanOrLater()) {
419fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            // Create a new-style expanded notification
420244d94f9a1a433fd3403452f2cdeba5c862b9650Daniel Sandler            Notification.BigTextStyle expandedBuilder = new Notification.BigTextStyle();
421fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            if (description != null) {
422fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                description = mBlankLinePattern.matcher(description).replaceAll("");
423fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                description = description.trim();
424fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            }
425fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            CharSequence text;
426fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            if (TextUtils.isEmpty(description)) {
427fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                text = summaryText;
428fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            } else {
429fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
430fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                stringBuilder.append(summaryText);
431fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                stringBuilder.append("\n\n");
432fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                stringBuilder.setSpan(new RelativeSizeSpan(0.5f), summaryText.length(),
433fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        stringBuilder.length(), 0);
434fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                stringBuilder.append(description);
435fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                text = stringBuilder;
436fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            }
437fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            expandedBuilder.bigText(text);
438244d94f9a1a433fd3403452f2cdeba5c862b9650Daniel Sandler            basicBuilder.setStyle(expandedBuilder);
439244d94f9a1a433fd3403452f2cdeba5c862b9650Daniel Sandler            notification = basicBuilder.build();
44042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        }
441855078e178cbe135b76a7e8deb75e849ca97773eSara Ting        return new NotificationWrapper(notification, notificationId, eventId, startMillis,
442855078e178cbe135b76a7e8deb75e849ca97773eSara Ting                endMillis, doPopup);
44342ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting    }
44442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
445d23299d34131ad917f1e84fdd8b48c28171239d5Mason Tang    /**
44642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting     * Creates an expanding digest notification for expired events.
447d23299d34131ad917f1e84fdd8b48c28171239d5Mason Tang     */
44825b09db869f377b131052447f62ad02e2505cd87Michael Chan    public static NotificationWrapper makeDigestNotification(Context context,
449660f1b4ab88bed6e8066ad05b324e95a9940cf73Sara Ting            ArrayList<AlertService.NotificationInfo> notificationInfos, String digestTitle,
4508748724e382ca014067a3ceb5ff4eacbd9c4021aSara Ting            boolean expandable) {
4514e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting        if (notificationInfos == null || notificationInfos.size() < 1) {
4524e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting            return null;
4534e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting        }
4544e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting
455146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Resources res = context.getResources();
4564e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting        int numEvents = notificationInfos.size();
457660f1b4ab88bed6e8066ad05b324e95a9940cf73Sara Ting        long[] eventIds = new long[notificationInfos.size()];
458b07322485b83b86f5807faacbb6a18e38bec5d6bChris Wren        long[] startMillis = new long[notificationInfos.size()];
459660f1b4ab88bed6e8066ad05b324e95a9940cf73Sara Ting        for (int i = 0; i < notificationInfos.size(); i++) {
460660f1b4ab88bed6e8066ad05b324e95a9940cf73Sara Ting            eventIds[i] = notificationInfos.get(i).eventId;
461b07322485b83b86f5807faacbb6a18e38bec5d6bChris Wren            startMillis[i] = notificationInfos.get(i).startMillis;
462660f1b4ab88bed6e8066ad05b324e95a9940cf73Sara Ting        }
4634e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting
46442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // Create an intent triggered by clicking on the status icon that shows the alerts list.
465567b626f5e9fe2e6dadf1bd017426c8d08e98b51Sara Ting        PendingIntent pendingClickIntent = createAlertActivityIntent(context);
466e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
46742ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // Create an intent triggered by dismissing the digest notification that clears all
46842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        // expired events.
469146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Intent deleteIntent = new Intent();
47042ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        deleteIntent.setClass(context, DismissAlarmsService.class);
471c0e5679492faf8b3d437e658449083324d062119Paul Sliwowski        deleteIntent.setAction(DismissAlarmsService.DISMISS_ACTION);
472660f1b4ab88bed6e8066ad05b324e95a9940cf73Sara Ting        deleteIntent.putExtra(AlertUtils.EVENT_IDS_KEY, eventIds);
473b07322485b83b86f5807faacbb6a18e38bec5d6bChris Wren        deleteIntent.putExtra(AlertUtils.EVENT_STARTS_KEY, startMillis);
47442ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        PendingIntent pendingDeleteIntent = PendingIntent.getService(context, 0, deleteIntent,
47542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting                PendingIntent.FLAG_UPDATE_CURRENT);
476e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4774e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting        if (digestTitle == null || digestTitle.length() == 0) {
4784e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting            digestTitle = res.getString(R.string.no_title_label);
479146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
480e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4814e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting        Notification.Builder notificationBuilder = new Notification.Builder(context);
482ab94b747c63244ed2e406e886f19b308d7b57fc6Sara Ting        notificationBuilder.setContentText(digestTitle);
4835797bd07fb7b05cb6ce60b8ca1129aa6cab2152aSara Ting        notificationBuilder.setSmallIcon(R.drawable.stat_notify_calendar_multiple);
4844e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting        notificationBuilder.setContentIntent(pendingClickIntent);
48542ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        notificationBuilder.setDeleteIntent(pendingDeleteIntent);
48642ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting        String nEventsStr = res.getQuantityString(R.plurals.Nevents, numEvents, numEvents);
487ab94b747c63244ed2e406e886f19b308d7b57fc6Sara Ting        notificationBuilder.setContentTitle(nEventsStr);
48842ba5efed5945b0e96735ec9ca4b388ae35b56f7Sara Ting
48925b09db869f377b131052447f62ad02e2505cd87Michael Chan        Notification n;
490fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting        if (Utils.isJellybeanOrLater()) {
491fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            // New-style notification...
492fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting
493fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            // Set to min priority to encourage the notification manager to collapse it.
494fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            notificationBuilder.setPriority(Notification.PRIORITY_MIN);
495fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting
496fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            if (expandable) {
497fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                // Multiple reminders.  Combine into an expanded digest notification.
498244d94f9a1a433fd3403452f2cdeba5c862b9650Daniel Sandler                Notification.InboxStyle expandedBuilder = new Notification.InboxStyle();
499fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                int i = 0;
500fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                for (AlertService.NotificationInfo info : notificationInfos) {
501fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                    if (i < NOTIFICATION_DIGEST_MAX_LENGTH) {
502fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        String name = info.eventName;
503fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        if (TextUtils.isEmpty(name)) {
504fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                            name = context.getResources().getString(R.string.no_title_label);
505fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        }
506fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        String timeLocation = AlertUtils.formatTimeLocation(context,
507fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                                info.startMillis, info.allDay, info.location);
508fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting
509fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        TextAppearanceSpan primaryTextSpan = new TextAppearanceSpan(context,
510fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                                R.style.NotificationPrimaryText);
511fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        TextAppearanceSpan secondaryTextSpan = new TextAppearanceSpan(context,
512fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                                R.style.NotificationSecondaryText);
513fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting
514fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        // Event title in bold.
515fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
516fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        stringBuilder.append(name);
517fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        stringBuilder.setSpan(primaryTextSpan, 0, stringBuilder.length(), 0);
518fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        stringBuilder.append("  ");
519fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting
520fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        // Followed by time and location.
521fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        int secondaryIndex = stringBuilder.length();
522fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        stringBuilder.append(timeLocation);
523fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        stringBuilder.setSpan(secondaryTextSpan, secondaryIndex,
524fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                                stringBuilder.length(), 0);
525fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        expandedBuilder.addLine(stringBuilder);
526fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        i++;
527fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                    } else {
528fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                        break;
5298748724e382ca014067a3ceb5ff4eacbd9c4021aSara Ting                    }
5309881907c47b2658fa85954bfb339c4b1eab9fc8eIsaac Katzenelson                }
5314e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting
532fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                // If there are too many to display, add "+X missed events" for the last line.
533fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                int remaining = numEvents - i;
534fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                if (remaining > 0) {
535fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                    String nMoreEventsStr = res.getQuantityString(R.plurals.N_remaining_events,
536fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                            remaining, remaining);
537fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                    // TODO: Add highlighting and icon to this last entry once framework allows it.
538fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                    expandedBuilder.setSummaryText(nMoreEventsStr);
539fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                }
5404e9544570d6df4cf4e8ae2f3ca9ff47b84bf899dSara Ting
541fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                // Remove the title in the expanded form (redundant with the listed items).
542fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting                expandedBuilder.setBigContentTitle("");
543244d94f9a1a433fd3403452f2cdeba5c862b9650Daniel Sandler                notificationBuilder.setStyle(expandedBuilder);
544fac2d1594f7ecae8e50dc7790ce67de0c9b70d22Sara Ting            }
545244d94f9a1a433fd3403452f2cdeba5c862b9650Daniel Sandler
546244d94f9a1a433fd3403452f2cdeba5c862b9650Daniel Sandler            n = notificationBuilder.build();
5478748724e382ca014067a3ceb5ff4eacbd9c4021aSara Ting        } else {
548af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            // Old-style notification (pre-JB).  We only need a standard notification (no
549af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            // buttons) but use a custom view so it is consistent with the others.
550af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            n = notificationBuilder.getNotification();
551af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting
552af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            // Use custom view with buttons to provide JB-like functionality (snooze/email).
553af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            RemoteViews contentView = new RemoteViews(context.getPackageName(),
554af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting                    R.layout.notification);
555af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setImageViewResource(R.id.image, R.drawable.stat_notify_calendar_multiple);
556af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setTextViewText(R.id.title, nEventsStr);
557af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setTextViewText(R.id.text, digestTitle);
558af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setViewVisibility(R.id.time, View.VISIBLE);
559321948d294d32d50f968e83716fa8500d8950d9fSam Blitzstein            contentView.setViewVisibility(R.id.map_button, View.GONE);
560321948d294d32d50f968e83716fa8500d8950d9fSam Blitzstein            contentView.setViewVisibility(R.id.call_button, View.GONE);
561af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setViewVisibility(R.id.email_button, View.GONE);
562af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setViewVisibility(R.id.snooze_button, View.GONE);
563af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            contentView.setViewVisibility(R.id.end_padding, View.VISIBLE);
564af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            n.contentView = contentView;
565af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting
566af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            // Use timestamp to force expired digest notification to the bottom (there is no
567af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            // priority setting before JB release).  This is hidden by the custom view.
568af589fb93e05051f026a41cd4b87b5b74636b05fSara Ting            n.when = 1;
56925b09db869f377b131052447f62ad02e2505cd87Michael Chan        }
57025b09db869f377b131052447f62ad02e2505cd87Michael Chan
57125b09db869f377b131052447f62ad02e2505cd87Michael Chan        NotificationWrapper nw = new NotificationWrapper(n);
57225b09db869f377b131052447f62ad02e2505cd87Michael Chan        if (AlertService.DEBUG) {
57325b09db869f377b131052447f62ad02e2505cd87Michael Chan            for (AlertService.NotificationInfo info : notificationInfos) {
57425b09db869f377b131052447f62ad02e2505cd87Michael Chan                nw.add(new NotificationWrapper(null, 0, info.eventId, info.startMillis,
57525b09db869f377b131052447f62ad02e2505cd87Michael Chan                        info.endMillis, false));
57625b09db869f377b131052447f62ad02e2505cd87Michael Chan            }
5778748724e382ca014067a3ceb5ff4eacbd9c4021aSara Ting        }
57825b09db869f377b131052447f62ad02e2505cd87Michael Chan        return nw;
579146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
5801946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting
5817e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private void closeNotificationShade(Context context) {
5827e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
5837e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        context.sendBroadcast(closeNotificationShadeIntent);
5847e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    }
5857e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
5861946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    private static final String[] ATTENDEES_PROJECTION = new String[] {
5871946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        Attendees.ATTENDEE_EMAIL,           // 0
5881946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        Attendees.ATTENDEE_STATUS,          // 1
5891946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    };
5901946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    private static final int ATTENDEES_INDEX_EMAIL = 0;
5911946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    private static final int ATTENDEES_INDEX_STATUS = 1;
5921946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
5931946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    private static final String ATTENDEES_SORT_ORDER = Attendees.ATTENDEE_NAME + " ASC, "
5941946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting            + Attendees.ATTENDEE_EMAIL + " ASC";
5951946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting
5961946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    private static final String[] EVENT_PROJECTION = new String[] {
5971946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        Calendars.OWNER_ACCOUNT, // 0
598247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        Calendars.ACCOUNT_NAME,  // 1
599247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        Events.TITLE,            // 2
6007ffa24cba2977925b737e6bd39be59ba39609611Sara Ting        Events.ORGANIZER,        // 3
6011946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    };
6021946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    private static final int EVENT_INDEX_OWNER_ACCOUNT = 0;
6031946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    private static final int EVENT_INDEX_ACCOUNT_NAME = 1;
604247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    private static final int EVENT_INDEX_TITLE = 2;
6057ffa24cba2977925b737e6bd39be59ba39609611Sara Ting    private static final int EVENT_INDEX_ORGANIZER = 3;
606247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting
607247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    private static Cursor getEventCursor(Context context, long eventId) {
608247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        return context.getContentResolver().query(
609247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                ContentUris.withAppendedId(Events.CONTENT_URI, eventId), EVENT_PROJECTION,
610247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                null, null, null);
611247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    }
612247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting
613247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    private static Cursor getAttendeesCursor(Context context, long eventId) {
614247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        return context.getContentResolver().query(Attendees.CONTENT_URI,
615247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                ATTENDEES_PROJECTION, ATTENDEES_WHERE, new String[] { Long.toString(eventId) },
616247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                ATTENDEES_SORT_ORDER);
617247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    }
618247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting
6197e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static Cursor getLocationCursor(Context context, long eventId) {
6207e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        return context.getContentResolver().query(
6217e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                ContentUris.withAppendedId(Events.CONTENT_URI, eventId),
6227e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                new String[] { Events.EVENT_LOCATION }, null, null, null);
6237e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    }
6247e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
625247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    /**
626247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting     * Creates a broadcast pending intent that fires to AlertReceiver when the email button
627247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting     * is clicked.
628247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting     */
629247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    private static PendingIntent createBroadcastMailIntent(Context context, long eventId,
630247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            String eventTitle) {
631247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        // Query for viewer account.
632247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        String syncAccount = null;
633247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        Cursor eventCursor = getEventCursor(context, eventId);
634247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        try {
635247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            if (eventCursor != null && eventCursor.moveToFirst()) {
636247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                syncAccount = eventCursor.getString(EVENT_INDEX_ACCOUNT_NAME);
637247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            }
638247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        } finally {
639247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            if (eventCursor != null) {
640247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                eventCursor.close();
641247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            }
642247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        }
643247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting
644247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        // Query attendees to see if there are any to email.
645247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        Cursor attendeesCursor = getAttendeesCursor(context, eventId);
646247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        try {
647247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            if (attendeesCursor != null && attendeesCursor.moveToFirst()) {
648247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                do {
649247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                    String email = attendeesCursor.getString(ATTENDEES_INDEX_EMAIL);
650247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                    if (Utils.isEmailableFrom(email, syncAccount)) {
651247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                        Intent broadcastIntent = new Intent(MAIL_ACTION);
652247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                        broadcastIntent.setClass(context, AlertReceiver.class);
653247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                        broadcastIntent.putExtra(EXTRA_EVENT_ID, eventId);
654247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                        return PendingIntent.getBroadcast(context,
655247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                                Long.valueOf(eventId).hashCode(), broadcastIntent,
656247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                                PendingIntent.FLAG_CANCEL_CURRENT);
657247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                    }
658247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                } while (attendeesCursor.moveToNext());
659247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            }
660247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            return null;
661247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting
662247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        } finally {
663247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            if (attendeesCursor != null) {
664247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                attendeesCursor.close();
665247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            }
666247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        }
667247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting    }
6681946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting
6691946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    /**
6701946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting     * Creates an Intent for emailing the attendees of the event.  Returns null if there
6711946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting     * are no emailable attendees.
6721946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting     */
673e98dca7e0042f7b36788b0e5d8dc6e94048c895cMichael Chan    static Intent createEmailIntent(Context context, long eventId, String body) {
6741946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        // TODO: Refactor to move query part into Utils.createEmailAttendeeIntent, to
6751946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        // be shared with EventInfoFragment.
6761946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting
6771946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        // Query for the owner account(s).
6781946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        String ownerAccount = null;
6791946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        String syncAccount = null;
680247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        String eventTitle = null;
6817ffa24cba2977925b737e6bd39be59ba39609611Sara Ting        String eventOrganizer = null;
682247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        Cursor eventCursor = getEventCursor(context, eventId);
683247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        try {
684247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            if (eventCursor != null && eventCursor.moveToFirst()) {
685247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                ownerAccount = eventCursor.getString(EVENT_INDEX_OWNER_ACCOUNT);
686247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                syncAccount = eventCursor.getString(EVENT_INDEX_ACCOUNT_NAME);
687247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                eventTitle = eventCursor.getString(EVENT_INDEX_TITLE);
6887ffa24cba2977925b737e6bd39be59ba39609611Sara Ting                eventOrganizer = eventCursor.getString(EVENT_INDEX_ORGANIZER);
689247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            }
690247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        } finally {
691247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            if (eventCursor != null) {
692247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                eventCursor.close();
693247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            }
694247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        }
695247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        if (TextUtils.isEmpty(eventTitle)) {
696247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            eventTitle = context.getResources().getString(R.string.no_title_label);
6971946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        }
6981946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting
6991946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        // Query for the attendees.
7001946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        List<String> toEmails = new ArrayList<String>();
7011946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        List<String> ccEmails = new ArrayList<String>();
702247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        Cursor attendeesCursor = getAttendeesCursor(context, eventId);
703247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        try {
704247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            if (attendeesCursor != null && attendeesCursor.moveToFirst()) {
705247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                do {
706247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                    int status = attendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
707247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                    String email = attendeesCursor.getString(ATTENDEES_INDEX_EMAIL);
708247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                    switch(status) {
709247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                        case Attendees.ATTENDEE_STATUS_DECLINED:
710247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                            addIfEmailable(ccEmails, email, syncAccount);
711247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                            break;
712247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                        default:
713247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                            addIfEmailable(toEmails, email, syncAccount);
714247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                    }
715247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                } while (attendeesCursor.moveToNext());
716247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            }
717247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        } finally {
718247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            if (attendeesCursor != null) {
719247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting                attendeesCursor.close();
720247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            }
7211946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        }
7221946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting
7237ffa24cba2977925b737e6bd39be59ba39609611Sara Ting        // Add organizer only if no attendees to email (the case when too many attendees
7247ffa24cba2977925b737e6bd39be59ba39609611Sara Ting        // in the event to sync or show).
7257ffa24cba2977925b737e6bd39be59ba39609611Sara Ting        if (toEmails.size() == 0 && ccEmails.size() == 0 && eventOrganizer != null) {
7267ffa24cba2977925b737e6bd39be59ba39609611Sara Ting            addIfEmailable(toEmails, eventOrganizer, syncAccount);
7277ffa24cba2977925b737e6bd39be59ba39609611Sara Ting        }
7287ffa24cba2977925b737e6bd39be59ba39609611Sara Ting
7291946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        Intent intent = null;
7301946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        if (ownerAccount != null && (toEmails.size() > 0 || ccEmails.size() > 0)) {
731e98dca7e0042f7b36788b0e5d8dc6e94048c895cMichael Chan            intent = Utils.createEmailAttendeesIntent(context.getResources(), eventTitle, body,
7321946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting                    toEmails, ccEmails, ownerAccount);
7331946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        }
7341946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting
7351946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        if (intent == null) {
7361946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting            return null;
7371946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        }
7381946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        else {
739247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
740247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting            return intent;
7411946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        }
7421946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    }
7431946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting
7441946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    private static void addIfEmailable(List<String> emailList, String email, String syncAccount) {
745247a2f141c2b70d1ed36dfba708667edd86d4b61Sara Ting        if (Utils.isEmailableFrom(email, syncAccount)) {
7461946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting            emailList.add(email);
7471946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting        }
7481946e278bd75db53b8c0c1609ccaa7c8c6a56ffdSara Ting    }
7497e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
7507e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    /**
7517e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * Using the linkify magic, get a list of URLs from the event's location. If no such links
7527e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * are found, we should end up with a single geo link of the entire string.
7537e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     */
7547e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static URLSpan[] getURLSpans(Context context, long eventId) {
7557e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        Cursor locationCursor = getLocationCursor(context, eventId);
7562c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski
7572c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski        // Default to empty list
7582c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski        URLSpan[] urlSpans = new URLSpan[0];
7597e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        if (locationCursor != null && locationCursor.moveToFirst()) {
7607e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            String location = locationCursor.getString(0); // Only one item in this cursor.
7612c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski            if (location != null && !location.isEmpty()) {
7622c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski                Spannable text = Utils.extendedLinkify(location, true);
7632c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski                // The linkify method should have found at least one link, at the very least.
7642c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski                // If no smart links were found, it should have set the whole string as a geo link.
7652c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski                urlSpans = text.getSpans(0, text.length(), URLSpan.class);
7667e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
7672c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski            locationCursor.close();
7687e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        }
7697e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
7702c265858d0b923a27300c40b4f2121b9fc744785Paul Sliwowski        return urlSpans;
7717e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    }
7727e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
7737e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    /**
7747e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * Create a pending intent to send ourself a broadcast to start maps, using the first map
7757e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * link available.
7767e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * If no links are found, return null.
7777e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     */
7787e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static PendingIntent createMapBroadcastIntent(Context context, URLSpan[] urlSpans,
7797e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            long eventId) {
7807e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        for (int span_i = 0; span_i < urlSpans.length; span_i++) {
7817e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            URLSpan urlSpan = urlSpans[span_i];
7827e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            String urlString = urlSpan.getURL();
7837e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (urlString.startsWith(GEO_PREFIX)) {
7847e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                Intent broadcastIntent = new Intent(MAP_ACTION);
7857e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                broadcastIntent.setClass(context, AlertReceiver.class);
7867e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                broadcastIntent.putExtra(EXTRA_EVENT_ID, eventId);
7877e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                return PendingIntent.getBroadcast(context,
7887e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                        Long.valueOf(eventId).hashCode(), broadcastIntent,
7897e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                        PendingIntent.FLAG_CANCEL_CURRENT);
7907e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
7917e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        }
7927e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
7937e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        // No geo link was found, so return null;
7947e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        return null;
7957e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    }
7967e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
7977e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    /**
7987e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * Create an intent to take the user to maps, using the first map link available.
7997e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * If no links are found, return null.
8007e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     */
8017e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static Intent createMapActivityIntent(Context context, URLSpan[] urlSpans) {
8027e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        for (int span_i = 0; span_i < urlSpans.length; span_i++) {
8037e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            URLSpan urlSpan = urlSpans[span_i];
8047e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            String urlString = urlSpan.getURL();
8057e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (urlString.startsWith(GEO_PREFIX)) {
8067e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                Intent geoIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlString));
8077e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                geoIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
8087e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                return geoIntent;
8097e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
8107e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        }
8117e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
8127e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        // No geo link was found, so return null;
8137e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        return null;
8147e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    }
8157e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
8167e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    /**
8177e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * Create a pending intent to send ourself a broadcast to take the user to dialer, or any other
8187e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * app capable of making phone calls. Use the first phone number available. If no phone number
8197e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * is found, or if the device is not capable of making phone calls (i.e. a tablet), return null.
8207e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     */
8217e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static PendingIntent createCallBroadcastIntent(Context context, URLSpan[] urlSpans,
8227e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            long eventId) {
8237e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        // Return null if the device is unable to make phone calls.
8247e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        TelephonyManager tm =
8257e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
8267e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) {
8277e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            return null;
8287e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        }
8297e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
8307e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        for (int span_i = 0; span_i < urlSpans.length; span_i++) {
8317e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            URLSpan urlSpan = urlSpans[span_i];
8327e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            String urlString = urlSpan.getURL();
8337e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (urlString.startsWith(TEL_PREFIX)) {
8347e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                Intent broadcastIntent = new Intent(CALL_ACTION);
8357e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                broadcastIntent.setClass(context, AlertReceiver.class);
8367e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                broadcastIntent.putExtra(EXTRA_EVENT_ID, eventId);
8377e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                return PendingIntent.getBroadcast(context,
8387e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                        Long.valueOf(eventId).hashCode(), broadcastIntent,
8397e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                        PendingIntent.FLAG_CANCEL_CURRENT);
8407e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
8417e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        }
8427e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
8437e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        // No tel link was found, so return null;
8447e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        return null;
8457e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    }
8467e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
8477e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    /**
8487e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * Create an intent to take the user to dialer, or any other app capable of making phone calls.
8497e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * Use the first phone number available. If no phone number is found, or if the device is
8507e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     * not capable of making phone calls (i.e. a tablet), return null.
8517e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein     */
8527e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    private static Intent createCallActivityIntent(Context context, URLSpan[] urlSpans) {
8537e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        // Return null if the device is unable to make phone calls.
8547e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        TelephonyManager tm =
8557e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
8567e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) {
8577e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            return null;
8587e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        }
8597e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
8607e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        for (int span_i = 0; span_i < urlSpans.length; span_i++) {
8617e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            URLSpan urlSpan = urlSpans[span_i];
8627e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            String urlString = urlSpan.getURL();
8637e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            if (urlString.startsWith(TEL_PREFIX)) {
8647e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                Intent callIntent = new Intent(Intent.ACTION_DIAL, Uri.parse(urlString));
8657e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                callIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
8667e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein                return callIntent;
8677e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein            }
8687e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        }
8697e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein
8707e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        // No tel link was found, so return null;
8717e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein        return null;
8727e19bf984bc280c0cc034adf1dfa8840c75a698dSam Blitzstein    }
873146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project}
874