blob: 1c1b6d4b193420c985df69418eac31008e69f766 [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;
27import android.content.res.Resources;
28import android.database.Cursor;
29import android.graphics.Bitmap;
30import android.graphics.BitmapFactory;
31import android.graphics.drawable.Drawable;
32import android.media.AudioManager;
33import android.media.ToneGenerator;
34import android.net.Uri;
35import android.os.Bundle;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080036import android.os.RemoteException;
37import android.os.ServiceManager;
38import android.os.SystemClock;
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -080039import android.provider.Settings;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080040import android.provider.Contacts.People;
41import android.provider.Contacts.Phones;
42import android.provider.Contacts.PhonesColumns;
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -080043import android.provider.Contacts.Intents.Insert;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080044import android.telephony.PhoneNumberFormattingTextWatcher;
45import android.telephony.PhoneNumberUtils;
46import android.telephony.PhoneStateListener;
47import android.telephony.TelephonyManager;
48import android.text.Editable;
49import android.text.TextUtils;
50import android.text.TextWatcher;
51import android.text.method.DialerKeyListener;
52import android.util.Log;
53import android.view.KeyEvent;
54import android.view.LayoutInflater;
55import android.view.Menu;
56import android.view.MenuItem;
57import android.view.View;
58import android.view.ViewConfiguration;
59import android.view.ViewGroup;
Karl Rosaenf46bc312009-03-24 18:20:48 -070060import android.view.inputmethod.InputMethodManager;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080061import android.widget.AdapterView;
62import android.widget.BaseAdapter;
63import android.widget.EditText;
64import android.widget.ImageView;
65import android.widget.ListView;
66import android.widget.TextView;
67
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080068/**
69 * Dialer activity that displays the typical twelve key interface.
70 */
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -080071@SuppressWarnings("deprecation")
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080072public class TwelveKeyDialer extends Activity implements View.OnClickListener,
73 View.OnLongClickListener, View.OnKeyListener,
74 AdapterView.OnItemClickListener, TextWatcher {
Nicolas Cataniac3be69e2010-01-14 14:03:53 -080075 private static final String EMPTY_NUMBER = "";
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080076 private static final String TAG = "TwelveKeyDialer";
Eric Laurentd9efc872009-07-17 11:52:06 -070077
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080078 /** The length of DTMF tones in milliseconds */
79 private static final int TONE_LENGTH_MS = 150;
Eric Laurentd9efc872009-07-17 11:52:06 -070080
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080081 /** The DTMF tone volume relative to other sounds in the stream */
Jean-Michel Trividd44f8c2009-11-10 13:00:45 -080082 private static final int TONE_RELATIVE_VOLUME = 80;
83
84 /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
85 private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_MUSIC;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080086
87 private EditText mDigits;
88 private View mDelete;
89 private MenuItem mAddToContactMenuItem;
90 private ToneGenerator mToneGenerator;
91 private Object mToneGeneratorLock = new Object();
92 private Drawable mDigitsBackground;
93 private Drawable mDigitsEmptyBackground;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080094 private View mDialpad;
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -070095 private View mVoicemailDialAndDeleteRow;
Nicolas Catania80bda0f2009-09-19 09:17:14 -070096 private View mVoicemailButton;
97 private View mDialButton;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080098 private ListView mDialpadChooser;
99 private DialpadChooserAdapter mDialpadChooserAdapter;
Reli Talc2a2a512009-06-10 16:48:00 -0400100 //Member variables for dialpad options
101 private MenuItem m2SecPauseMenuItem;
102 private MenuItem mWaitMenuItem;
103 private static final int MENU_ADD_CONTACTS = 1;
104 private static final int MENU_2S_PAUSE = 2;
105 private static final int MENU_WAIT = 3;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800106
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800107 // Last number dialed, retrieved asynchronously from the call DB
108 // in onCreate. This number is displayed when the user hits the
109 // send key and cleared in onPause.
110 CallLogAsync mCallLog = new CallLogAsync();
111 private String mLastNumberDialed = EMPTY_NUMBER;
112
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800113 // determines if we want to playback local DTMF tones.
114 private boolean mDTMFToneEnabled;
David Brownc29c7ab2009-07-07 16:00:18 -0700115
116 // Vibration (haptic feedback) for dialer key presses.
Nicolas Catania905e7622009-12-01 08:51:20 -0800117 private HapticFeedback mHaptic = new HapticFeedback();
Eric Laurentd9efc872009-07-17 11:52:06 -0700118
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800119 /** Identifier for the "Add Call" intent extra. */
120 static final String ADD_CALL_MODE_KEY = "add_call_mode";
Paul Bermandbdcde22009-10-09 12:04:10 -0400121
122 /**
123 * Identifier for intent extra for sending an empty Flash message for
124 * CDMA networks. This message is used by the network to simulate a
125 * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
126 *
127 * TODO: Using an intent extra to tell the phone to send this flash is a
128 * temporary measure. To be replaced with an ITelephony call in the future.
129 * TODO: Keep in sync with the string defined in OutgoingCallBroadcaster.java
130 * in Phone app until this is replaced with the ITelephony API.
131 */
132 static final String EXTRA_SEND_EMPTY_FLASH
133 = "com.android.phone.extra.SEND_EMPTY_FLASH";
134
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800135 /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
136 private boolean mIsAddCallMode;
137
138 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
139 /**
140 * Listen for phone state changes so that we can take down the
141 * "dialpad chooser" if the phone becomes idle while the
142 * chooser UI is visible.
143 */
144 @Override
145 public void onCallStateChanged(int state, String incomingNumber) {
146 // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
147 // + state + ", '" + incomingNumber + "'");
148 if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
149 // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
150 // Note there's a race condition in the UI here: the
151 // dialpad chooser could conceivably disappear (on its
152 // own) at the exact moment the user was trying to select
153 // one of the choices, which would be confusing. (But at
154 // least that's better than leaving the dialpad chooser
155 // onscreen, but useless...)
156 showDialpadChooser(false);
157 }
158 }
159 };
160
161 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
162 // Do nothing
163 }
164
165 public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
166 // Do nothing
Eric Laurentd9efc872009-07-17 11:52:06 -0700167 // DTMF Tones do not need to be played here any longer -
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800168 // the DTMF dialer handles that functionality now.
169 }
170
171 public void afterTextChanged(Editable input) {
172 if (SpecialCharSequenceMgr.handleChars(this, input.toString(), mDigits)) {
173 // A special sequence was entered, clear the digits
174 mDigits.getText().clear();
175 }
176
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800177 if (!isDigitsEmpty()) {
Nicolas Catania75993762009-09-21 16:42:00 -0700178 mDigits.setBackgroundDrawable(mDigitsBackground);
179 } else {
Nicolas Catania3040fa32009-10-01 13:00:53 -0700180 mDigits.setCursorVisible(false);
Nicolas Catania75993762009-09-21 16:42:00 -0700181 mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
182 }
183
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800184 updateDialAndDeleteButtonEnabledState();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800185 }
186
187 @Override
188 protected void onCreate(Bundle icicle) {
189 super.onCreate(icicle);
190
191 // Set the content view
192 setContentView(getContentViewResource());
193
Nicolas Catania75993762009-09-21 16:42:00 -0700194 // Load up the resources for the text field.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800195 Resources r = getResources();
196 mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800197 mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800198
199 mDigits = (EditText) findViewById(R.id.digits);
200 mDigits.setKeyListener(DialerKeyListener.getInstance());
201 mDigits.setOnClickListener(this);
202 mDigits.setOnKeyListener(this);
Nicolas Catania3040fa32009-10-01 13:00:53 -0700203
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800204 maybeAddNumberFormatting();
205
206 // Check for the presence of the keypad
207 View view = findViewById(R.id.one);
208 if (view != null) {
209 setupKeypad();
210 }
211
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700212 mVoicemailDialAndDeleteRow = findViewById(R.id.voicemailAndDialAndDelete);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700213
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700214 initVoicemailButton();
215
David Brown3d07e6d2009-08-04 20:30:09 -0700216 // Check whether we should show the onscreen "Dial" button.
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700217 mDialButton = mVoicemailDialAndDeleteRow.findViewById(R.id.dialButton);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700218
David Brown3d07e6d2009-08-04 20:30:09 -0700219 if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
David Brown3d07e6d2009-08-04 20:30:09 -0700220 mDialButton.setOnClickListener(this);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700221 } else {
222 mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
223 mDialButton = null;
David Brown3d07e6d2009-08-04 20:30:09 -0700224 }
225
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700226 view = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800227 view.setOnClickListener(this);
228 view.setOnLongClickListener(this);
229 mDelete = view;
230
Nicolas Catania901f8562009-10-09 11:09:45 -0700231 mDialpad = findViewById(R.id.dialpad); // This is null in landscape mode.
232
233 // In landscape we put the keyboard in phone mode.
234 // In portrait we prevent the soft keyboard to show since the
235 // dialpad acts as one already.
236 if (null == mDialpad) {
237 mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
238 } else {
239 mDigits.setInputType(android.text.InputType.TYPE_NULL);
240 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800241
242 // Set up the "dialpad chooser" UI; see showDialpadChooser().
243 mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
244 mDialpadChooser.setOnItemClickListener(this);
245
246 if (!resolveIntent() && icicle != null) {
247 super.onRestoreInstanceState(icicle);
248 }
249
Nicolas Catania905e7622009-12-01 08:51:20 -0800250 try {
251 mHaptic.init(this, r.getBoolean(R.bool.config_enable_dialer_key_vibration));
252 } catch (Resources.NotFoundException nfe) {
253 Log.e(TAG, "Vibrate control bool missing.", nfe);
254 }
255
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800256 }
257
258 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800259 protected void onRestoreInstanceState(Bundle icicle) {
260 // Do nothing, state is restored in onCreate() if needed
261 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700262
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800263 protected void maybeAddNumberFormatting() {
264 mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
265 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700266
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800267 /**
Eric Laurentd9efc872009-07-17 11:52:06 -0700268 * Overridden by subclasses to control the resource used by the content view.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800269 */
270 protected int getContentViewResource() {
271 return R.layout.twelve_key_dialer;
272 }
273
274 private boolean resolveIntent() {
275 boolean ignoreState = false;
276
277 // Find the proper intent
278 final Intent intent;
279 if (isChild()) {
280 intent = getParent().getIntent();
281 ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
282 } else {
283 intent = getIntent();
284 }
285 // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
286
287 // by default we are not adding a call.
288 mIsAddCallMode = false;
289
290 // By default we don't show the "dialpad chooser" UI.
291 boolean needToShowDialpadChooser = false;
292
293 // Resolve the intent
294 final String action = intent.getAction();
295 if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
296 // see if we are "adding a call" from the InCallScreen; false by default.
297 mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
298 Uri uri = intent.getData();
299 if (uri != null) {
300 if ("tel".equals(uri.getScheme())) {
301 // Put the requested number into the input area
Nicolas Catania43094f62009-12-08 14:09:46 -0800302 String data = uri.toString().substring("tel:".length());
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800303 setFormattedDigits(data);
304 } else {
305 String type = intent.getType();
306 if (People.CONTENT_ITEM_TYPE.equals(type)
307 || Phones.CONTENT_ITEM_TYPE.equals(type)) {
308 // Query the phone number
309 Cursor c = getContentResolver().query(intent.getData(),
310 new String[] {PhonesColumns.NUMBER}, null, null, null);
311 if (c != null) {
312 if (c.moveToFirst()) {
313 // Put the number into the input area
314 setFormattedDigits(c.getString(0));
315 }
316 c.close();
317 }
318 }
319 }
320 }
321 } else if (Intent.ACTION_MAIN.equals(action)) {
322 // The MAIN action means we're bringing up a blank dialer
323 // (e.g. by selecting the Home shortcut, or tabbing over from
324 // Contacts or Call log.)
325 //
326 // At this point, IF there's already an active call, there's a
327 // good chance that the user got here accidentally (but really
328 // wanted the in-call dialpad instead). So we bring up an
329 // intermediate UI to make the user confirm what they really
330 // want to do.
331 if (phoneIsInUse()) {
332 // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
333 needToShowDialpadChooser = true;
334 }
335 }
336
337 // Bring up the "dialpad chooser" IFF we need to make the user
338 // confirm which dialpad they really want.
339 showDialpadChooser(needToShowDialpadChooser);
340
341 return ignoreState;
342 }
343
344 protected void setFormattedDigits(String data) {
345 // strip the non-dialable numbers out of the data string.
346 String dialString = PhoneNumberUtils.extractNetworkPortion(data);
347 dialString = PhoneNumberUtils.formatNumber(dialString);
348 if (!TextUtils.isEmpty(dialString)) {
349 Editable digits = mDigits.getText();
350 digits.replace(0, digits.length(), dialString);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700351 // for some reason this isn't getting called in the digits.replace call above..
352 // but in any case, this will make sure the background drawable looks right
353 afterTextChanged(digits);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800354 }
355 }
356
357 @Override
358 protected void onNewIntent(Intent newIntent) {
359 setIntent(newIntent);
360 resolveIntent();
361 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700362
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800363 @Override
364 protected void onPostCreate(Bundle savedInstanceState) {
365 super.onPostCreate(savedInstanceState);
366
367 // This can't be done in onCreate(), since the auto-restoring of the digits
368 // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
369 // is called. This method will be called every time the activity is created, and
370 // will always happen after onRestoreSavedInstanceState().
371 mDigits.addTextChangedListener(this);
372 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700373
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800374 private void setupKeypad() {
375 // Setup the listeners for the buttons
376 View view = findViewById(R.id.one);
377 view.setOnClickListener(this);
378 view.setOnLongClickListener(this);
379
380 findViewById(R.id.two).setOnClickListener(this);
381 findViewById(R.id.three).setOnClickListener(this);
382 findViewById(R.id.four).setOnClickListener(this);
383 findViewById(R.id.five).setOnClickListener(this);
384 findViewById(R.id.six).setOnClickListener(this);
385 findViewById(R.id.seven).setOnClickListener(this);
386 findViewById(R.id.eight).setOnClickListener(this);
387 findViewById(R.id.nine).setOnClickListener(this);
388 findViewById(R.id.star).setOnClickListener(this);
389
390 view = findViewById(R.id.zero);
391 view.setOnClickListener(this);
392 view.setOnLongClickListener(this);
393
394 findViewById(R.id.pound).setOnClickListener(this);
395 }
396
397 @Override
398 protected void onResume() {
399 super.onResume();
David Browndc1dfe22010-03-01 14:34:57 -0800400
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800401 // Query the last dialed number. Do it first because hitting
402 // the DB is 'slow'. This call is asynchronous.
403 queryLastOutgoingCall();
David Brownc29c7ab2009-07-07 16:00:18 -0700404
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800405 // retrieve the DTMF tone play back setting.
406 mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
407 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
408
Nicolas Catania905e7622009-12-01 08:51:20 -0800409 // Retrieve the haptic feedback setting.
410 mHaptic.checkSystemSetting();
411
Eric Laurentd9efc872009-07-17 11:52:06 -0700412 // if the mToneGenerator creation fails, just continue without it. It is
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800413 // a local audio signal, and is not as important as the dtmf tone itself.
414 synchronized(mToneGeneratorLock) {
415 if (mToneGenerator == null) {
416 try {
Jean-Michel Trividd44f8c2009-11-10 13:00:45 -0800417 // we want the user to be able to control the volume of the dial tones
418 // outside of a call, so we use the stream type that is also mapped to the
419 // volume control keys for this activity
420 mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
421 setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800422 } catch (RuntimeException e) {
423 Log.w(TAG, "Exception caught while creating local tone generator: " + e);
424 mToneGenerator = null;
425 }
426 }
427 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700428
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800429 Activity parent = getParent();
430 // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
431 // digits in the dialer field.
432 if (parent != null && parent instanceof DialtactsActivity) {
433 Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
434 if (dialUri != null) {
435 resolveIntent();
436 }
437 }
438
439 // While we're in the foreground, listen for phone state changes,
440 // purely so that we can take down the "dialpad chooser" if the
441 // phone becomes idle while the chooser UI is visible.
442 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
443 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
444
445 // Potentially show hint text in the mDigits field when the user
446 // hasn't typed any digits yet. (If there's already an active call,
447 // this hint text will remind the user that he's about to add a new
448 // call.)
449 //
450 // TODO: consider adding better UI for the case where *both* lines
451 // are currently in use. (Right now we let the user try to add
452 // another call, but that call is guaranteed to fail. Perhaps the
453 // entire dialer UI should be disabled instead.)
454 if (phoneIsInUse()) {
455 mDigits.setHint(R.string.dialerDialpadHintText);
456 } else {
457 // Common case; no hint necessary.
458 mDigits.setHint(null);
459
460 // Also, a sanity-check: the "dialpad chooser" UI should NEVER
461 // be visible if the phone is idle!
462 showDialpadChooser(false);
463 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700464
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800465 updateDialAndDeleteButtonEnabledState();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800466 }
467
468 @Override
Karl Rosaenf46bc312009-03-24 18:20:48 -0700469 public void onWindowFocusChanged(boolean hasFocus) {
470 if (hasFocus) {
471 // Hide soft keyboard, if visible (it's fugly over button dialer).
472 // The only known case where this will be true is when launching the dialer with
473 // ACTION_DIAL via a soft keyboard. we dismiss it here because we don't
474 // have a window token yet in onCreate / onNewIntent
475 InputMethodManager inputMethodManager = (InputMethodManager)
476 getSystemService(Context.INPUT_METHOD_SERVICE);
Eric Laurentd9efc872009-07-17 11:52:06 -0700477 inputMethodManager.hideSoftInputFromWindow(mDigits.getWindowToken(), 0);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700478 }
479 }
480
481 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800482 protected void onPause() {
483 super.onPause();
484
485 // Stop listening for phone state changes.
486 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
487 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
488
489 synchronized(mToneGeneratorLock) {
490 if (mToneGenerator != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800491 mToneGenerator.release();
492 mToneGenerator = null;
493 }
494 }
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800495 // TODO: I wonder if we should not check if the AsyncTask that
496 // lookup the last dialed number has completed.
497 mLastNumberDialed = EMPTY_NUMBER; // Since we are going to query again, free stale number.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800498 }
499
500 @Override
501 public boolean onCreateOptionsMenu(Menu menu) {
Reli Talc2a2a512009-06-10 16:48:00 -0400502 mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800503 .setIcon(android.R.drawable.ic_menu_add);
Reli Talc2a2a512009-06-10 16:48:00 -0400504 m2SecPauseMenuItem = menu.add(0, MENU_2S_PAUSE, 0, R.string.add_2sec_pause)
505 .setIcon(R.drawable.ic_menu_2sec_pause);
506 mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
507 .setIcon(R.drawable.ic_menu_wait);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800508 return true;
509 }
510
511 @Override
512 public boolean onPrepareOptionsMenu(Menu menu) {
513 // We never show a menu if the "choose dialpad" UI is up.
514 if (dialpadChooserVisible()) {
515 return false;
516 }
517
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800518 if (isDigitsEmpty()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800519 mAddToContactMenuItem.setVisible(false);
Reli Talc2a2a512009-06-10 16:48:00 -0400520 m2SecPauseMenuItem.setVisible(false);
521 mWaitMenuItem.setVisible(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800522 } else {
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800523 CharSequence digits = mDigits.getText();
524
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800525 // Put the current digits string into an intent
526 Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800527 intent.putExtra(Insert.PHONE, digits);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800528 intent.setType(People.CONTENT_ITEM_TYPE);
529 mAddToContactMenuItem.setIntent(intent);
530 mAddToContactMenuItem.setVisible(true);
Reli Talc2a2a512009-06-10 16:48:00 -0400531
532 // Check out whether to show Pause & Wait option menu items
533 int selectionStart;
534 int selectionEnd;
535 String strDigits = digits.toString();
536
537 selectionStart = mDigits.getSelectionStart();
538 selectionEnd = mDigits.getSelectionEnd();
539
540 if (selectionStart != -1) {
541 if (selectionStart > selectionEnd) {
542 // swap it as we want start to be less then end
543 int tmp = selectionStart;
544 selectionStart = selectionEnd;
545 selectionEnd = tmp;
546 }
547
548 if (selectionStart != 0) {
549 // Pause can be visible if cursor is not in the begining
550 m2SecPauseMenuItem.setVisible(true);
551
552 // For Wait to be visible set of condition to meet
553 mWaitMenuItem.setVisible(showWait(selectionStart,
554 selectionEnd, strDigits));
555 } else {
556 // cursor in the beginning both pause and wait to be invisible
557 m2SecPauseMenuItem.setVisible(false);
558 mWaitMenuItem.setVisible(false);
559 }
560 } else {
561 // cursor is not selected so assume new digit is added to the end
562 int strLength = strDigits.length();
563 mWaitMenuItem.setVisible(showWait(strLength,
564 strLength, strDigits));
565 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800566 }
567 return true;
568 }
569
570 @Override
571 public boolean onKeyDown(int keyCode, KeyEvent event) {
572 switch (keyCode) {
573 case KeyEvent.KEYCODE_CALL: {
574 long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
575 if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
576 // Launch voice dialer
577 Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
578 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
579 try {
580 startActivity(intent);
581 } catch (ActivityNotFoundException e) {
582 }
583 }
584 return true;
585 }
586 case KeyEvent.KEYCODE_1: {
Eric Laurentd9efc872009-07-17 11:52:06 -0700587 long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800588 if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
589 // Long press detected, call voice mail
590 callVoicemail();
591 }
592 return true;
593 }
594 }
595 return super.onKeyDown(keyCode, event);
596 }
597
598 @Override
599 public boolean onKeyUp(int keyCode, KeyEvent event) {
600 switch (keyCode) {
601 case KeyEvent.KEYCODE_CALL: {
Nicolas Catania998763d2010-01-14 14:03:53 -0800602 // TODO: In dialButtonPressed we do some of these
603 // tests again. We should try to consolidate them in
604 // one place.
605 if (!phoneIsCdma() && mIsAddCallMode && isDigitsEmpty()) {
606 // For CDMA phones, we always call
607 // dialButtonPressed() because we may need to send
608 // an empty flash command to the network.
609 // Otherwise, if we are adding a call from the
610 // InCallScreen and the phone number entered is
611 // empty, we just close the dialer to expose the
612 // InCallScreen under it.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800613 finish();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800614 }
Nicolas Catania998763d2010-01-14 14:03:53 -0800615
616 // If we're CDMA, regardless of where we are adding a call from (either
617 // InCallScreen or Dialtacts), the user may need to send an empty
618 // flash command to the network. So let's call dialButtonPressed() regardless
619 // and dialButtonPressed will handle this functionality for us.
620 // otherwise, we place the call.
621 dialButtonPressed();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800622 return true;
623 }
624 }
625 return super.onKeyUp(keyCode, event);
626 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700627
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800628 private void keyPressed(int keyCode) {
Nicolas Catania905e7622009-12-01 08:51:20 -0800629 mHaptic.vibrate();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800630 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
631 mDigits.onKeyDown(keyCode, event);
632 }
633
634 public boolean onKey(View view, int keyCode, KeyEvent event) {
635 switch (view.getId()) {
636 case R.id.digits:
637 if (keyCode == KeyEvent.KEYCODE_ENTER) {
Nicolas Catania998763d2010-01-14 14:03:53 -0800638 dialButtonPressed();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800639 return true;
640 }
641 break;
642 }
643 return false;
644 }
645
646 public void onClick(View view) {
647 switch (view.getId()) {
648 case R.id.one: {
649 playTone(ToneGenerator.TONE_DTMF_1);
650 keyPressed(KeyEvent.KEYCODE_1);
651 return;
652 }
653 case R.id.two: {
654 playTone(ToneGenerator.TONE_DTMF_2);
655 keyPressed(KeyEvent.KEYCODE_2);
656 return;
657 }
658 case R.id.three: {
659 playTone(ToneGenerator.TONE_DTMF_3);
660 keyPressed(KeyEvent.KEYCODE_3);
661 return;
662 }
663 case R.id.four: {
664 playTone(ToneGenerator.TONE_DTMF_4);
665 keyPressed(KeyEvent.KEYCODE_4);
666 return;
667 }
668 case R.id.five: {
669 playTone(ToneGenerator.TONE_DTMF_5);
670 keyPressed(KeyEvent.KEYCODE_5);
671 return;
672 }
673 case R.id.six: {
674 playTone(ToneGenerator.TONE_DTMF_6);
675 keyPressed(KeyEvent.KEYCODE_6);
676 return;
677 }
678 case R.id.seven: {
679 playTone(ToneGenerator.TONE_DTMF_7);
680 keyPressed(KeyEvent.KEYCODE_7);
681 return;
682 }
683 case R.id.eight: {
684 playTone(ToneGenerator.TONE_DTMF_8);
685 keyPressed(KeyEvent.KEYCODE_8);
686 return;
687 }
688 case R.id.nine: {
689 playTone(ToneGenerator.TONE_DTMF_9);
690 keyPressed(KeyEvent.KEYCODE_9);
691 return;
692 }
693 case R.id.zero: {
694 playTone(ToneGenerator.TONE_DTMF_0);
695 keyPressed(KeyEvent.KEYCODE_0);
696 return;
697 }
698 case R.id.pound: {
699 playTone(ToneGenerator.TONE_DTMF_P);
700 keyPressed(KeyEvent.KEYCODE_POUND);
701 return;
702 }
703 case R.id.star: {
704 playTone(ToneGenerator.TONE_DTMF_S);
705 keyPressed(KeyEvent.KEYCODE_STAR);
706 return;
707 }
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700708 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800709 keyPressed(KeyEvent.KEYCODE_DEL);
710 return;
711 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700712 case R.id.dialButton: {
Nicolas Catania905e7622009-12-01 08:51:20 -0800713 mHaptic.vibrate(); // Vibrate here too, just like we do for the regular keys
Nicolas Catania998763d2010-01-14 14:03:53 -0800714 dialButtonPressed();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800715 return;
716 }
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700717 case R.id.voicemailButton: {
718 callVoicemail();
Nicolas Catania905e7622009-12-01 08:51:20 -0800719 mHaptic.vibrate();
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700720 return;
721 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700722 case R.id.digits: {
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800723 if (!isDigitsEmpty()) {
Nicolas Catania3040fa32009-10-01 13:00:53 -0700724 mDigits.setCursorVisible(true);
725 }
726 return;
727 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800728 }
729 }
730
731 public boolean onLongClick(View view) {
732 final Editable digits = mDigits.getText();
733 int id = view.getId();
734 switch (id) {
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700735 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800736 digits.clear();
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700737 // TODO: The framework forgets to clear the pressed
738 // status of disabled button. Until this is fixed,
739 // clear manually the pressed status. b/2133127
740 mDelete.setPressed(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800741 return true;
742 }
743 case R.id.one: {
Nicolas Cataniabe8821e2010-01-15 09:28:13 -0800744 if (isDigitsEmpty()) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800745 callVoicemail();
746 return true;
747 }
748 return false;
749 }
750 case R.id.zero: {
751 keyPressed(KeyEvent.KEYCODE_PLUS);
752 return true;
753 }
754 }
755 return false;
756 }
757
758 void callVoicemail() {
759 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800760 Uri.fromParts("voicemail", EMPTY_NUMBER, null));
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800761 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
762 startActivity(intent);
763 mDigits.getText().clear();
764 finish();
765 }
766
Nicolas Catania998763d2010-01-14 14:03:53 -0800767 void dialButtonPressed() {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800768 final String number = mDigits.getText().toString();
Paul Bermandbdcde22009-10-09 12:04:10 -0400769 boolean sendEmptyFlash = false;
Nicolas Catania998763d2010-01-14 14:03:53 -0800770 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED);
David Browndc1dfe22010-03-01 14:34:57 -0800771
Nicolas Catania998763d2010-01-14 14:03:53 -0800772 if (isDigitsEmpty()) { // There is no number entered.
Paul Bermandbdcde22009-10-09 12:04:10 -0400773 if (phoneIsCdma() && phoneIsOffhook()) {
David Browndc1dfe22010-03-01 14:34:57 -0800774 // On CDMA phones, if we're already on a call, pressing
775 // the Dial button without entering any digits means "send
776 // an empty flash."
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800777 intent.setData(Uri.fromParts("tel", EMPTY_NUMBER, null));
Paul Bermandbdcde22009-10-09 12:04:10 -0400778 intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
779 sendEmptyFlash = true;
David Browndc1dfe22010-03-01 14:34:57 -0800780 } else if (!TextUtils.isEmpty(mLastNumberDialed)) {
781 // Otherwise, pressing the Dial button without entering
782 // any digits means "recall the last number dialed".
Nicolas Cataniac3be69e2010-01-14 14:03:53 -0800783 mDigits.setText(mLastNumberDialed);
Nicolas Catania998763d2010-01-14 14:03:53 -0800784 return;
Paul Bermandbdcde22009-10-09 12:04:10 -0400785 } else {
David Browndc1dfe22010-03-01 14:34:57 -0800786 // Rare case: there's no "last number dialed". There's
787 // nothing useful for the Dial button to do in this case.
Paul Bermandbdcde22009-10-09 12:04:10 -0400788 playTone(ToneGenerator.TONE_PROP_NACK);
789 return;
790 }
Nicolas Catania998763d2010-01-14 14:03:53 -0800791 } else { // There is a number.
792 intent.setData(Uri.fromParts("tel", number, null));
Paul Bermandbdcde22009-10-09 12:04:10 -0400793 }
Nicolas Catania998763d2010-01-14 14:03:53 -0800794
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800795 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
796 startActivity(intent);
797 mDigits.getText().clear();
David Browndc1dfe22010-03-01 14:34:57 -0800798
Paul Bermandbdcde22009-10-09 12:04:10 -0400799 // Don't finish TwelveKeyDialer yet if we're sending a blank flash for CDMA. CDMA
800 // networks use Flash messages when special processing needs to be done, mainly for
801 // 3-way or call waiting scenarios. Presumably, here we're in a special 3-way scenario
802 // where the network needs a blank flash before being able to add the new participant.
803 // (This is not the case with all 3-way calls, just certain CDMA infrastructures.)
804 if (!sendEmptyFlash) {
805 finish();
806 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800807 }
808
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800809
810 /**
David Brown22f615f2009-06-25 16:19:19 -0700811 * Plays the specified tone for TONE_LENGTH_MS milliseconds.
812 *
813 * The tone is played locally, using the audio stream for phone calls.
814 * Tones are played only if the "Audible touch tones" user preference
815 * is checked, and are NOT played if the device is in silent mode.
816 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800817 * @param tone a tone code from {@link ToneGenerator}
818 */
819 void playTone(int tone) {
820 // if local tone playback is disabled, just return.
821 if (!mDTMFToneEnabled) {
822 return;
823 }
David Brown22f615f2009-06-25 16:19:19 -0700824
825 // Also do nothing if the phone is in silent mode.
826 // We need to re-check the ringer mode for *every* playTone()
827 // call, rather than keeping a local flag that's updated in
828 // onResume(), since it's possible to toggle silent mode without
829 // leaving the current activity (via the ENDCALL-longpress menu.)
830 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
David Brownd5a15302009-07-20 16:39:47 -0700831 int ringerMode = audioManager.getRingerMode();
832 if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
833 || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
David Brown22f615f2009-06-25 16:19:19 -0700834 return;
835 }
836
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800837 synchronized(mToneGeneratorLock) {
838 if (mToneGenerator == null) {
839 Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
840 return;
841 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700842
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800843 // Start the new tone (will stop any playing tone)
Eric Laurent8487fed2009-09-07 08:45:14 -0700844 mToneGenerator.startTone(tone, TONE_LENGTH_MS);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800845 }
846 }
847
848 /**
849 * Brings up the "dialpad chooser" UI in place of the usual Dialer
850 * elements (the textfield/button and the dialpad underneath).
851 *
852 * We show this UI if the user brings up the Dialer while a call is
853 * already in progress, since there's a good chance we got here
854 * accidentally (and the user really wanted the in-call dialpad instead).
855 * So in this situation we display an intermediate UI that lets the user
856 * explicitly choose between the in-call dialpad ("Use touch tone
857 * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
858 * to call in progress" just goes back to the in-call UI with no dialpad
859 * at all.)
860 *
861 * @param enabled If true, show the "dialpad chooser" instead
862 * of the regular Dialer UI
863 */
864 private void showDialpadChooser(boolean enabled) {
865 if (enabled) {
866 // Log.i(TAG, "Showing dialpad chooser!");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700867 mDigits.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800868 if (mDialpad != null) mDialpad.setVisibility(View.GONE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700869 mVoicemailDialAndDeleteRow.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800870 mDialpadChooser.setVisibility(View.VISIBLE);
871
872 // Instantiate the DialpadChooserAdapter and hook it up to the
873 // ListView. We do this only once.
874 if (mDialpadChooserAdapter == null) {
875 mDialpadChooserAdapter = new DialpadChooserAdapter(this);
876 mDialpadChooser.setAdapter(mDialpadChooserAdapter);
877 }
878 } else {
879 // Log.i(TAG, "Displaying normal Dialer UI.");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700880 mDigits.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800881 if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700882 mVoicemailDialAndDeleteRow.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800883 mDialpadChooser.setVisibility(View.GONE);
884 }
885 }
886
887 /**
888 * @return true if we're currently showing the "dialpad chooser" UI.
889 */
890 private boolean dialpadChooserVisible() {
891 return mDialpadChooser.getVisibility() == View.VISIBLE;
892 }
893
894 /**
895 * Simple list adapter, binding to an icon + text label
896 * for each item in the "dialpad chooser" list.
897 */
898 private static class DialpadChooserAdapter extends BaseAdapter {
899 private LayoutInflater mInflater;
900
901 // Simple struct for a single "choice" item.
902 static class ChoiceItem {
903 String text;
904 Bitmap icon;
905 int id;
906
907 public ChoiceItem(String s, Bitmap b, int i) {
908 text = s;
909 icon = b;
910 id = i;
911 }
912 }
913
914 // IDs for the possible "choices":
915 static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
916 static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
917 static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
918
919 private static final int NUM_ITEMS = 3;
920 private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
921
922 public DialpadChooserAdapter(Context context) {
923 // Cache the LayoutInflate to avoid asking for a new one each time.
924 mInflater = LayoutInflater.from(context);
925
926 // Initialize the possible choices.
927 // TODO: could this be specified entirely in XML?
928
929 // - "Use touch tone keypad"
930 mChoiceItems[0] = new ChoiceItem(
931 context.getString(R.string.dialer_useDtmfDialpad),
932 BitmapFactory.decodeResource(context.getResources(),
933 R.drawable.ic_dialer_fork_tt_keypad),
934 DIALPAD_CHOICE_USE_DTMF_DIALPAD);
935
936 // - "Return to call in progress"
937 mChoiceItems[1] = new ChoiceItem(
938 context.getString(R.string.dialer_returnToInCallScreen),
939 BitmapFactory.decodeResource(context.getResources(),
940 R.drawable.ic_dialer_fork_current_call),
941 DIALPAD_CHOICE_RETURN_TO_CALL);
942
943 // - "Add call"
944 mChoiceItems[2] = new ChoiceItem(
945 context.getString(R.string.dialer_addAnotherCall),
946 BitmapFactory.decodeResource(context.getResources(),
947 R.drawable.ic_dialer_fork_add_call),
948 DIALPAD_CHOICE_ADD_NEW_CALL);
949 }
950
951 public int getCount() {
952 return NUM_ITEMS;
953 }
954
955 /**
956 * Return the ChoiceItem for a given position.
957 */
958 public Object getItem(int position) {
959 return mChoiceItems[position];
960 }
961
962 /**
963 * Return a unique ID for each possible choice.
964 */
965 public long getItemId(int position) {
966 return position;
967 }
968
969 /**
970 * Make a view for each row.
971 */
972 public View getView(int position, View convertView, ViewGroup parent) {
973 // When convertView is non-null, we can reuse it (there's no need
974 // to reinflate it.)
975 if (convertView == null) {
976 convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
977 }
978
979 TextView text = (TextView) convertView.findViewById(R.id.text);
980 text.setText(mChoiceItems[position].text);
981
982 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
983 icon.setImageBitmap(mChoiceItems[position].icon);
984
985 return convertView;
986 }
987 }
988
989 /**
990 * Handle clicks from the dialpad chooser.
991 */
992 public void onItemClick(AdapterView parent, View v, int position, long id) {
993 DialpadChooserAdapter.ChoiceItem item =
994 (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
995 int itemId = item.id;
996 switch (itemId) {
997 case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
998 // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
999 // Fire off an intent to go back to the in-call UI
1000 // with the dialpad visible.
1001 returnToInCallScreen(true);
1002 break;
1003
1004 case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
1005 // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
1006 // Fire off an intent to go back to the in-call UI
1007 // (with the dialpad hidden).
1008 returnToInCallScreen(false);
1009 break;
1010
1011 case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
1012 // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
1013 // Ok, guess the user really did want to be here (in the
1014 // regular Dialer) after all. Bring back the normal Dialer UI.
1015 showDialpadChooser(false);
1016 break;
1017
1018 default:
1019 Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
1020 break;
1021 }
1022 }
1023
1024 /**
1025 * Returns to the in-call UI (where there's presumably a call in
1026 * progress) in response to the user selecting "use touch tone keypad"
1027 * or "return to call" from the dialpad chooser.
1028 */
1029 private void returnToInCallScreen(boolean showDialpad) {
1030 try {
1031 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1032 if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
1033 } catch (RemoteException e) {
1034 Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
1035 }
1036
1037 // Finally, finish() ourselves so that we don't stay on the
1038 // activity stack.
1039 // Note that we do this whether or not the showCallScreenWithDialpad()
1040 // call above had any effect or not! (That call is a no-op if the
1041 // phone is idle, which can happen if the current call ends while
1042 // the dialpad chooser is up. In this case we can't show the
1043 // InCallScreen, and there's no point staying here in the Dialer,
1044 // so we just take the user back where he came from...)
1045 finish();
1046 }
1047
1048 /**
1049 * @return true if the phone is "in use", meaning that at least one line
1050 * is active (ie. off hook or ringing or dialing).
1051 */
1052 private boolean phoneIsInUse() {
1053 boolean phoneInUse = false;
1054 try {
1055 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1056 if (phone != null) phoneInUse = !phone.isIdle();
1057 } catch (RemoteException e) {
1058 Log.w(TAG, "phone.isIdle() failed", e);
1059 }
1060 return phoneInUse;
1061 }
David Brownc29c7ab2009-07-07 16:00:18 -07001062
1063 /**
Paul Bermandbdcde22009-10-09 12:04:10 -04001064 * @return true if the phone is a CDMA phone type
1065 */
1066 private boolean phoneIsCdma() {
1067 boolean isCdma = false;
1068 try {
1069 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1070 if (phone != null) {
1071 isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA);
1072 }
1073 } catch (RemoteException e) {
1074 Log.w(TAG, "phone.getActivePhoneType() failed", e);
1075 }
1076 return isCdma;
1077 }
1078
1079 /**
1080 * @return true if the phone state is OFFHOOK
1081 */
1082 private boolean phoneIsOffhook() {
1083 boolean phoneOffhook = false;
1084 try {
1085 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1086 if (phone != null) phoneOffhook = phone.isOffhook();
1087 } catch (RemoteException e) {
1088 Log.w(TAG, "phone.isOffhook() failed", e);
1089 }
1090 return phoneOffhook;
1091 }
1092
Reli Talc2a2a512009-06-10 16:48:00 -04001093
1094 /**
1095 * Returns true whenever any one of the options from the menu is selected.
1096 * Code changes to support dialpad options
1097 */
1098 @Override
1099 public boolean onOptionsItemSelected(MenuItem item) {
1100 switch (item.getItemId()) {
1101 case MENU_2S_PAUSE:
1102 updateDialString(",");
1103 return true;
1104 case MENU_WAIT:
1105 updateDialString(";");
1106 return true;
1107 }
1108 return false;
1109 }
1110
1111 /**
1112 * Updates the dial string (mDigits) after inserting a Pause character (,)
1113 * or Wait character (;).
1114 */
1115 private void updateDialString(String newDigits) {
1116 int selectionStart;
1117 int selectionEnd;
1118
1119 // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
Eric Fischer686782e2009-09-10 17:57:45 -07001120 int anchor = mDigits.getSelectionStart();
1121 int point = mDigits.getSelectionEnd();
1122
1123 selectionStart = Math.min(anchor, point);
1124 selectionEnd = Math.max(anchor, point);
Reli Talc2a2a512009-06-10 16:48:00 -04001125
1126 Editable digits = mDigits.getText();
1127 if (selectionStart != -1 ) {
1128 if (selectionStart == selectionEnd) {
1129 // then there is no selection. So insert the pause at this
1130 // position and update the mDigits.
1131 digits.replace(selectionStart, selectionStart, newDigits);
1132 } else {
Eric Fischer1e2d3a22009-09-17 10:53:10 -07001133 digits.replace(selectionStart, selectionEnd, newDigits);
Nicolas Catania7edbd0c2009-09-28 20:37:33 -07001134 // Unselect: back to a regular cursor, just pass the character inserted.
1135 mDigits.setSelection(selectionStart + 1);
Reli Talc2a2a512009-06-10 16:48:00 -04001136 }
1137 } else {
1138 int len = mDigits.length();
1139 digits.replace(len, len, newDigits);
1140 }
1141 }
1142
1143 /**
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001144 * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001145 */
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001146 private void updateDialAndDeleteButtonEnabledState() {
1147 final boolean digitsNotEmpty = !isDigitsEmpty();
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001148
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001149 if (mDialButton != null) {
David Browndc1dfe22010-03-01 14:34:57 -08001150 // On CDMA phones, if we're already on a call, we *always*
1151 // enable the Dial button (since you can press it without
1152 // entering any digits to send an empty flash.)
1153 if (phoneIsCdma() && phoneIsOffhook()) {
1154 mDialButton.setEnabled(true);
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001155 } else {
David Browndc1dfe22010-03-01 14:34:57 -08001156 // Common case: GSM, or CDMA but not on a call.
1157 // Enable the Dial button if some digits have
1158 // been entered, or if there is a last dialed number
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001159 // that could be redialed.
1160 mDialButton.setEnabled(digitsNotEmpty ||
1161 !TextUtils.isEmpty(mLastNumberDialed));
Paul Bermandbdcde22009-10-09 12:04:10 -04001162 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001163 }
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001164 mDelete.setEnabled(digitsNotEmpty);
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001165 }
1166
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001167
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001168 /**
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001169 * Check if voicemail is enabled/accessible.
1170 */
1171 private void initVoicemailButton() {
1172 boolean hasVoicemail = false;
1173 try {
1174 hasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
1175 } catch (SecurityException se) {
1176 // Possibly no READ_PHONE_STATE privilege.
1177 }
1178
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001179 mVoicemailButton = mVoicemailDialAndDeleteRow.findViewById(R.id.voicemailButton);
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001180 if (hasVoicemail) {
1181 mVoicemailButton.setOnClickListener(this);
1182 } else {
1183 mVoicemailButton.setEnabled(false);
1184 }
1185 }
1186
1187 /**
Reli Talc2a2a512009-06-10 16:48:00 -04001188 * This function return true if Wait menu item can be shown
1189 * otherwise returns false. Assumes the passed string is non-empty
1190 * and the 0th index check is not required.
1191 */
1192 private boolean showWait(int start, int end, String digits) {
1193 if (start == end) {
1194 // visible false in this case
1195 if (start > digits.length()) return false;
1196
1197 // preceding char is ';', so visible should be false
1198 if (digits.charAt(start-1) == ';') return false;
1199
1200 // next char is ';', so visible should be false
1201 if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
1202 } else {
1203 // visible false in this case
1204 if (start > digits.length() || end > digits.length()) return false;
1205
1206 // In this case we need to just check for ';' preceding to start
1207 // or next to end
1208 if (digits.charAt(start-1) == ';') return false;
1209 }
1210 return true;
1211 }
Nicolas Cataniabe8821e2010-01-15 09:28:13 -08001212
1213 /**
1214 * @return true if the widget with the phone number digits is empty.
1215 */
1216 private boolean isDigitsEmpty() {
Nicolas Catania941b76f2010-01-19 14:09:40 -08001217 return mDigits.length() == 0;
Nicolas Cataniabe8821e2010-01-15 09:28:13 -08001218 }
Nicolas Cataniac3be69e2010-01-14 14:03:53 -08001219
1220 /**
1221 * Starts the asyn query to get the last dialed/outgoing
1222 * number. When the background query finishes, mLastNumberDialed
1223 * is set to the last dialed number or an empty string if none
1224 * exists yet.
1225 */
1226 private void queryLastOutgoingCall() {
1227 mLastNumberDialed = EMPTY_NUMBER;
1228 CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
1229 new CallLogAsync.GetLastOutgoingCallArgs(
1230 this,
1231 new CallLogAsync.OnLastOutgoingCallComplete() {
1232 public void lastOutgoingCall(String number) {
1233 // TODO: Filter out emergency numbers if
1234 // the carrier does not want redial for
1235 // these.
1236 mLastNumberDialed = number;
1237 updateDialAndDeleteButtonEnabledState();
1238 }
1239 });
1240 mCallLog.getLastOutgoingCall(lastCallArgs);
1241 }
Dmitri Plotnikov8e86b752010-02-22 17:47:57 -08001242
1243 @Override
1244 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
1245 boolean globalSearch) {
1246 if (globalSearch) {
1247 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1248 } else {
1249 ContactsSearchManager.startSearch(this, initialQuery);
1250 }
1251 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001252}