13168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy/* 23168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * Copyright (C) 2012 The Android Open Source Project 33168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * 43168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * Licensed under the Apache License, Version 2.0 (the "License"); 53168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * you may not use this file except in compliance with the License. 63168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * You may obtain a copy of the License at 73168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * 83168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * http://www.apache.org/licenses/LICENSE-2.0 93168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * 103168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * Unless required by applicable law or agreed to in writing, software 113168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * distributed under the License is distributed on an "AS IS" BASIS, 123168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * See the License for the specific language governing permissions and 143168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * limitations under the License. 153168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy */ 163168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedypackage com.android.mail; 173168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 183168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport android.app.IntentService; 193168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport android.content.ContentResolver; 203168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport android.content.ContentValues; 213168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport android.content.Context; 223168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport android.content.Intent; 233168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport android.net.Uri; 24621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedyimport android.os.Parcel; 253806cb53f880094d811ced050342fd6ee7c4bf99Alan Lauimport android.support.v4.app.NotificationManagerCompat; 263168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 274fe0af81874976a1995191321e35c844b2229811Andy Huangimport com.android.mail.analytics.Analytics; 283168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport com.android.mail.providers.Message; 293168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport com.android.mail.providers.UIProvider; 30621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedyimport com.android.mail.utils.LogUtils; 313168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport com.android.mail.utils.NotificationActionUtils; 323168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedyimport com.android.mail.utils.NotificationActionUtils.NotificationAction; 333168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 343168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy/** 353168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * Processes notification action {@link Intent}s that need to run off the main thread. 363168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy */ 373168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedypublic class NotificationActionIntentService extends IntentService { 38621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy private static final String LOG_TAG = "NotifActionIS"; 39621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy 40f2566a42b5dec4e846391ee0d7ecae0891356653Scott Kennedy // Compose actions 415fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu public static final String ACTION_REPLY = "com.android.mail.action.notification.REPLY"; 425fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu public static final String ACTION_REPLY_ALL = "com.android.mail.action.notification.REPLY_ALL"; 435fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu public static final String ACTION_FORWARD = "com.android.mail.action.notification.FORWARD"; 443168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy // Toggle actions 455fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu public static final String ACTION_MARK_READ = "com.android.mail.action.notification.MARK_READ"; 463168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 473168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy // Destructive actions - These just display the undo bar 483168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy public static final String ACTION_ARCHIVE_REMOVE_LABEL = 495fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu "com.android.mail.action.notification.ARCHIVE"; 505fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu public static final String ACTION_DELETE = "com.android.mail.action.notification.DELETE"; 513168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 523168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy /** 533168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * This action cancels the undo notification, and does not commit any changes. 543168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy */ 555fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu public static final String ACTION_UNDO = "com.android.mail.action.notification.UNDO"; 563168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 573168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy /** 583168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy * This action performs the actual destructive action. 593168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy */ 605fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu public static final String ACTION_DESTRUCT = "com.android.mail.action.notification.DESTRUCT"; 613168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 623168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy public static final String EXTRA_NOTIFICATION_ACTION = 633168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy "com.android.mail.extra.EXTRA_NOTIFICATION_ACTION"; 645fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu public static final String ACTION_UNDO_TIMEOUT = 655fa60977a5d0316dd7cfc70ebe1fcb0c49bc8f95Yu Ping Hu "com.android.mail.action.notification.UNDO_TIMEOUT"; 663168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 673168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy public NotificationActionIntentService() { 683168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy super("NotificationActionIntentService"); 693168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy } 703168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 714fe0af81874976a1995191321e35c844b2229811Andy Huang private static void logNotificationAction(String intentAction, NotificationAction action) { 724fe0af81874976a1995191321e35c844b2229811Andy Huang final String eventAction; 734fe0af81874976a1995191321e35c844b2229811Andy Huang final String eventLabel; 744fe0af81874976a1995191321e35c844b2229811Andy Huang 754fe0af81874976a1995191321e35c844b2229811Andy Huang if (ACTION_ARCHIVE_REMOVE_LABEL.equals(intentAction)) { 764fe0af81874976a1995191321e35c844b2229811Andy Huang eventAction = "archive_remove_label"; 774fe0af81874976a1995191321e35c844b2229811Andy Huang eventLabel = action.getFolder().getTypeDescription(); 784fe0af81874976a1995191321e35c844b2229811Andy Huang } else if (ACTION_DELETE.equals(intentAction)) { 794fe0af81874976a1995191321e35c844b2229811Andy Huang eventAction = "delete"; 804fe0af81874976a1995191321e35c844b2229811Andy Huang eventLabel = null; 814fe0af81874976a1995191321e35c844b2229811Andy Huang } else { 824fe0af81874976a1995191321e35c844b2229811Andy Huang eventAction = intentAction; 834fe0af81874976a1995191321e35c844b2229811Andy Huang eventLabel = null; 844fe0af81874976a1995191321e35c844b2229811Andy Huang } 854fe0af81874976a1995191321e35c844b2229811Andy Huang 864fe0af81874976a1995191321e35c844b2229811Andy Huang Analytics.getInstance().sendEvent("notification_action", eventAction, eventLabel, 0); 874fe0af81874976a1995191321e35c844b2229811Andy Huang } 884fe0af81874976a1995191321e35c844b2229811Andy Huang 893168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy @Override 903168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy protected void onHandleIntent(final Intent intent) { 913168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy final Context context = this; 923168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy final String action = intent.getAction(); 93621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy 94621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy /* 95621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy * Grab the alarm from the intent. Since the remote AlarmManagerService fills in the Intent 96621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy * to add some extra data, it must unparcel the NotificationAction object. It throws a 97621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy * ClassNotFoundException when unparcelling. 98621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy * To avoid this, do the marshalling ourselves. 99621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy */ 100621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy final NotificationAction notificationAction; 101621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy final byte[] data = intent.getByteArrayExtra(EXTRA_NOTIFICATION_ACTION); 102621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy if (data != null) { 103621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy final Parcel in = Parcel.obtain(); 104621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy in.unmarshall(data, 0, data.length); 105621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy in.setDataPosition(0); 106621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy notificationAction = NotificationAction.CREATOR.createFromParcel(in, 107621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy NotificationAction.class.getClassLoader()); 108621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy } else { 109621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy LogUtils.wtf(LOG_TAG, "data was null trying to unparcel the NotificationAction"); 110621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy return; 111621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy } 112621981ca3fb5f7b461edeac7ac4df765fe72f582Scott Kennedy 1133168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy final Message message = notificationAction.getMessage(); 1143168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 1153168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy final ContentResolver contentResolver = getContentResolver(); 1163168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 117781f7e322283229483b048776fcb6857c38a72b5Scott Kennedy LogUtils.i(LOG_TAG, "Handling %s", action); 118781f7e322283229483b048776fcb6857c38a72b5Scott Kennedy 1194fe0af81874976a1995191321e35c844b2229811Andy Huang logNotificationAction(action, notificationAction); 1204fe0af81874976a1995191321e35c844b2229811Andy Huang 121e806c9447c7137d2a7a828e7ccdc1f8961aa1c2aAlan Lau if (notificationAction.getSource() == NotificationAction.SOURCE_REMOTE) { 122e806c9447c7137d2a7a828e7ccdc1f8961aa1c2aAlan Lau // Skip undo if the action is bridged from remote node. This should be similar to the 123e806c9447c7137d2a7a828e7ccdc1f8961aa1c2aAlan Lau // logic after the Undo notification expires in a regular flow. 1243806cb53f880094d811ced050342fd6ee7c4bf99Alan Lau LogUtils.d(LOG_TAG, "Canceling %s", notificationAction.getNotificationId()); 1253806cb53f880094d811ced050342fd6ee7c4bf99Alan Lau NotificationManagerCompat.from(context).cancel(notificationAction.getNotificationId()); 126e806c9447c7137d2a7a828e7ccdc1f8961aa1c2aAlan Lau NotificationActionUtils.processDestructiveAction(this, notificationAction); 127e806c9447c7137d2a7a828e7ccdc1f8961aa1c2aAlan Lau NotificationActionUtils.resendNotifications(context, notificationAction.getAccount(), 128e806c9447c7137d2a7a828e7ccdc1f8961aa1c2aAlan Lau notificationAction.getFolder()); 129e806c9447c7137d2a7a828e7ccdc1f8961aa1c2aAlan Lau return; 130e806c9447c7137d2a7a828e7ccdc1f8961aa1c2aAlan Lau } 131e806c9447c7137d2a7a828e7ccdc1f8961aa1c2aAlan Lau 1323168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy if (ACTION_UNDO.equals(action)) { 1333168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy NotificationActionUtils.cancelUndoTimeout(context, notificationAction); 1343168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy NotificationActionUtils.cancelUndoNotification(context, notificationAction); 135d53560ee123acfe5fa3f8185814c137704767efcScott Kennedy } else if (ACTION_ARCHIVE_REMOVE_LABEL.equals(action) || ACTION_DELETE.equals(action)) { 1363168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy // All we need to do is switch to an Undo notification 1373168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy NotificationActionUtils.createUndoNotification(context, notificationAction); 1383168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 1393168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy NotificationActionUtils.registerUndoTimeout(context, notificationAction); 1403168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy } else { 1413168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy if (ACTION_UNDO_TIMEOUT.equals(action) || ACTION_DESTRUCT.equals(action)) { 1423168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy // Process the action 1433168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy NotificationActionUtils.cancelUndoTimeout(this, notificationAction); 1443168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy NotificationActionUtils.processUndoNotification(this, notificationAction); 1453168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy } else if (ACTION_MARK_READ.equals(action)) { 1463168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy final Uri uri = message.uri; 1473168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 1483168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy final ContentValues values = new ContentValues(1); 1493168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy values.put(UIProvider.MessageColumns.READ, 1); 1503168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 1513168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy contentResolver.update(uri, values, null, null); 1523168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy } 1533168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy 154ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein NotificationActionUtils.resendNotifications(context, notificationAction.getAccount(), 155ddd17bc2963b67ba07dfd1e2d9b1f17abf8c8e4aAndrew Sapperstein notificationAction.getFolder()); 1563168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy } 1573168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy } 1583168c2a75edbf597932ab9edfd96037ceba99b35Scott Kennedy} 159