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