1 /* 2 * Copyright (C) 2008 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.phone; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.Dialog; 22 import android.app.StatusBarManager; 23 import android.app.WallpaperManager; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.graphics.Point; 29 import android.media.AudioManager; 30 import android.media.ToneGenerator; 31 import android.net.Uri; 32 import android.os.Bundle; 33 import android.os.PersistableBundle; 34 import android.provider.Settings; 35 import android.telecom.PhoneAccount; 36 import android.telephony.CarrierConfigManager; 37 import android.telephony.PhoneNumberUtils; 38 import android.telephony.SubscriptionManager; 39 import android.text.Editable; 40 import android.text.InputType; 41 import android.text.Spannable; 42 import android.text.SpannableString; 43 import android.text.TextUtils; 44 import android.text.TextWatcher; 45 import android.text.method.DialerKeyListener; 46 import android.text.style.TtsSpan; 47 import android.util.Log; 48 import android.view.HapticFeedbackConstants; 49 import android.view.KeyEvent; 50 import android.view.MenuItem; 51 import android.view.MotionEvent; 52 import android.view.View; 53 import android.view.WindowManager; 54 import android.widget.EditText; 55 56 import com.android.internal.colorextraction.ColorExtractor; 57 import com.android.internal.colorextraction.ColorExtractor.GradientColors; 58 import com.android.internal.colorextraction.drawable.GradientDrawable; 59 import com.android.phone.common.dialpad.DialpadKeyButton; 60 import com.android.phone.common.util.ViewUtil; 61 62 /** 63 * EmergencyDialer is a special dialer that is used ONLY for dialing emergency calls. 64 * 65 * It's a simplified version of the regular dialer (i.e. the TwelveKeyDialer 66 * activity from apps/Contacts) that: 67 * 1. Allows ONLY emergency calls to be dialed 68 * 2. Disallows voicemail functionality 69 * 3. Uses the FLAG_SHOW_WHEN_LOCKED window manager flag to allow this 70 * activity to stay in front of the keyguard. 71 * 72 * TODO: Even though this is an ultra-simplified version of the normal 73 * dialer, there's still lots of code duplication between this class and 74 * the TwelveKeyDialer class from apps/Contacts. Could the common code be 75 * moved into a shared base class that would live in the framework? 76 * Or could we figure out some way to move *this* class into apps/Contacts 77 * also? 78 */ 79 public class EmergencyDialer extends Activity implements View.OnClickListener, 80 View.OnLongClickListener, View.OnKeyListener, TextWatcher, 81 DialpadKeyButton.OnPressedListener, ColorExtractor.OnColorsChangedListener { 82 // Keys used with onSaveInstanceState(). 83 private static final String LAST_NUMBER = "lastNumber"; 84 85 // Intent action for this activity. 86 public static final String ACTION_DIAL = "com.android.phone.EmergencyDialer.DIAL"; 87 88 // List of dialer button IDs. 89 private static final int[] DIALER_KEYS = new int[] { 90 R.id.one, R.id.two, R.id.three, 91 R.id.four, R.id.five, R.id.six, 92 R.id.seven, R.id.eight, R.id.nine, 93 R.id.star, R.id.zero, R.id.pound }; 94 95 // Debug constants. 96 private static final boolean DBG = false; 97 private static final String LOG_TAG = "EmergencyDialer"; 98 99 /** The length of DTMF tones in milliseconds */ 100 private static final int TONE_LENGTH_MS = 150; 101 102 /** The DTMF tone volume relative to other sounds in the stream */ 103 private static final int TONE_RELATIVE_VOLUME = 80; 104 105 /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */ 106 private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF; 107 108 private static final int BAD_EMERGENCY_NUMBER_DIALOG = 0; 109 110 /** 90% opacity, different from other gradients **/ 111 private static final int BACKGROUND_GRADIENT_ALPHA = 230; 112 113 EditText mDigits; 114 private View mDialButton; 115 private View mDelete; 116 117 private ToneGenerator mToneGenerator; 118 private Object mToneGeneratorLock = new Object(); 119 120 // determines if we want to playback local DTMF tones. 121 private boolean mDTMFToneEnabled; 122 123 private EmergencyActionGroup mEmergencyActionGroup; 124 125 // close activity when screen turns off 126 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 127 @Override 128 public void onReceive(Context context, Intent intent) { 129 if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { 130 finishAndRemoveTask(); 131 } 132 } 133 }; 134 135 private String mLastNumber; // last number we tried to dial. Used to restore error dialog. 136 137 // Background gradient 138 private ColorExtractor mColorExtractor; 139 private GradientDrawable mBackgroundGradient; 140 private boolean mSupportsDarkText; 141 142 @Override 143 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 144 // Do nothing 145 } 146 147 @Override 148 public void onTextChanged(CharSequence input, int start, int before, int changeCount) { 149 // Do nothing 150 } 151 152 @Override 153 public void afterTextChanged(Editable input) { 154 // Check for special sequences, in particular the "**04" or "**05" 155 // sequences that allow you to enter PIN or PUK-related codes. 156 // 157 // But note we *don't* allow most other special sequences here, 158 // like "secret codes" (*#*#<code>#*#*) or IMEI display ("*#06#"), 159 // since those shouldn't be available if the device is locked. 160 // 161 // So we call SpecialCharSequenceMgr.handleCharsForLockedDevice() 162 // here, not the regular handleChars() method. 163 if (SpecialCharSequenceMgr.handleCharsForLockedDevice(this, input.toString(), this)) { 164 // A special sequence was entered, clear the digits 165 mDigits.getText().clear(); 166 } 167 168 updateDialAndDeleteButtonStateEnabledAttr(); 169 updateTtsSpans(); 170 } 171 172 @Override 173 protected void onCreate(Bundle icicle) { 174 super.onCreate(icicle); 175 176 // Allow this activity to be displayed in front of the keyguard / lockscreen. 177 WindowManager.LayoutParams lp = getWindow().getAttributes(); 178 lp.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 179 180 // When no proximity sensor is available, use a shorter timeout. 181 // TODO: Do we enable this for non proximity devices any more? 182 // lp.userActivityTimeout = USER_ACTIVITY_TIMEOUT_WHEN_NO_PROX_SENSOR; 183 184 getWindow().setAttributes(lp); 185 186 mColorExtractor = new ColorExtractor(this); 187 GradientColors lockScreenColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, 188 ColorExtractor.TYPE_EXTRA_DARK); 189 updateTheme(lockScreenColors.supportsDarkText()); 190 191 setContentView(R.layout.emergency_dialer); 192 193 mDigits = (EditText) findViewById(R.id.digits); 194 mDigits.setKeyListener(DialerKeyListener.getInstance()); 195 mDigits.setOnClickListener(this); 196 mDigits.setOnKeyListener(this); 197 mDigits.setLongClickable(false); 198 mDigits.setInputType(InputType.TYPE_NULL); 199 maybeAddNumberFormatting(); 200 201 mBackgroundGradient = new GradientDrawable(this); 202 Point displaySize = new Point(); 203 ((WindowManager) getSystemService(Context.WINDOW_SERVICE)) 204 .getDefaultDisplay().getSize(displaySize); 205 mBackgroundGradient.setScreenSize(displaySize.x, displaySize.y); 206 mBackgroundGradient.setAlpha(BACKGROUND_GRADIENT_ALPHA); 207 getWindow().setBackgroundDrawable(mBackgroundGradient); 208 209 // Check for the presence of the keypad 210 View view = findViewById(R.id.one); 211 if (view != null) { 212 setupKeypad(); 213 } 214 215 mDelete = findViewById(R.id.deleteButton); 216 mDelete.setOnClickListener(this); 217 mDelete.setOnLongClickListener(this); 218 219 mDialButton = findViewById(R.id.floating_action_button); 220 221 // Check whether we should show the onscreen "Dial" button and co. 222 // Read carrier config through the public API because PhoneGlobals is not available when we 223 // run as a secondary user. 224 CarrierConfigManager configMgr = 225 (CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE); 226 PersistableBundle carrierConfig = 227 configMgr.getConfigForSubId(SubscriptionManager.getDefaultVoiceSubscriptionId()); 228 if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL)) { 229 mDialButton.setOnClickListener(this); 230 } else { 231 mDialButton.setVisibility(View.GONE); 232 } 233 ViewUtil.setupFloatingActionButton(mDialButton, getResources()); 234 235 if (icicle != null) { 236 super.onRestoreInstanceState(icicle); 237 } 238 239 // Extract phone number from intent 240 Uri data = getIntent().getData(); 241 if (data != null && (PhoneAccount.SCHEME_TEL.equals(data.getScheme()))) { 242 String number = PhoneNumberUtils.getNumberFromIntent(getIntent(), this); 243 if (number != null) { 244 mDigits.setText(number); 245 } 246 } 247 248 // if the mToneGenerator creation fails, just continue without it. It is 249 // a local audio signal, and is not as important as the dtmf tone itself. 250 synchronized (mToneGeneratorLock) { 251 if (mToneGenerator == null) { 252 try { 253 mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME); 254 } catch (RuntimeException e) { 255 Log.w(LOG_TAG, "Exception caught while creating local tone generator: " + e); 256 mToneGenerator = null; 257 } 258 } 259 } 260 261 final IntentFilter intentFilter = new IntentFilter(); 262 intentFilter.addAction(Intent.ACTION_SCREEN_OFF); 263 registerReceiver(mBroadcastReceiver, intentFilter); 264 265 mEmergencyActionGroup = (EmergencyActionGroup) findViewById(R.id.emergency_action_group); 266 } 267 268 @Override 269 protected void onDestroy() { 270 super.onDestroy(); 271 synchronized (mToneGeneratorLock) { 272 if (mToneGenerator != null) { 273 mToneGenerator.release(); 274 mToneGenerator = null; 275 } 276 } 277 unregisterReceiver(mBroadcastReceiver); 278 } 279 280 @Override 281 protected void onRestoreInstanceState(Bundle icicle) { 282 mLastNumber = icicle.getString(LAST_NUMBER); 283 } 284 285 @Override 286 protected void onSaveInstanceState(Bundle outState) { 287 super.onSaveInstanceState(outState); 288 outState.putString(LAST_NUMBER, mLastNumber); 289 } 290 291 /** 292 * Explicitly turn off number formatting, since it gets in the way of the emergency 293 * number detector 294 */ 295 protected void maybeAddNumberFormatting() { 296 // Do nothing. 297 } 298 299 @Override 300 protected void onPostCreate(Bundle savedInstanceState) { 301 super.onPostCreate(savedInstanceState); 302 303 // This can't be done in onCreate(), since the auto-restoring of the digits 304 // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState() 305 // is called. This method will be called every time the activity is created, and 306 // will always happen after onRestoreSavedInstanceState(). 307 mDigits.addTextChangedListener(this); 308 } 309 310 private void setupKeypad() { 311 // Setup the listeners for the buttons 312 for (int id : DIALER_KEYS) { 313 final DialpadKeyButton key = (DialpadKeyButton) findViewById(id); 314 key.setOnPressedListener(this); 315 } 316 317 View view = findViewById(R.id.zero); 318 view.setOnLongClickListener(this); 319 } 320 321 /** 322 * handle key events 323 */ 324 @Override 325 public boolean onKeyDown(int keyCode, KeyEvent event) { 326 switch (keyCode) { 327 // Happen when there's a "Call" hard button. 328 case KeyEvent.KEYCODE_CALL: { 329 if (TextUtils.isEmpty(mDigits.getText().toString())) { 330 // if we are adding a call from the InCallScreen and the phone 331 // number entered is empty, we just close the dialer to expose 332 // the InCallScreen under it. 333 finish(); 334 } else { 335 // otherwise, we place the call. 336 placeCall(); 337 } 338 return true; 339 } 340 } 341 return super.onKeyDown(keyCode, event); 342 } 343 344 private void keyPressed(int keyCode) { 345 mDigits.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 346 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode); 347 mDigits.onKeyDown(keyCode, event); 348 } 349 350 @Override 351 public boolean onKey(View view, int keyCode, KeyEvent event) { 352 switch (view.getId()) { 353 case R.id.digits: 354 // Happen when "Done" button of the IME is pressed. This can happen when this 355 // Activity is forced into landscape mode due to a desk dock. 356 if (keyCode == KeyEvent.KEYCODE_ENTER 357 && event.getAction() == KeyEvent.ACTION_UP) { 358 placeCall(); 359 return true; 360 } 361 break; 362 } 363 return false; 364 } 365 366 @Override 367 public boolean dispatchTouchEvent(MotionEvent ev) { 368 mEmergencyActionGroup.onPreTouchEvent(ev); 369 boolean handled = super.dispatchTouchEvent(ev); 370 mEmergencyActionGroup.onPostTouchEvent(ev); 371 return handled; 372 } 373 374 @Override 375 public void onClick(View view) { 376 switch (view.getId()) { 377 case R.id.deleteButton: { 378 keyPressed(KeyEvent.KEYCODE_DEL); 379 return; 380 } 381 case R.id.floating_action_button: { 382 view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); 383 placeCall(); 384 return; 385 } 386 case R.id.digits: { 387 if (mDigits.length() != 0) { 388 mDigits.setCursorVisible(true); 389 } 390 return; 391 } 392 } 393 } 394 395 @Override 396 public void onPressed(View view, boolean pressed) { 397 if (!pressed) { 398 return; 399 } 400 switch (view.getId()) { 401 case R.id.one: { 402 playTone(ToneGenerator.TONE_DTMF_1); 403 keyPressed(KeyEvent.KEYCODE_1); 404 return; 405 } 406 case R.id.two: { 407 playTone(ToneGenerator.TONE_DTMF_2); 408 keyPressed(KeyEvent.KEYCODE_2); 409 return; 410 } 411 case R.id.three: { 412 playTone(ToneGenerator.TONE_DTMF_3); 413 keyPressed(KeyEvent.KEYCODE_3); 414 return; 415 } 416 case R.id.four: { 417 playTone(ToneGenerator.TONE_DTMF_4); 418 keyPressed(KeyEvent.KEYCODE_4); 419 return; 420 } 421 case R.id.five: { 422 playTone(ToneGenerator.TONE_DTMF_5); 423 keyPressed(KeyEvent.KEYCODE_5); 424 return; 425 } 426 case R.id.six: { 427 playTone(ToneGenerator.TONE_DTMF_6); 428 keyPressed(KeyEvent.KEYCODE_6); 429 return; 430 } 431 case R.id.seven: { 432 playTone(ToneGenerator.TONE_DTMF_7); 433 keyPressed(KeyEvent.KEYCODE_7); 434 return; 435 } 436 case R.id.eight: { 437 playTone(ToneGenerator.TONE_DTMF_8); 438 keyPressed(KeyEvent.KEYCODE_8); 439 return; 440 } 441 case R.id.nine: { 442 playTone(ToneGenerator.TONE_DTMF_9); 443 keyPressed(KeyEvent.KEYCODE_9); 444 return; 445 } 446 case R.id.zero: { 447 playTone(ToneGenerator.TONE_DTMF_0); 448 keyPressed(KeyEvent.KEYCODE_0); 449 return; 450 } 451 case R.id.pound: { 452 playTone(ToneGenerator.TONE_DTMF_P); 453 keyPressed(KeyEvent.KEYCODE_POUND); 454 return; 455 } 456 case R.id.star: { 457 playTone(ToneGenerator.TONE_DTMF_S); 458 keyPressed(KeyEvent.KEYCODE_STAR); 459 return; 460 } 461 } 462 } 463 464 /** 465 * called for long touch events 466 */ 467 @Override 468 public boolean onLongClick(View view) { 469 int id = view.getId(); 470 switch (id) { 471 case R.id.deleteButton: { 472 mDigits.getText().clear(); 473 return true; 474 } 475 case R.id.zero: { 476 removePreviousDigitIfPossible(); 477 keyPressed(KeyEvent.KEYCODE_PLUS); 478 return true; 479 } 480 } 481 return false; 482 } 483 484 @Override 485 protected void onStart() { 486 super.onStart(); 487 488 mColorExtractor.addOnColorsChangedListener(this); 489 GradientColors lockScreenColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, 490 ColorExtractor.TYPE_EXTRA_DARK); 491 // Do not animate when view isn't visible yet, just set an initial state. 492 mBackgroundGradient.setColors(lockScreenColors, false); 493 updateTheme(lockScreenColors.supportsDarkText()); 494 } 495 496 @Override 497 protected void onResume() { 498 super.onResume(); 499 500 // retrieve the DTMF tone play back setting. 501 mDTMFToneEnabled = Settings.System.getInt(getContentResolver(), 502 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1; 503 504 // if the mToneGenerator creation fails, just continue without it. It is 505 // a local audio signal, and is not as important as the dtmf tone itself. 506 synchronized (mToneGeneratorLock) { 507 if (mToneGenerator == null) { 508 try { 509 mToneGenerator = new ToneGenerator(AudioManager.STREAM_DTMF, 510 TONE_RELATIVE_VOLUME); 511 } catch (RuntimeException e) { 512 Log.w(LOG_TAG, "Exception caught while creating local tone generator: " + e); 513 mToneGenerator = null; 514 } 515 } 516 } 517 518 updateDialAndDeleteButtonStateEnabledAttr(); 519 } 520 521 @Override 522 public void onPause() { 523 super.onPause(); 524 525 synchronized (mToneGeneratorLock) { 526 if (mToneGenerator != null) { 527 mToneGenerator.release(); 528 mToneGenerator = null; 529 } 530 } 531 } 532 533 @Override 534 protected void onStop() { 535 super.onStop(); 536 537 mColorExtractor.removeOnColorsChangedListener(this); 538 } 539 540 /** 541 * Sets theme based on gradient colors 542 * @param supportsDarkText true if gradient supports dark text 543 */ 544 private void updateTheme(boolean supportsDarkText) { 545 if (mSupportsDarkText == supportsDarkText) { 546 return; 547 } 548 mSupportsDarkText = supportsDarkText; 549 550 // We can't change themes after inflation, in this case we'll have to recreate 551 // the whole activity. 552 if (mBackgroundGradient != null) { 553 recreate(); 554 return; 555 } 556 557 int vis = getWindow().getDecorView().getSystemUiVisibility(); 558 if (supportsDarkText) { 559 vis |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; 560 vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 561 setTheme(R.style.EmergencyDialerThemeDark); 562 } else { 563 vis &= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; 564 vis &= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 565 setTheme(R.style.EmergencyDialerTheme); 566 } 567 getWindow().getDecorView().setSystemUiVisibility(vis); 568 } 569 570 /** 571 * place the call, but check to make sure it is a viable number. 572 */ 573 private void placeCall() { 574 mLastNumber = mDigits.getText().toString(); 575 576 // Convert into emergency number according to emergency conversion map. 577 // If conversion map is not defined (this is default), this method does 578 // nothing and just returns input number. 579 mLastNumber = PhoneNumberUtils.convertToEmergencyNumber(this, mLastNumber); 580 581 if (PhoneNumberUtils.isLocalEmergencyNumber(this, mLastNumber)) { 582 if (DBG) Log.d(LOG_TAG, "placing call to " + mLastNumber); 583 584 // place the call if it is a valid number 585 if (mLastNumber == null || !TextUtils.isGraphic(mLastNumber)) { 586 // There is no number entered. 587 playTone(ToneGenerator.TONE_PROP_NACK); 588 return; 589 } 590 Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY); 591 intent.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null)); 592 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 593 startActivity(intent); 594 } else { 595 if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber); 596 597 showDialog(BAD_EMERGENCY_NUMBER_DIALOG); 598 } 599 mDigits.getText().delete(0, mDigits.getText().length()); 600 } 601 602 /** 603 * Plays the specified tone for TONE_LENGTH_MS milliseconds. 604 * 605 * The tone is played locally, using the audio stream for phone calls. 606 * Tones are played only if the "Audible touch tones" user preference 607 * is checked, and are NOT played if the device is in silent mode. 608 * 609 * @param tone a tone code from {@link ToneGenerator} 610 */ 611 void playTone(int tone) { 612 // if local tone playback is disabled, just return. 613 if (!mDTMFToneEnabled) { 614 return; 615 } 616 617 // Also do nothing if the phone is in silent mode. 618 // We need to re-check the ringer mode for *every* playTone() 619 // call, rather than keeping a local flag that's updated in 620 // onResume(), since it's possible to toggle silent mode without 621 // leaving the current activity (via the ENDCALL-longpress menu.) 622 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 623 int ringerMode = audioManager.getRingerMode(); 624 if ((ringerMode == AudioManager.RINGER_MODE_SILENT) 625 || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) { 626 return; 627 } 628 629 synchronized (mToneGeneratorLock) { 630 if (mToneGenerator == null) { 631 Log.w(LOG_TAG, "playTone: mToneGenerator == null, tone: " + tone); 632 return; 633 } 634 635 // Start the new tone (will stop any playing tone) 636 mToneGenerator.startTone(tone, TONE_LENGTH_MS); 637 } 638 } 639 640 private CharSequence createErrorMessage(String number) { 641 if (!TextUtils.isEmpty(number)) { 642 String errorString = getString(R.string.dial_emergency_error, number); 643 int startingPosition = errorString.indexOf(number); 644 int endingPosition = startingPosition + number.length(); 645 Spannable result = new SpannableString(errorString); 646 PhoneNumberUtils.addTtsSpan(result, startingPosition, endingPosition); 647 return result; 648 } else { 649 return getText(R.string.dial_emergency_empty_error).toString(); 650 } 651 } 652 653 @Override 654 protected Dialog onCreateDialog(int id) { 655 AlertDialog dialog = null; 656 if (id == BAD_EMERGENCY_NUMBER_DIALOG) { 657 // construct dialog 658 dialog = new AlertDialog.Builder(this, R.style.EmergencyDialerAlertDialogTheme) 659 .setTitle(getText(R.string.emergency_enable_radio_dialog_title)) 660 .setMessage(createErrorMessage(mLastNumber)) 661 .setPositiveButton(R.string.ok, null) 662 .setCancelable(true).create(); 663 664 // blur stuff behind the dialog 665 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 666 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); 667 } 668 return dialog; 669 } 670 671 @Override 672 protected void onPrepareDialog(int id, Dialog dialog) { 673 super.onPrepareDialog(id, dialog); 674 if (id == BAD_EMERGENCY_NUMBER_DIALOG) { 675 AlertDialog alert = (AlertDialog) dialog; 676 alert.setMessage(createErrorMessage(mLastNumber)); 677 } 678 } 679 680 @Override 681 public boolean onOptionsItemSelected(MenuItem item) { 682 final int itemId = item.getItemId(); 683 if (itemId == android.R.id.home) { 684 onBackPressed(); 685 return true; 686 } 687 return super.onOptionsItemSelected(item); 688 } 689 690 /** 691 * Update the enabledness of the "Dial" and "Backspace" buttons if applicable. 692 */ 693 private void updateDialAndDeleteButtonStateEnabledAttr() { 694 final boolean notEmpty = mDigits.length() != 0; 695 696 mDelete.setEnabled(notEmpty); 697 } 698 699 /** 700 * Remove the digit just before the current position. Used by various long pressed callbacks 701 * to remove the digit that was populated as a result of the short click. 702 */ 703 private void removePreviousDigitIfPossible() { 704 final int currentPosition = mDigits.getSelectionStart(); 705 if (currentPosition > 0) { 706 mDigits.setSelection(currentPosition); 707 mDigits.getText().delete(currentPosition - 1, currentPosition); 708 } 709 } 710 711 /** 712 * Update the text-to-speech annotations in the edit field. 713 */ 714 private void updateTtsSpans() { 715 for (Object o : mDigits.getText().getSpans(0, mDigits.getText().length(), TtsSpan.class)) { 716 mDigits.getText().removeSpan(o); 717 } 718 PhoneNumberUtils.ttsSpanAsPhoneNumber(mDigits.getText(), 0, mDigits.getText().length()); 719 } 720 721 @Override 722 public void onColorsChanged(ColorExtractor extractor, int which) { 723 if ((which & WallpaperManager.FLAG_LOCK) != 0) { 724 GradientColors colors = extractor.getColors(WallpaperManager.FLAG_LOCK, 725 ColorExtractor.TYPE_EXTRA_DARK); 726 mBackgroundGradient.setColors(colors); 727 updateTheme(colors.supportsDarkText()); 728 } 729 } 730 } 731