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