1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.mail; 17 18 import android.app.IntentService; 19 import android.content.ContentResolver; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.net.Uri; 24 import android.os.Parcel; 25 import androidx.core.app.NotificationManagerCompat; 26 27 import com.android.mail.analytics.Analytics; 28 import com.android.mail.providers.Message; 29 import com.android.mail.providers.UIProvider; 30 import com.android.mail.utils.LogUtils; 31 import com.android.mail.utils.NotificationActionUtils; 32 import com.android.mail.utils.NotificationActionUtils.NotificationAction; 33 34 /** 35 * Processes notification action {@link Intent}s that need to run off the main thread. 36 */ 37 public class NotificationActionIntentService extends IntentService { 38 private static final String LOG_TAG = "NotifActionIS"; 39 40 // Compose actions 41 public static final String ACTION_REPLY = "com.android.mail.action.notification.REPLY"; 42 public static final String ACTION_REPLY_ALL = "com.android.mail.action.notification.REPLY_ALL"; 43 public static final String ACTION_FORWARD = "com.android.mail.action.notification.FORWARD"; 44 // Toggle actions 45 public static final String ACTION_MARK_READ = "com.android.mail.action.notification.MARK_READ"; 46 47 // Destructive actions - These just display the undo bar 48 public static final String ACTION_ARCHIVE_REMOVE_LABEL = 49 "com.android.mail.action.notification.ARCHIVE"; 50 public static final String ACTION_DELETE = "com.android.mail.action.notification.DELETE"; 51 52 /** 53 * This action cancels the undo notification, and does not commit any changes. 54 */ 55 public static final String ACTION_UNDO = "com.android.mail.action.notification.UNDO"; 56 57 /** 58 * This action performs the actual destructive action. 59 */ 60 public static final String ACTION_DESTRUCT = "com.android.mail.action.notification.DESTRUCT"; 61 62 public static final String EXTRA_NOTIFICATION_ACTION = 63 "com.android.mail.extra.EXTRA_NOTIFICATION_ACTION"; 64 public static final String ACTION_UNDO_TIMEOUT = 65 "com.android.mail.action.notification.UNDO_TIMEOUT"; 66 NotificationActionIntentService()67 public NotificationActionIntentService() { 68 super("NotificationActionIntentService"); 69 } 70 logNotificationAction(String intentAction, NotificationAction action)71 private static void logNotificationAction(String intentAction, NotificationAction action) { 72 final String eventAction; 73 final String eventLabel; 74 75 if (ACTION_ARCHIVE_REMOVE_LABEL.equals(intentAction)) { 76 eventAction = "archive_remove_label"; 77 eventLabel = action.getFolder().getTypeDescription(); 78 } else if (ACTION_DELETE.equals(intentAction)) { 79 eventAction = "delete"; 80 eventLabel = null; 81 } else { 82 eventAction = intentAction; 83 eventLabel = null; 84 } 85 86 Analytics.getInstance().sendEvent("notification_action", eventAction, eventLabel, 0); 87 } 88 89 @Override onHandleIntent(final Intent intent)90 protected void onHandleIntent(final Intent intent) { 91 final Context context = this; 92 final String action = intent.getAction(); 93 94 /* 95 * Grab the alarm from the intent. Since the remote AlarmManagerService fills in the Intent 96 * to add some extra data, it must unparcel the NotificationAction object. It throws a 97 * ClassNotFoundException when unparcelling. 98 * To avoid this, do the marshalling ourselves. 99 */ 100 final NotificationAction notificationAction; 101 final byte[] data = intent.getByteArrayExtra(EXTRA_NOTIFICATION_ACTION); 102 if (data != null) { 103 final Parcel in = Parcel.obtain(); 104 in.unmarshall(data, 0, data.length); 105 in.setDataPosition(0); 106 notificationAction = NotificationAction.CREATOR.createFromParcel(in, 107 NotificationAction.class.getClassLoader()); 108 } else { 109 LogUtils.wtf(LOG_TAG, "data was null trying to unparcel the NotificationAction"); 110 return; 111 } 112 113 final Message message = notificationAction.getMessage(); 114 115 final ContentResolver contentResolver = getContentResolver(); 116 117 LogUtils.i(LOG_TAG, "Handling %s", action); 118 119 logNotificationAction(action, notificationAction); 120 121 if (notificationAction.getSource() == NotificationAction.SOURCE_REMOTE) { 122 // Skip undo if the action is bridged from remote node. This should be similar to the 123 // logic after the Undo notification expires in a regular flow. 124 LogUtils.d(LOG_TAG, "Canceling %s", notificationAction.getNotificationId()); 125 NotificationManagerCompat.from(context).cancel(notificationAction.getNotificationId()); 126 NotificationActionUtils.processDestructiveAction(this, notificationAction); 127 NotificationActionUtils.resendNotifications(context, notificationAction.getAccount(), 128 notificationAction.getFolder()); 129 return; 130 } 131 132 if (ACTION_UNDO.equals(action)) { 133 NotificationActionUtils.cancelUndoTimeout(context, notificationAction); 134 NotificationActionUtils.cancelUndoNotification(context, notificationAction); 135 } else if (ACTION_ARCHIVE_REMOVE_LABEL.equals(action) || ACTION_DELETE.equals(action)) { 136 // All we need to do is switch to an Undo notification 137 NotificationActionUtils.createUndoNotification(context, notificationAction); 138 139 NotificationActionUtils.registerUndoTimeout(context, notificationAction); 140 } else { 141 if (ACTION_UNDO_TIMEOUT.equals(action) || ACTION_DESTRUCT.equals(action)) { 142 // Process the action 143 NotificationActionUtils.cancelUndoTimeout(this, notificationAction); 144 NotificationActionUtils.processUndoNotification(this, notificationAction); 145 } else if (ACTION_MARK_READ.equals(action)) { 146 final Uri uri = message.uri; 147 148 final ContentValues values = new ContentValues(1); 149 values.put(UIProvider.MessageColumns.READ, 1); 150 151 contentResolver.update(uri, values, null, null); 152 } 153 154 NotificationActionUtils.resendNotifications(context, notificationAction.getAccount(), 155 notificationAction.getFolder()); 156 } 157 } 158 } 159