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