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