• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
show( Integer blockId, String number, String countryIso, String displayNumber, Integer parentViewId, FragmentManager fragmentManager, Callback callback)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 
newInstance( Integer blockId, String number, String countryIso, String displayNumber, Integer parentViewId)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 
setFilteredNumberAsyncQueryHandlerForTesting( FilteredNumberAsyncQueryHandler handler)98   public void setFilteredNumberAsyncQueryHandlerForTesting(
99       FilteredNumberAsyncQueryHandler handler) {
100     mHandler = handler;
101   }
102 
103   @Override
getContext()104   public Context getContext() {
105     return getActivity();
106   }
107 
108   @Override
onCreateDialog(Bundle savedInstanceState)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
onActivityCreated(Bundle savedInstanceState)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
onPause()187   public void onPause() {
188     // Dismiss on rotation.
189     dismiss();
190     mCallback = null;
191 
192     super.onPause();
193   }
194 
setCallback(Callback callback)195   public void setCallback(Callback callback) {
196     mCallback = callback;
197   }
198 
getBlockedMessage()199   private CharSequence getBlockedMessage() {
200     return ContactDisplayUtils.getTtsSpannedPhoneNumber(
201         getResources(), R.string.snackbar_number_blocked, mDisplayNumber);
202   }
203 
getUnblockedMessage()204   private CharSequence getUnblockedMessage() {
205     return ContactDisplayUtils.getTtsSpannedPhoneNumber(
206         getResources(), R.string.snackbar_number_unblocked, mDisplayNumber);
207   }
208 
getActionTextColor()209   private int getActionTextColor() {
210     return getContext().getResources().getColor(R.color.dialer_snackbar_action_text_color);
211   }
212 
blockNumber()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 
unblockNumber()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 */
onFilterNumberSuccess()320     void onFilterNumberSuccess();
321 
322     /** Called when a number is successfully removed from the set of filtered numbers */
onUnfilterNumberSuccess()323     void onUnfilterNumberSuccess();
324 
325     /** Called when the action of filtering or unfiltering a number is undone */
onChangeFilteredNumberUndo()326     void onChangeFilteredNumberUndo();
327   }
328 }
329