• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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.contacts;
18 
19 import com.android.internal.telephony.ITelephony;
20 import com.android.phone.CallLogAsync;
21 import com.android.phone.HapticFeedback;
22 
23 import android.app.Activity;
24 import android.content.ActivityNotFoundException;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.res.Configuration;
28 import android.content.res.Resources;
29 import android.database.Cursor;
30 import android.graphics.Bitmap;
31 import android.graphics.BitmapFactory;
32 import android.graphics.drawable.Drawable;
33 import android.media.AudioManager;
34 import android.media.ToneGenerator;
35 import android.net.Uri;
36 import android.os.Bundle;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.os.SystemClock;
40 import android.provider.Settings;
41 import android.provider.Contacts.People;
42 import android.provider.Contacts.Phones;
43 import android.provider.Contacts.PhonesColumns;
44 import android.provider.Contacts.Intents.Insert;
45 import android.telephony.PhoneNumberFormattingTextWatcher;
46 import android.telephony.PhoneNumberUtils;
47 import android.telephony.PhoneStateListener;
48 import android.telephony.TelephonyManager;
49 import android.text.Editable;
50 import android.text.TextUtils;
51 import android.text.TextWatcher;
52 import android.text.method.DialerKeyListener;
53 import android.util.Log;
54 import android.view.KeyEvent;
55 import android.view.LayoutInflater;
56 import android.view.Menu;
57 import android.view.MenuItem;
58 import android.view.View;
59 import android.view.ViewConfiguration;
60 import android.view.ViewGroup;
61 import android.view.Window;
62 import android.view.inputmethod.InputMethodManager;
63 import android.widget.AdapterView;
64 import android.widget.BaseAdapter;
65 import android.widget.EditText;
66 import android.widget.ImageView;
67 import android.widget.ListView;
68 import android.widget.TextView;
69 
70 /**
71  * Dialer activity that displays the typical twelve key interface.
72  */
73 @SuppressWarnings("deprecation")
74 public class TwelveKeyDialer extends Activity implements View.OnClickListener,
75         View.OnLongClickListener, View.OnKeyListener,
76         AdapterView.OnItemClickListener, TextWatcher {
77     private static final String EMPTY_NUMBER = "";
78     private static final String TAG = "TwelveKeyDialer";
79 
80     /** The length of DTMF tones in milliseconds */
81     private static final int TONE_LENGTH_MS = 150;
82 
83     /** The DTMF tone volume relative to other sounds in the stream */
84     private static final int TONE_RELATIVE_VOLUME = 80;
85 
86     /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
87     private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_MUSIC;
88 
89     private EditText mDigits;
90     private View mDelete;
91     private MenuItem mAddToContactMenuItem;
92     private ToneGenerator mToneGenerator;
93     private Object mToneGeneratorLock = new Object();
94     private Drawable mDigitsBackground;
95     private Drawable mDigitsEmptyBackground;
96     private View mDialpad;
97     private View mVoicemailDialAndDeleteRow;
98     private View mVoicemailButton;
99     private View mDialButton;
100     private ListView mDialpadChooser;
101     private DialpadChooserAdapter mDialpadChooserAdapter;
102     //Member variables for dialpad options
103     private MenuItem m2SecPauseMenuItem;
104     private MenuItem mWaitMenuItem;
105     private static final int MENU_ADD_CONTACTS = 1;
106     private static final int MENU_2S_PAUSE = 2;
107     private static final int MENU_WAIT = 3;
108 
109     // Last number dialed, retrieved asynchronously from the call DB
110     // in onCreate. This number is displayed when the user hits the
111     // send key and cleared in onPause.
112     CallLogAsync mCallLog = new CallLogAsync();
113     private String mLastNumberDialed = EMPTY_NUMBER;
114 
115     // determines if we want to playback local DTMF tones.
116     private boolean mDTMFToneEnabled;
117 
118     // Vibration (haptic feedback) for dialer key presses.
119     private HapticFeedback mHaptic = new HapticFeedback();
120 
121     /** Identifier for the "Add Call" intent extra. */
122     static final String ADD_CALL_MODE_KEY = "add_call_mode";
123 
124     /**
125      * Identifier for intent extra for sending an empty Flash message for
126      * CDMA networks. This message is used by the network to simulate a
127      * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
128      *
129      * TODO: Using an intent extra to tell the phone to send this flash is a
130      * temporary measure. To be replaced with an ITelephony call in the future.
131      * TODO: Keep in sync with the string defined in OutgoingCallBroadcaster.java
132      * in Phone app until this is replaced with the ITelephony API.
133      */
134     static final String EXTRA_SEND_EMPTY_FLASH
135             = "com.android.phone.extra.SEND_EMPTY_FLASH";
136 
137     /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
138     private boolean mIsAddCallMode;
139 
140     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
141             /**
142              * Listen for phone state changes so that we can take down the
143              * "dialpad chooser" if the phone becomes idle while the
144              * chooser UI is visible.
145              */
146             @Override
147             public void onCallStateChanged(int state, String incomingNumber) {
148                 // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
149                 //       + state + ", '" + incomingNumber + "'");
150                 if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
151                     // Log.i(TAG, "Call ended with dialpad chooser visible!  Taking it down...");
152                     // Note there's a race condition in the UI here: the
153                     // dialpad chooser could conceivably disappear (on its
154                     // own) at the exact moment the user was trying to select
155                     // one of the choices, which would be confusing.  (But at
156                     // least that's better than leaving the dialpad chooser
157                     // onscreen, but useless...)
158                     showDialpadChooser(false);
159                 }
160             }
161         };
162 
beforeTextChanged(CharSequence s, int start, int count, int after)163     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
164         // Do nothing
165     }
166 
onTextChanged(CharSequence input, int start, int before, int changeCount)167     public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
168         // Do nothing
169         // DTMF Tones do not need to be played here any longer -
170         // the DTMF dialer handles that functionality now.
171     }
172 
afterTextChanged(Editable input)173     public void afterTextChanged(Editable input) {
174         if (SpecialCharSequenceMgr.handleChars(this, input.toString(), mDigits)) {
175             // A special sequence was entered, clear the digits
176             mDigits.getText().clear();
177         }
178 
179         if (!isDigitsEmpty()) {
180             mDigits.setBackgroundDrawable(mDigitsBackground);
181         } else {
182             mDigits.setCursorVisible(false);
183             mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
184         }
185 
186         updateDialAndDeleteButtonEnabledState();
187     }
188 
189     @Override
onCreate(Bundle icicle)190     protected void onCreate(Bundle icicle) {
191         super.onCreate(icicle);
192 
193         Resources r = getResources();
194         // Do not show title in the case the device is in carmode.
195         if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) ==
196                 Configuration.UI_MODE_TYPE_CAR) {
197             requestWindowFeature(Window.FEATURE_NO_TITLE);
198         }
199         // Set the content view
200         setContentView(getContentViewResource());
201 
202         // Load up the resources for the text field.
203         mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
204         mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
205 
206         mDigits = (EditText) findViewById(R.id.digits);
207         mDigits.setKeyListener(DialerKeyListener.getInstance());
208         mDigits.setOnClickListener(this);
209         mDigits.setOnKeyListener(this);
210 
211         maybeAddNumberFormatting();
212 
213         // Check for the presence of the keypad
214         View view = findViewById(R.id.one);
215         if (view != null) {
216             setupKeypad();
217         }
218 
219         mVoicemailDialAndDeleteRow = findViewById(R.id.voicemailAndDialAndDelete);
220 
221         initVoicemailButton();
222 
223         // Check whether we should show the onscreen "Dial" button.
224         mDialButton = mVoicemailDialAndDeleteRow.findViewById(R.id.dialButton);
225 
226         if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
227             mDialButton.setOnClickListener(this);
228         } else {
229             mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
230             mDialButton = null;
231         }
232 
233         view = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
234         view.setOnClickListener(this);
235         view.setOnLongClickListener(this);
236         mDelete = view;
237 
238         mDialpad = findViewById(R.id.dialpad);  // This is null in landscape mode.
239 
240         // In landscape we put the keyboard in phone mode.
241         // In portrait we prevent the soft keyboard to show since the
242         // dialpad acts as one already.
243         if (null == mDialpad) {
244             mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
245         } else {
246             mDigits.setInputType(android.text.InputType.TYPE_NULL);
247         }
248 
249         // Set up the "dialpad chooser" UI; see showDialpadChooser().
250         mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
251         mDialpadChooser.setOnItemClickListener(this);
252 
253         if (!resolveIntent() && icicle != null) {
254             super.onRestoreInstanceState(icicle);
255         }
256 
257         try {
258             mHaptic.init(this, r.getBoolean(R.bool.config_enable_dialer_key_vibration));
259         } catch (Resources.NotFoundException nfe) {
260              Log.e(TAG, "Vibrate control bool missing.", nfe);
261         }
262 
263     }
264 
265     @Override
onRestoreInstanceState(Bundle icicle)266     protected void onRestoreInstanceState(Bundle icicle) {
267         // Do nothing, state is restored in onCreate() if needed
268     }
269 
maybeAddNumberFormatting()270     protected void maybeAddNumberFormatting() {
271         mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
272     }
273 
274     /**
275      * Overridden by subclasses to control the resource used by the content view.
276      */
getContentViewResource()277     protected int getContentViewResource() {
278         return R.layout.twelve_key_dialer;
279     }
280 
resolveIntent()281     private boolean resolveIntent() {
282         boolean ignoreState = false;
283 
284         // Find the proper intent
285         final Intent intent;
286         if (isChild()) {
287             intent = getParent().getIntent();
288             ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
289         } else {
290             intent = getIntent();
291         }
292         // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
293 
294         // by default we are not adding a call.
295         mIsAddCallMode = false;
296 
297         // By default we don't show the "dialpad chooser" UI.
298         boolean needToShowDialpadChooser = false;
299 
300         // Resolve the intent
301         final String action = intent.getAction();
302         if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
303             // see if we are "adding a call" from the InCallScreen; false by default.
304             mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
305 
306             Uri uri = intent.getData();
307             if (uri != null) {
308                 if ("tel".equals(uri.getScheme())) {
309                     // Put the requested number into the input area
310                     String data = uri.getSchemeSpecificPart();
311                     setFormattedDigits(data);
312                 } else {
313                     String type = intent.getType();
314                     if (People.CONTENT_ITEM_TYPE.equals(type)
315                             || Phones.CONTENT_ITEM_TYPE.equals(type)) {
316                         // Query the phone number
317                         Cursor c = getContentResolver().query(intent.getData(),
318                                 new String[] {PhonesColumns.NUMBER}, null, null, null);
319                         if (c != null) {
320                             if (c.moveToFirst()) {
321                                 // Put the number into the input area
322                                 setFormattedDigits(c.getString(0));
323                             }
324                             c.close();
325                         }
326                     }
327                 }
328             } else {
329                 // ACTION_DIAL or ACTION_VIEW with no data.
330                 // This behaves basically like ACTION_MAIN: If there's
331                 // already an active call, bring up an intermediate UI to
332                 // make the user confirm what they really want to do.
333                 // Be sure *not* to show the dialpad chooser if this is an
334                 // explicit "Add call" action, though.
335                 if (!mIsAddCallMode && phoneIsInUse()) {
336                     needToShowDialpadChooser = true;
337                 }
338             }
339         } else if (Intent.ACTION_MAIN.equals(action)) {
340             // The MAIN action means we're bringing up a blank dialer
341             // (e.g. by selecting the Home shortcut, or tabbing over from
342             // Contacts or Call log.)
343             //
344             // At this point, IF there's already an active call, there's a
345             // good chance that the user got here accidentally (but really
346             // wanted the in-call dialpad instead).  So we bring up an
347             // intermediate UI to make the user confirm what they really
348             // want to do.
349             if (phoneIsInUse()) {
350                 // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
351                 needToShowDialpadChooser = true;
352             }
353         }
354 
355         // Bring up the "dialpad chooser" IFF we need to make the user
356         // confirm which dialpad they really want.
357         showDialpadChooser(needToShowDialpadChooser);
358 
359         return ignoreState;
360     }
361 
setFormattedDigits(String data)362     protected void setFormattedDigits(String data) {
363         // strip the non-dialable numbers out of the data string.
364         String dialString = PhoneNumberUtils.extractNetworkPortion(data);
365         dialString = PhoneNumberUtils.formatNumber(dialString);
366         if (!TextUtils.isEmpty(dialString)) {
367             Editable digits = mDigits.getText();
368             digits.replace(0, digits.length(), dialString);
369             // for some reason this isn't getting called in the digits.replace call above..
370             // but in any case, this will make sure the background drawable looks right
371             afterTextChanged(digits);
372         }
373     }
374 
375     @Override
onNewIntent(Intent newIntent)376     protected void onNewIntent(Intent newIntent) {
377         setIntent(newIntent);
378         resolveIntent();
379     }
380 
381     @Override
onPostCreate(Bundle savedInstanceState)382     protected void onPostCreate(Bundle savedInstanceState) {
383         super.onPostCreate(savedInstanceState);
384 
385         // This can't be done in onCreate(), since the auto-restoring of the digits
386         // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
387         // is called. This method will be called every time the activity is created, and
388         // will always happen after onRestoreSavedInstanceState().
389         mDigits.addTextChangedListener(this);
390     }
391 
setupKeypad()392     private void setupKeypad() {
393         // Setup the listeners for the buttons
394         View view = findViewById(R.id.one);
395         view.setOnClickListener(this);
396         view.setOnLongClickListener(this);
397 
398         findViewById(R.id.two).setOnClickListener(this);
399         findViewById(R.id.three).setOnClickListener(this);
400         findViewById(R.id.four).setOnClickListener(this);
401         findViewById(R.id.five).setOnClickListener(this);
402         findViewById(R.id.six).setOnClickListener(this);
403         findViewById(R.id.seven).setOnClickListener(this);
404         findViewById(R.id.eight).setOnClickListener(this);
405         findViewById(R.id.nine).setOnClickListener(this);
406         findViewById(R.id.star).setOnClickListener(this);
407 
408         view = findViewById(R.id.zero);
409         view.setOnClickListener(this);
410         view.setOnLongClickListener(this);
411 
412         findViewById(R.id.pound).setOnClickListener(this);
413     }
414 
415     @Override
onResume()416     protected void onResume() {
417         super.onResume();
418 
419         // Query the last dialed number. Do it first because hitting
420         // the DB is 'slow'. This call is asynchronous.
421         queryLastOutgoingCall();
422 
423         // retrieve the DTMF tone play back setting.
424         mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
425                 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
426 
427         // Retrieve the haptic feedback setting.
428         mHaptic.checkSystemSetting();
429 
430         // if the mToneGenerator creation fails, just continue without it.  It is
431         // a local audio signal, and is not as important as the dtmf tone itself.
432         synchronized(mToneGeneratorLock) {
433             if (mToneGenerator == null) {
434                 try {
435                     // we want the user to be able to control the volume of the dial tones
436                     // outside of a call, so we use the stream type that is also mapped to the
437                     // volume control keys for this activity
438                     mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
439                     setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
440                 } catch (RuntimeException e) {
441                     Log.w(TAG, "Exception caught while creating local tone generator: " + e);
442                     mToneGenerator = null;
443                 }
444             }
445         }
446 
447         Activity parent = getParent();
448         // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
449         // digits in the dialer field.
450         if (parent != null && parent instanceof DialtactsActivity) {
451             Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
452             if (dialUri != null) {
453                 resolveIntent();
454             }
455         }
456 
457         // While we're in the foreground, listen for phone state changes,
458         // purely so that we can take down the "dialpad chooser" if the
459         // phone becomes idle while the chooser UI is visible.
460         TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
461         telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
462 
463         // Potentially show hint text in the mDigits field when the user
464         // hasn't typed any digits yet.  (If there's already an active call,
465         // this hint text will remind the user that he's about to add a new
466         // call.)
467         //
468         // TODO: consider adding better UI for the case where *both* lines
469         // are currently in use.  (Right now we let the user try to add
470         // another call, but that call is guaranteed to fail.  Perhaps the
471         // entire dialer UI should be disabled instead.)
472         if (phoneIsInUse()) {
473             mDigits.setHint(R.string.dialerDialpadHintText);
474         } else {
475             // Common case; no hint necessary.
476             mDigits.setHint(null);
477 
478             // Also, a sanity-check: the "dialpad chooser" UI should NEVER
479             // be visible if the phone is idle!
480             showDialpadChooser(false);
481         }
482 
483         updateDialAndDeleteButtonEnabledState();
484     }
485 
486     @Override
onWindowFocusChanged(boolean hasFocus)487     public void onWindowFocusChanged(boolean hasFocus) {
488         if (hasFocus) {
489             // Hide soft keyboard, if visible (it's fugly over button dialer).
490             // The only known case where this will be true is when launching the dialer with
491             // ACTION_DIAL via a soft keyboard.  we dismiss it here because we don't
492             // have a window token yet in onCreate / onNewIntent
493             InputMethodManager inputMethodManager = (InputMethodManager)
494                     getSystemService(Context.INPUT_METHOD_SERVICE);
495             inputMethodManager.hideSoftInputFromWindow(mDigits.getWindowToken(), 0);
496         }
497     }
498 
499     @Override
onPause()500     protected void onPause() {
501         super.onPause();
502 
503         // Stop listening for phone state changes.
504         TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
505         telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
506 
507         synchronized(mToneGeneratorLock) {
508             if (mToneGenerator != null) {
509                 mToneGenerator.release();
510                 mToneGenerator = null;
511             }
512         }
513         // TODO: I wonder if we should not check if the AsyncTask that
514         // lookup the last dialed number has completed.
515         mLastNumberDialed = EMPTY_NUMBER;  // Since we are going to query again, free stale number.
516     }
517 
518     @Override
onCreateOptionsMenu(Menu menu)519     public boolean onCreateOptionsMenu(Menu menu) {
520         mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
521                 .setIcon(android.R.drawable.ic_menu_add);
522         m2SecPauseMenuItem = menu.add(0, MENU_2S_PAUSE, 0, R.string.add_2sec_pause)
523                 .setIcon(R.drawable.ic_menu_2sec_pause);
524         mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
525                 .setIcon(R.drawable.ic_menu_wait);
526         return true;
527     }
528 
529     @Override
onPrepareOptionsMenu(Menu menu)530     public boolean onPrepareOptionsMenu(Menu menu) {
531         // We never show a menu if the "choose dialpad" UI is up.
532         if (dialpadChooserVisible()) {
533             return false;
534         }
535 
536         if (isDigitsEmpty()) {
537             mAddToContactMenuItem.setVisible(false);
538             m2SecPauseMenuItem.setVisible(false);
539             mWaitMenuItem.setVisible(false);
540         } else {
541             CharSequence digits = mDigits.getText();
542 
543             // Put the current digits string into an intent
544             Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
545             intent.putExtra(Insert.PHONE, digits);
546             intent.setType(People.CONTENT_ITEM_TYPE);
547             mAddToContactMenuItem.setIntent(intent);
548             mAddToContactMenuItem.setVisible(true);
549 
550             // Check out whether to show Pause & Wait option menu items
551             int selectionStart;
552             int selectionEnd;
553             String strDigits = digits.toString();
554 
555             selectionStart = mDigits.getSelectionStart();
556             selectionEnd = mDigits.getSelectionEnd();
557 
558             if (selectionStart != -1) {
559                 if (selectionStart > selectionEnd) {
560                     // swap it as we want start to be less then end
561                     int tmp = selectionStart;
562                     selectionStart = selectionEnd;
563                     selectionEnd = tmp;
564                 }
565 
566                 if (selectionStart != 0) {
567                     // Pause can be visible if cursor is not in the begining
568                     m2SecPauseMenuItem.setVisible(true);
569 
570                     // For Wait to be visible set of condition to meet
571                     mWaitMenuItem.setVisible(showWait(selectionStart,
572                                                       selectionEnd, strDigits));
573                 } else {
574                     // cursor in the beginning both pause and wait to be invisible
575                     m2SecPauseMenuItem.setVisible(false);
576                     mWaitMenuItem.setVisible(false);
577                 }
578             } else {
579                 // cursor is not selected so assume new digit is added to the end
580                 int strLength = strDigits.length();
581                 mWaitMenuItem.setVisible(showWait(strLength,
582                                                       strLength, strDigits));
583             }
584         }
585         return true;
586     }
587 
588     @Override
onKeyDown(int keyCode, KeyEvent event)589     public boolean onKeyDown(int keyCode, KeyEvent event) {
590         switch (keyCode) {
591             case KeyEvent.KEYCODE_CALL: {
592                 long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
593                 if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
594                     // Launch voice dialer
595                     Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
596                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
597                     try {
598                         startActivity(intent);
599                     } catch (ActivityNotFoundException e) {
600                     }
601                 }
602                 return true;
603             }
604             case KeyEvent.KEYCODE_1: {
605                 long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
606                 if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
607                     // Long press detected, call voice mail
608                     callVoicemail();
609                 }
610                 return true;
611             }
612         }
613         return super.onKeyDown(keyCode, event);
614     }
615 
616     @Override
onKeyUp(int keyCode, KeyEvent event)617     public boolean onKeyUp(int keyCode, KeyEvent event) {
618         switch (keyCode) {
619             case KeyEvent.KEYCODE_CALL: {
620                 // TODO: In dialButtonPressed we do some of these
621                 // tests again. We should try to consolidate them in
622                 // one place.
623                 if (!phoneIsCdma() && mIsAddCallMode && isDigitsEmpty()) {
624                     // For CDMA phones, we always call
625                     // dialButtonPressed() because we may need to send
626                     // an empty flash command to the network.
627                     // Otherwise, if we are adding a call from the
628                     // InCallScreen and the phone number entered is
629                     // empty, we just close the dialer to expose the
630                     // InCallScreen under it.
631                     finish();
632                 }
633 
634                 // If we're CDMA, regardless of where we are adding a call from (either
635                 // InCallScreen or Dialtacts), the user may need to send an empty
636                 // flash command to the network. So let's call dialButtonPressed() regardless
637                 // and dialButtonPressed will handle this functionality for us.
638                 // otherwise, we place the call.
639                 dialButtonPressed();
640                 return true;
641             }
642         }
643         return super.onKeyUp(keyCode, event);
644     }
645 
keyPressed(int keyCode)646     private void keyPressed(int keyCode) {
647         mHaptic.vibrate();
648         KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
649         mDigits.onKeyDown(keyCode, event);
650     }
651 
onKey(View view, int keyCode, KeyEvent event)652     public boolean onKey(View view, int keyCode, KeyEvent event) {
653         switch (view.getId()) {
654             case R.id.digits:
655                 if (keyCode == KeyEvent.KEYCODE_ENTER) {
656                     dialButtonPressed();
657                     return true;
658                 }
659                 break;
660         }
661         return false;
662     }
663 
onClick(View view)664     public void onClick(View view) {
665         switch (view.getId()) {
666             case R.id.one: {
667                 playTone(ToneGenerator.TONE_DTMF_1);
668                 keyPressed(KeyEvent.KEYCODE_1);
669                 return;
670             }
671             case R.id.two: {
672                 playTone(ToneGenerator.TONE_DTMF_2);
673                 keyPressed(KeyEvent.KEYCODE_2);
674                 return;
675             }
676             case R.id.three: {
677                 playTone(ToneGenerator.TONE_DTMF_3);
678                 keyPressed(KeyEvent.KEYCODE_3);
679                 return;
680             }
681             case R.id.four: {
682                 playTone(ToneGenerator.TONE_DTMF_4);
683                 keyPressed(KeyEvent.KEYCODE_4);
684                 return;
685             }
686             case R.id.five: {
687                 playTone(ToneGenerator.TONE_DTMF_5);
688                 keyPressed(KeyEvent.KEYCODE_5);
689                 return;
690             }
691             case R.id.six: {
692                 playTone(ToneGenerator.TONE_DTMF_6);
693                 keyPressed(KeyEvent.KEYCODE_6);
694                 return;
695             }
696             case R.id.seven: {
697                 playTone(ToneGenerator.TONE_DTMF_7);
698                 keyPressed(KeyEvent.KEYCODE_7);
699                 return;
700             }
701             case R.id.eight: {
702                 playTone(ToneGenerator.TONE_DTMF_8);
703                 keyPressed(KeyEvent.KEYCODE_8);
704                 return;
705             }
706             case R.id.nine: {
707                 playTone(ToneGenerator.TONE_DTMF_9);
708                 keyPressed(KeyEvent.KEYCODE_9);
709                 return;
710             }
711             case R.id.zero: {
712                 playTone(ToneGenerator.TONE_DTMF_0);
713                 keyPressed(KeyEvent.KEYCODE_0);
714                 return;
715             }
716             case R.id.pound: {
717                 playTone(ToneGenerator.TONE_DTMF_P);
718                 keyPressed(KeyEvent.KEYCODE_POUND);
719                 return;
720             }
721             case R.id.star: {
722                 playTone(ToneGenerator.TONE_DTMF_S);
723                 keyPressed(KeyEvent.KEYCODE_STAR);
724                 return;
725             }
726             case R.id.deleteButton: {
727                 keyPressed(KeyEvent.KEYCODE_DEL);
728                 return;
729             }
730             case R.id.dialButton: {
731                 mHaptic.vibrate();  // Vibrate here too, just like we do for the regular keys
732                 dialButtonPressed();
733                 return;
734             }
735             case R.id.voicemailButton: {
736                 callVoicemail();
737                 mHaptic.vibrate();
738                 return;
739             }
740             case R.id.digits: {
741                 if (!isDigitsEmpty()) {
742                     mDigits.setCursorVisible(true);
743                 }
744                 return;
745             }
746         }
747     }
748 
onLongClick(View view)749     public boolean onLongClick(View view) {
750         final Editable digits = mDigits.getText();
751         int id = view.getId();
752         switch (id) {
753             case R.id.deleteButton: {
754                 digits.clear();
755                 // TODO: The framework forgets to clear the pressed
756                 // status of disabled button. Until this is fixed,
757                 // clear manually the pressed status. b/2133127
758                 mDelete.setPressed(false);
759                 return true;
760             }
761             case R.id.one: {
762                 if (isDigitsEmpty()) {
763                     callVoicemail();
764                     return true;
765                 }
766                 return false;
767             }
768             case R.id.zero: {
769                 keyPressed(KeyEvent.KEYCODE_PLUS);
770                 return true;
771             }
772         }
773         return false;
774     }
775 
callVoicemail()776     void callVoicemail() {
777         StickyTabs.saveTab(this, getIntent());
778         Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
779                 Uri.fromParts("voicemail", EMPTY_NUMBER, null));
780         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
781         startActivity(intent);
782         mDigits.getText().clear();
783         finish();
784     }
785 
dialButtonPressed()786     void dialButtonPressed() {
787         final String number = mDigits.getText().toString();
788         boolean sendEmptyFlash = false;
789         Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED);
790 
791         if (isDigitsEmpty()) { // There is no number entered.
792             if (phoneIsCdma() && phoneIsOffhook()) {
793                 // On CDMA phones, if we're already on a call, pressing
794                 // the Dial button without entering any digits means "send
795                 // an empty flash."
796                 intent.setData(Uri.fromParts("tel", EMPTY_NUMBER, null));
797                 intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
798                 sendEmptyFlash = true;
799             } else if (!TextUtils.isEmpty(mLastNumberDialed)) {
800                 // Otherwise, pressing the Dial button without entering
801                 // any digits means "recall the last number dialed".
802                 mDigits.setText(mLastNumberDialed);
803                 return;
804             } else {
805                 // Rare case: there's no "last number dialed".  There's
806                 // nothing useful for the Dial button to do in this case.
807                 playTone(ToneGenerator.TONE_PROP_NACK);
808                 return;
809             }
810         } else {  // There is a number.
811             intent.setData(Uri.fromParts("tel", number, null));
812         }
813 
814         StickyTabs.saveTab(this, getIntent());
815         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
816         startActivity(intent);
817         mDigits.getText().clear();
818 
819         // Don't finish TwelveKeyDialer yet if we're sending a blank flash for CDMA. CDMA
820         // networks use Flash messages when special processing needs to be done, mainly for
821         // 3-way or call waiting scenarios. Presumably, here we're in a special 3-way scenario
822         // where the network needs a blank flash before being able to add the new participant.
823         // (This is not the case with all 3-way calls, just certain CDMA infrastructures.)
824         if (!sendEmptyFlash) {
825             finish();
826         }
827     }
828 
829 
830     /**
831      * Plays the specified tone for TONE_LENGTH_MS milliseconds.
832      *
833      * The tone is played locally, using the audio stream for phone calls.
834      * Tones are played only if the "Audible touch tones" user preference
835      * is checked, and are NOT played if the device is in silent mode.
836      *
837      * @param tone a tone code from {@link ToneGenerator}
838      */
playTone(int tone)839     void playTone(int tone) {
840         // if local tone playback is disabled, just return.
841         if (!mDTMFToneEnabled) {
842             return;
843         }
844 
845         // Also do nothing if the phone is in silent mode.
846         // We need to re-check the ringer mode for *every* playTone()
847         // call, rather than keeping a local flag that's updated in
848         // onResume(), since it's possible to toggle silent mode without
849         // leaving the current activity (via the ENDCALL-longpress menu.)
850         AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
851         int ringerMode = audioManager.getRingerMode();
852         if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
853             || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
854             return;
855         }
856 
857         synchronized(mToneGeneratorLock) {
858             if (mToneGenerator == null) {
859                 Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
860                 return;
861             }
862 
863             // Start the new tone (will stop any playing tone)
864             mToneGenerator.startTone(tone, TONE_LENGTH_MS);
865         }
866     }
867 
868     /**
869      * Brings up the "dialpad chooser" UI in place of the usual Dialer
870      * elements (the textfield/button and the dialpad underneath).
871      *
872      * We show this UI if the user brings up the Dialer while a call is
873      * already in progress, since there's a good chance we got here
874      * accidentally (and the user really wanted the in-call dialpad instead).
875      * So in this situation we display an intermediate UI that lets the user
876      * explicitly choose between the in-call dialpad ("Use touch tone
877      * keypad") and the regular Dialer ("Add call").  (Or, the option "Return
878      * to call in progress" just goes back to the in-call UI with no dialpad
879      * at all.)
880      *
881      * @param enabled If true, show the "dialpad chooser" instead
882      *                of the regular Dialer UI
883      */
showDialpadChooser(boolean enabled)884     private void showDialpadChooser(boolean enabled) {
885         if (enabled) {
886             // Log.i(TAG, "Showing dialpad chooser!");
887             mDigits.setVisibility(View.GONE);
888             if (mDialpad != null) mDialpad.setVisibility(View.GONE);
889             mVoicemailDialAndDeleteRow.setVisibility(View.GONE);
890             mDialpadChooser.setVisibility(View.VISIBLE);
891 
892             // Instantiate the DialpadChooserAdapter and hook it up to the
893             // ListView.  We do this only once.
894             if (mDialpadChooserAdapter == null) {
895                 mDialpadChooserAdapter = new DialpadChooserAdapter(this);
896                 mDialpadChooser.setAdapter(mDialpadChooserAdapter);
897             }
898         } else {
899             // Log.i(TAG, "Displaying normal Dialer UI.");
900             mDigits.setVisibility(View.VISIBLE);
901             if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
902             mVoicemailDialAndDeleteRow.setVisibility(View.VISIBLE);
903             mDialpadChooser.setVisibility(View.GONE);
904         }
905     }
906 
907     /**
908      * @return true if we're currently showing the "dialpad chooser" UI.
909      */
dialpadChooserVisible()910     private boolean dialpadChooserVisible() {
911         return mDialpadChooser.getVisibility() == View.VISIBLE;
912     }
913 
914     /**
915      * Simple list adapter, binding to an icon + text label
916      * for each item in the "dialpad chooser" list.
917      */
918     private static class DialpadChooserAdapter extends BaseAdapter {
919         private LayoutInflater mInflater;
920 
921         // Simple struct for a single "choice" item.
922         static class ChoiceItem {
923             String text;
924             Bitmap icon;
925             int id;
926 
ChoiceItem(String s, Bitmap b, int i)927             public ChoiceItem(String s, Bitmap b, int i) {
928                 text = s;
929                 icon = b;
930                 id = i;
931             }
932         }
933 
934         // IDs for the possible "choices":
935         static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
936         static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
937         static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
938 
939         private static final int NUM_ITEMS = 3;
940         private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
941 
DialpadChooserAdapter(Context context)942         public DialpadChooserAdapter(Context context) {
943             // Cache the LayoutInflate to avoid asking for a new one each time.
944             mInflater = LayoutInflater.from(context);
945 
946             // Initialize the possible choices.
947             // TODO: could this be specified entirely in XML?
948 
949             // - "Use touch tone keypad"
950             mChoiceItems[0] = new ChoiceItem(
951                     context.getString(R.string.dialer_useDtmfDialpad),
952                     BitmapFactory.decodeResource(context.getResources(),
953                                                  R.drawable.ic_dialer_fork_tt_keypad),
954                     DIALPAD_CHOICE_USE_DTMF_DIALPAD);
955 
956             // - "Return to call in progress"
957             mChoiceItems[1] = new ChoiceItem(
958                     context.getString(R.string.dialer_returnToInCallScreen),
959                     BitmapFactory.decodeResource(context.getResources(),
960                                                  R.drawable.ic_dialer_fork_current_call),
961                     DIALPAD_CHOICE_RETURN_TO_CALL);
962 
963             // - "Add call"
964             mChoiceItems[2] = new ChoiceItem(
965                     context.getString(R.string.dialer_addAnotherCall),
966                     BitmapFactory.decodeResource(context.getResources(),
967                                                  R.drawable.ic_dialer_fork_add_call),
968                     DIALPAD_CHOICE_ADD_NEW_CALL);
969         }
970 
getCount()971         public int getCount() {
972             return NUM_ITEMS;
973         }
974 
975         /**
976          * Return the ChoiceItem for a given position.
977          */
getItem(int position)978         public Object getItem(int position) {
979             return mChoiceItems[position];
980         }
981 
982         /**
983          * Return a unique ID for each possible choice.
984          */
getItemId(int position)985         public long getItemId(int position) {
986             return position;
987         }
988 
989         /**
990          * Make a view for each row.
991          */
getView(int position, View convertView, ViewGroup parent)992         public View getView(int position, View convertView, ViewGroup parent) {
993             // When convertView is non-null, we can reuse it (there's no need
994             // to reinflate it.)
995             if (convertView == null) {
996                 convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
997             }
998 
999             TextView text = (TextView) convertView.findViewById(R.id.text);
1000             text.setText(mChoiceItems[position].text);
1001 
1002             ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
1003             icon.setImageBitmap(mChoiceItems[position].icon);
1004 
1005             return convertView;
1006         }
1007     }
1008 
1009     /**
1010      * Handle clicks from the dialpad chooser.
1011      */
onItemClick(AdapterView parent, View v, int position, long id)1012     public void onItemClick(AdapterView parent, View v, int position, long id) {
1013         DialpadChooserAdapter.ChoiceItem item =
1014                 (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
1015         int itemId = item.id;
1016         switch (itemId) {
1017             case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
1018                 // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
1019                 // Fire off an intent to go back to the in-call UI
1020                 // with the dialpad visible.
1021                 returnToInCallScreen(true);
1022                 break;
1023 
1024             case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
1025                 // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
1026                 // Fire off an intent to go back to the in-call UI
1027                 // (with the dialpad hidden).
1028                 returnToInCallScreen(false);
1029                 break;
1030 
1031             case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
1032                 // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
1033                 // Ok, guess the user really did want to be here (in the
1034                 // regular Dialer) after all.  Bring back the normal Dialer UI.
1035                 showDialpadChooser(false);
1036                 break;
1037 
1038             default:
1039                 Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
1040                 break;
1041         }
1042     }
1043 
1044     /**
1045      * Returns to the in-call UI (where there's presumably a call in
1046      * progress) in response to the user selecting "use touch tone keypad"
1047      * or "return to call" from the dialpad chooser.
1048      */
returnToInCallScreen(boolean showDialpad)1049     private void returnToInCallScreen(boolean showDialpad) {
1050         try {
1051             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1052             if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
1053         } catch (RemoteException e) {
1054             Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
1055         }
1056 
1057         // Finally, finish() ourselves so that we don't stay on the
1058         // activity stack.
1059         // Note that we do this whether or not the showCallScreenWithDialpad()
1060         // call above had any effect or not!  (That call is a no-op if the
1061         // phone is idle, which can happen if the current call ends while
1062         // the dialpad chooser is up.  In this case we can't show the
1063         // InCallScreen, and there's no point staying here in the Dialer,
1064         // so we just take the user back where he came from...)
1065         finish();
1066     }
1067 
1068     /**
1069      * @return true if the phone is "in use", meaning that at least one line
1070      *              is active (ie. off hook or ringing or dialing).
1071      */
phoneIsInUse()1072     private boolean phoneIsInUse() {
1073         boolean phoneInUse = false;
1074         try {
1075             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1076             if (phone != null) phoneInUse = !phone.isIdle();
1077         } catch (RemoteException e) {
1078             Log.w(TAG, "phone.isIdle() failed", e);
1079         }
1080         return phoneInUse;
1081     }
1082 
1083     /**
1084      * @return true if the phone is a CDMA phone type
1085      */
phoneIsCdma()1086     private boolean phoneIsCdma() {
1087         boolean isCdma = false;
1088         try {
1089             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1090             if (phone != null) {
1091                 isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA);
1092             }
1093         } catch (RemoteException e) {
1094             Log.w(TAG, "phone.getActivePhoneType() failed", e);
1095         }
1096         return isCdma;
1097     }
1098 
1099     /**
1100      * @return true if the phone state is OFFHOOK
1101      */
phoneIsOffhook()1102     private boolean phoneIsOffhook() {
1103         boolean phoneOffhook = false;
1104         try {
1105             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1106             if (phone != null) phoneOffhook = phone.isOffhook();
1107         } catch (RemoteException e) {
1108             Log.w(TAG, "phone.isOffhook() failed", e);
1109         }
1110         return phoneOffhook;
1111     }
1112 
1113 
1114     /**
1115      * Returns true whenever any one of the options from the menu is selected.
1116      * Code changes to support dialpad options
1117      */
1118     @Override
onOptionsItemSelected(MenuItem item)1119     public boolean onOptionsItemSelected(MenuItem item) {
1120         switch (item.getItemId()) {
1121             case MENU_2S_PAUSE:
1122                 updateDialString(",");
1123                 return true;
1124             case MENU_WAIT:
1125                 updateDialString(";");
1126                 return true;
1127         }
1128         return false;
1129     }
1130 
1131     /**
1132      * Updates the dial string (mDigits) after inserting a Pause character (,)
1133      * or Wait character (;).
1134      */
updateDialString(String newDigits)1135     private void updateDialString(String newDigits) {
1136         int selectionStart;
1137         int selectionEnd;
1138 
1139         // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
1140         int anchor = mDigits.getSelectionStart();
1141         int point = mDigits.getSelectionEnd();
1142 
1143         selectionStart = Math.min(anchor, point);
1144         selectionEnd = Math.max(anchor, point);
1145 
1146         Editable digits = mDigits.getText();
1147         if (selectionStart != -1 ) {
1148             if (selectionStart == selectionEnd) {
1149                 // then there is no selection. So insert the pause at this
1150                 // position and update the mDigits.
1151                 digits.replace(selectionStart, selectionStart, newDigits);
1152             } else {
1153                 digits.replace(selectionStart, selectionEnd, newDigits);
1154                 // Unselect: back to a regular cursor, just pass the character inserted.
1155                 mDigits.setSelection(selectionStart + 1);
1156             }
1157         } else {
1158             int len = mDigits.length();
1159             digits.replace(len, len, newDigits);
1160         }
1161     }
1162 
1163     /**
1164      * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
1165      */
updateDialAndDeleteButtonEnabledState()1166     private void updateDialAndDeleteButtonEnabledState() {
1167         final boolean digitsNotEmpty = !isDigitsEmpty();
1168 
1169         if (mDialButton != null) {
1170             // On CDMA phones, if we're already on a call, we *always*
1171             // enable the Dial button (since you can press it without
1172             // entering any digits to send an empty flash.)
1173             if (phoneIsCdma() && phoneIsOffhook()) {
1174                 mDialButton.setEnabled(true);
1175             } else {
1176                 // Common case: GSM, or CDMA but not on a call.
1177                 // Enable the Dial button if some digits have
1178                 // been entered, or if there is a last dialed number
1179                 // that could be redialed.
1180                 mDialButton.setEnabled(digitsNotEmpty ||
1181                                        !TextUtils.isEmpty(mLastNumberDialed));
1182             }
1183         }
1184         mDelete.setEnabled(digitsNotEmpty);
1185     }
1186 
1187 
1188     /**
1189      * Check if voicemail is enabled/accessible.
1190      */
initVoicemailButton()1191     private void initVoicemailButton() {
1192         boolean hasVoicemail = false;
1193         try {
1194             hasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
1195         } catch (SecurityException se) {
1196             // Possibly no READ_PHONE_STATE privilege.
1197         }
1198 
1199         mVoicemailButton = mVoicemailDialAndDeleteRow.findViewById(R.id.voicemailButton);
1200         if (hasVoicemail) {
1201             mVoicemailButton.setOnClickListener(this);
1202         } else {
1203             mVoicemailButton.setEnabled(false);
1204         }
1205     }
1206 
1207     /**
1208      * This function return true if Wait menu item can be shown
1209      * otherwise returns false. Assumes the passed string is non-empty
1210      * and the 0th index check is not required.
1211      */
showWait(int start, int end, String digits)1212     private boolean showWait(int start, int end, String digits) {
1213         if (start == end) {
1214             // visible false in this case
1215             if (start > digits.length()) return false;
1216 
1217             // preceding char is ';', so visible should be false
1218             if (digits.charAt(start-1) == ';') return false;
1219 
1220             // next char is ';', so visible should be false
1221             if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
1222         } else {
1223             // visible false in this case
1224             if (start > digits.length() || end > digits.length()) return false;
1225 
1226             // In this case we need to just check for ';' preceding to start
1227             // or next to end
1228             if (digits.charAt(start-1) == ';') return false;
1229         }
1230         return true;
1231     }
1232 
1233     /**
1234      * @return true if the widget with the phone number digits is empty.
1235      */
isDigitsEmpty()1236     private boolean isDigitsEmpty() {
1237         return mDigits.length() == 0;
1238     }
1239 
1240     /**
1241      * Starts the asyn query to get the last dialed/outgoing
1242      * number. When the background query finishes, mLastNumberDialed
1243      * is set to the last dialed number or an empty string if none
1244      * exists yet.
1245      */
queryLastOutgoingCall()1246     private void queryLastOutgoingCall() {
1247         mLastNumberDialed = EMPTY_NUMBER;
1248         CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
1249                 new CallLogAsync.GetLastOutgoingCallArgs(
1250                     this,
1251                     new CallLogAsync.OnLastOutgoingCallComplete() {
1252                         public void lastOutgoingCall(String number) {
1253                             // TODO: Filter out emergency numbers if
1254                             // the carrier does not want redial for
1255                             // these.
1256                             mLastNumberDialed = number;
1257                             updateDialAndDeleteButtonEnabledState();
1258                         }
1259                     });
1260         mCallLog.getLastOutgoingCall(lastCallArgs);
1261     }
1262 
1263     @Override
startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch)1264     public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
1265             boolean globalSearch) {
1266         if (globalSearch) {
1267             super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1268         } else {
1269             ContactsSearchManager.startSearch(this, initialQuery);
1270         }
1271     }
1272 }
1273