2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.contacts;
19 import android.app.Activity;
20 import android.content.ActivityNotFoundException;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.res.Resources;
24 import android.database.Cursor;
25 import android.graphics.Bitmap;
26 import android.graphics.BitmapFactory;
27 import android.graphics.drawable.Drawable;
28 import android.media.AudioManager;
29 import android.media.ToneGenerator;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.Message;
34 import android.os.RemoteException;
35 import android.os.ServiceManager;
36 import android.os.SystemClock;
37 import android.provider.Contacts.Intents.Insert;
38 import android.provider.Contacts.People;
39 import android.provider.Contacts.Phones;
40 import android.provider.Contacts.PhonesColumns;
41 import android.provider.Settings;
42 import android.telephony.PhoneNumberFormattingTextWatcher;
43 import android.telephony.PhoneNumberUtils;
44 import android.telephony.PhoneStateListener;
45 import android.telephony.TelephonyManager;
46 import android.text.Editable;
47 import android.text.TextUtils;
48 import android.text.TextWatcher;
49 import android.text.method.DialerKeyListener;
50 import android.util.Log;
51 import android.view.KeyEvent;
52 import android.view.LayoutInflater;
53 import android.view.Menu;
54 import android.view.MenuItem;
55 import android.view.View;
56 import android.view.ViewConfiguration;
57 import android.view.ViewGroup;
58 import android.widget.AdapterView;
59 import android.widget.BaseAdapter;
60 import android.widget.EditText;
61 import android.widget.ImageView;
62 import android.widget.ListView;
63 import android.widget.TextView;
65 import com.android.internal.telephony.ITelephony;
68 * Dialer activity that displays the typical twelve key interface.
70 public class TwelveKeyDialer extends Activity implements View.OnClickListener,
71 View.OnLongClickListener, View.OnKeyListener,
72 AdapterView.OnItemClickListener, TextWatcher {
74 private static final String TAG = "TwelveKeyDialer";
76 private static final int STOP_TONE = 1;
78 /** The length of DTMF tones in milliseconds */
79 private static final int TONE_LENGTH_MS = 150;
81 /** The DTMF tone volume relative to other sounds in the stream */
82 private static final int TONE_RELATIVE_VOLUME = 50;
84 private EditText mDigits;
86 private MenuItem mAddToContactMenuItem;
87 private ToneGenerator mToneGenerator;
88 private Object mToneGeneratorLock = new Object();
89 private Drawable mDigitsBackground;
90 private Drawable mDigitsEmptyBackground;
91 private Drawable mDeleteBackground;
92 private Drawable mDeleteEmptyBackground;
93 private View mDigitsAndBackspace;
94 private View mDialpad;
95 private ListView mDialpadChooser;
96 private DialpadChooserAdapter mDialpadChooserAdapter;
98 // determines if we want to playback local DTMF tones.
99 private boolean mDTMFToneEnabled;
101 /** Identifier for the "Add Call" intent extra. */
102 static final String ADD_CALL_MODE_KEY = "add_call_mode";
103 /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
104 private boolean mIsAddCallMode;
106 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
108 * Listen for phone state changes so that we can take down the
109 * "dialpad chooser" if the phone becomes idle while the
110 * chooser UI is visible.
113 public void onCallStateChanged(int state, String incomingNumber) {
114 // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
115 // + state + ", '" + incomingNumber + "'");
116 if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
117 // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
118 // Note there's a race condition in the UI here: the
119 // dialpad chooser could conceivably disappear (on its
120 // own) at the exact moment the user was trying to select
121 // one of the choices, which would be confusing. (But at
122 // least that's better than leaving the dialpad chooser
123 // onscreen, but useless...)
124 showDialpadChooser(false);
129 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
133 public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
135 // DTMF Tones do not need to be played here any longer -
136 // the DTMF dialer handles that functionality now.
139 public void afterTextChanged(Editable input) {
140 if (SpecialCharSequenceMgr.handleChars(this, input.toString(), mDigits)) {
141 // A special sequence was entered, clear the digits
142 mDigits.getText().clear();
145 // Set the proper background for the dial input area
146 if (mDigits.length() != 0) {
147 mDelete.setBackgroundDrawable(mDeleteBackground);
148 mDigits.setBackgroundDrawable(mDigitsBackground);
149 mDigits.setCompoundDrawablesWithIntrinsicBounds(
150 getResources().getDrawable(R.drawable.ic_dial_number), null, null, null);
152 mDelete.setBackgroundDrawable(mDeleteEmptyBackground);
153 mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
154 mDigits.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
159 protected void onCreate(Bundle icicle) {
160 super.onCreate(icicle);
162 // Set the content view
163 setContentView(getContentViewResource());
165 // Load up the resources for the text field and delete button
166 Resources r = getResources();
167 mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
168 //mDigitsBackground.setDither(true);
169 mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
170 //mDigitsEmptyBackground.setDither(true);
171 mDeleteBackground = r.getDrawable(R.drawable.btn_dial_delete_active);
172 //mDeleteBackground.setDither(true);
173 mDeleteEmptyBackground = r.getDrawable(R.drawable.btn_dial_delete);
174 //mDeleteEmptyBackground.setDither(true);
176 mDigits = (EditText) findViewById(R.id.digits);
177 mDigits.setKeyListener(DialerKeyListener.getInstance());
178 mDigits.setOnClickListener(this);
179 mDigits.setOnKeyListener(this);
180 maybeAddNumberFormatting();
182 // Check for the presence of the keypad
183 View view = findViewById(R.id.one);
188 view = findViewById(R.id.backspace);
189 view.setOnClickListener(this);
190 view.setOnLongClickListener(this);
193 mDigitsAndBackspace = (View) findViewById(R.id.digitsAndBackspace);
194 mDialpad = (View) findViewById(R.id.dialpad); // This is null in landscape mode
196 // Set up the "dialpad chooser" UI; see showDialpadChooser().
197 mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
198 mDialpadChooser.setOnItemClickListener(this);
200 if (!resolveIntent() && icicle != null) {
201 super.onRestoreInstanceState(icicle);
204 // If the mToneGenerator creation fails, just continue without it. It is
205 // a local audio signal, and is not as important as the dtmf tone itself.
206 synchronized (mToneGeneratorLock) {
207 if (mToneGenerator == null) {
209 mToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,
210 TONE_RELATIVE_VOLUME);
211 } catch (RuntimeException e) {
212 Log.w(TAG, "Exception caught while creating local tone generator: " + e);
213 mToneGenerator = null;
220 protected void onDestroy() {
222 synchronized(mToneGeneratorLock) {
223 if (mToneGenerator != null) {
224 mToneStopper.removeMessages(STOP_TONE);
225 mToneGenerator.release();
226 mToneGenerator = null;
232 protected void onRestoreInstanceState(Bundle icicle) {
233 // Do nothing, state is restored in onCreate() if needed
236 protected void maybeAddNumberFormatting() {
237 mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
241 * Overridden by subclasses to control the resource used by the content view.
243 protected int getContentViewResource() {
244 return R.layout.twelve_key_dialer;
247 private boolean resolveIntent() {
248 boolean ignoreState = false;
250 // Find the proper intent
253 intent = getParent().getIntent();
254 ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
256 intent = getIntent();
258 // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
260 // by default we are not adding a call.
261 mIsAddCallMode = false;
263 // By default we don't show the "dialpad chooser" UI.
264 boolean needToShowDialpadChooser = false;
266 // Resolve the intent
267 final String action = intent.getAction();
268 if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
269 // see if we are "adding a call" from the InCallScreen; false by default.
270 mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
271 Uri uri = intent.getData();
273 if ("tel".equals(uri.getScheme())) {
274 // Put the requested number into the input area
275 String data = uri.getSchemeSpecificPart();
276 setFormattedDigits(data);
278 String type = intent.getType();
279 if (People.CONTENT_ITEM_TYPE.equals(type)
280 || Phones.CONTENT_ITEM_TYPE.equals(type)) {
281 // Query the phone number
282 Cursor c = getContentResolver().query(intent.getData(),
283 new String[] {PhonesColumns.NUMBER}, null, null, null);
285 if (c.moveToFirst()) {
286 // Put the number into the input area
287 setFormattedDigits(c.getString(0));
294 } else if (Intent.ACTION_MAIN.equals(action)) {
295 // The MAIN action means we're bringing up a blank dialer
296 // (e.g. by selecting the Home shortcut, or tabbing over from
297 // Contacts or Call log.)
299 // At this point, IF there's already an active call, there's a
300 // good chance that the user got here accidentally (but really
301 // wanted the in-call dialpad instead). So we bring up an
302 // intermediate UI to make the user confirm what they really
304 if (phoneIsInUse()) {
305 // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
306 needToShowDialpadChooser = true;
310 // Bring up the "dialpad chooser" IFF we need to make the user
311 // confirm which dialpad they really want.
312 showDialpadChooser(needToShowDialpadChooser);
317 protected void setFormattedDigits(String data) {
318 // strip the non-dialable numbers out of the data string.
319 String dialString = PhoneNumberUtils.extractNetworkPortion(data);
320 dialString = PhoneNumberUtils.formatNumber(dialString);
321 if (!TextUtils.isEmpty(dialString)) {
322 Editable digits = mDigits.getText();
323 digits.replace(0, digits.length(), dialString);
324 mDigits.setCompoundDrawablesWithIntrinsicBounds(
325 getResources().getDrawable(R.drawable.ic_dial_number), null, null, null);
330 protected void onNewIntent(Intent newIntent) {
331 setIntent(newIntent);
336 protected void onPostCreate(Bundle savedInstanceState) {
337 super.onPostCreate(savedInstanceState);
339 // This can't be done in onCreate(), since the auto-restoring of the digits
340 // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
341 // is called. This method will be called every time the activity is created, and
342 // will always happen after onRestoreSavedInstanceState().
343 mDigits.addTextChangedListener(this);
346 private void setupKeypad() {
347 // Setup the listeners for the buttons
348 View view = findViewById(R.id.one);
349 view.setOnClickListener(this);
350 view.setOnLongClickListener(this);
352 findViewById(R.id.two).setOnClickListener(this);
353 findViewById(R.id.three).setOnClickListener(this);
354 findViewById(R.id.four).setOnClickListener(this);
355 findViewById(R.id.five).setOnClickListener(this);
356 findViewById(R.id.six).setOnClickListener(this);
357 findViewById(R.id.seven).setOnClickListener(this);
358 findViewById(R.id.eight).setOnClickListener(this);
359 findViewById(R.id.nine).setOnClickListener(this);
360 findViewById(R.id.star).setOnClickListener(this);
362 view = findViewById(R.id.zero);
363 view.setOnClickListener(this);
364 view.setOnLongClickListener(this);
366 findViewById(R.id.pound).setOnClickListener(this);
370 protected void onResume() {
373 // retrieve the DTMF tone play back setting.
374 mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
375 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
377 // if the mToneGenerator creation fails, just continue without it. It is
378 // a local audio signal, and is not as important as the dtmf tone itself.
379 synchronized(mToneGeneratorLock) {
380 if (mToneGenerator == null) {
382 mToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,
383 TONE_RELATIVE_VOLUME);
384 } catch (RuntimeException e) {
385 Log.w(TAG, "Exception caught while creating local tone generator: " + e);
386 mToneGenerator = null;
391 Activity parent = getParent();
392 // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
393 // digits in the dialer field.
394 if (parent != null && parent instanceof DialtactsActivity) {
395 Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
396 if (dialUri != null) {
401 // While we're in the foreground, listen for phone state changes,
402 // purely so that we can take down the "dialpad chooser" if the
403 // phone becomes idle while the chooser UI is visible.
404 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
405 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
407 // Potentially show hint text in the mDigits field when the user
408 // hasn't typed any digits yet. (If there's already an active call,
409 // this hint text will remind the user that he's about to add a new
412 // TODO: consider adding better UI for the case where *both* lines
413 // are currently in use. (Right now we let the user try to add
414 // another call, but that call is guaranteed to fail. Perhaps the
415 // entire dialer UI should be disabled instead.)
416 if (phoneIsInUse()) {
417 mDigits.setHint(R.string.dialerDialpadHintText);
419 // Common case; no hint necessary.
420 mDigits.setHint(null);
422 // Also, a sanity-check: the "dialpad chooser" UI should NEVER
423 // be visible if the phone is idle!
424 showDialpadChooser(false);
429 protected void onPause() {
432 // Stop listening for phone state changes.
433 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
434 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
436 synchronized(mToneGeneratorLock) {
437 if (mToneGenerator != null) {
438 mToneStopper.removeMessages(STOP_TONE);
439 mToneGenerator.release();
440 mToneGenerator = null;
446 public boolean onCreateOptionsMenu(Menu menu) {
447 mAddToContactMenuItem = menu.add(0, 0, 0, R.string.recentCalls_addToContact)
448 .setIcon(android.R.drawable.ic_menu_add);
454 public boolean onPrepareOptionsMenu(Menu menu) {
455 // We never show a menu if the "choose dialpad" UI is up.
456 if (dialpadChooserVisible()) {
460 CharSequence digits = mDigits.getText();
461 if (digits == null || !TextUtils.isGraphic(digits)) {
462 mAddToContactMenuItem.setVisible(false);
464 // Put the current digits string into an intent
465 Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
466 intent.putExtra(Insert.PHONE, mDigits.getText());
467 intent.setType(People.CONTENT_ITEM_TYPE);
468 mAddToContactMenuItem.setIntent(intent);
469 mAddToContactMenuItem.setVisible(true);
475 public boolean onKeyDown(int keyCode, KeyEvent event) {
477 case KeyEvent.KEYCODE_CALL: {
478 long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
479 if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
480 // Launch voice dialer
481 Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
482 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
484 startActivity(intent);
485 } catch (ActivityNotFoundException e) {
490 case KeyEvent.KEYCODE_1: {
491 long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
492 if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
493 // Long press detected, call voice mail
499 return super.onKeyDown(keyCode, event);
503 public boolean onKeyUp(int keyCode, KeyEvent event) {
505 case KeyEvent.KEYCODE_CALL: {
506 if (mIsAddCallMode && (TextUtils.isEmpty(mDigits.getText().toString()))) {
507 // if we are adding a call from the InCallScreen and the phone
508 // number entered is empty, we just close the dialer to expose
509 // the InCallScreen under it.
512 // otherwise, we place the call.
518 return super.onKeyUp(keyCode, event);
521 private void keyPressed(int keyCode) {
522 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
523 mDigits.onKeyDown(keyCode, event);
526 public boolean onKey(View view, int keyCode, KeyEvent event) {
527 switch (view.getId()) {
529 if (keyCode == KeyEvent.KEYCODE_ENTER) {
538 public void onClick(View view) {
539 switch (view.getId()) {
541 playTone(ToneGenerator.TONE_DTMF_1);
542 keyPressed(KeyEvent.KEYCODE_1);
546 playTone(ToneGenerator.TONE_DTMF_2);
547 keyPressed(KeyEvent.KEYCODE_2);
551 playTone(ToneGenerator.TONE_DTMF_3);
552 keyPressed(KeyEvent.KEYCODE_3);
556 playTone(ToneGenerator.TONE_DTMF_4);
557 keyPressed(KeyEvent.KEYCODE_4);
561 playTone(ToneGenerator.TONE_DTMF_5);
562 keyPressed(KeyEvent.KEYCODE_5);
566 playTone(ToneGenerator.TONE_DTMF_6);
567 keyPressed(KeyEvent.KEYCODE_6);
571 playTone(ToneGenerator.TONE_DTMF_7);
572 keyPressed(KeyEvent.KEYCODE_7);
576 playTone(ToneGenerator.TONE_DTMF_8);
577 keyPressed(KeyEvent.KEYCODE_8);
581 playTone(ToneGenerator.TONE_DTMF_9);
582 keyPressed(KeyEvent.KEYCODE_9);
586 playTone(ToneGenerator.TONE_DTMF_0);
587 keyPressed(KeyEvent.KEYCODE_0);
591 playTone(ToneGenerator.TONE_DTMF_P);
592 keyPressed(KeyEvent.KEYCODE_POUND);
596 playTone(ToneGenerator.TONE_DTMF_S);
597 keyPressed(KeyEvent.KEYCODE_STAR);
600 case R.id.backspace: {
601 keyPressed(KeyEvent.KEYCODE_DEL);
611 public boolean onLongClick(View view) {
612 final Editable digits = mDigits.getText();
613 int id = view.getId();
615 case R.id.backspace: {
620 if (digits.length() == 0) {
627 keyPressed(KeyEvent.KEYCODE_PLUS);
634 void callVoicemail() {
635 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
636 Uri.fromParts("voicemail", "", null));
637 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
638 startActivity(intent);
639 mDigits.getText().clear();
644 final String number = mDigits.getText().toString();
645 if (number == null || !TextUtils.isGraphic(number)) {
646 // There is no number entered.
647 playTone(ToneGenerator.TONE_PROP_NACK);
650 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
651 Uri.fromParts("tel", number, null));
652 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
653 startActivity(intent);
654 mDigits.getText().clear();
658 Handler mToneStopper = new Handler() {
660 public void handleMessage(Message msg) {
663 synchronized(mToneGeneratorLock) {
664 if (mToneGenerator == null) {
665 Log.w(TAG, "mToneStopper: mToneGenerator == null");
667 mToneGenerator.stopTone();
676 * Play a tone for TONE_LENGTH_MS milliseconds.
678 * @param tone a tone code from {@link ToneGenerator}
680 void playTone(int tone) {
681 // if local tone playback is disabled, just return.
682 if (!mDTMFToneEnabled) {
686 synchronized(mToneGeneratorLock) {
687 if (mToneGenerator == null) {
688 Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
692 // Remove pending STOP_TONE messages
693 mToneStopper.removeMessages(STOP_TONE);
695 // Start the new tone (will stop any playing tone)
696 mToneGenerator.startTone(tone);
697 mToneStopper.sendEmptyMessageDelayed(STOP_TONE, TONE_LENGTH_MS);
702 * Brings up the "dialpad chooser" UI in place of the usual Dialer
703 * elements (the textfield/button and the dialpad underneath).
705 * We show this UI if the user brings up the Dialer while a call is
706 * already in progress, since there's a good chance we got here
707 * accidentally (and the user really wanted the in-call dialpad instead).
708 * So in this situation we display an intermediate UI that lets the user
709 * explicitly choose between the in-call dialpad ("Use touch tone
710 * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
711 * to call in progress" just goes back to the in-call UI with no dialpad
714 * @param enabled If true, show the "dialpad chooser" instead
715 * of the regular Dialer UI
717 private void showDialpadChooser(boolean enabled) {
719 // Log.i(TAG, "Showing dialpad chooser!");
720 mDigitsAndBackspace.setVisibility(View.GONE);
721 if (mDialpad != null) mDialpad.setVisibility(View.GONE);
722 mDialpadChooser.setVisibility(View.VISIBLE);
724 // Instantiate the DialpadChooserAdapter and hook it up to the
725 // ListView. We do this only once.
726 if (mDialpadChooserAdapter == null) {
727 mDialpadChooserAdapter = new DialpadChooserAdapter(this);
728 mDialpadChooser.setAdapter(mDialpadChooserAdapter);
731 // Log.i(TAG, "Displaying normal Dialer UI.");
732 mDigitsAndBackspace.setVisibility(View.VISIBLE);
733 if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
734 mDialpadChooser.setVisibility(View.GONE);
739 * @return true if we're currently showing the "dialpad chooser" UI.
741 private boolean dialpadChooserVisible() {
742 return mDialpadChooser.getVisibility() == View.VISIBLE;
746 * Simple list adapter, binding to an icon + text label
747 * for each item in the "dialpad chooser" list.
749 private static class DialpadChooserAdapter extends BaseAdapter {
750 private LayoutInflater mInflater;
752 // Simple struct for a single "choice" item.
753 static class ChoiceItem {
758 public ChoiceItem(String s, Bitmap b, int i) {
765 // IDs for the possible "choices":
766 static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
767 static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
768 static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
770 private static final int NUM_ITEMS = 3;
771 private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
773 public DialpadChooserAdapter(Context context) {
774 // Cache the LayoutInflate to avoid asking for a new one each time.
775 mInflater = LayoutInflater.from(context);
777 // Initialize the possible choices.
778 // TODO: could this be specified entirely in XML?
780 // - "Use touch tone keypad"
781 mChoiceItems[0] = new ChoiceItem(
782 context.getString(R.string.dialer_useDtmfDialpad),
783 BitmapFactory.decodeResource(context.getResources(),
784 R.drawable.ic_dialer_fork_tt_keypad),
785 DIALPAD_CHOICE_USE_DTMF_DIALPAD);
787 // - "Return to call in progress"
788 mChoiceItems[1] = new ChoiceItem(
789 context.getString(R.string.dialer_returnToInCallScreen),
790 BitmapFactory.decodeResource(context.getResources(),
791 R.drawable.ic_dialer_fork_current_call),
792 DIALPAD_CHOICE_RETURN_TO_CALL);
795 mChoiceItems[2] = new ChoiceItem(
796 context.getString(R.string.dialer_addAnotherCall),
797 BitmapFactory.decodeResource(context.getResources(),
798 R.drawable.ic_dialer_fork_add_call),
799 DIALPAD_CHOICE_ADD_NEW_CALL);
802 public int getCount() {
807 * Return the ChoiceItem for a given position.
809 public Object getItem(int position) {
810 return mChoiceItems[position];
814 * Return a unique ID for each possible choice.
816 public long getItemId(int position) {
821 * Make a view for each row.
823 public View getView(int position, View convertView, ViewGroup parent) {
824 // When convertView is non-null, we can reuse it (there's no need
826 if (convertView == null) {
827 convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
830 TextView text = (TextView) convertView.findViewById(R.id.text);
831 text.setText(mChoiceItems[position].text);
833 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
834 icon.setImageBitmap(mChoiceItems[position].icon);
841 * Handle clicks from the dialpad chooser.
843 public void onItemClick(AdapterView parent, View v, int position, long id) {
844 DialpadChooserAdapter.ChoiceItem item =
845 (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
846 int itemId = item.id;
848 case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
849 // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
850 // Fire off an intent to go back to the in-call UI
851 // with the dialpad visible.
852 returnToInCallScreen(true);
855 case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
856 // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
857 // Fire off an intent to go back to the in-call UI
858 // (with the dialpad hidden).
859 returnToInCallScreen(false);
862 case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
863 // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
864 // Ok, guess the user really did want to be here (in the
865 // regular Dialer) after all. Bring back the normal Dialer UI.
866 showDialpadChooser(false);
870 Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
876 * Returns to the in-call UI (where there's presumably a call in
877 * progress) in response to the user selecting "use touch tone keypad"
878 * or "return to call" from the dialpad chooser.
880 private void returnToInCallScreen(boolean showDialpad) {
882 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
883 if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
884 } catch (RemoteException e) {
885 Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
888 // Finally, finish() ourselves so that we don't stay on the
890 // Note that we do this whether or not the showCallScreenWithDialpad()
891 // call above had any effect or not! (That call is a no-op if the
892 // phone is idle, which can happen if the current call ends while
893 // the dialpad chooser is up. In this case we can't show the
894 // InCallScreen, and there's no point staying here in the Dialer,
895 // so we just take the user back where he came from...)
900 * @return true if the phone is "in use", meaning that at least one line
901 * is active (ie. off hook or ringing or dialing).
903 private boolean phoneIsInUse() {
904 boolean phoneInUse = false;
906 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
907 if (phone != null) phoneInUse = !phone.isIdle();
908 } catch (RemoteException e) {
909 Log.w(TAG, "phone.isIdle() failed", e);