1 /* 2 * Copyright (C) 2016 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.server.telecom.settings; 18 19 import android.annotation.Nullable; 20 import android.app.ActionBar; 21 import android.app.AlertDialog; 22 import android.app.Fragment; 23 import android.app.FragmentManager; 24 import android.app.FragmentTransaction; 25 import android.app.ListActivity; 26 import android.app.LoaderManager; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.CursorLoader; 30 import android.content.DialogInterface; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.Loader; 34 import android.database.Cursor; 35 import android.os.Bundle; 36 import android.provider.BlockedNumberContract; 37 import android.provider.BlockedNumbersManager; 38 import android.telephony.PhoneNumberFormattingTextWatcher; 39 import android.telephony.PhoneNumberUtils; 40 import android.telephony.TelephonyManager; 41 import android.text.Editable; 42 import android.text.TextUtils; 43 import android.text.TextWatcher; 44 import android.view.LayoutInflater; 45 import android.view.MenuItem; 46 import android.view.View; 47 import android.view.inputmethod.InputMethodManager; 48 import android.widget.Button; 49 import android.widget.EditText; 50 import android.widget.LinearLayout; 51 import android.widget.ListView; 52 import android.widget.ProgressBar; 53 import android.widget.RelativeLayout; 54 import android.widget.TextView; 55 import android.widget.Toast; 56 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.server.telecom.R; 59 import com.android.server.telecom.flags.FeatureFlags; 60 import com.android.server.telecom.flags.FeatureFlagsImpl; 61 62 63 /** 64 * Activity to manage blocked numbers using {@link BlockedNumberContract}. 65 */ 66 public class BlockedNumbersActivity extends ListActivity 67 implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener, TextWatcher, 68 BlockNumberTaskFragment.Listener { 69 private static final String ACTION_MANAGE_BLOCKED_NUMBERS = 70 "android.telecom.action.MANAGE_BLOCKED_NUMBERS"; 71 private static final String TAG_BLOCK_NUMBER_TASK_FRAGMENT = "block_number_task_fragment"; 72 private static final String TELECOM_PACKAGE = "com.android.server.telecom"; 73 private static final String[] PROJECTION = new String[] { 74 BlockedNumberContract.BlockedNumbers.COLUMN_ID, 75 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER 76 }; 77 78 private static final String SELECTION = "((" + 79 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " NOTNULL) AND (" + 80 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " != '' ))"; 81 82 private BlockedNumbersManager mBlockedNumbersManager; 83 private BlockNumberTaskFragment mBlockNumberTaskFragment; 84 private BlockedNumbersAdapter mAdapter; 85 private FeatureFlags mFeatureFlags; 86 private TextView mAddButton; 87 private ProgressBar mProgressBar; 88 private RelativeLayout mButterBar; 89 @Nullable private Button mBlockButton; 90 @Nullable private Button mBlockButtonNegative; 91 private TextView mReEnableButton; 92 93 private BroadcastReceiver mBlockingStatusReceiver; 94 getIntentForStartingActivity()95 public static Intent getIntentForStartingActivity() { 96 Intent intent = new Intent(ACTION_MANAGE_BLOCKED_NUMBERS); 97 intent.setPackage(TELECOM_PACKAGE); 98 return intent; 99 } 100 101 @Override onCreate(Bundle savedInstanceState)102 public void onCreate(Bundle savedInstanceState) { 103 super.onCreate(savedInstanceState); 104 setContentView(R.xml.activity_blocked_numbers); 105 106 ActionBar actionBar = getActionBar(); 107 if (actionBar != null) { 108 actionBar.setDisplayHomeAsUpEnabled(true); 109 // set the talkback voice prompt to "Back" instead of "Navigate Up" 110 actionBar.setHomeActionContentDescription(R.string.back); 111 } 112 113 if (!BlockedNumberContract.canCurrentUserBlockNumbers(this)) { 114 TextView nonPrimaryUserText = (TextView) findViewById(R.id.non_primary_user); 115 nonPrimaryUserText.setVisibility(View.VISIBLE); 116 117 LinearLayout manageBlockedNumbersUi = 118 (LinearLayout) findViewById(R.id.manage_blocked_ui); 119 manageBlockedNumbersUi.setVisibility(View.GONE); 120 return; 121 } 122 123 mFeatureFlags = new FeatureFlagsImpl(); 124 FragmentManager fm = getFragmentManager(); 125 mBlockNumberTaskFragment = 126 (BlockNumberTaskFragment) fm.findFragmentByTag(TAG_BLOCK_NUMBER_TASK_FRAGMENT); 127 128 if (mBlockNumberTaskFragment == null) { 129 mBlockNumberTaskFragment = new BlockNumberTaskFragment(); 130 fm.beginTransaction() 131 .add(mBlockNumberTaskFragment, TAG_BLOCK_NUMBER_TASK_FRAGMENT).commit(); 132 } 133 134 mAddButton = (TextView) findViewById(R.id.add_blocked); 135 mAddButton.setOnClickListener(this); 136 mAddButton.setContentDescription(getText(R.string.block_number)); 137 138 mProgressBar = (ProgressBar) findViewById(R.id.progress_bar); 139 String[] fromColumns = {BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER}; 140 int[] toViews = {R.id.blocked_number}; 141 mAdapter = new BlockedNumbersAdapter(this, R.xml.layout_blocked_number, null, fromColumns, 142 toViews, 0); 143 144 ListView listView = getListView(); 145 listView.setAdapter(mAdapter); 146 listView.setDivider(null); 147 listView.setDividerHeight(0); 148 149 mButterBar = (RelativeLayout) findViewById(R.id.butter_bar); 150 mReEnableButton = (TextView) mButterBar.findViewById(R.id.reenable_button); 151 mReEnableButton.setOnClickListener(this); 152 153 updateButterBar(); 154 155 updateEnhancedCallBlockingFragment( 156 BlockedNumbersUtil.isEnhancedCallBlockingEnabledByPlatform(this)); 157 158 mBlockingStatusReceiver = new BroadcastReceiver() { 159 @Override 160 public void onReceive(Context context, Intent intent) { 161 updateButterBar(); 162 } 163 }; 164 IntentFilter blockStatusIntentFilter = new IntentFilter( 165 BlockedNumbersManager.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED); 166 blockStatusIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 167 registerReceiver(mBlockingStatusReceiver, blockStatusIntentFilter, 168 Context.RECEIVER_EXPORTED); 169 170 getLoaderManager().initLoader(0, null, this); 171 mBlockedNumbersManager = mFeatureFlags.telecomMainlineBlockedNumbersManager() 172 ? getSystemService(BlockedNumbersManager.class) 173 : null; 174 } 175 176 @Override onDestroy()177 protected void onDestroy() { 178 if (mBlockingStatusReceiver != null) { 179 unregisterReceiver(mBlockingStatusReceiver); 180 } 181 super.onDestroy(); 182 } 183 184 @Override onOptionsItemSelected(MenuItem item)185 public boolean onOptionsItemSelected(MenuItem item) { 186 switch (item.getItemId()) { 187 case android.R.id.home: 188 this.finish(); 189 return true; 190 default: 191 return super.onOptionsItemSelected(item); 192 } 193 } 194 updateButterBar()195 private void updateButterBar() { 196 boolean isBlockSuppressionEnabled = mBlockedNumbersManager != null 197 ? mBlockedNumbersManager.getBlockSuppressionStatus().getIsSuppressed() 198 : BlockedNumberContract.SystemContract.getBlockSuppressionStatus(this).isSuppressed; 199 if (isBlockSuppressionEnabled) { 200 mButterBar.setVisibility(View.VISIBLE); 201 } else { 202 mButterBar.setVisibility(View.GONE); 203 } 204 } 205 206 /** 207 * Update the visibility of {@link EnhancedCallBlockingFragment}. 208 */ updateEnhancedCallBlockingFragment(boolean show)209 private void updateEnhancedCallBlockingFragment(boolean show) { 210 FragmentManager fragmentManager = getFragmentManager(); 211 Fragment fragment = fragmentManager.findFragmentById(R.id.enhanced_call_blocking_container); 212 if (!show && fragment == null) { 213 // Nothing to show, so bail early. 214 return; 215 } 216 final FragmentTransaction transaction = fragmentManager.beginTransaction(); 217 if (show) { 218 if (fragment == null) { 219 fragment = new EnhancedCallBlockingFragment(); 220 transaction.add(R.id.enhanced_call_blocking_container, fragment); 221 } else { 222 transaction.show(fragment); 223 } 224 } else { 225 transaction.hide(fragment); 226 } 227 transaction.commit(); 228 } 229 230 @Override onCreateLoader(int id, Bundle args)231 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 232 return new CursorLoader(this, BlockedNumberContract.BlockedNumbers.CONTENT_URI, 233 PROJECTION, SELECTION, null, 234 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " ASC"); 235 } 236 237 @Override onLoadFinished(Loader<Cursor> loader, Cursor data)238 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 239 mAdapter.swapCursor(data); 240 mProgressBar.setVisibility(View.GONE); 241 } 242 243 @Override onLoaderReset(Loader<Cursor> loader)244 public void onLoaderReset(Loader<Cursor> loader) { 245 mAdapter.swapCursor(null); 246 mProgressBar.setVisibility(View.VISIBLE); 247 } 248 249 @Override onClick(View view)250 public void onClick(View view) { 251 if (view == mAddButton) { 252 showAddBlockedNumberDialog(); 253 } else if (view == mReEnableButton) { 254 if (mBlockedNumbersManager != null) { 255 mBlockedNumbersManager.endBlockSuppression(); 256 } else { 257 BlockedNumberContract.SystemContract.endBlockSuppression(this); 258 } 259 mButterBar.setVisibility(View.GONE); 260 } 261 } 262 showAddBlockedNumberDialog()263 private void showAddBlockedNumberDialog() { 264 LayoutInflater inflater = this.getLayoutInflater(); 265 View dialogView = inflater.inflate(R.xml.add_blocked_number_dialog, null); 266 final EditText editText = (EditText) dialogView.findViewById(R.id.add_blocked_number); 267 editText.addTextChangedListener(new PhoneNumberFormattingTextWatcher()); 268 editText.addTextChangedListener(this); 269 AlertDialog dialog = new AlertDialog.Builder(this) 270 .setView(dialogView) 271 .setPositiveButton(R.string.block_button, new DialogInterface.OnClickListener() { 272 public void onClick(DialogInterface dialog, int id) { 273 addBlockedNumber(PhoneNumberUtils.stripSeparators( 274 editText.getText().toString())); 275 } 276 }) 277 .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 278 public void onClick(DialogInterface dialog, int id) { 279 dialog.dismiss(); 280 } 281 }) 282 .create(); 283 dialog.setOnShowListener(new AlertDialog.OnShowListener() { 284 @Override 285 public void onShow(DialogInterface dialog) { 286 mBlockButton = ((AlertDialog) dialog) 287 .getButton(AlertDialog.BUTTON_POSITIVE); 288 mBlockButtonNegative = ((AlertDialog) dialog) 289 .getButton(AlertDialog.BUTTON_NEGATIVE); 290 mBlockButton.setAllCaps(false); 291 mBlockButtonNegative.setAllCaps(false); 292 mBlockButton.setEnabled(false); 293 // show keyboard 294 InputMethodManager inputMethodManager = 295 (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 296 inputMethodManager.showSoftInput(editText, 297 InputMethodManager.SHOW_IMPLICIT); 298 299 } 300 }); 301 dialog.show(); 302 } 303 304 /** 305 * Add blocked number if it does not exist. 306 */ addBlockedNumber(String number)307 private void addBlockedNumber(String number) { 308 if (isEmergencyNumber(this, number)) { 309 Toast.makeText( 310 this, 311 getString(R.string.blocked_numbers_block_emergency_number_message), 312 Toast.LENGTH_SHORT).show(); 313 } else { 314 // We disable the add button, to prevent the user from adding other numbers until the 315 // current number is added. 316 mAddButton.setEnabled(false); 317 mBlockNumberTaskFragment.blockIfNotAlreadyBlocked(number, this); 318 } 319 } 320 321 @VisibleForTesting isEmergencyNumber(Context context, String number)322 public static boolean isEmergencyNumber(Context context, String number) { 323 try { 324 TelephonyManager tm = (TelephonyManager) context.getSystemService( 325 Context.TELEPHONY_SERVICE); 326 return tm.isEmergencyNumber(number); 327 } catch (UnsupportedOperationException | IllegalStateException ignored) { 328 return false; 329 } 330 } 331 332 @Override beforeTextChanged(CharSequence s, int start, int count, int after)333 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 334 // no-op 335 } 336 337 @Override onTextChanged(CharSequence text, int start, int before, int count)338 public void onTextChanged(CharSequence text, int start, int before, int count) { 339 if (mBlockButton != null) { 340 mBlockButton.setEnabled( 341 !TextUtils.isEmpty(PhoneNumberUtils.stripSeparators(text.toString()))); 342 } 343 } 344 345 @Override afterTextChanged(Editable s)346 public void afterTextChanged(Editable s) { 347 // no-op 348 } 349 350 @Override onBlocked(String number, boolean alreadyBlocked)351 public void onBlocked(String number, boolean alreadyBlocked) { 352 if (alreadyBlocked) { 353 BlockedNumbersUtil.showToastWithFormattedNumber(this, 354 R.string.blocked_numbers_number_already_blocked_message, number); 355 } else { 356 BlockedNumbersUtil.showToastWithFormattedNumber(this, 357 R.string.blocked_numbers_number_blocked_message, number); 358 } 359 mAddButton.setEnabled(true); 360 } 361 }