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