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