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  */
16 package com.android.messaging.datamodel.action;
17 
18 import android.content.Context;
19 import android.content.res.Resources;
20 import android.widget.Toast;
21 
22 import com.android.messaging.Factory;
23 import com.android.messaging.R;
24 import com.android.messaging.datamodel.DataModel;
25 import com.android.messaging.datamodel.data.MessageData;
26 import com.android.messaging.datamodel.data.ParticipantData;
27 import com.android.messaging.sms.MmsUtils;
28 import com.android.messaging.util.AccessibilityUtil;
29 import com.android.messaging.util.PhoneUtils;
30 import com.android.messaging.util.ThreadUtil;
31 
32 import 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  */
38 public 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      */
onSendMessageOrManualDownloadActionCompleted( final String conversationId, final boolean success, final int status, final boolean isSms, final int subId, final boolean isSend)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 
onMessageReceived(final String conversationId, @Nullable final ParticipantData sender, @Nullable final MessageData message)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 
onConversationDeleted()116     public static void onConversationDeleted() {
117         showToast(R.string.conversation_deleted);
118     }
119 
showToast(final int messageResId)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 
showToast(final String message)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 
getApplicationContext()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 
UpdateDestinationBlockedActionToast(final Context context)147         UpdateDestinationBlockedActionToast(final Context context) {
148             mContext = context;
149         }
150 
151         @Override
onUpdateDestinationBlockedAction( final UpdateDestinationBlockedAction action, final boolean success, final boolean block, final String destination)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
makeUpdateDestinationBlockedActionListener(final Context context)169             makeUpdateDestinationBlockedActionListener(final Context context) {
170         return new UpdateDestinationBlockedActionToast(context);
171     }
172 }
173