blob: c0390eb9b73a4c2115119367ccd3dbaabea3b36c [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
19import android.app.Activity;
20import android.content.ActivityNotFoundException;
21import android.content.Context;
22import android.content.Intent;
23import android.content.res.Resources;
24import android.database.Cursor;
25import android.graphics.Bitmap;
26import android.graphics.BitmapFactory;
27import android.graphics.drawable.Drawable;
28import android.media.AudioManager;
29import android.media.ToneGenerator;
30import android.net.Uri;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.Message;
34import android.os.RemoteException;
35import android.os.ServiceManager;
36import android.os.SystemClock;
37import android.provider.Contacts.Intents.Insert;
38import android.provider.Contacts.People;
39import android.provider.Contacts.Phones;
40import android.provider.Contacts.PhonesColumns;
41import android.provider.Settings;
42import android.telephony.PhoneNumberFormattingTextWatcher;
43import android.telephony.PhoneNumberUtils;
44import android.telephony.PhoneStateListener;
45import android.telephony.TelephonyManager;
46import android.text.Editable;
Reli Talc2a2a512009-06-10 16:48:00 -040047import android.text.SpannableStringBuilder;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080048import android.text.TextUtils;
49import android.text.TextWatcher;
50import android.text.method.DialerKeyListener;
51import android.util.Log;
52import android.view.KeyEvent;
53import android.view.LayoutInflater;
54import android.view.Menu;
55import android.view.MenuItem;
56import android.view.View;
57import android.view.ViewConfiguration;
58import android.view.ViewGroup;
Karl Rosaenf46bc312009-03-24 18:20:48 -070059import android.view.inputmethod.InputMethodManager;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080060import android.widget.AdapterView;
61import android.widget.BaseAdapter;
62import android.widget.EditText;
63import android.widget.ImageView;
64import android.widget.ListView;
65import android.widget.TextView;
66
67import com.android.internal.telephony.ITelephony;
Nicolas Cataniac3be69e2010-01-14 14:03:53 -080068import com.android.phone.CallLogAsync;
Nicolas Catania905e7622009-12-01 08:51:20 -080069import com.android.phone.HapticFeedback;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080070
71/**
72 * Dialer activity that displays the typical twelve key interface.
73 */
74public 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
193 // Set the content view
194 setContentView(getContentViewResource());
195
Nicolas Catania75993762009-09-21 16:42:00 -0700196 // Load up the resources for the text field.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800197 Resources r = getResources();
198 mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800199 mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800200
201 mDigits = (EditText) findViewById(R.id.digits);
202 mDigits.setKeyListener(DialerKeyListener.getInstance());
203 mDigits.setOnClickListener(this);
204 mDigits.setOnKeyListener(this);
Nicolas Catania3040fa32009-10-01 13:00:53 -0700205
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800206 maybeAddNumberFormatting();
207
208 // Check for the presence of the keypad
209 View view = findViewById(R.id.one);
210 if (view != null) {
211 setupKeypad();
212 }
213
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700214 mVoicemailDialAndDeleteRow = findViewById(R.id.voicemailAndDialAndDelete);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700215
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700216 initVoicemailButton();
217
David Brown3d07e6d2009-08-04 20:30:09 -0700218 // Check whether we should show the onscreen "Dial" button.
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700219 mDialButton = mVoicemailDialAndDeleteRow.findViewById(R.id.dialButton);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700220
David Brown3d07e6d2009-08-04 20:30:09 -0700221 if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
David Brown3d07e6d2009-08-04 20:30:09 -0700222 mDialButton.setOnClickListener(this);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700223 } else {
224 mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
225 mDialButton = null;
David Brown3d07e6d2009-08-04 20:30:09 -0700226 }
227
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700228 view = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800229 view.setOnClickListener(this);
230 view.setOnLongClickListener(this);
231 mDelete = view;
232
Nicolas Catania901f8562009-10-09 11:09:45 -0700233 mDialpad = findViewById(R.id.dialpad); // This is null in landscape mode.
234
235 // In landscape we put the keyboard in phone mode.
236 // In portrait we prevent the soft keyboard to show since the
237 // dialpad acts as one already.
238 if (null == mDialpad) {
239 mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
240 } else {
241 mDigits.setInputType(android.text.InputType.TYPE_NULL);
242 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800243
244 // Set up the "dialpad chooser" UI; see showDialpadChooser().
245 mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
246 mDialpadChooser.setOnItemClickListener(this);
247
248 if (!resolveIntent() && icicle != null) {
249 super.onRestoreInstanceState(icicle);
250 }
251
Nicolas Catania905e7622009-12-01 08:51:20 -0800252 try {
253 mHaptic.init(this, r.getBoolean(R.bool.config_enable_dialer_key_vibration));
254 } catch (Resources.NotFoundException nfe) {
255 Log.e(TAG, "Vibrate control bool missing.", nfe);
256 }
257
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800258 }
259
260 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800261 protected void onRestoreInstanceState(Bundle icicle) {
262 // Do nothing, state is restored in onCreate() if needed
263 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700264
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800265 protected void maybeAddNumberFormatting() {
266 mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
267 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700268
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800269 /**
Eric Laurentd9efc872009-07-17 11:52:06 -0700270 * Overridden by subclasses to control the resource used by the content view.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800271 */
272 protected int getContentViewResource() {
273 return R.layout.twelve_key_dialer;
274 }
275
276 private boolean resolveIntent() {
277 boolean ignoreState = false;
278
279 // Find the proper intent
280 final Intent intent;
281 if (isChild()) {
282 intent = getParent().getIntent();
283 ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
284 } else {
285 intent = getIntent();
286 }
287 // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
288
289 // by default we are not adding a call.
290 mIsAddCallMode = false;
291
292 // By default we don't show the "dialpad chooser" UI.
293 boolean needToShowDialpadChooser = false;
294
295 // Resolve the intent
296 final String action = intent.getAction();
297 if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
298 // see if we are "adding a call" from the InCallScreen; false by default.
299 mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
300 Uri uri = intent.getData();
301 if (uri != null) {
302 if ("tel".equals(uri.getScheme())) {
303 // Put the requested number into the input area
Nicolas Catania43094f62009-12-08 14:09:46 -0800304 String data = uri.toString().substring("tel:".length());
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800305 setFormattedDigits(data);
306 } else {
307 String type = intent.getType();
308 if (People.CONTENT_ITEM_TYPE.equals(type)
309 || Phones.CONTENT_ITEM_TYPE.equals(type)) {
310 // Query the phone number
311 Cursor c = getContentResolver().query(intent.getData(),
312 new String[] {PhonesColumns.NUMBER}, null, null, null);
313 if (c != null) {
314 if (c.moveToFirst()) {
315 // Put the number into the input area
316 setFormattedDigits(c.getString(0));
317 }
318 c.close();
319 }
320 }
321 }
322 }
323 } else if (Intent.ACTION_MAIN.equals(action)) {
324 // The MAIN action means we're bringing up a blank dialer
325 // (e.g. by selecting the Home shortcut, or tabbing over from
326 // Contacts or Call log.)
327 //
328 // At this point, IF there's already an active call, there's a
329 // good chance that the user got here accidentally (but really
330 // wanted the in-call dialpad instead). So we bring up an
331 // intermediate UI to make the user confirm what they really
332 // want to do.
333 if (phoneIsInUse()) {
334 // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
335 needToShowDialpadChooser = true;
336 }
337 }
338
339 // Bring up the "dialpad chooser" IFF we need to make the user
340 // confirm which dialpad they really want.
341 showDialpadChooser(needToShowDialpadChooser);
342
343 return ignoreState;
344 }
345
346 protected void setFormattedDigits(String data) {
347 // strip the non-dialable numbers out of the data string.
348 String dialString = PhoneNumberUtils.extractNetworkPortion(data);
349 dialString = PhoneNumberUtils.formatNumber(dialString);
350 if (!TextUtils.isEmpty(dialString)) {
351 Editable digits = mDigits.getText();
352 digits.replace(0, digits.length(), dialString);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700353 // for some reason this isn't getting called in the digits.replace call above..
354 // but in any case, this will make sure the background drawable looks right
355 afterTextChanged(digits);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800356 }
357 }
358
359 @Override
360 protected void onNewIntent(Intent newIntent) {
361 setIntent(newIntent);
362 resolveIntent();
363 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700364
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800365 @Override
366 protected void onPostCreate(Bundle savedInstanceState) {
367 super.onPostCreate(savedInstanceState);
368
369 // This can't be done in onCreate(), since the auto-restoring of the digits
370 // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
371 // is called. This method will be called every time the activity is created, and
372 // will always happen after onRestoreSavedInstanceState().
373 mDigits.addTextChangedListener(this);
374 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700375
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800376 private void setupKeypad() {
377 // Setup the listeners for the buttons
378 View view = findViewById(R.id.one);
379 view.setOnClickListener(this);
380 view.setOnLongClickListener(this);
381
382 findViewById(R.id.two).setOnClickListener(this);
383 findViewById(R.id.three).setOnClickListener(this);
384 findViewById(R.id.four).setOnClickListener(this);
385 findViewById(R.id.five).setOnClickListener(this);
386 findViewById(R.id.six).setOnClickListener(this);
387 findViewById(R.id.seven).setOnClickListener(this);
388 findViewById(R.id.eight).setOnClickListener(this);
389 findViewById(R.id.nine).setOnClickListener(this);
390 findViewById(R.id.star).setOnClickListener(this);
391
392 view = findViewById(R.id.zero);
393 view.setOnClickListener(this);
394 view.setOnLongClickListener(this);
395
396 findViewById(R.id.pound).setOnClickListener(this);
397 }
398
399 @Override
400 protected void onResume() {
401 super.onResume();
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800402 // Query the last dialed number. Do it first because hitting
403 // the DB is 'slow'. This call is asynchronous.
404 queryLastOutgoingCall();
David Brownc29c7ab2009-07-07 16:00:18 -0700405
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800406 // retrieve the DTMF tone play back setting.
407 mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
408 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
409
Nicolas Catania905e7622009-12-01 08:51:20 -0800410 // Retrieve the haptic feedback setting.
411 mHaptic.checkSystemSetting();
412
Eric Laurentd9efc872009-07-17 11:52:06 -0700413 // if the mToneGenerator creation fails, just continue without it. It is
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800414 // a local audio signal, and is not as important as the dtmf tone itself.
415 synchronized(mToneGeneratorLock) {
416 if (mToneGenerator == null) {
417 try {
Jean-Michel Trividd44f8c2009-11-10 13:00:45 -0800418 // we want the user to be able to control the volume of the dial tones
419 // outside of a call, so we use the stream type that is also mapped to the
420 // volume control keys for this activity
421 mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
422 setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800423 } catch (RuntimeException e) {
424 Log.w(TAG, "Exception caught while creating local tone generator: " + e);
425 mToneGenerator = null;
426 }
427 }
428 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700429
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800430 Activity parent = getParent();
431 // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
432 // digits in the dialer field.
433 if (parent != null && parent instanceof DialtactsActivity) {
434 Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
435 if (dialUri != null) {
436 resolveIntent();
437 }
438 }
439
440 // While we're in the foreground, listen for phone state changes,
441 // purely so that we can take down the "dialpad chooser" if the
442 // phone becomes idle while the chooser UI is visible.
443 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
444 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
445
446 // Potentially show hint text in the mDigits field when the user
447 // hasn't typed any digits yet. (If there's already an active call,
448 // this hint text will remind the user that he's about to add a new
449 // call.)
450 //
451 // TODO: consider adding better UI for the case where *both* lines
452 // are currently in use. (Right now we let the user try to add
453 // another call, but that call is guaranteed to fail. Perhaps the
454 // entire dialer UI should be disabled instead.)
455 if (phoneIsInUse()) {
456 mDigits.setHint(R.string.dialerDialpadHintText);
457 } else {
458 // Common case; no hint necessary.
459 mDigits.setHint(null);
460
461 // Also, a sanity-check: the "dialpad chooser" UI should NEVER
462 // be visible if the phone is idle!
463 showDialpadChooser(false);
464 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700465
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800466 updateDialAndDeleteButtonEnabledState();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800467 }
468
469 @Override
Karl Rosaenf46bc312009-03-24 18:20:48 -0700470 public void onWindowFocusChanged(boolean hasFocus) {
471 if (hasFocus) {
472 // Hide soft keyboard, if visible (it's fugly over button dialer).
473 // The only known case where this will be true is when launching the dialer with
474 // ACTION_DIAL via a soft keyboard. we dismiss it here because we don't
475 // have a window token yet in onCreate / onNewIntent
476 InputMethodManager inputMethodManager = (InputMethodManager)
477 getSystemService(Context.INPUT_METHOD_SERVICE);
Eric Laurentd9efc872009-07-17 11:52:06 -0700478 inputMethodManager.hideSoftInputFromWindow(mDigits.getWindowToken(), 0);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700479 }
480 }
481
482 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800483 protected void onPause() {
484 super.onPause();
485
486 // Stop listening for phone state changes.
487 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
488 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
489
490 synchronized(mToneGeneratorLock) {
491 if (mToneGenerator != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800492 mToneGenerator.release();
493 mToneGenerator = null;
494 }
495 }
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800496 // TODO: I wonder if we should not check if the AsyncTask that
497 // lookup the last dialed number has completed.
498 mLastNumberDialed = EMPTY_NUMBER; // Since we are going to query again, free stale number.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800499 }
500
501 @Override
502 public boolean onCreateOptionsMenu(Menu menu) {
Reli Talc2a2a512009-06-10 16:48:00 -0400503 mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800504 .setIcon(android.R.drawable.ic_menu_add);
Reli Talc2a2a512009-06-10 16:48:00 -0400505 m2SecPauseMenuItem = menu.add(0, MENU_2S_PAUSE, 0, R.string.add_2sec_pause)
506 .setIcon(R.drawable.ic_menu_2sec_pause);
507 mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
508 .setIcon(R.drawable.ic_menu_wait);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800509 return true;
510 }
511
512 @Override
513 public boolean onPrepareOptionsMenu(Menu menu) {
514 // We never show a menu if the "choose dialpad" UI is up.
515 if (dialpadChooserVisible()) {
516 return false;
517 }
518
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800519 if (isDigitsEmpty()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800520 mAddToContactMenuItem.setVisible(false);
Reli Talc2a2a512009-06-10 16:48:00 -0400521 m2SecPauseMenuItem.setVisible(false);
522 mWaitMenuItem.setVisible(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800523 } else {
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800524 CharSequence digits = mDigits.getText();
525
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800526 // Put the current digits string into an intent
527 Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800528 intent.putExtra(Insert.PHONE, digits);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800529 intent.setType(People.CONTENT_ITEM_TYPE);
530 mAddToContactMenuItem.setIntent(intent);
531 mAddToContactMenuItem.setVisible(true);
Reli Talc2a2a512009-06-10 16:48:00 -0400532
533 // Check out whether to show Pause & Wait option menu items
534 int selectionStart;
535 int selectionEnd;
536 String strDigits = digits.toString();
537
538 selectionStart = mDigits.getSelectionStart();
539 selectionEnd = mDigits.getSelectionEnd();
540
541 if (selectionStart != -1) {
542 if (selectionStart > selectionEnd) {
543 // swap it as we want start to be less then end
544 int tmp = selectionStart;
545 selectionStart = selectionEnd;
546 selectionEnd = tmp;
547 }
548
549 if (selectionStart != 0) {
550 // Pause can be visible if cursor is not in the begining
551 m2SecPauseMenuItem.setVisible(true);
552
553 // For Wait to be visible set of condition to meet
554 mWaitMenuItem.setVisible(showWait(selectionStart,
555 selectionEnd, strDigits));
556 } else {
557 // cursor in the beginning both pause and wait to be invisible
558 m2SecPauseMenuItem.setVisible(false);
559 mWaitMenuItem.setVisible(false);
560 }
561 } else {
562 // cursor is not selected so assume new digit is added to the end
563 int strLength = strDigits.length();
564 mWaitMenuItem.setVisible(showWait(strLength,
565 strLength, strDigits));
566 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800567 }
568 return true;
569 }
570
571 @Override
572 public boolean onKeyDown(int keyCode, KeyEvent event) {
573 switch (keyCode) {
574 case KeyEvent.KEYCODE_CALL: {
575 long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
576 if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
577 // Launch voice dialer
578 Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
579 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
580 try {
581 startActivity(intent);
582 } catch (ActivityNotFoundException e) {
583 }
584 }
585 return true;
586 }
587 case KeyEvent.KEYCODE_1: {
Eric Laurentd9efc872009-07-17 11:52:06 -0700588 long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800589 if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
590 // Long press detected, call voice mail
591 callVoicemail();
592 }
593 return true;
594 }
595 }
596 return super.onKeyDown(keyCode, event);
597 }
598
599 @Override
600 public boolean onKeyUp(int keyCode, KeyEvent event) {
601 switch (keyCode) {
602 case KeyEvent.KEYCODE_CALL: {
Nicolas Catania998763d2010-01-14 14:03:53 -0800603 // TODO: In dialButtonPressed we do some of these
604 // tests again. We should try to consolidate them in
605 // one place.
606 if (!phoneIsCdma() && mIsAddCallMode && isDigitsEmpty()) {
607 // For CDMA phones, we always call
608 // dialButtonPressed() because we may need to send
609 // an empty flash command to the network.
610 // Otherwise, if we are adding a call from the
611 // InCallScreen and the phone number entered is
612 // empty, we just close the dialer to expose the
613 // InCallScreen under it.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800614 finish();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800615 }
Nicolas Catania998763d2010-01-14 14:03:53 -0800616
617 // If we're CDMA, regardless of where we are adding a call from (either
618 // InCallScreen or Dialtacts), the user may need to send an empty
619 // flash command to the network. So let's call dialButtonPressed() regardless
620 // and dialButtonPressed will handle this functionality for us.
621 // otherwise, we place the call.
622 dialButtonPressed();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800623 return true;
624 }
625 }
626 return super.onKeyUp(keyCode, event);
627 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700628
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800629 private void keyPressed(int keyCode) {
Nicolas Catania905e7622009-12-01 08:51:20 -0800630 mHaptic.vibrate();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800631 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
632 mDigits.onKeyDown(keyCode, event);
633 }
634
635 public boolean onKey(View view, int keyCode, KeyEvent event) {
636 switch (view.getId()) {
637 case R.id.digits:
638 if (keyCode == KeyEvent.KEYCODE_ENTER) {
Nicolas Catania998763d2010-01-14 14:03:53 -0800639 dialButtonPressed();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800640 return true;
641 }
642 break;
643 }
644 return false;
645 }
646
647 public void onClick(View view) {
648 switch (view.getId()) {
649 case R.id.one: {
650 playTone(ToneGenerator.TONE_DTMF_1);
651 keyPressed(KeyEvent.KEYCODE_1);
652 return;
653 }
654 case R.id.two: {
655 playTone(ToneGenerator.TONE_DTMF_2);
656 keyPressed(KeyEvent.KEYCODE_2);
657 return;
658 }
659 case R.id.three: {
660 playTone(ToneGenerator.TONE_DTMF_3);
661 keyPressed(KeyEvent.KEYCODE_3);
662 return;
663 }
664 case R.id.four: {
665 playTone(ToneGenerator.TONE_DTMF_4);
666 keyPressed(KeyEvent.KEYCODE_4);
667 return;
668 }
669 case R.id.five: {
670 playTone(ToneGenerator.TONE_DTMF_5);
671 keyPressed(KeyEvent.KEYCODE_5);
672 return;
673 }
674 case R.id.six: {
675 playTone(ToneGenerator.TONE_DTMF_6);
676 keyPressed(KeyEvent.KEYCODE_6);
677 return;
678 }
679 case R.id.seven: {
680 playTone(ToneGenerator.TONE_DTMF_7);
681 keyPressed(KeyEvent.KEYCODE_7);
682 return;
683 }
684 case R.id.eight: {
685 playTone(ToneGenerator.TONE_DTMF_8);
686 keyPressed(KeyEvent.KEYCODE_8);
687 return;
688 }
689 case R.id.nine: {
690 playTone(ToneGenerator.TONE_DTMF_9);
691 keyPressed(KeyEvent.KEYCODE_9);
692 return;
693 }
694 case R.id.zero: {
695 playTone(ToneGenerator.TONE_DTMF_0);
696 keyPressed(KeyEvent.KEYCODE_0);
697 return;
698 }
699 case R.id.pound: {
700 playTone(ToneGenerator.TONE_DTMF_P);
701 keyPressed(KeyEvent.KEYCODE_POUND);
702 return;
703 }
704 case R.id.star: {
705 playTone(ToneGenerator.TONE_DTMF_S);
706 keyPressed(KeyEvent.KEYCODE_STAR);
707 return;
708 }
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700709 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800710 keyPressed(KeyEvent.KEYCODE_DEL);
711 return;
712 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700713 case R.id.dialButton: {
Nicolas Catania905e7622009-12-01 08:51:20 -0800714 mHaptic.vibrate(); // Vibrate here too, just like we do for the regular keys
Nicolas Catania998763d2010-01-14 14:03:53 -0800715 dialButtonPressed();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800716 return;
717 }
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700718 case R.id.voicemailButton: {
719 callVoicemail();
Nicolas Catania905e7622009-12-01 08:51:20 -0800720 mHaptic.vibrate();
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700721 return;
722 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700723 case R.id.digits: {
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800724 if (!isDigitsEmpty()) {
Nicolas Catania3040fa32009-10-01 13:00:53 -0700725 mDigits.setCursorVisible(true);
726 }
727 return;
728 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800729 }
730 }
731
732 public boolean onLongClick(View view) {
733 final Editable digits = mDigits.getText();
734 int id = view.getId();
735 switch (id) {
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700736 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800737 digits.clear();
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700738 // TODO: The framework forgets to clear the pressed
739 // status of disabled button. Until this is fixed,
740 // clear manually the pressed status. b/2133127
741 mDelete.setPressed(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800742 return true;
743 }
744 case R.id.one: {
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800745 if (isDigitsEmpty()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800746 callVoicemail();
747 return true;
748 }
749 return false;
750 }
751 case R.id.zero: {
752 keyPressed(KeyEvent.KEYCODE_PLUS);
753 return true;
754 }
755 }
756 return false;
757 }
758
759 void callVoicemail() {
760 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800761 Uri.fromParts("voicemail", EMPTY_NUMBER, null));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800762 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
763 startActivity(intent);
764 mDigits.getText().clear();
765 finish();
766 }
767
Nicolas Catania998763d2010-01-14 14:03:53 -0800768 void dialButtonPressed() {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800769 final String number = mDigits.getText().toString();
Paul Bermandbdcde22009-10-09 12:04:10 -0400770 boolean sendEmptyFlash = false;
Nicolas Catania998763d2010-01-14 14:03:53 -0800771 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED);
772 if (isDigitsEmpty()) { // There is no number entered.
Paul Bermandbdcde22009-10-09 12:04:10 -0400773 if (phoneIsCdma() && phoneIsOffhook()) {
774 // We only want to send this empty flash extra if we're CDMA and the
775 // phone is offhook (don't want to send if ringing or dialing)
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800776 intent.setData(Uri.fromParts("tel", EMPTY_NUMBER, null));
Paul Bermandbdcde22009-10-09 12:04:10 -0400777 intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
778 sendEmptyFlash = true;
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800779 } else if (!phoneIsOffhook() && !TextUtils.isEmpty(mLastNumberDialed)) {
780 mDigits.setText(mLastNumberDialed);
Nicolas Catania998763d2010-01-14 14:03:53 -0800781 return;
Paul Bermandbdcde22009-10-09 12:04:10 -0400782 } else {
Nicolas Catania998763d2010-01-14 14:03:53 -0800783 // TODO: Is this dead code? Hit only if phoneIsOffHook
784 // and dial button is pressed. Can this happen? How
785 // does this compare to the finish() called in
786 // onKeyUp? Should this play tone be moved there?
Paul Bermandbdcde22009-10-09 12:04:10 -0400787 playTone(ToneGenerator.TONE_PROP_NACK);
788 return;
789 }
Nicolas Catania998763d2010-01-14 14:03:53 -0800790 } else { // There is a number.
791 intent.setData(Uri.fromParts("tel", number, null));
Paul Bermandbdcde22009-10-09 12:04:10 -0400792 }
Nicolas Catania998763d2010-01-14 14:03:53 -0800793
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800794 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
795 startActivity(intent);
796 mDigits.getText().clear();
Paul Bermandbdcde22009-10-09 12:04:10 -0400797 // Don't finish TwelveKeyDialer yet if we're sending a blank flash for CDMA. CDMA
798 // networks use Flash messages when special processing needs to be done, mainly for
799 // 3-way or call waiting scenarios. Presumably, here we're in a special 3-way scenario
800 // where the network needs a blank flash before being able to add the new participant.
801 // (This is not the case with all 3-way calls, just certain CDMA infrastructures.)
802 if (!sendEmptyFlash) {
803 finish();
804 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800805 }
806
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800807
808 /**
David Brown22f615f2009-06-25 16:19:19 -0700809 * Plays the specified tone for TONE_LENGTH_MS milliseconds.
810 *
811 * The tone is played locally, using the audio stream for phone calls.
812 * Tones are played only if the "Audible touch tones" user preference
813 * is checked, and are NOT played if the device is in silent mode.
814 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800815 * @param tone a tone code from {@link ToneGenerator}
816 */
817 void playTone(int tone) {
818 // if local tone playback is disabled, just return.
819 if (!mDTMFToneEnabled) {
820 return;
821 }
David Brown22f615f2009-06-25 16:19:19 -0700822
823 // Also do nothing if the phone is in silent mode.
824 // We need to re-check the ringer mode for *every* playTone()
825 // call, rather than keeping a local flag that's updated in
826 // onResume(), since it's possible to toggle silent mode without
827 // leaving the current activity (via the ENDCALL-longpress menu.)
828 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
David Brownd5a15302009-07-20 16:39:47 -0700829 int ringerMode = audioManager.getRingerMode();
830 if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
831 || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
David Brown22f615f2009-06-25 16:19:19 -0700832 return;
833 }
834
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800835 synchronized(mToneGeneratorLock) {
836 if (mToneGenerator == null) {
837 Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
838 return;
839 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700840
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800841 // Start the new tone (will stop any playing tone)
Eric Laurent8487fed2009-09-07 08:45:14 -0700842 mToneGenerator.startTone(tone, TONE_LENGTH_MS);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800843 }
844 }
845
846 /**
847 * Brings up the "dialpad chooser" UI in place of the usual Dialer
848 * elements (the textfield/button and the dialpad underneath).
849 *
850 * We show this UI if the user brings up the Dialer while a call is
851 * already in progress, since there's a good chance we got here
852 * accidentally (and the user really wanted the in-call dialpad instead).
853 * So in this situation we display an intermediate UI that lets the user
854 * explicitly choose between the in-call dialpad ("Use touch tone
855 * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
856 * to call in progress" just goes back to the in-call UI with no dialpad
857 * at all.)
858 *
859 * @param enabled If true, show the "dialpad chooser" instead
860 * of the regular Dialer UI
861 */
862 private void showDialpadChooser(boolean enabled) {
863 if (enabled) {
864 // Log.i(TAG, "Showing dialpad chooser!");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700865 mDigits.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800866 if (mDialpad != null) mDialpad.setVisibility(View.GONE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700867 mVoicemailDialAndDeleteRow.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800868 mDialpadChooser.setVisibility(View.VISIBLE);
869
870 // Instantiate the DialpadChooserAdapter and hook it up to the
871 // ListView. We do this only once.
872 if (mDialpadChooserAdapter == null) {
873 mDialpadChooserAdapter = new DialpadChooserAdapter(this);
874 mDialpadChooser.setAdapter(mDialpadChooserAdapter);
875 }
876 } else {
877 // Log.i(TAG, "Displaying normal Dialer UI.");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700878 mDigits.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800879 if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700880 mVoicemailDialAndDeleteRow.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800881 mDialpadChooser.setVisibility(View.GONE);
882 }
883 }
884
885 /**
886 * @return true if we're currently showing the "dialpad chooser" UI.
887 */
888 private boolean dialpadChooserVisible() {
889 return mDialpadChooser.getVisibility() == View.VISIBLE;
890 }
891
892 /**
893 * Simple list adapter, binding to an icon + text label
894 * for each item in the "dialpad chooser" list.
895 */
896 private static class DialpadChooserAdapter extends BaseAdapter {
897 private LayoutInflater mInflater;
898
899 // Simple struct for a single "choice" item.
900 static class ChoiceItem {
901 String text;
902 Bitmap icon;
903 int id;
904
905 public ChoiceItem(String s, Bitmap b, int i) {
906 text = s;
907 icon = b;
908 id = i;
909 }
910 }
911
912 // IDs for the possible "choices":
913 static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
914 static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
915 static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
916
917 private static final int NUM_ITEMS = 3;
918 private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
919
920 public DialpadChooserAdapter(Context context) {
921 // Cache the LayoutInflate to avoid asking for a new one each time.
922 mInflater = LayoutInflater.from(context);
923
924 // Initialize the possible choices.
925 // TODO: could this be specified entirely in XML?
926
927 // - "Use touch tone keypad"
928 mChoiceItems[0] = new ChoiceItem(
929 context.getString(R.string.dialer_useDtmfDialpad),
930 BitmapFactory.decodeResource(context.getResources(),
931 R.drawable.ic_dialer_fork_tt_keypad),
932 DIALPAD_CHOICE_USE_DTMF_DIALPAD);
933
934 // - "Return to call in progress"
935 mChoiceItems[1] = new ChoiceItem(
936 context.getString(R.string.dialer_returnToInCallScreen),
937 BitmapFactory.decodeResource(context.getResources(),
938 R.drawable.ic_dialer_fork_current_call),
939 DIALPAD_CHOICE_RETURN_TO_CALL);
940
941 // - "Add call"
942 mChoiceItems[2] = new ChoiceItem(
943 context.getString(R.string.dialer_addAnotherCall),
944 BitmapFactory.decodeResource(context.getResources(),
945 R.drawable.ic_dialer_fork_add_call),
946 DIALPAD_CHOICE_ADD_NEW_CALL);
947 }
948
949 public int getCount() {
950 return NUM_ITEMS;
951 }
952
953 /**
954 * Return the ChoiceItem for a given position.
955 */
956 public Object getItem(int position) {
957 return mChoiceItems[position];
958 }
959
960 /**
961 * Return a unique ID for each possible choice.
962 */
963 public long getItemId(int position) {
964 return position;
965 }
966
967 /**
968 * Make a view for each row.
969 */
970 public View getView(int position, View convertView, ViewGroup parent) {
971 // When convertView is non-null, we can reuse it (there's no need
972 // to reinflate it.)
973 if (convertView == null) {
974 convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
975 }
976
977 TextView text = (TextView) convertView.findViewById(R.id.text);
978 text.setText(mChoiceItems[position].text);
979
980 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
981 icon.setImageBitmap(mChoiceItems[position].icon);
982
983 return convertView;
984 }
985 }
986
987 /**
988 * Handle clicks from the dialpad chooser.
989 */
990 public void onItemClick(AdapterView parent, View v, int position, long id) {
991 DialpadChooserAdapter.ChoiceItem item =
992 (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
993 int itemId = item.id;
994 switch (itemId) {
995 case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
996 // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
997 // Fire off an intent to go back to the in-call UI
998 // with the dialpad visible.
999 returnToInCallScreen(true);
1000 break;
1001
1002 case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
1003 // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
1004 // Fire off an intent to go back to the in-call UI
1005 // (with the dialpad hidden).
1006 returnToInCallScreen(false);
1007 break;
1008
1009 case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
1010 // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
1011 // Ok, guess the user really did want to be here (in the
1012 // regular Dialer) after all. Bring back the normal Dialer UI.
1013 showDialpadChooser(false);
1014 break;
1015
1016 default:
1017 Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
1018 break;
1019 }
1020 }
1021
1022 /**
1023 * Returns to the in-call UI (where there's presumably a call in
1024 * progress) in response to the user selecting "use touch tone keypad"
1025 * or "return to call" from the dialpad chooser.
1026 */
1027 private void returnToInCallScreen(boolean showDialpad) {
1028 try {
1029 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1030 if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
1031 } catch (RemoteException e) {
1032 Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
1033 }
1034
1035 // Finally, finish() ourselves so that we don't stay on the
1036 // activity stack.
1037 // Note that we do this whether or not the showCallScreenWithDialpad()
1038 // call above had any effect or not! (That call is a no-op if the
1039 // phone is idle, which can happen if the current call ends while
1040 // the dialpad chooser is up. In this case we can't show the
1041 // InCallScreen, and there's no point staying here in the Dialer,
1042 // so we just take the user back where he came from...)
1043 finish();
1044 }
1045
1046 /**
1047 * @return true if the phone is "in use", meaning that at least one line
1048 * is active (ie. off hook or ringing or dialing).
1049 */
1050 private boolean phoneIsInUse() {
1051 boolean phoneInUse = false;
1052 try {
1053 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1054 if (phone != null) phoneInUse = !phone.isIdle();
1055 } catch (RemoteException e) {
1056 Log.w(TAG, "phone.isIdle() failed", e);
1057 }
1058 return phoneInUse;
1059 }
David Brownc29c7ab2009-07-07 16:00:18 -07001060
1061 /**
Paul Bermandbdcde22009-10-09 12:04:10 -04001062 * @return true if the phone is a CDMA phone type
1063 */
1064 private boolean phoneIsCdma() {
1065 boolean isCdma = false;
1066 try {
1067 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1068 if (phone != null) {
1069 isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA);
1070 }
1071 } catch (RemoteException e) {
1072 Log.w(TAG, "phone.getActivePhoneType() failed", e);
1073 }
1074 return isCdma;
1075 }
1076
1077 /**
1078 * @return true if the phone state is OFFHOOK
1079 */
1080 private boolean phoneIsOffhook() {
1081 boolean phoneOffhook = false;
1082 try {
1083 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1084 if (phone != null) phoneOffhook = phone.isOffhook();
1085 } catch (RemoteException e) {
1086 Log.w(TAG, "phone.isOffhook() failed", e);
1087 }
1088 return phoneOffhook;
1089 }
1090
Reli Talc2a2a512009-06-10 16:48:00 -04001091
1092 /**
1093 * Returns true whenever any one of the options from the menu is selected.
1094 * Code changes to support dialpad options
1095 */
1096 @Override
1097 public boolean onOptionsItemSelected(MenuItem item) {
1098 switch (item.getItemId()) {
1099 case MENU_2S_PAUSE:
1100 updateDialString(",");
1101 return true;
1102 case MENU_WAIT:
1103 updateDialString(";");
1104 return true;
1105 }
1106 return false;
1107 }
1108
1109 /**
1110 * Updates the dial string (mDigits) after inserting a Pause character (,)
1111 * or Wait character (;).
1112 */
1113 private void updateDialString(String newDigits) {
1114 int selectionStart;
1115 int selectionEnd;
1116
1117 // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
Eric Fischer686782e2009-09-10 17:57:45 -07001118 int anchor = mDigits.getSelectionStart();
1119 int point = mDigits.getSelectionEnd();
1120
1121 selectionStart = Math.min(anchor, point);
1122 selectionEnd = Math.max(anchor, point);
Reli Talc2a2a512009-06-10 16:48:00 -04001123
1124 Editable digits = mDigits.getText();
1125 if (selectionStart != -1 ) {
1126 if (selectionStart == selectionEnd) {
1127 // then there is no selection. So insert the pause at this
1128 // position and update the mDigits.
1129 digits.replace(selectionStart, selectionStart, newDigits);
1130 } else {
Eric Fischer1e2d3a22009-09-17 10:53:10 -07001131 digits.replace(selectionStart, selectionEnd, newDigits);
Nicolas Catania7edbd0c2009-09-28 20:37:33 -07001132 // Unselect: back to a regular cursor, just pass the character inserted.
1133 mDigits.setSelection(selectionStart + 1);
Reli Talc2a2a512009-06-10 16:48:00 -04001134 }
1135 } else {
1136 int len = mDigits.length();
1137 digits.replace(len, len, newDigits);
1138 }
1139 }
1140
1141 /**
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001142 * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001143 */
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001144 private void updateDialAndDeleteButtonEnabledState() {
1145 final boolean digitsNotEmpty = !isDigitsEmpty();
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001146
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001147 if (mDialButton != null) {
1148 // If we're already on a CDMA call, then we want to enable
1149 // the Call button so we can send a flash.
1150 if (phoneIsOffhook()) {
1151 mDialButton.setEnabled(phoneIsCdma());
1152 } else {
1153 // Not in a call, enable the button if digits have
1154 // been entered or if there is a last dialed number
1155 // that could be redialed.
1156 mDialButton.setEnabled(digitsNotEmpty ||
1157 !TextUtils.isEmpty(mLastNumberDialed));
Paul Bermandbdcde22009-10-09 12:04:10 -04001158 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001159 }
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001160 mDelete.setEnabled(digitsNotEmpty);
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001161 }
1162
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001163
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001164 /**
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001165 * Check if voicemail is enabled/accessible.
1166 */
1167 private void initVoicemailButton() {
1168 boolean hasVoicemail = false;
1169 try {
1170 hasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
1171 } catch (SecurityException se) {
1172 // Possibly no READ_PHONE_STATE privilege.
1173 }
1174
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001175 mVoicemailButton = mVoicemailDialAndDeleteRow.findViewById(R.id.voicemailButton);
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001176 if (hasVoicemail) {
1177 mVoicemailButton.setOnClickListener(this);
1178 } else {
1179 mVoicemailButton.setEnabled(false);
1180 }
1181 }
1182
1183 /**
Reli Talc2a2a512009-06-10 16:48:00 -04001184 * This function return true if Wait menu item can be shown
1185 * otherwise returns false. Assumes the passed string is non-empty
1186 * and the 0th index check is not required.
1187 */
1188 private boolean showWait(int start, int end, String digits) {
1189 if (start == end) {
1190 // visible false in this case
1191 if (start > digits.length()) return false;
1192
1193 // preceding char is ';', so visible should be false
1194 if (digits.charAt(start-1) == ';') return false;
1195
1196 // next char is ';', so visible should be false
1197 if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
1198 } else {
1199 // visible false in this case
1200 if (start > digits.length() || end > digits.length()) return false;
1201
1202 // In this case we need to just check for ';' preceding to start
1203 // or next to end
1204 if (digits.charAt(start-1) == ';') return false;
1205 }
1206 return true;
1207 }
Nicolas Cataniabe8821e2010-01-15 09:28:13 -08001208
1209 /**
1210 * @return true if the widget with the phone number digits is empty.
1211 */
1212 private boolean isDigitsEmpty() {
Nicolas Catania941b76f2010-01-19 14:09:40 -08001213 return mDigits.length() == 0;
Nicolas Cataniabe8821e2010-01-15 09:28:13 -08001214 }
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001215
1216 /**
1217 * Starts the asyn query to get the last dialed/outgoing
1218 * number. When the background query finishes, mLastNumberDialed
1219 * is set to the last dialed number or an empty string if none
1220 * exists yet.
1221 */
1222 private void queryLastOutgoingCall() {
1223 mLastNumberDialed = EMPTY_NUMBER;
1224 CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
1225 new CallLogAsync.GetLastOutgoingCallArgs(
1226 this,
1227 new CallLogAsync.OnLastOutgoingCallComplete() {
1228 public void lastOutgoingCall(String number) {
1229 // TODO: Filter out emergency numbers if
1230 // the carrier does not want redial for
1231 // these.
1232 mLastNumberDialed = number;
1233 updateDialAndDeleteButtonEnabledState();
1234 }
1235 });
1236 mCallLog.getLastOutgoingCall(lastCallArgs);
1237 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001238}