Home | History | Annotate | Download | only in blocking
      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 
     17 package com.android.dialer.blocking;
     18 
     19 import android.app.AlertDialog;
     20 import android.app.Dialog;
     21 import android.app.DialogFragment;
     22 import android.app.FragmentManager;
     23 import android.content.ContentValues;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.net.Uri;
     27 import android.os.Bundle;
     28 import android.support.design.widget.Snackbar;
     29 import android.telephony.PhoneNumberUtils;
     30 import android.text.TextUtils;
     31 import android.view.View;
     32 import android.widget.Toast;
     33 import com.android.contacts.common.util.ContactDisplayUtils;
     34 import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnBlockNumberListener;
     35 import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler.OnUnblockNumberListener;
     36 import com.android.dialer.logging.InteractionEvent;
     37 import com.android.dialer.logging.Logger;
     38 import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker;
     39 
     40 /**
     41  * Fragment for confirming and enacting blocking/unblocking a number. Also invokes snackbar
     42  * providing undo functionality.
     43  */
     44 public class BlockNumberDialogFragment extends DialogFragment {
     45 
     46   private static final String BLOCK_DIALOG_FRAGMENT = "BlockNumberDialog";
     47   private static final String ARG_BLOCK_ID = "argBlockId";
     48   private static final String ARG_NUMBER = "argNumber";
     49   private static final String ARG_COUNTRY_ISO = "argCountryIso";
     50   private static final String ARG_DISPLAY_NUMBER = "argDisplayNumber";
     51   private static final String ARG_PARENT_VIEW_ID = "parentViewId";
     52   private String mNumber;
     53   private String mDisplayNumber;
     54   private String mCountryIso;
     55   private FilteredNumberAsyncQueryHandler mHandler;
     56   private View mParentView;
     57   private VisualVoicemailEnabledChecker mVoicemailEnabledChecker;
     58   private Callback mCallback;
     59 
     60   public static BlockNumberDialogFragment show(
     61       Integer blockId,
     62       String number,
     63       String countryIso,
     64       String displayNumber,
     65       Integer parentViewId,
     66       FragmentManager fragmentManager,
     67       Callback callback) {
     68     final BlockNumberDialogFragment newFragment =
     69         BlockNumberDialogFragment.newInstance(
     70             blockId, number, countryIso, displayNumber, parentViewId);
     71 
     72     newFragment.setCallback(callback);
     73     newFragment.show(fragmentManager, BlockNumberDialogFragment.BLOCK_DIALOG_FRAGMENT);
     74     return newFragment;
     75   }
     76 
     77   private static BlockNumberDialogFragment newInstance(
     78       Integer blockId,
     79       String number,
     80       String countryIso,
     81       String displayNumber,
     82       Integer parentViewId) {
     83     final BlockNumberDialogFragment fragment = new BlockNumberDialogFragment();
     84     final Bundle args = new Bundle();
     85     if (blockId != null) {
     86       args.putInt(ARG_BLOCK_ID, blockId.intValue());
     87     }
     88     if (parentViewId != null) {
     89       args.putInt(ARG_PARENT_VIEW_ID, parentViewId.intValue());
     90     }
     91     args.putString(ARG_NUMBER, number);
     92     args.putString(ARG_COUNTRY_ISO, countryIso);
     93     args.putString(ARG_DISPLAY_NUMBER, displayNumber);
     94     fragment.setArguments(args);
     95     return fragment;
     96   }
     97 
     98   public void setFilteredNumberAsyncQueryHandlerForTesting(
     99       FilteredNumberAsyncQueryHandler handler) {
    100     mHandler = handler;
    101   }
    102 
    103   @Override
    104   public Context getContext() {
    105     return getActivity();
    106   }
    107 
    108   @Override
    109   public Dialog onCreateDialog(Bundle savedInstanceState) {
    110     super.onCreateDialog(savedInstanceState);
    111     final boolean isBlocked = getArguments().containsKey(ARG_BLOCK_ID);
    112 
    113     mNumber = getArguments().getString(ARG_NUMBER);
    114     mDisplayNumber = getArguments().getString(ARG_DISPLAY_NUMBER);
    115     mCountryIso = getArguments().getString(ARG_COUNTRY_ISO);
    116 
    117     if (TextUtils.isEmpty(mDisplayNumber)) {
    118       mDisplayNumber = mNumber;
    119     }
    120 
    121     mHandler = new FilteredNumberAsyncQueryHandler(getContext());
    122     mVoicemailEnabledChecker = new VisualVoicemailEnabledChecker(getActivity(), null);
    123     // Choose not to update VoicemailEnabledChecker, as checks should already been done in
    124     // all current use cases.
    125     mParentView = getActivity().findViewById(getArguments().getInt(ARG_PARENT_VIEW_ID));
    126 
    127     CharSequence title;
    128     String okText;
    129     String message;
    130     if (isBlocked) {
    131       title = null;
    132       okText = getString(R.string.unblock_number_ok);
    133       message =
    134           ContactDisplayUtils.getTtsSpannedPhoneNumber(
    135                   getResources(), R.string.unblock_number_confirmation_title, mDisplayNumber)
    136               .toString();
    137     } else {
    138       title =
    139           ContactDisplayUtils.getTtsSpannedPhoneNumber(
    140               getResources(), R.string.block_number_confirmation_title, mDisplayNumber);
    141       okText = getString(R.string.block_number_ok);
    142       if (FilteredNumberCompat.useNewFiltering(getContext())) {
    143         message = getString(R.string.block_number_confirmation_message_new_filtering);
    144       } else if (mVoicemailEnabledChecker.isVisualVoicemailEnabled()) {
    145         message = getString(R.string.block_number_confirmation_message_vvm);
    146       } else {
    147         message = getString(R.string.block_number_confirmation_message_no_vvm);
    148       }
    149     }
    150 
    151     AlertDialog.Builder builder =
    152         new AlertDialog.Builder(getActivity())
    153             .setTitle(title)
    154             .setMessage(message)
    155             .setPositiveButton(
    156                 okText,
    157                 new DialogInterface.OnClickListener() {
    158                   @Override
    159                   public void onClick(DialogInterface dialog, int id) {
    160                     if (isBlocked) {
    161                       unblockNumber();
    162                     } else {
    163                       blockNumber();
    164                     }
    165                   }
    166                 })
    167             .setNegativeButton(android.R.string.cancel, null);
    168     return builder.create();
    169   }
    170 
    171   @Override
    172   public void onActivityCreated(Bundle savedInstanceState) {
    173     super.onActivityCreated(savedInstanceState);
    174     String e164Number = PhoneNumberUtils.formatNumberToE164(mNumber, mCountryIso);
    175     if (!FilteredNumbersUtil.canBlockNumber(getContext(), e164Number, mNumber)) {
    176       dismiss();
    177       Toast.makeText(
    178               getContext(),
    179               ContactDisplayUtils.getTtsSpannedPhoneNumber(
    180                   getResources(), R.string.invalidNumber, mDisplayNumber),
    181               Toast.LENGTH_SHORT)
    182           .show();
    183     }
    184   }
    185 
    186   @Override
    187   public void onPause() {
    188     // Dismiss on rotation.
    189     dismiss();
    190     mCallback = null;
    191 
    192     super.onPause();
    193   }
    194 
    195   public void setCallback(Callback callback) {
    196     mCallback = callback;
    197   }
    198 
    199   private CharSequence getBlockedMessage() {
    200     return ContactDisplayUtils.getTtsSpannedPhoneNumber(
    201         getResources(), R.string.snackbar_number_blocked, mDisplayNumber);
    202   }
    203 
    204   private CharSequence getUnblockedMessage() {
    205     return ContactDisplayUtils.getTtsSpannedPhoneNumber(
    206         getResources(), R.string.snackbar_number_unblocked, mDisplayNumber);
    207   }
    208 
    209   private int getActionTextColor() {
    210     return getContext().getResources().getColor(R.color.dialer_snackbar_action_text_color);
    211   }
    212 
    213   private void blockNumber() {
    214     final CharSequence message = getBlockedMessage();
    215     final CharSequence undoMessage = getUnblockedMessage();
    216     final Callback callback = mCallback;
    217     final int actionTextColor = getActionTextColor();
    218     final Context applicationContext = getContext().getApplicationContext();
    219 
    220     final OnUnblockNumberListener onUndoListener =
    221         new OnUnblockNumberListener() {
    222           @Override
    223           public void onUnblockComplete(int rows, ContentValues values) {
    224             Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
    225             if (callback != null) {
    226               callback.onChangeFilteredNumberUndo();
    227             }
    228           }
    229         };
    230 
    231     final OnBlockNumberListener onBlockNumberListener =
    232         new OnBlockNumberListener() {
    233           @Override
    234           public void onBlockComplete(final Uri uri) {
    235             final View.OnClickListener undoListener =
    236                 new View.OnClickListener() {
    237                   @Override
    238                   public void onClick(View view) {
    239                     // Delete the newly created row on 'undo'.
    240                     Logger.get(applicationContext)
    241                         .logInteraction(InteractionEvent.Type.UNDO_BLOCK_NUMBER);
    242                     mHandler.unblock(onUndoListener, uri);
    243                   }
    244                 };
    245 
    246             Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
    247                 .setAction(R.string.block_number_undo, undoListener)
    248                 .setActionTextColor(actionTextColor)
    249                 .show();
    250 
    251             if (callback != null) {
    252               callback.onFilterNumberSuccess();
    253             }
    254 
    255             if (FilteredNumbersUtil.hasRecentEmergencyCall(applicationContext)) {
    256               FilteredNumbersUtil.maybeNotifyCallBlockingDisabled(applicationContext);
    257             }
    258           }
    259         };
    260 
    261     mHandler.blockNumber(onBlockNumberListener, mNumber, mCountryIso);
    262   }
    263 
    264   private void unblockNumber() {
    265     final CharSequence message = getUnblockedMessage();
    266     final CharSequence undoMessage = getBlockedMessage();
    267     final Callback callback = mCallback;
    268     final int actionTextColor = getActionTextColor();
    269     final Context applicationContext = getContext().getApplicationContext();
    270 
    271     final OnBlockNumberListener onUndoListener =
    272         new OnBlockNumberListener() {
    273           @Override
    274           public void onBlockComplete(final Uri uri) {
    275             Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
    276             if (callback != null) {
    277               callback.onChangeFilteredNumberUndo();
    278             }
    279           }
    280         };
    281 
    282     mHandler.unblock(
    283         new OnUnblockNumberListener() {
    284           @Override
    285           public void onUnblockComplete(int rows, final ContentValues values) {
    286             final View.OnClickListener undoListener =
    287                 new View.OnClickListener() {
    288                   @Override
    289                   public void onClick(View view) {
    290                     // Re-insert the row on 'undo', with a new ID.
    291                     Logger.get(applicationContext)
    292                         .logInteraction(InteractionEvent.Type.UNDO_UNBLOCK_NUMBER);
    293                     mHandler.blockNumber(onUndoListener, values);
    294                   }
    295                 };
    296 
    297             Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
    298                 .setAction(R.string.block_number_undo, undoListener)
    299                 .setActionTextColor(actionTextColor)
    300                 .show();
    301 
    302             if (callback != null) {
    303               callback.onUnfilterNumberSuccess();
    304             }
    305           }
    306         },
    307         getArguments().getInt(ARG_BLOCK_ID));
    308   }
    309 
    310   /**
    311    * Use a callback interface to update UI after success/undo. Favor this approach over other more
    312    * standard paradigms because of the variety of scenarios in which the DialogFragment can be
    313    * invoked (by an Activity, by a fragment, by an adapter, by an adapter list item). Because of
    314    * this, we do NOT support retaining state on rotation, and will dismiss the dialog upon rotation
    315    * instead.
    316    */
    317   public interface Callback {
    318 
    319     /** Called when a number is successfully added to the set of filtered numbers */
    320     void onFilterNumberSuccess();
    321 
    322     /** Called when a number is successfully removed from the set of filtered numbers */
    323     void onUnfilterNumberSuccess();
    324 
    325     /** Called when the action of filtering or unfiltering a number is undone */
    326     void onChangeFilteredNumberUndo();
    327   }
    328 }
    329