blob: 53c59f330a6a7c8556b9478ed8e904ee86754b3b [file] [log] [blame]
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001/*
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
17package com.android.contacts;
18
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -080019import com.android.internal.telephony.ITelephony;
20import com.android.phone.CallLogAsync;
21import com.android.phone.HapticFeedback;
22
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080023import android.app.Activity;
24import android.content.ActivityNotFoundException;
25import android.content.Context;
26import android.content.Intent;
Bernd Holzheyd0bfafc2010-03-02 09:00:02 +010027import android.content.res.Configuration;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080028import android.content.res.Resources;
29import android.database.Cursor;
30import android.graphics.Bitmap;
31import android.graphics.BitmapFactory;
32import android.graphics.drawable.Drawable;
33import android.media.AudioManager;
34import android.media.ToneGenerator;
35import android.net.Uri;
36import android.os.Bundle;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080037import android.os.RemoteException;
38import android.os.ServiceManager;
39import android.os.SystemClock;
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -080040import android.provider.Settings;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080041import android.provider.Contacts.People;
42import android.provider.Contacts.Phones;
43import android.provider.Contacts.PhonesColumns;
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -080044import android.provider.Contacts.Intents.Insert;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080045import android.telephony.PhoneNumberFormattingTextWatcher;
46import android.telephony.PhoneNumberUtils;
47import android.telephony.PhoneStateListener;
48import android.telephony.TelephonyManager;
49import android.text.Editable;
50import android.text.TextUtils;
51import android.text.TextWatcher;
52import android.text.method.DialerKeyListener;
53import android.util.Log;
54import android.view.KeyEvent;
55import android.view.LayoutInflater;
56import android.view.Menu;
57import android.view.MenuItem;
58import android.view.View;
59import android.view.ViewConfiguration;
60import android.view.ViewGroup;
Bernd Holzheyd0bfafc2010-03-02 09:00:02 +010061import android.view.Window;
Karl Rosaenf46bc312009-03-24 18:20:48 -070062import android.view.inputmethod.InputMethodManager;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080063import android.widget.AdapterView;
64import android.widget.BaseAdapter;
65import android.widget.EditText;
66import android.widget.ImageView;
67import android.widget.ListView;
68import android.widget.TextView;
69
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080070/**
71 * Dialer activity that displays the typical twelve key interface.
72 */
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -080073@SuppressWarnings("deprecation")
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080074public class TwelveKeyDialer extends Activity implements View.OnClickListener,
75 View.OnLongClickListener, View.OnKeyListener,
76 AdapterView.OnItemClickListener, TextWatcher {
Nicolas Cataniac3be69e2010-01-14 14:03:53 -080077 private static final String EMPTY_NUMBER = "";
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080078 private static final String TAG = "TwelveKeyDialer";
Eric Laurentd9efc872009-07-17 11:52:06 -070079
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080080 /** The length of DTMF tones in milliseconds */
81 private static final int TONE_LENGTH_MS = 150;
Eric Laurentd9efc872009-07-17 11:52:06 -070082
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080083 /** The DTMF tone volume relative to other sounds in the stream */
Jean-Michel Trividd44f8c2009-11-10 13:00:45 -080084 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;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080088
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;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080096 private View mDialpad;
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -070097 private View mVoicemailDialAndDeleteRow;
Nicolas Catania80bda0f2009-09-19 09:17:14 -070098 private View mVoicemailButton;
99 private View mDialButton;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800100 private ListView mDialpadChooser;
101 private DialpadChooserAdapter mDialpadChooserAdapter;
Reli Talc2a2a512009-06-10 16:48:00 -0400102 //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;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800108
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800109 // 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
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800115 // determines if we want to playback local DTMF tones.
116 private boolean mDTMFToneEnabled;
David Brownc29c7ab2009-07-07 16:00:18 -0700117
118 // Vibration (haptic feedback) for dialer key presses.
Nicolas Catania905e7622009-12-01 08:51:20 -0800119 private HapticFeedback mHaptic = new HapticFeedback();
Eric Laurentd9efc872009-07-17 11:52:06 -0700120
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800121 /** Identifier for the "Add Call" intent extra. */
122 static final String ADD_CALL_MODE_KEY = "add_call_mode";
Paul Bermandbdcde22009-10-09 12:04:10 -0400123
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
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800137 /** 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
163 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
164 // Do nothing
165 }
166
167 public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
168 // Do nothing
Eric Laurentd9efc872009-07-17 11:52:06 -0700169 // DTMF Tones do not need to be played here any longer -
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800170 // the DTMF dialer handles that functionality now.
171 }
172
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
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800179 if (!isDigitsEmpty()) {
Nicolas Catania75993762009-09-21 16:42:00 -0700180 mDigits.setBackgroundDrawable(mDigitsBackground);
181 } else {
Nicolas Catania3040fa32009-10-01 13:00:53 -0700182 mDigits.setCursorVisible(false);
Nicolas Catania75993762009-09-21 16:42:00 -0700183 mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
184 }
185
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800186 updateDialAndDeleteButtonEnabledState();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800187 }
188
189 @Override
190 protected void onCreate(Bundle icicle) {
191 super.onCreate(icicle);
192
Bernd Holzheyd0bfafc2010-03-02 09:00:02 +0100193 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 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800199 // Set the content view
200 setContentView(getContentViewResource());
201
Nicolas Catania75993762009-09-21 16:42:00 -0700202 // Load up the resources for the text field.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800203 mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800204 mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800205
206 mDigits = (EditText) findViewById(R.id.digits);
207 mDigits.setKeyListener(DialerKeyListener.getInstance());
208 mDigits.setOnClickListener(this);
209 mDigits.setOnKeyListener(this);
Nicolas Catania3040fa32009-10-01 13:00:53 -0700210
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800211 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
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700219 mVoicemailDialAndDeleteRow = findViewById(R.id.voicemailAndDialAndDelete);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700220
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700221 initVoicemailButton();
222
David Brown3d07e6d2009-08-04 20:30:09 -0700223 // Check whether we should show the onscreen "Dial" button.
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700224 mDialButton = mVoicemailDialAndDeleteRow.findViewById(R.id.dialButton);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700225
David Brown3d07e6d2009-08-04 20:30:09 -0700226 if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
David Brown3d07e6d2009-08-04 20:30:09 -0700227 mDialButton.setOnClickListener(this);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700228 } else {
229 mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
230 mDialButton = null;
David Brown3d07e6d2009-08-04 20:30:09 -0700231 }
232
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700233 view = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800234 view.setOnClickListener(this);
235 view.setOnLongClickListener(this);
236 mDelete = view;
237
Nicolas Catania901f8562009-10-09 11:09:45 -0700238 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 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800248
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
Nicolas Catania905e7622009-12-01 08:51:20 -0800257 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
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800263 }
264
265 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800266 protected void onRestoreInstanceState(Bundle icicle) {
267 // Do nothing, state is restored in onCreate() if needed
268 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700269
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800270 protected void maybeAddNumberFormatting() {
271 mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
272 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700273
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800274 /**
Eric Laurentd9efc872009-07-17 11:52:06 -0700275 * Overridden by subclasses to control the resource used by the content view.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800276 */
277 protected int getContentViewResource() {
278 return R.layout.twelve_key_dialer;
279 }
280
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);
David Brown4dd082b2010-08-11 15:46:59 -0700305
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800306 Uri uri = intent.getData();
307 if (uri != null) {
308 if ("tel".equals(uri.getScheme())) {
309 // Put the requested number into the input area
Virgil Kingd8831122010-03-10 13:44:11 -0800310 String data = uri.getSchemeSpecificPart();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800311 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 }
Bernd Holzhey7ca5e4d2010-08-04 17:26:03 +0200328 } else {
David Brown4dd082b2010-08-11 15:46:59 -0700329 // 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 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800338 }
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
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);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700369 // 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);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800372 }
373 }
374
375 @Override
376 protected void onNewIntent(Intent newIntent) {
377 setIntent(newIntent);
378 resolveIntent();
379 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700380
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800381 @Override
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 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700391
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800392 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
416 protected void onResume() {
417 super.onResume();
David Browndc1dfe22010-03-01 14:34:57 -0800418
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800419 // Query the last dialed number. Do it first because hitting
420 // the DB is 'slow'. This call is asynchronous.
421 queryLastOutgoingCall();
David Brownc29c7ab2009-07-07 16:00:18 -0700422
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800423 // retrieve the DTMF tone play back setting.
424 mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
425 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
426
Nicolas Catania905e7622009-12-01 08:51:20 -0800427 // Retrieve the haptic feedback setting.
428 mHaptic.checkSystemSetting();
429
Eric Laurentd9efc872009-07-17 11:52:06 -0700430 // if the mToneGenerator creation fails, just continue without it. It is
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800431 // a local audio signal, and is not as important as the dtmf tone itself.
432 synchronized(mToneGeneratorLock) {
433 if (mToneGenerator == null) {
434 try {
Jean-Michel Trividd44f8c2009-11-10 13:00:45 -0800435 // 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);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800440 } catch (RuntimeException e) {
441 Log.w(TAG, "Exception caught while creating local tone generator: " + e);
442 mToneGenerator = null;
443 }
444 }
445 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700446
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800447 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 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700482
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800483 updateDialAndDeleteButtonEnabledState();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800484 }
485
486 @Override
Karl Rosaenf46bc312009-03-24 18:20:48 -0700487 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);
Eric Laurentd9efc872009-07-17 11:52:06 -0700495 inputMethodManager.hideSoftInputFromWindow(mDigits.getWindowToken(), 0);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700496 }
497 }
498
499 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800500 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) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800509 mToneGenerator.release();
510 mToneGenerator = null;
511 }
512 }
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800513 // 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.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800516 }
517
518 @Override
519 public boolean onCreateOptionsMenu(Menu menu) {
Reli Talc2a2a512009-06-10 16:48:00 -0400520 mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800521 .setIcon(android.R.drawable.ic_menu_add);
Reli Talc2a2a512009-06-10 16:48:00 -0400522 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);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800526 return true;
527 }
528
529 @Override
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
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800536 if (isDigitsEmpty()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800537 mAddToContactMenuItem.setVisible(false);
Reli Talc2a2a512009-06-10 16:48:00 -0400538 m2SecPauseMenuItem.setVisible(false);
539 mWaitMenuItem.setVisible(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800540 } else {
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800541 CharSequence digits = mDigits.getText();
542
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800543 // Put the current digits string into an intent
544 Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800545 intent.putExtra(Insert.PHONE, digits);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800546 intent.setType(People.CONTENT_ITEM_TYPE);
547 mAddToContactMenuItem.setIntent(intent);
548 mAddToContactMenuItem.setVisible(true);
Reli Talc2a2a512009-06-10 16:48:00 -0400549
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 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800584 }
585 return true;
586 }
587
588 @Override
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: {
Eric Laurentd9efc872009-07-17 11:52:06 -0700605 long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800606 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
617 public boolean onKeyUp(int keyCode, KeyEvent event) {
618 switch (keyCode) {
619 case KeyEvent.KEYCODE_CALL: {
Nicolas Catania998763d2010-01-14 14:03:53 -0800620 // 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.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800631 finish();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800632 }
Nicolas Catania998763d2010-01-14 14:03:53 -0800633
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();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800640 return true;
641 }
642 }
643 return super.onKeyUp(keyCode, event);
644 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700645
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800646 private void keyPressed(int keyCode) {
Nicolas Catania905e7622009-12-01 08:51:20 -0800647 mHaptic.vibrate();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800648 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
649 mDigits.onKeyDown(keyCode, event);
650 }
651
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) {
Nicolas Catania998763d2010-01-14 14:03:53 -0800656 dialButtonPressed();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800657 return true;
658 }
659 break;
660 }
661 return false;
662 }
663
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 }
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700726 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800727 keyPressed(KeyEvent.KEYCODE_DEL);
728 return;
729 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700730 case R.id.dialButton: {
Nicolas Catania905e7622009-12-01 08:51:20 -0800731 mHaptic.vibrate(); // Vibrate here too, just like we do for the regular keys
Nicolas Catania998763d2010-01-14 14:03:53 -0800732 dialButtonPressed();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800733 return;
734 }
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700735 case R.id.voicemailButton: {
736 callVoicemail();
Nicolas Catania905e7622009-12-01 08:51:20 -0800737 mHaptic.vibrate();
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700738 return;
739 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700740 case R.id.digits: {
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800741 if (!isDigitsEmpty()) {
Nicolas Catania3040fa32009-10-01 13:00:53 -0700742 mDigits.setCursorVisible(true);
743 }
744 return;
745 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800746 }
747 }
748
749 public boolean onLongClick(View view) {
750 final Editable digits = mDigits.getText();
751 int id = view.getId();
752 switch (id) {
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700753 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800754 digits.clear();
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700755 // 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);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800759 return true;
760 }
761 case R.id.one: {
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800762 if (isDigitsEmpty()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800763 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
776 void callVoicemail() {
777 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800778 Uri.fromParts("voicemail", EMPTY_NUMBER, null));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800779 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
780 startActivity(intent);
781 mDigits.getText().clear();
782 finish();
783 }
784
Nicolas Catania998763d2010-01-14 14:03:53 -0800785 void dialButtonPressed() {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800786 final String number = mDigits.getText().toString();
Paul Bermandbdcde22009-10-09 12:04:10 -0400787 boolean sendEmptyFlash = false;
Nicolas Catania998763d2010-01-14 14:03:53 -0800788 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED);
David Browndc1dfe22010-03-01 14:34:57 -0800789
Nicolas Catania998763d2010-01-14 14:03:53 -0800790 if (isDigitsEmpty()) { // There is no number entered.
Paul Bermandbdcde22009-10-09 12:04:10 -0400791 if (phoneIsCdma() && phoneIsOffhook()) {
David Browndc1dfe22010-03-01 14:34:57 -0800792 // On CDMA phones, if we're already on a call, pressing
793 // the Dial button without entering any digits means "send
794 // an empty flash."
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800795 intent.setData(Uri.fromParts("tel", EMPTY_NUMBER, null));
Paul Bermandbdcde22009-10-09 12:04:10 -0400796 intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
797 sendEmptyFlash = true;
David Browndc1dfe22010-03-01 14:34:57 -0800798 } else if (!TextUtils.isEmpty(mLastNumberDialed)) {
799 // Otherwise, pressing the Dial button without entering
800 // any digits means "recall the last number dialed".
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800801 mDigits.setText(mLastNumberDialed);
Nicolas Catania998763d2010-01-14 14:03:53 -0800802 return;
Paul Bermandbdcde22009-10-09 12:04:10 -0400803 } else {
David Browndc1dfe22010-03-01 14:34:57 -0800804 // Rare case: there's no "last number dialed". There's
805 // nothing useful for the Dial button to do in this case.
Paul Bermandbdcde22009-10-09 12:04:10 -0400806 playTone(ToneGenerator.TONE_PROP_NACK);
807 return;
808 }
Nicolas Catania998763d2010-01-14 14:03:53 -0800809 } else { // There is a number.
810 intent.setData(Uri.fromParts("tel", number, null));
Paul Bermandbdcde22009-10-09 12:04:10 -0400811 }
Nicolas Catania998763d2010-01-14 14:03:53 -0800812
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800813 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
814 startActivity(intent);
815 mDigits.getText().clear();
David Browndc1dfe22010-03-01 14:34:57 -0800816
Paul Bermandbdcde22009-10-09 12:04:10 -0400817 // Don't finish TwelveKeyDialer yet if we're sending a blank flash for CDMA. CDMA
818 // networks use Flash messages when special processing needs to be done, mainly for
819 // 3-way or call waiting scenarios. Presumably, here we're in a special 3-way scenario
820 // where the network needs a blank flash before being able to add the new participant.
821 // (This is not the case with all 3-way calls, just certain CDMA infrastructures.)
822 if (!sendEmptyFlash) {
823 finish();
824 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800825 }
826
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800827
828 /**
David Brown22f615f2009-06-25 16:19:19 -0700829 * Plays the specified tone for TONE_LENGTH_MS milliseconds.
830 *
831 * The tone is played locally, using the audio stream for phone calls.
832 * Tones are played only if the "Audible touch tones" user preference
833 * is checked, and are NOT played if the device is in silent mode.
834 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800835 * @param tone a tone code from {@link ToneGenerator}
836 */
837 void playTone(int tone) {
838 // if local tone playback is disabled, just return.
839 if (!mDTMFToneEnabled) {
840 return;
841 }
David Brown22f615f2009-06-25 16:19:19 -0700842
843 // Also do nothing if the phone is in silent mode.
844 // We need to re-check the ringer mode for *every* playTone()
845 // call, rather than keeping a local flag that's updated in
846 // onResume(), since it's possible to toggle silent mode without
847 // leaving the current activity (via the ENDCALL-longpress menu.)
848 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
David Brownd5a15302009-07-20 16:39:47 -0700849 int ringerMode = audioManager.getRingerMode();
850 if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
851 || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
David Brown22f615f2009-06-25 16:19:19 -0700852 return;
853 }
854
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800855 synchronized(mToneGeneratorLock) {
856 if (mToneGenerator == null) {
857 Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
858 return;
859 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700860
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800861 // Start the new tone (will stop any playing tone)
Eric Laurent8487fed2009-09-07 08:45:14 -0700862 mToneGenerator.startTone(tone, TONE_LENGTH_MS);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800863 }
864 }
865
866 /**
867 * Brings up the "dialpad chooser" UI in place of the usual Dialer
868 * elements (the textfield/button and the dialpad underneath).
869 *
870 * We show this UI if the user brings up the Dialer while a call is
871 * already in progress, since there's a good chance we got here
872 * accidentally (and the user really wanted the in-call dialpad instead).
873 * So in this situation we display an intermediate UI that lets the user
874 * explicitly choose between the in-call dialpad ("Use touch tone
875 * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
876 * to call in progress" just goes back to the in-call UI with no dialpad
877 * at all.)
878 *
879 * @param enabled If true, show the "dialpad chooser" instead
880 * of the regular Dialer UI
881 */
882 private void showDialpadChooser(boolean enabled) {
883 if (enabled) {
884 // Log.i(TAG, "Showing dialpad chooser!");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700885 mDigits.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800886 if (mDialpad != null) mDialpad.setVisibility(View.GONE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700887 mVoicemailDialAndDeleteRow.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800888 mDialpadChooser.setVisibility(View.VISIBLE);
889
890 // Instantiate the DialpadChooserAdapter and hook it up to the
891 // ListView. We do this only once.
892 if (mDialpadChooserAdapter == null) {
893 mDialpadChooserAdapter = new DialpadChooserAdapter(this);
894 mDialpadChooser.setAdapter(mDialpadChooserAdapter);
895 }
896 } else {
897 // Log.i(TAG, "Displaying normal Dialer UI.");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700898 mDigits.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800899 if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700900 mVoicemailDialAndDeleteRow.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800901 mDialpadChooser.setVisibility(View.GONE);
902 }
903 }
904
905 /**
906 * @return true if we're currently showing the "dialpad chooser" UI.
907 */
908 private boolean dialpadChooserVisible() {
909 return mDialpadChooser.getVisibility() == View.VISIBLE;
910 }
911
912 /**
913 * Simple list adapter, binding to an icon + text label
914 * for each item in the "dialpad chooser" list.
915 */
916 private static class DialpadChooserAdapter extends BaseAdapter {
917 private LayoutInflater mInflater;
918
919 // Simple struct for a single "choice" item.
920 static class ChoiceItem {
921 String text;
922 Bitmap icon;
923 int id;
924
925 public ChoiceItem(String s, Bitmap b, int i) {
926 text = s;
927 icon = b;
928 id = i;
929 }
930 }
931
932 // IDs for the possible "choices":
933 static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
934 static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
935 static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
936
937 private static final int NUM_ITEMS = 3;
938 private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
939
940 public DialpadChooserAdapter(Context context) {
941 // Cache the LayoutInflate to avoid asking for a new one each time.
942 mInflater = LayoutInflater.from(context);
943
944 // Initialize the possible choices.
945 // TODO: could this be specified entirely in XML?
946
947 // - "Use touch tone keypad"
948 mChoiceItems[0] = new ChoiceItem(
949 context.getString(R.string.dialer_useDtmfDialpad),
950 BitmapFactory.decodeResource(context.getResources(),
951 R.drawable.ic_dialer_fork_tt_keypad),
952 DIALPAD_CHOICE_USE_DTMF_DIALPAD);
953
954 // - "Return to call in progress"
955 mChoiceItems[1] = new ChoiceItem(
956 context.getString(R.string.dialer_returnToInCallScreen),
957 BitmapFactory.decodeResource(context.getResources(),
958 R.drawable.ic_dialer_fork_current_call),
959 DIALPAD_CHOICE_RETURN_TO_CALL);
960
961 // - "Add call"
962 mChoiceItems[2] = new ChoiceItem(
963 context.getString(R.string.dialer_addAnotherCall),
964 BitmapFactory.decodeResource(context.getResources(),
965 R.drawable.ic_dialer_fork_add_call),
966 DIALPAD_CHOICE_ADD_NEW_CALL);
967 }
968
969 public int getCount() {
970 return NUM_ITEMS;
971 }
972
973 /**
974 * Return the ChoiceItem for a given position.
975 */
976 public Object getItem(int position) {
977 return mChoiceItems[position];
978 }
979
980 /**
981 * Return a unique ID for each possible choice.
982 */
983 public long getItemId(int position) {
984 return position;
985 }
986
987 /**
988 * Make a view for each row.
989 */
990 public View getView(int position, View convertView, ViewGroup parent) {
991 // When convertView is non-null, we can reuse it (there's no need
992 // to reinflate it.)
993 if (convertView == null) {
994 convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
995 }
996
997 TextView text = (TextView) convertView.findViewById(R.id.text);
998 text.setText(mChoiceItems[position].text);
999
1000 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
1001 icon.setImageBitmap(mChoiceItems[position].icon);
1002
1003 return convertView;
1004 }
1005 }
1006
1007 /**
1008 * Handle clicks from the dialpad chooser.
1009 */
1010 public void onItemClick(AdapterView parent, View v, int position, long id) {
1011 DialpadChooserAdapter.ChoiceItem item =
1012 (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
1013 int itemId = item.id;
1014 switch (itemId) {
1015 case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
1016 // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
1017 // Fire off an intent to go back to the in-call UI
1018 // with the dialpad visible.
1019 returnToInCallScreen(true);
1020 break;
1021
1022 case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
1023 // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
1024 // Fire off an intent to go back to the in-call UI
1025 // (with the dialpad hidden).
1026 returnToInCallScreen(false);
1027 break;
1028
1029 case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
1030 // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
1031 // Ok, guess the user really did want to be here (in the
1032 // regular Dialer) after all. Bring back the normal Dialer UI.
1033 showDialpadChooser(false);
1034 break;
1035
1036 default:
1037 Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
1038 break;
1039 }
1040 }
1041
1042 /**
1043 * Returns to the in-call UI (where there's presumably a call in
1044 * progress) in response to the user selecting "use touch tone keypad"
1045 * or "return to call" from the dialpad chooser.
1046 */
1047 private void returnToInCallScreen(boolean showDialpad) {
1048 try {
1049 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1050 if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
1051 } catch (RemoteException e) {
1052 Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
1053 }
1054
1055 // Finally, finish() ourselves so that we don't stay on the
1056 // activity stack.
1057 // Note that we do this whether or not the showCallScreenWithDialpad()
1058 // call above had any effect or not! (That call is a no-op if the
1059 // phone is idle, which can happen if the current call ends while
1060 // the dialpad chooser is up. In this case we can't show the
1061 // InCallScreen, and there's no point staying here in the Dialer,
1062 // so we just take the user back where he came from...)
1063 finish();
1064 }
1065
1066 /**
1067 * @return true if the phone is "in use", meaning that at least one line
1068 * is active (ie. off hook or ringing or dialing).
1069 */
1070 private boolean phoneIsInUse() {
1071 boolean phoneInUse = false;
1072 try {
1073 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1074 if (phone != null) phoneInUse = !phone.isIdle();
1075 } catch (RemoteException e) {
1076 Log.w(TAG, "phone.isIdle() failed", e);
1077 }
1078 return phoneInUse;
1079 }
David Brownc29c7ab2009-07-07 16:00:18 -07001080
1081 /**
Paul Bermandbdcde22009-10-09 12:04:10 -04001082 * @return true if the phone is a CDMA phone type
1083 */
1084 private boolean phoneIsCdma() {
1085 boolean isCdma = false;
1086 try {
1087 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1088 if (phone != null) {
1089 isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA);
1090 }
1091 } catch (RemoteException e) {
1092 Log.w(TAG, "phone.getActivePhoneType() failed", e);
1093 }
1094 return isCdma;
1095 }
1096
1097 /**
1098 * @return true if the phone state is OFFHOOK
1099 */
1100 private boolean phoneIsOffhook() {
1101 boolean phoneOffhook = false;
1102 try {
1103 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1104 if (phone != null) phoneOffhook = phone.isOffhook();
1105 } catch (RemoteException e) {
1106 Log.w(TAG, "phone.isOffhook() failed", e);
1107 }
1108 return phoneOffhook;
1109 }
1110
Reli Talc2a2a512009-06-10 16:48:00 -04001111
1112 /**
1113 * Returns true whenever any one of the options from the menu is selected.
1114 * Code changes to support dialpad options
1115 */
1116 @Override
1117 public boolean onOptionsItemSelected(MenuItem item) {
1118 switch (item.getItemId()) {
1119 case MENU_2S_PAUSE:
1120 updateDialString(",");
1121 return true;
1122 case MENU_WAIT:
1123 updateDialString(";");
1124 return true;
1125 }
1126 return false;
1127 }
1128
1129 /**
1130 * Updates the dial string (mDigits) after inserting a Pause character (,)
1131 * or Wait character (;).
1132 */
1133 private void updateDialString(String newDigits) {
1134 int selectionStart;
1135 int selectionEnd;
1136
1137 // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
Eric Fischer686782e2009-09-10 17:57:45 -07001138 int anchor = mDigits.getSelectionStart();
1139 int point = mDigits.getSelectionEnd();
1140
1141 selectionStart = Math.min(anchor, point);
1142 selectionEnd = Math.max(anchor, point);
Reli Talc2a2a512009-06-10 16:48:00 -04001143
1144 Editable digits = mDigits.getText();
1145 if (selectionStart != -1 ) {
1146 if (selectionStart == selectionEnd) {
1147 // then there is no selection. So insert the pause at this
1148 // position and update the mDigits.
1149 digits.replace(selectionStart, selectionStart, newDigits);
1150 } else {
Eric Fischer1e2d3a22009-09-17 10:53:10 -07001151 digits.replace(selectionStart, selectionEnd, newDigits);
Nicolas Catania7edbd0c2009-09-28 20:37:33 -07001152 // Unselect: back to a regular cursor, just pass the character inserted.
1153 mDigits.setSelection(selectionStart + 1);
Reli Talc2a2a512009-06-10 16:48:00 -04001154 }
1155 } else {
1156 int len = mDigits.length();
1157 digits.replace(len, len, newDigits);
1158 }
1159 }
1160
1161 /**
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001162 * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001163 */
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001164 private void updateDialAndDeleteButtonEnabledState() {
1165 final boolean digitsNotEmpty = !isDigitsEmpty();
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001166
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001167 if (mDialButton != null) {
David Browndc1dfe22010-03-01 14:34:57 -08001168 // On CDMA phones, if we're already on a call, we *always*
1169 // enable the Dial button (since you can press it without
1170 // entering any digits to send an empty flash.)
1171 if (phoneIsCdma() && phoneIsOffhook()) {
1172 mDialButton.setEnabled(true);
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001173 } else {
David Browndc1dfe22010-03-01 14:34:57 -08001174 // Common case: GSM, or CDMA but not on a call.
1175 // Enable the Dial button if some digits have
1176 // been entered, or if there is a last dialed number
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001177 // that could be redialed.
1178 mDialButton.setEnabled(digitsNotEmpty ||
1179 !TextUtils.isEmpty(mLastNumberDialed));
Paul Bermandbdcde22009-10-09 12:04:10 -04001180 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001181 }
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001182 mDelete.setEnabled(digitsNotEmpty);
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001183 }
1184
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001185
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001186 /**
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001187 * Check if voicemail is enabled/accessible.
1188 */
1189 private void initVoicemailButton() {
1190 boolean hasVoicemail = false;
1191 try {
1192 hasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
1193 } catch (SecurityException se) {
1194 // Possibly no READ_PHONE_STATE privilege.
1195 }
1196
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001197 mVoicemailButton = mVoicemailDialAndDeleteRow.findViewById(R.id.voicemailButton);
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001198 if (hasVoicemail) {
1199 mVoicemailButton.setOnClickListener(this);
1200 } else {
1201 mVoicemailButton.setEnabled(false);
1202 }
1203 }
1204
1205 /**
Reli Talc2a2a512009-06-10 16:48:00 -04001206 * This function return true if Wait menu item can be shown
1207 * otherwise returns false. Assumes the passed string is non-empty
1208 * and the 0th index check is not required.
1209 */
1210 private boolean showWait(int start, int end, String digits) {
1211 if (start == end) {
1212 // visible false in this case
1213 if (start > digits.length()) return false;
1214
1215 // preceding char is ';', so visible should be false
1216 if (digits.charAt(start-1) == ';') return false;
1217
1218 // next char is ';', so visible should be false
1219 if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
1220 } else {
1221 // visible false in this case
1222 if (start > digits.length() || end > digits.length()) return false;
1223
1224 // In this case we need to just check for ';' preceding to start
1225 // or next to end
1226 if (digits.charAt(start-1) == ';') return false;
1227 }
1228 return true;
1229 }
Nicolas Cataniabe8821e2010-01-15 09:28:13 -08001230
1231 /**
1232 * @return true if the widget with the phone number digits is empty.
1233 */
1234 private boolean isDigitsEmpty() {
Nicolas Catania941b76f2010-01-19 14:09:40 -08001235 return mDigits.length() == 0;
Nicolas Cataniabe8821e2010-01-15 09:28:13 -08001236 }
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001237
1238 /**
1239 * Starts the asyn query to get the last dialed/outgoing
1240 * number. When the background query finishes, mLastNumberDialed
1241 * is set to the last dialed number or an empty string if none
1242 * exists yet.
1243 */
1244 private void queryLastOutgoingCall() {
1245 mLastNumberDialed = EMPTY_NUMBER;
1246 CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
1247 new CallLogAsync.GetLastOutgoingCallArgs(
1248 this,
1249 new CallLogAsync.OnLastOutgoingCallComplete() {
1250 public void lastOutgoingCall(String number) {
1251 // TODO: Filter out emergency numbers if
1252 // the carrier does not want redial for
1253 // these.
1254 mLastNumberDialed = number;
1255 updateDialAndDeleteButtonEnabledState();
1256 }
1257 });
1258 mCallLog.getLastOutgoingCall(lastCallArgs);
1259 }
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -08001260
1261 @Override
1262 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
1263 boolean globalSearch) {
1264 if (globalSearch) {
1265 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1266 } else {
1267 ContactsSearchManager.startSearch(this, initialQuery);
1268 }
1269 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001270}