1/*
2 * Copyright (C) 2015 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 */
16package com.android.messaging.datamodel.action;
17
18import android.content.Context;
19import android.content.res.Resources;
20import android.widget.Toast;
21
22import com.android.messaging.Factory;
23import com.android.messaging.R;
24import com.android.messaging.datamodel.DataModel;
25import com.android.messaging.datamodel.data.MessageData;
26import com.android.messaging.datamodel.data.ParticipantData;
27import com.android.messaging.sms.MmsUtils;
28import com.android.messaging.util.AccessibilityUtil;
29import com.android.messaging.util.PhoneUtils;
30import com.android.messaging.util.ThreadUtil;
31
32import javax.annotation.Nullable;
33
34/**
35 * Shows one-time, transient notifications in response to action failures (i.e. permanent failures
36 * when sending a message) by showing toasts.
37 */
38public class BugleActionToasts {
39    /**
40     * Called when SendMessageAction or DownloadMmsAction finishes
41     * @param conversationId the conversation of the sent or downloaded message
42     * @param success did the action succeed
43     * @param status the message sending status
44     * @param isSms whether the message is sent using SMS
45     * @param subId the subId of the SIM related to this send
46     * @param isSend whether it is a send (false for download)
47     */
48    static void onSendMessageOrManualDownloadActionCompleted(
49            final String conversationId,
50            final boolean success,
51            final int status,
52            final boolean isSms,
53            final int subId,
54            final boolean isSend) {
55        // We only show notifications for two cases, i.e. when mobile data is off or when we are
56        // in airplane mode, both of which fail fast with permanent failures.
57        if (!success && status == MmsUtils.MMS_REQUEST_MANUAL_RETRY) {
58            final PhoneUtils phoneUtils = PhoneUtils.get(subId);
59            if (phoneUtils.isAirplaneModeOn()) {
60                if (isSend) {
61                    showToast(R.string.send_message_failure_airplane_mode);
62                } else {
63                    showToast(R.string.download_message_failure_airplane_mode);
64                }
65                return;
66            } else if (!isSms && !phoneUtils.isMobileDataEnabled()) {
67                if (isSend) {
68                    showToast(R.string.send_message_failure_no_data);
69                } else {
70                    showToast(R.string.download_message_failure_no_data);
71                }
72                return;
73            }
74        }
75
76        if (AccessibilityUtil.isTouchExplorationEnabled(Factory.get().getApplicationContext())) {
77            final boolean isFocusedConversation = DataModel.get().isFocusedConversation(conversationId);
78            if (isFocusedConversation && success) {
79                // Using View.announceForAccessibility may be preferable, but we do not have a
80                // View, and so we use a toast instead.
81                showToast(isSend ? R.string.send_message_success
82                        : R.string.download_message_success);
83                return;
84            }
85
86            // {@link MessageNotificationState#checkFailedMessages} does not post a notification for
87            // failures in observable conversations. For accessibility, we provide an indication
88            // here.
89            final boolean isObservableConversation = DataModel.get().isNewMessageObservable(
90                    conversationId);
91            if (isObservableConversation && !success) {
92                showToast(isSend ? R.string.send_message_failure
93                        : R.string.download_message_failure);
94            }
95        }
96    }
97
98    public static void onMessageReceived(final String conversationId,
99            @Nullable final ParticipantData sender, @Nullable final MessageData message) {
100        final Context context = Factory.get().getApplicationContext();
101        if (AccessibilityUtil.isTouchExplorationEnabled(context)) {
102            final boolean isFocusedConversation = DataModel.get().isFocusedConversation(
103                    conversationId);
104            if (isFocusedConversation) {
105                final Resources res = context.getResources();
106                final String senderDisplayName = (sender == null)
107                        ? res.getString(R.string.unknown_sender) : sender.getDisplayName(false);
108                final String announcement = res.getString(
109                        R.string.incoming_message_announcement, senderDisplayName,
110                        (message == null) ? "" : message.getMessageText());
111                showToast(announcement);
112            }
113        }
114    }
115
116    public static void onConversationDeleted() {
117        showToast(R.string.conversation_deleted);
118    }
119
120    private static void showToast(final int messageResId) {
121        ThreadUtil.getMainThreadHandler().post(new Runnable() {
122            @Override
123            public void run() {
124                Toast.makeText(getApplicationContext(),
125                        getApplicationContext().getString(messageResId), Toast.LENGTH_LONG).show();
126            }
127        });
128    }
129
130    private static void showToast(final String message) {
131        ThreadUtil.getMainThreadHandler().post(new Runnable() {
132            @Override
133            public void run() {
134                Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
135            }
136        });
137    }
138
139    private static Context getApplicationContext() {
140        return Factory.get().getApplicationContext();
141    }
142
143    private static class UpdateDestinationBlockedActionToast
144            implements UpdateDestinationBlockedAction.UpdateDestinationBlockedActionListener {
145        private final Context mContext;
146
147        UpdateDestinationBlockedActionToast(final Context context) {
148            mContext = context;
149        }
150
151        @Override
152        public void onUpdateDestinationBlockedAction(
153                final UpdateDestinationBlockedAction action,
154                final boolean success,
155                final boolean block,
156                final String destination) {
157            if (success) {
158                Toast.makeText(mContext,
159                        block
160                                ? R.string.update_destination_blocked
161                                : R.string.update_destination_unblocked,
162                        Toast.LENGTH_LONG
163                ).show();
164            }
165        }
166    }
167
168    public static UpdateDestinationBlockedAction.UpdateDestinationBlockedActionListener
169            makeUpdateDestinationBlockedActionListener(final Context context) {
170        return new UpdateDestinationBlockedActionToast(context);
171    }
172}
173