• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 package com.android.dialer.voicemail.listui.error;
18 
19 import android.content.Context;
20 import android.preference.PreferenceManager;
21 import android.provider.VoicemailContract.Status;
22 import android.support.annotation.Nullable;
23 import android.telecom.PhoneAccountHandle;
24 import com.android.dialer.common.LogUtil;
25 import com.android.dialer.common.PerAccountSharedPreferences;
26 import com.android.dialer.logging.DialerImpression;
27 import com.android.dialer.logging.Logger;
28 import com.android.dialer.voicemail.listui.error.VoicemailErrorMessage.Action;
29 import com.android.voicemail.VoicemailClient;
30 import com.android.voicemail.VoicemailComponent;
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 /**
35  * Create error message from {@link VoicemailStatus} for OMTP visual voicemail. This is also the
36  * default behavior if other message creator does not handle the status.
37  */
38 public class OmtpVoicemailMessageCreator {
39 
40   private static final float QUOTA_NEAR_FULL_THRESHOLD = 0.9f;
41   private static final float QUOTA_FULL_THRESHOLD = 0.99f;
42   protected static final String VOICEMAIL_PROMO_DISMISSED_KEY =
43       "voicemail_archive_promo_was_dismissed";
44   protected static final String VOICEMAIL_PROMO_ALMOST_FULL_DISMISSED_KEY =
45       "voicemail_archive_almost_full_promo_was_dismissed";
46 
47   @Nullable
create( Context context, VoicemailStatus status, final VoicemailStatusReader statusReader)48   public static VoicemailErrorMessage create(
49       Context context, VoicemailStatus status, final VoicemailStatusReader statusReader) {
50     VoicemailErrorMessage tosMessage =
51         new VoicemailTosMessageCreator(context, status, statusReader).maybeCreateTosMessage();
52     if (tosMessage != null) {
53       return tosMessage;
54     }
55 
56     if (Status.CONFIGURATION_STATE_OK == status.configurationState
57         && Status.DATA_CHANNEL_STATE_OK == status.dataChannelState
58         && Status.NOTIFICATION_CHANNEL_STATE_OK == status.notificationChannelState) {
59       return checkQuota(context, status, statusReader);
60     }
61     // Initial state when the source is activating. Other error might be written into data and
62     // notification channel during activation.
63     if (Status.CONFIGURATION_STATE_CONFIGURING == status.configurationState
64         && Status.DATA_CHANNEL_STATE_OK == status.dataChannelState
65         && Status.NOTIFICATION_CHANNEL_STATE_OK == status.notificationChannelState) {
66       return new VoicemailErrorMessage(
67           context.getString(R.string.voicemail_error_activating_title),
68           context.getString(R.string.voicemail_error_activating_message),
69           VoicemailErrorMessage.createCallVoicemailAction(context, status.getPhoneAccountHandle()));
70     }
71 
72     if (Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION == status.notificationChannelState) {
73       return createNoSignalMessage(context, status);
74     }
75 
76     if (Status.CONFIGURATION_STATE_FAILED == status.configurationState) {
77       return new VoicemailErrorMessage(
78           context.getString(R.string.voicemail_error_activation_failed_title),
79           context.getString(R.string.voicemail_error_activation_failed_message),
80           VoicemailErrorMessage.createCallVoicemailAction(context, status.getPhoneAccountHandle()),
81           VoicemailErrorMessage.createRetryAction(context, status));
82     }
83 
84     if (Status.DATA_CHANNEL_STATE_NO_CONNECTION == status.dataChannelState) {
85       return new VoicemailErrorMessage(
86           context.getString(R.string.voicemail_error_no_data_title),
87           context.getString(R.string.voicemail_error_no_data_message),
88           VoicemailErrorMessage.createCallVoicemailAction(context, status.getPhoneAccountHandle()),
89           VoicemailErrorMessage.createRetryAction(context, status));
90     }
91 
92     if (Status.DATA_CHANNEL_STATE_NO_CONNECTION_CELLULAR_REQUIRED == status.dataChannelState) {
93       return new VoicemailErrorMessage(
94           context.getString(R.string.voicemail_error_no_data_title),
95           context.getString(R.string.voicemail_error_no_data_cellular_required_message),
96           VoicemailErrorMessage.createCallVoicemailAction(context, status.getPhoneAccountHandle()),
97           VoicemailErrorMessage.createRetryAction(context, status));
98     }
99 
100     if (Status.DATA_CHANNEL_STATE_BAD_CONFIGURATION == status.dataChannelState) {
101       return new VoicemailErrorMessage(
102           context.getString(R.string.voicemail_error_bad_config_title),
103           context.getString(R.string.voicemail_error_bad_config_message),
104           VoicemailErrorMessage.createCallVoicemailAction(context, status.getPhoneAccountHandle()),
105           VoicemailErrorMessage.createRetryAction(context, status));
106     }
107 
108     if (Status.DATA_CHANNEL_STATE_COMMUNICATION_ERROR == status.dataChannelState) {
109       return new VoicemailErrorMessage(
110           context.getString(R.string.voicemail_error_communication_title),
111           context.getString(R.string.voicemail_error_communication_message),
112           VoicemailErrorMessage.createCallVoicemailAction(context, status.getPhoneAccountHandle()),
113           VoicemailErrorMessage.createRetryAction(context, status));
114     }
115 
116     if (Status.DATA_CHANNEL_STATE_SERVER_ERROR == status.dataChannelState) {
117       return new VoicemailErrorMessage(
118           context.getString(R.string.voicemail_error_server_title),
119           context.getString(R.string.voicemail_error_server_message),
120           VoicemailErrorMessage.createCallVoicemailAction(context, status.getPhoneAccountHandle()),
121           VoicemailErrorMessage.createRetryAction(context, status));
122     }
123 
124     if (Status.DATA_CHANNEL_STATE_SERVER_CONNECTION_ERROR == status.dataChannelState) {
125       return new VoicemailErrorMessage(
126           context.getString(R.string.voicemail_error_server_connection_title),
127           context.getString(R.string.voicemail_error_server_connection_message),
128           VoicemailErrorMessage.createCallVoicemailAction(context, status.getPhoneAccountHandle()),
129           VoicemailErrorMessage.createRetryAction(context, status));
130     }
131 
132     // This should be an assertion error, but there's a bug in NYC-DR (a bug) that will
133     // sometimes give status mixed from multiple SIMs. There's no meaningful message to be displayed
134     // from it, so just suppress the message.
135     LogUtil.e("OmtpVoicemailMessageCreator.create", "Unhandled status: " + status);
136     return null;
137   }
138 
isSyncBlockingError(VoicemailStatus status)139   public static boolean isSyncBlockingError(VoicemailStatus status) {
140     if (status.notificationChannelState != Status.NOTIFICATION_CHANNEL_STATE_OK) {
141       return true;
142     }
143 
144     if (status.dataChannelState != Status.DATA_CHANNEL_STATE_OK) {
145       return true;
146     }
147 
148     switch (status.configurationState) {
149       case Status.CONFIGURATION_STATE_OK:
150         // allow activation to be queued again in case it is interrupted
151       case Status.CONFIGURATION_STATE_CONFIGURING:
152         return false;
153       default:
154         return true;
155     }
156   }
157 
158   @Nullable
checkQuota( Context context, VoicemailStatus status, VoicemailStatusReader statusReader)159   private static VoicemailErrorMessage checkQuota(
160       Context context, VoicemailStatus status, VoicemailStatusReader statusReader) {
161     if (status.quotaOccupied != Status.QUOTA_UNAVAILABLE
162         && status.quotaTotal != Status.QUOTA_UNAVAILABLE) {
163       return createInboxErrorMessage(context, status, statusReader);
164     }
165     Logger.get(context).logImpression(DialerImpression.Type.VVM_QUOTA_CHECK_UNAVAILABLE);
166     return null;
167   }
168 
169   @Nullable
createInboxErrorMessage( Context context, VoicemailStatus status, VoicemailStatusReader statusReader)170   private static VoicemailErrorMessage createInboxErrorMessage(
171       Context context, VoicemailStatus status, VoicemailStatusReader statusReader) {
172 
173     float voicemailOccupiedFraction = (float) status.quotaOccupied / (float) status.quotaTotal;
174 
175     if (voicemailOccupiedFraction < QUOTA_NEAR_FULL_THRESHOLD) {
176       return null;
177     }
178 
179     boolean isFull = voicemailOccupiedFraction >= QUOTA_FULL_THRESHOLD;
180 
181     PhoneAccountHandle phoneAccountHandle = status.getPhoneAccountHandle();
182 
183     PerAccountSharedPreferences sharedPreferenceForAccount =
184         new PerAccountSharedPreferences(
185             context, phoneAccountHandle, PreferenceManager.getDefaultSharedPreferences(context));
186 
187     VoicemailClient voicemailClient = VoicemailComponent.get(context).getVoicemailClient();
188 
189     boolean shouldShowPromoForArchive =
190         !isPromoForArchiveDismissed(sharedPreferenceForAccount, isFull)
191             && !voicemailClient.isVoicemailArchiveEnabled(context, phoneAccountHandle)
192             && voicemailClient.isVoicemailArchiveAvailable(context);
193 
194     if (!shouldShowPromoForArchive) {
195       if (isFull) {
196         Logger.get(context)
197             .logImpression(DialerImpression.Type.VVM_USER_SHOWN_VM_FULL_ERROR_MESSAGE);
198         return new VoicemailErrorMessage(
199             context.getString(R.string.voicemail_error_inbox_full_title),
200             context.getString(R.string.voicemail_error_inbox_full_message));
201       } else {
202         Logger.get(context)
203             .logImpression(DialerImpression.Type.VVM_USER_SHOWN_VM_ALMOST_FULL_ERROR_MESSAGE);
204         return new VoicemailErrorMessage(
205             context.getString(R.string.voicemail_error_inbox_near_full_title),
206             context.getString(R.string.voicemail_error_inbox_near_full_message));
207       }
208     }
209 
210     String title;
211     CharSequence message;
212     DialerImpression.Type enabledImpression;
213     DialerImpression.Type dismissedImpression;
214     String dismissedKey;
215 
216     if (isFull) {
217       Logger.get(context).logImpression(DialerImpression.Type.VVM_USER_SHOWN_VM_FULL_PROMO);
218       title = context.getString(R.string.voicemail_error_inbox_full_turn_archive_on_title);
219       message = context.getText(R.string.voicemail_error_inbox_full_turn_archive_on_message);
220       enabledImpression = DialerImpression.Type.VVM_USER_ENABLED_ARCHIVE_FROM_VM_FULL_PROMO;
221       dismissedImpression = DialerImpression.Type.VVM_USER_DISMISSED_VM_FULL_PROMO;
222       dismissedKey = VOICEMAIL_PROMO_DISMISSED_KEY;
223     } else {
224       Logger.get(context).logImpression(DialerImpression.Type.VVM_USER_SHOWN_VM_ALMOST_FULL_PROMO);
225       title = context.getString(R.string.voicemail_error_inbox_almost_full_turn_archive_on_title);
226       message = context.getText(R.string.voicemail_error_inbox_almost_full_turn_archive_on_message);
227       enabledImpression = DialerImpression.Type.VVM_USER_ENABLED_ARCHIVE_FROM_VM_ALMOST_FULL_PROMO;
228       dismissedImpression = DialerImpression.Type.VVM_USER_DISMISSED_VM_ALMOST_FULL_PROMO;
229       dismissedKey = VOICEMAIL_PROMO_ALMOST_FULL_DISMISSED_KEY;
230     }
231 
232     return createVMQuotaPromo(
233         context,
234         phoneAccountHandle,
235         status,
236         statusReader,
237         voicemailClient,
238         sharedPreferenceForAccount,
239         title,
240         message,
241         enabledImpression,
242         dismissedImpression,
243         dismissedKey);
244   }
245 
isPromoForArchiveDismissed( PerAccountSharedPreferences sharedPreferenceForAccount, boolean isFull)246   private static boolean isPromoForArchiveDismissed(
247       PerAccountSharedPreferences sharedPreferenceForAccount, boolean isFull) {
248     if (isFull) {
249       return sharedPreferenceForAccount.getBoolean(VOICEMAIL_PROMO_DISMISSED_KEY, false);
250     } else {
251       return sharedPreferenceForAccount.getBoolean(
252           VOICEMAIL_PROMO_ALMOST_FULL_DISMISSED_KEY, false);
253     }
254   }
255 
createVMQuotaPromo( Context context, PhoneAccountHandle phoneAccountHandle, VoicemailStatus status, VoicemailStatusReader statusReader, VoicemailClient voicemailClient, PerAccountSharedPreferences sharedPreferenceForAccount, String title, CharSequence message, DialerImpression.Type impressionToLogOnEnable, DialerImpression.Type impressionToLogOnDismiss, String preferenceKeyToUpdate)256   private static VoicemailErrorMessage createVMQuotaPromo(
257       Context context,
258       PhoneAccountHandle phoneAccountHandle,
259       VoicemailStatus status,
260       VoicemailStatusReader statusReader,
261       VoicemailClient voicemailClient,
262       PerAccountSharedPreferences sharedPreferenceForAccount,
263       String title,
264       CharSequence message,
265       DialerImpression.Type impressionToLogOnEnable,
266       DialerImpression.Type impressionToLogOnDismiss,
267       String preferenceKeyToUpdate) {
268     return new VoicemailErrorMessage(
269         title,
270         message,
271         VoicemailErrorMessage.createTurnArchiveOnAction(
272             context,
273             impressionToLogOnEnable,
274             status,
275             statusReader,
276             voicemailClient,
277             phoneAccountHandle),
278         VoicemailErrorMessage.createDismissTurnArchiveOnAction(
279             context,
280             impressionToLogOnDismiss,
281             statusReader,
282             sharedPreferenceForAccount,
283             preferenceKeyToUpdate));
284   }
285 
286   @Nullable
createNoSignalMessage( Context context, VoicemailStatus status)287   private static VoicemailErrorMessage createNoSignalMessage(
288       Context context, VoicemailStatus status) {
289     CharSequence title;
290     CharSequence description;
291     List<Action> actions = new ArrayList<>();
292     if (Status.CONFIGURATION_STATE_OK == status.configurationState) {
293       if (Status.DATA_CHANNEL_STATE_NO_CONNECTION_CELLULAR_REQUIRED == status.dataChannelState) {
294         title = context.getString(R.string.voicemail_error_no_signal_title);
295         description =
296             context.getString(R.string.voicemail_error_no_signal_cellular_required_message);
297       } else {
298         title = context.getString(R.string.voicemail_error_no_signal_title);
299         if (status.isAirplaneMode) {
300           description = context.getString(R.string.voicemail_error_no_signal_airplane_mode_message);
301         } else {
302           description = context.getString(R.string.voicemail_error_no_signal_message);
303         }
304         actions.add(VoicemailErrorMessage.createSyncAction(context, status));
305       }
306     } else {
307       title = context.getString(R.string.voicemail_error_not_activate_no_signal_title);
308       if (status.isAirplaneMode) {
309         description =
310             context.getString(
311                 R.string.voicemail_error_not_activate_no_signal_airplane_mode_message);
312       } else {
313         description = context.getString(R.string.voicemail_error_not_activate_no_signal_message);
314         actions.add(VoicemailErrorMessage.createRetryAction(context, status));
315       }
316     }
317     if (status.isAirplaneMode) {
318       actions.add(VoicemailErrorMessage.createChangeAirplaneModeAction(context));
319     }
320     return new VoicemailErrorMessage(title, description, actions);
321   }
322 }
323