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