blob: d28f01de48168f08c3309d5f424c4b900e64d103 [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 Catania905e7622009-12-01 08:51:20 -080068import com.android.phone.HapticFeedback;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080069
70/**
71 * Dialer activity that displays the typical twelve key interface.
72 */
73public class TwelveKeyDialer extends Activity implements View.OnClickListener,
74 View.OnLongClickListener, View.OnKeyListener,
75 AdapterView.OnItemClickListener, TextWatcher {
76
77 private static final String TAG = "TwelveKeyDialer";
Eric Laurentd9efc872009-07-17 11:52:06 -070078
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080079 /** The length of DTMF tones in milliseconds */
80 private static final int TONE_LENGTH_MS = 150;
Eric Laurentd9efc872009-07-17 11:52:06 -070081
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080082 /** The DTMF tone volume relative to other sounds in the stream */
Jean-Michel Trividd44f8c2009-11-10 13:00:45 -080083 private static final int TONE_RELATIVE_VOLUME = 80;
84
85 /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
86 private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_MUSIC;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080087
88 private EditText mDigits;
89 private View mDelete;
90 private MenuItem mAddToContactMenuItem;
91 private ToneGenerator mToneGenerator;
92 private Object mToneGeneratorLock = new Object();
93 private Drawable mDigitsBackground;
94 private Drawable mDigitsEmptyBackground;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080095 private View mDialpad;
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -070096 private View mVoicemailDialAndDeleteRow;
Nicolas Catania80bda0f2009-09-19 09:17:14 -070097 private View mVoicemailButton;
98 private View mDialButton;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080099 private ListView mDialpadChooser;
100 private DialpadChooserAdapter mDialpadChooserAdapter;
Reli Talc2a2a512009-06-10 16:48:00 -0400101 //Member variables for dialpad options
102 private MenuItem m2SecPauseMenuItem;
103 private MenuItem mWaitMenuItem;
104 private static final int MENU_ADD_CONTACTS = 1;
105 private static final int MENU_2S_PAUSE = 2;
106 private static final int MENU_WAIT = 3;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800107
108 // determines if we want to playback local DTMF tones.
109 private boolean mDTMFToneEnabled;
David Brownc29c7ab2009-07-07 16:00:18 -0700110
111 // Vibration (haptic feedback) for dialer key presses.
Nicolas Catania905e7622009-12-01 08:51:20 -0800112 private HapticFeedback mHaptic = new HapticFeedback();
Eric Laurentd9efc872009-07-17 11:52:06 -0700113
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800114 /** Identifier for the "Add Call" intent extra. */
115 static final String ADD_CALL_MODE_KEY = "add_call_mode";
Paul Bermandbdcde22009-10-09 12:04:10 -0400116
117 /**
118 * Identifier for intent extra for sending an empty Flash message for
119 * CDMA networks. This message is used by the network to simulate a
120 * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
121 *
122 * TODO: Using an intent extra to tell the phone to send this flash is a
123 * temporary measure. To be replaced with an ITelephony call in the future.
124 * TODO: Keep in sync with the string defined in OutgoingCallBroadcaster.java
125 * in Phone app until this is replaced with the ITelephony API.
126 */
127 static final String EXTRA_SEND_EMPTY_FLASH
128 = "com.android.phone.extra.SEND_EMPTY_FLASH";
129
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800130 /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
131 private boolean mIsAddCallMode;
132
133 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
134 /**
135 * Listen for phone state changes so that we can take down the
136 * "dialpad chooser" if the phone becomes idle while the
137 * chooser UI is visible.
138 */
139 @Override
140 public void onCallStateChanged(int state, String incomingNumber) {
141 // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
142 // + state + ", '" + incomingNumber + "'");
143 if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
144 // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
145 // Note there's a race condition in the UI here: the
146 // dialpad chooser could conceivably disappear (on its
147 // own) at the exact moment the user was trying to select
148 // one of the choices, which would be confusing. (But at
149 // least that's better than leaving the dialpad chooser
150 // onscreen, but useless...)
151 showDialpadChooser(false);
152 }
153 }
154 };
155
156 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
157 // Do nothing
158 }
159
160 public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
161 // Do nothing
Eric Laurentd9efc872009-07-17 11:52:06 -0700162 // DTMF Tones do not need to be played here any longer -
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800163 // the DTMF dialer handles that functionality now.
164 }
165
166 public void afterTextChanged(Editable input) {
167 if (SpecialCharSequenceMgr.handleChars(this, input.toString(), mDigits)) {
168 // A special sequence was entered, clear the digits
169 mDigits.getText().clear();
170 }
171
Nicolas Catania75993762009-09-21 16:42:00 -0700172 final boolean notEmpty = mDigits.length() != 0;
173 if (notEmpty) {
174 mDigits.setBackgroundDrawable(mDigitsBackground);
175 } else {
Nicolas Catania3040fa32009-10-01 13:00:53 -0700176 mDigits.setCursorVisible(false);
Nicolas Catania75993762009-09-21 16:42:00 -0700177 mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
178 }
179
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700180 updateDialAndDeleteButtonStateEnabledAttr();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800181 }
182
183 @Override
184 protected void onCreate(Bundle icicle) {
185 super.onCreate(icicle);
186
187 // Set the content view
188 setContentView(getContentViewResource());
189
Nicolas Catania75993762009-09-21 16:42:00 -0700190 // Load up the resources for the text field.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800191 Resources r = getResources();
192 mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800193 mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800194
195 mDigits = (EditText) findViewById(R.id.digits);
196 mDigits.setKeyListener(DialerKeyListener.getInstance());
197 mDigits.setOnClickListener(this);
198 mDigits.setOnKeyListener(this);
Nicolas Catania3040fa32009-10-01 13:00:53 -0700199
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800200 maybeAddNumberFormatting();
201
202 // Check for the presence of the keypad
203 View view = findViewById(R.id.one);
204 if (view != null) {
205 setupKeypad();
206 }
207
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700208 mVoicemailDialAndDeleteRow = findViewById(R.id.voicemailAndDialAndDelete);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700209
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700210 initVoicemailButton();
211
David Brown3d07e6d2009-08-04 20:30:09 -0700212 // Check whether we should show the onscreen "Dial" button.
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700213 mDialButton = mVoicemailDialAndDeleteRow.findViewById(R.id.dialButton);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700214
David Brown3d07e6d2009-08-04 20:30:09 -0700215 if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
David Brown3d07e6d2009-08-04 20:30:09 -0700216 mDialButton.setOnClickListener(this);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700217 } else {
218 mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
219 mDialButton = null;
David Brown3d07e6d2009-08-04 20:30:09 -0700220 }
221
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700222 view = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800223 view.setOnClickListener(this);
224 view.setOnLongClickListener(this);
225 mDelete = view;
226
Nicolas Catania901f8562009-10-09 11:09:45 -0700227 mDialpad = findViewById(R.id.dialpad); // This is null in landscape mode.
228
229 // In landscape we put the keyboard in phone mode.
230 // In portrait we prevent the soft keyboard to show since the
231 // dialpad acts as one already.
232 if (null == mDialpad) {
233 mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
234 } else {
235 mDigits.setInputType(android.text.InputType.TYPE_NULL);
236 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800237
238 // Set up the "dialpad chooser" UI; see showDialpadChooser().
239 mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
240 mDialpadChooser.setOnItemClickListener(this);
241
242 if (!resolveIntent() && icicle != null) {
243 super.onRestoreInstanceState(icicle);
244 }
245
Nicolas Catania905e7622009-12-01 08:51:20 -0800246 try {
247 mHaptic.init(this, r.getBoolean(R.bool.config_enable_dialer_key_vibration));
248 } catch (Resources.NotFoundException nfe) {
249 Log.e(TAG, "Vibrate control bool missing.", nfe);
250 }
251
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800252 }
253
254 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800255 protected void onRestoreInstanceState(Bundle icicle) {
256 // Do nothing, state is restored in onCreate() if needed
257 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700258
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800259 protected void maybeAddNumberFormatting() {
260 mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
261 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700262
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800263 /**
Eric Laurentd9efc872009-07-17 11:52:06 -0700264 * Overridden by subclasses to control the resource used by the content view.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800265 */
266 protected int getContentViewResource() {
267 return R.layout.twelve_key_dialer;
268 }
269
270 private boolean resolveIntent() {
271 boolean ignoreState = false;
272
273 // Find the proper intent
274 final Intent intent;
275 if (isChild()) {
276 intent = getParent().getIntent();
277 ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
278 } else {
279 intent = getIntent();
280 }
281 // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
282
283 // by default we are not adding a call.
284 mIsAddCallMode = false;
285
286 // By default we don't show the "dialpad chooser" UI.
287 boolean needToShowDialpadChooser = false;
288
289 // Resolve the intent
290 final String action = intent.getAction();
291 if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
292 // see if we are "adding a call" from the InCallScreen; false by default.
293 mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
294 Uri uri = intent.getData();
295 if (uri != null) {
296 if ("tel".equals(uri.getScheme())) {
297 // Put the requested number into the input area
Nicolas Catania43094f62009-12-08 14:09:46 -0800298 String data = uri.toString().substring("tel:".length());
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800299 setFormattedDigits(data);
300 } else {
301 String type = intent.getType();
302 if (People.CONTENT_ITEM_TYPE.equals(type)
303 || Phones.CONTENT_ITEM_TYPE.equals(type)) {
304 // Query the phone number
305 Cursor c = getContentResolver().query(intent.getData(),
306 new String[] {PhonesColumns.NUMBER}, null, null, null);
307 if (c != null) {
308 if (c.moveToFirst()) {
309 // Put the number into the input area
310 setFormattedDigits(c.getString(0));
311 }
312 c.close();
313 }
314 }
315 }
316 }
317 } else if (Intent.ACTION_MAIN.equals(action)) {
318 // The MAIN action means we're bringing up a blank dialer
319 // (e.g. by selecting the Home shortcut, or tabbing over from
320 // Contacts or Call log.)
321 //
322 // At this point, IF there's already an active call, there's a
323 // good chance that the user got here accidentally (but really
324 // wanted the in-call dialpad instead). So we bring up an
325 // intermediate UI to make the user confirm what they really
326 // want to do.
327 if (phoneIsInUse()) {
328 // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
329 needToShowDialpadChooser = true;
330 }
331 }
332
333 // Bring up the "dialpad chooser" IFF we need to make the user
334 // confirm which dialpad they really want.
335 showDialpadChooser(needToShowDialpadChooser);
336
337 return ignoreState;
338 }
339
340 protected void setFormattedDigits(String data) {
341 // strip the non-dialable numbers out of the data string.
342 String dialString = PhoneNumberUtils.extractNetworkPortion(data);
343 dialString = PhoneNumberUtils.formatNumber(dialString);
344 if (!TextUtils.isEmpty(dialString)) {
345 Editable digits = mDigits.getText();
346 digits.replace(0, digits.length(), dialString);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700347 // for some reason this isn't getting called in the digits.replace call above..
348 // but in any case, this will make sure the background drawable looks right
349 afterTextChanged(digits);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800350 }
351 }
352
353 @Override
354 protected void onNewIntent(Intent newIntent) {
355 setIntent(newIntent);
356 resolveIntent();
357 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700358
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800359 @Override
360 protected void onPostCreate(Bundle savedInstanceState) {
361 super.onPostCreate(savedInstanceState);
362
363 // This can't be done in onCreate(), since the auto-restoring of the digits
364 // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
365 // is called. This method will be called every time the activity is created, and
366 // will always happen after onRestoreSavedInstanceState().
367 mDigits.addTextChangedListener(this);
368 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700369
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800370 private void setupKeypad() {
371 // Setup the listeners for the buttons
372 View view = findViewById(R.id.one);
373 view.setOnClickListener(this);
374 view.setOnLongClickListener(this);
375
376 findViewById(R.id.two).setOnClickListener(this);
377 findViewById(R.id.three).setOnClickListener(this);
378 findViewById(R.id.four).setOnClickListener(this);
379 findViewById(R.id.five).setOnClickListener(this);
380 findViewById(R.id.six).setOnClickListener(this);
381 findViewById(R.id.seven).setOnClickListener(this);
382 findViewById(R.id.eight).setOnClickListener(this);
383 findViewById(R.id.nine).setOnClickListener(this);
384 findViewById(R.id.star).setOnClickListener(this);
385
386 view = findViewById(R.id.zero);
387 view.setOnClickListener(this);
388 view.setOnLongClickListener(this);
389
390 findViewById(R.id.pound).setOnClickListener(this);
391 }
392
393 @Override
394 protected void onResume() {
395 super.onResume();
David Brownc29c7ab2009-07-07 16:00:18 -0700396
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800397 // retrieve the DTMF tone play back setting.
398 mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
399 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
400
Nicolas Catania905e7622009-12-01 08:51:20 -0800401 // Retrieve the haptic feedback setting.
402 mHaptic.checkSystemSetting();
403
Eric Laurentd9efc872009-07-17 11:52:06 -0700404 // if the mToneGenerator creation fails, just continue without it. It is
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800405 // a local audio signal, and is not as important as the dtmf tone itself.
406 synchronized(mToneGeneratorLock) {
407 if (mToneGenerator == null) {
408 try {
Jean-Michel Trividd44f8c2009-11-10 13:00:45 -0800409 // we want the user to be able to control the volume of the dial tones
410 // outside of a call, so we use the stream type that is also mapped to the
411 // volume control keys for this activity
412 mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
413 setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800414 } catch (RuntimeException e) {
415 Log.w(TAG, "Exception caught while creating local tone generator: " + e);
416 mToneGenerator = null;
417 }
418 }
419 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700420
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800421 Activity parent = getParent();
422 // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
423 // digits in the dialer field.
424 if (parent != null && parent instanceof DialtactsActivity) {
425 Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
426 if (dialUri != null) {
427 resolveIntent();
428 }
429 }
430
431 // While we're in the foreground, listen for phone state changes,
432 // purely so that we can take down the "dialpad chooser" if the
433 // phone becomes idle while the chooser UI is visible.
434 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
435 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
436
437 // Potentially show hint text in the mDigits field when the user
438 // hasn't typed any digits yet. (If there's already an active call,
439 // this hint text will remind the user that he's about to add a new
440 // call.)
441 //
442 // TODO: consider adding better UI for the case where *both* lines
443 // are currently in use. (Right now we let the user try to add
444 // another call, but that call is guaranteed to fail. Perhaps the
445 // entire dialer UI should be disabled instead.)
446 if (phoneIsInUse()) {
447 mDigits.setHint(R.string.dialerDialpadHintText);
448 } else {
449 // Common case; no hint necessary.
450 mDigits.setHint(null);
451
452 // Also, a sanity-check: the "dialpad chooser" UI should NEVER
453 // be visible if the phone is idle!
454 showDialpadChooser(false);
455 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700456
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700457 updateDialAndDeleteButtonStateEnabledAttr();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800458 }
459
460 @Override
Karl Rosaenf46bc312009-03-24 18:20:48 -0700461 public void onWindowFocusChanged(boolean hasFocus) {
462 if (hasFocus) {
463 // Hide soft keyboard, if visible (it's fugly over button dialer).
464 // The only known case where this will be true is when launching the dialer with
465 // ACTION_DIAL via a soft keyboard. we dismiss it here because we don't
466 // have a window token yet in onCreate / onNewIntent
467 InputMethodManager inputMethodManager = (InputMethodManager)
468 getSystemService(Context.INPUT_METHOD_SERVICE);
Eric Laurentd9efc872009-07-17 11:52:06 -0700469 inputMethodManager.hideSoftInputFromWindow(mDigits.getWindowToken(), 0);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700470 }
471 }
472
473 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800474 protected void onPause() {
475 super.onPause();
476
477 // Stop listening for phone state changes.
478 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
479 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
480
481 synchronized(mToneGeneratorLock) {
482 if (mToneGenerator != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800483 mToneGenerator.release();
484 mToneGenerator = null;
485 }
486 }
487 }
488
489 @Override
490 public boolean onCreateOptionsMenu(Menu menu) {
Reli Talc2a2a512009-06-10 16:48:00 -0400491 mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800492 .setIcon(android.R.drawable.ic_menu_add);
Reli Talc2a2a512009-06-10 16:48:00 -0400493 m2SecPauseMenuItem = menu.add(0, MENU_2S_PAUSE, 0, R.string.add_2sec_pause)
494 .setIcon(R.drawable.ic_menu_2sec_pause);
495 mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
496 .setIcon(R.drawable.ic_menu_wait);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800497 return true;
498 }
499
500 @Override
501 public boolean onPrepareOptionsMenu(Menu menu) {
502 // We never show a menu if the "choose dialpad" UI is up.
503 if (dialpadChooserVisible()) {
504 return false;
505 }
506
507 CharSequence digits = mDigits.getText();
508 if (digits == null || !TextUtils.isGraphic(digits)) {
509 mAddToContactMenuItem.setVisible(false);
Reli Talc2a2a512009-06-10 16:48:00 -0400510 m2SecPauseMenuItem.setVisible(false);
511 mWaitMenuItem.setVisible(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800512 } else {
513 // Put the current digits string into an intent
514 Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
515 intent.putExtra(Insert.PHONE, mDigits.getText());
516 intent.setType(People.CONTENT_ITEM_TYPE);
517 mAddToContactMenuItem.setIntent(intent);
518 mAddToContactMenuItem.setVisible(true);
Reli Talc2a2a512009-06-10 16:48:00 -0400519
520 // Check out whether to show Pause & Wait option menu items
521 int selectionStart;
522 int selectionEnd;
523 String strDigits = digits.toString();
524
525 selectionStart = mDigits.getSelectionStart();
526 selectionEnd = mDigits.getSelectionEnd();
527
528 if (selectionStart != -1) {
529 if (selectionStart > selectionEnd) {
530 // swap it as we want start to be less then end
531 int tmp = selectionStart;
532 selectionStart = selectionEnd;
533 selectionEnd = tmp;
534 }
535
536 if (selectionStart != 0) {
537 // Pause can be visible if cursor is not in the begining
538 m2SecPauseMenuItem.setVisible(true);
539
540 // For Wait to be visible set of condition to meet
541 mWaitMenuItem.setVisible(showWait(selectionStart,
542 selectionEnd, strDigits));
543 } else {
544 // cursor in the beginning both pause and wait to be invisible
545 m2SecPauseMenuItem.setVisible(false);
546 mWaitMenuItem.setVisible(false);
547 }
548 } else {
549 // cursor is not selected so assume new digit is added to the end
550 int strLength = strDigits.length();
551 mWaitMenuItem.setVisible(showWait(strLength,
552 strLength, strDigits));
553 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800554 }
555 return true;
556 }
557
558 @Override
559 public boolean onKeyDown(int keyCode, KeyEvent event) {
560 switch (keyCode) {
561 case KeyEvent.KEYCODE_CALL: {
562 long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
563 if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
564 // Launch voice dialer
565 Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
566 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
567 try {
568 startActivity(intent);
569 } catch (ActivityNotFoundException e) {
570 }
571 }
572 return true;
573 }
574 case KeyEvent.KEYCODE_1: {
Eric Laurentd9efc872009-07-17 11:52:06 -0700575 long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800576 if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
577 // Long press detected, call voice mail
578 callVoicemail();
579 }
580 return true;
581 }
582 }
583 return super.onKeyDown(keyCode, event);
584 }
585
586 @Override
587 public boolean onKeyUp(int keyCode, KeyEvent event) {
588 switch (keyCode) {
589 case KeyEvent.KEYCODE_CALL: {
Paul Bermandbdcde22009-10-09 12:04:10 -0400590 if (phoneIsCdma()) {
591 // If we're CDMA, regardless of where we are adding a call from (either
592 // InCallScreen or Dialtacts), the user may need to send an empty
593 // flash command to the network. So let's call placeCall() regardless
594 // and placeCall will handle this functionality for us.
595 placeCall();
596 } else if (mIsAddCallMode && (TextUtils.isEmpty(mDigits.getText().toString()))) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800597 // if we are adding a call from the InCallScreen and the phone
598 // number entered is empty, we just close the dialer to expose
599 // the InCallScreen under it.
600 finish();
601 } else {
602 // otherwise, we place the call.
603 placeCall();
604 }
605 return true;
606 }
607 }
608 return super.onKeyUp(keyCode, event);
609 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700610
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800611 private void keyPressed(int keyCode) {
Nicolas Catania905e7622009-12-01 08:51:20 -0800612 mHaptic.vibrate();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800613 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
614 mDigits.onKeyDown(keyCode, event);
615 }
616
617 public boolean onKey(View view, int keyCode, KeyEvent event) {
618 switch (view.getId()) {
619 case R.id.digits:
620 if (keyCode == KeyEvent.KEYCODE_ENTER) {
621 placeCall();
622 return true;
623 }
624 break;
625 }
626 return false;
627 }
628
629 public void onClick(View view) {
630 switch (view.getId()) {
631 case R.id.one: {
632 playTone(ToneGenerator.TONE_DTMF_1);
633 keyPressed(KeyEvent.KEYCODE_1);
634 return;
635 }
636 case R.id.two: {
637 playTone(ToneGenerator.TONE_DTMF_2);
638 keyPressed(KeyEvent.KEYCODE_2);
639 return;
640 }
641 case R.id.three: {
642 playTone(ToneGenerator.TONE_DTMF_3);
643 keyPressed(KeyEvent.KEYCODE_3);
644 return;
645 }
646 case R.id.four: {
647 playTone(ToneGenerator.TONE_DTMF_4);
648 keyPressed(KeyEvent.KEYCODE_4);
649 return;
650 }
651 case R.id.five: {
652 playTone(ToneGenerator.TONE_DTMF_5);
653 keyPressed(KeyEvent.KEYCODE_5);
654 return;
655 }
656 case R.id.six: {
657 playTone(ToneGenerator.TONE_DTMF_6);
658 keyPressed(KeyEvent.KEYCODE_6);
659 return;
660 }
661 case R.id.seven: {
662 playTone(ToneGenerator.TONE_DTMF_7);
663 keyPressed(KeyEvent.KEYCODE_7);
664 return;
665 }
666 case R.id.eight: {
667 playTone(ToneGenerator.TONE_DTMF_8);
668 keyPressed(KeyEvent.KEYCODE_8);
669 return;
670 }
671 case R.id.nine: {
672 playTone(ToneGenerator.TONE_DTMF_9);
673 keyPressed(KeyEvent.KEYCODE_9);
674 return;
675 }
676 case R.id.zero: {
677 playTone(ToneGenerator.TONE_DTMF_0);
678 keyPressed(KeyEvent.KEYCODE_0);
679 return;
680 }
681 case R.id.pound: {
682 playTone(ToneGenerator.TONE_DTMF_P);
683 keyPressed(KeyEvent.KEYCODE_POUND);
684 return;
685 }
686 case R.id.star: {
687 playTone(ToneGenerator.TONE_DTMF_S);
688 keyPressed(KeyEvent.KEYCODE_STAR);
689 return;
690 }
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700691 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800692 keyPressed(KeyEvent.KEYCODE_DEL);
693 return;
694 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700695 case R.id.dialButton: {
Nicolas Catania905e7622009-12-01 08:51:20 -0800696 mHaptic.vibrate(); // Vibrate here too, just like we do for the regular keys
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800697 placeCall();
698 return;
699 }
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700700 case R.id.voicemailButton: {
701 callVoicemail();
Nicolas Catania905e7622009-12-01 08:51:20 -0800702 mHaptic.vibrate();
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700703 return;
704 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700705 case R.id.digits: {
706 if (mDigits.length() != 0) {
707 mDigits.setCursorVisible(true);
708 }
709 return;
710 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800711 }
712 }
713
714 public boolean onLongClick(View view) {
715 final Editable digits = mDigits.getText();
716 int id = view.getId();
717 switch (id) {
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700718 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800719 digits.clear();
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700720 // TODO: The framework forgets to clear the pressed
721 // status of disabled button. Until this is fixed,
722 // clear manually the pressed status. b/2133127
723 mDelete.setPressed(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800724 return true;
725 }
726 case R.id.one: {
727 if (digits.length() == 0) {
728 callVoicemail();
729 return true;
730 }
731 return false;
732 }
733 case R.id.zero: {
734 keyPressed(KeyEvent.KEYCODE_PLUS);
735 return true;
736 }
737 }
738 return false;
739 }
740
741 void callVoicemail() {
742 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
743 Uri.fromParts("voicemail", "", null));
744 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
745 startActivity(intent);
746 mDigits.getText().clear();
747 finish();
748 }
749
750 void placeCall() {
751 final String number = mDigits.getText().toString();
Paul Bermandbdcde22009-10-09 12:04:10 -0400752 boolean sendEmptyFlash = false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800753 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
754 Uri.fromParts("tel", number, null));
Paul Bermandbdcde22009-10-09 12:04:10 -0400755 if (number == null || !TextUtils.isGraphic(number)) {
756 // There is no number entered.
757 if (phoneIsCdma() && phoneIsOffhook()) {
758 // We only want to send this empty flash extra if we're CDMA and the
759 // phone is offhook (don't want to send if ringing or dialing)
760 intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
761 sendEmptyFlash = true;
762 } else {
763 playTone(ToneGenerator.TONE_PROP_NACK);
764 return;
765 }
766 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800767 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
768 startActivity(intent);
769 mDigits.getText().clear();
Paul Bermandbdcde22009-10-09 12:04:10 -0400770 // Don't finish TwelveKeyDialer yet if we're sending a blank flash for CDMA. CDMA
771 // networks use Flash messages when special processing needs to be done, mainly for
772 // 3-way or call waiting scenarios. Presumably, here we're in a special 3-way scenario
773 // where the network needs a blank flash before being able to add the new participant.
774 // (This is not the case with all 3-way calls, just certain CDMA infrastructures.)
775 if (!sendEmptyFlash) {
776 finish();
777 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800778 }
779
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800780
781 /**
David Brown22f615f2009-06-25 16:19:19 -0700782 * Plays the specified tone for TONE_LENGTH_MS milliseconds.
783 *
784 * The tone is played locally, using the audio stream for phone calls.
785 * Tones are played only if the "Audible touch tones" user preference
786 * is checked, and are NOT played if the device is in silent mode.
787 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800788 * @param tone a tone code from {@link ToneGenerator}
789 */
790 void playTone(int tone) {
791 // if local tone playback is disabled, just return.
792 if (!mDTMFToneEnabled) {
793 return;
794 }
David Brown22f615f2009-06-25 16:19:19 -0700795
796 // Also do nothing if the phone is in silent mode.
797 // We need to re-check the ringer mode for *every* playTone()
798 // call, rather than keeping a local flag that's updated in
799 // onResume(), since it's possible to toggle silent mode without
800 // leaving the current activity (via the ENDCALL-longpress menu.)
801 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
David Brownd5a15302009-07-20 16:39:47 -0700802 int ringerMode = audioManager.getRingerMode();
803 if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
804 || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
David Brown22f615f2009-06-25 16:19:19 -0700805 return;
806 }
807
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800808 synchronized(mToneGeneratorLock) {
809 if (mToneGenerator == null) {
810 Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
811 return;
812 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700813
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800814 // Start the new tone (will stop any playing tone)
Eric Laurent8487fed2009-09-07 08:45:14 -0700815 mToneGenerator.startTone(tone, TONE_LENGTH_MS);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800816 }
817 }
818
819 /**
820 * Brings up the "dialpad chooser" UI in place of the usual Dialer
821 * elements (the textfield/button and the dialpad underneath).
822 *
823 * We show this UI if the user brings up the Dialer while a call is
824 * already in progress, since there's a good chance we got here
825 * accidentally (and the user really wanted the in-call dialpad instead).
826 * So in this situation we display an intermediate UI that lets the user
827 * explicitly choose between the in-call dialpad ("Use touch tone
828 * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
829 * to call in progress" just goes back to the in-call UI with no dialpad
830 * at all.)
831 *
832 * @param enabled If true, show the "dialpad chooser" instead
833 * of the regular Dialer UI
834 */
835 private void showDialpadChooser(boolean enabled) {
836 if (enabled) {
837 // Log.i(TAG, "Showing dialpad chooser!");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700838 mDigits.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800839 if (mDialpad != null) mDialpad.setVisibility(View.GONE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700840 mVoicemailDialAndDeleteRow.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800841 mDialpadChooser.setVisibility(View.VISIBLE);
842
843 // Instantiate the DialpadChooserAdapter and hook it up to the
844 // ListView. We do this only once.
845 if (mDialpadChooserAdapter == null) {
846 mDialpadChooserAdapter = new DialpadChooserAdapter(this);
847 mDialpadChooser.setAdapter(mDialpadChooserAdapter);
848 }
849 } else {
850 // Log.i(TAG, "Displaying normal Dialer UI.");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700851 mDigits.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800852 if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700853 mVoicemailDialAndDeleteRow.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800854 mDialpadChooser.setVisibility(View.GONE);
855 }
856 }
857
858 /**
859 * @return true if we're currently showing the "dialpad chooser" UI.
860 */
861 private boolean dialpadChooserVisible() {
862 return mDialpadChooser.getVisibility() == View.VISIBLE;
863 }
864
865 /**
866 * Simple list adapter, binding to an icon + text label
867 * for each item in the "dialpad chooser" list.
868 */
869 private static class DialpadChooserAdapter extends BaseAdapter {
870 private LayoutInflater mInflater;
871
872 // Simple struct for a single "choice" item.
873 static class ChoiceItem {
874 String text;
875 Bitmap icon;
876 int id;
877
878 public ChoiceItem(String s, Bitmap b, int i) {
879 text = s;
880 icon = b;
881 id = i;
882 }
883 }
884
885 // IDs for the possible "choices":
886 static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
887 static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
888 static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
889
890 private static final int NUM_ITEMS = 3;
891 private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
892
893 public DialpadChooserAdapter(Context context) {
894 // Cache the LayoutInflate to avoid asking for a new one each time.
895 mInflater = LayoutInflater.from(context);
896
897 // Initialize the possible choices.
898 // TODO: could this be specified entirely in XML?
899
900 // - "Use touch tone keypad"
901 mChoiceItems[0] = new ChoiceItem(
902 context.getString(R.string.dialer_useDtmfDialpad),
903 BitmapFactory.decodeResource(context.getResources(),
904 R.drawable.ic_dialer_fork_tt_keypad),
905 DIALPAD_CHOICE_USE_DTMF_DIALPAD);
906
907 // - "Return to call in progress"
908 mChoiceItems[1] = new ChoiceItem(
909 context.getString(R.string.dialer_returnToInCallScreen),
910 BitmapFactory.decodeResource(context.getResources(),
911 R.drawable.ic_dialer_fork_current_call),
912 DIALPAD_CHOICE_RETURN_TO_CALL);
913
914 // - "Add call"
915 mChoiceItems[2] = new ChoiceItem(
916 context.getString(R.string.dialer_addAnotherCall),
917 BitmapFactory.decodeResource(context.getResources(),
918 R.drawable.ic_dialer_fork_add_call),
919 DIALPAD_CHOICE_ADD_NEW_CALL);
920 }
921
922 public int getCount() {
923 return NUM_ITEMS;
924 }
925
926 /**
927 * Return the ChoiceItem for a given position.
928 */
929 public Object getItem(int position) {
930 return mChoiceItems[position];
931 }
932
933 /**
934 * Return a unique ID for each possible choice.
935 */
936 public long getItemId(int position) {
937 return position;
938 }
939
940 /**
941 * Make a view for each row.
942 */
943 public View getView(int position, View convertView, ViewGroup parent) {
944 // When convertView is non-null, we can reuse it (there's no need
945 // to reinflate it.)
946 if (convertView == null) {
947 convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
948 }
949
950 TextView text = (TextView) convertView.findViewById(R.id.text);
951 text.setText(mChoiceItems[position].text);
952
953 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
954 icon.setImageBitmap(mChoiceItems[position].icon);
955
956 return convertView;
957 }
958 }
959
960 /**
961 * Handle clicks from the dialpad chooser.
962 */
963 public void onItemClick(AdapterView parent, View v, int position, long id) {
964 DialpadChooserAdapter.ChoiceItem item =
965 (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
966 int itemId = item.id;
967 switch (itemId) {
968 case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
969 // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
970 // Fire off an intent to go back to the in-call UI
971 // with the dialpad visible.
972 returnToInCallScreen(true);
973 break;
974
975 case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
976 // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
977 // Fire off an intent to go back to the in-call UI
978 // (with the dialpad hidden).
979 returnToInCallScreen(false);
980 break;
981
982 case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
983 // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
984 // Ok, guess the user really did want to be here (in the
985 // regular Dialer) after all. Bring back the normal Dialer UI.
986 showDialpadChooser(false);
987 break;
988
989 default:
990 Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
991 break;
992 }
993 }
994
995 /**
996 * Returns to the in-call UI (where there's presumably a call in
997 * progress) in response to the user selecting "use touch tone keypad"
998 * or "return to call" from the dialpad chooser.
999 */
1000 private void returnToInCallScreen(boolean showDialpad) {
1001 try {
1002 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1003 if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
1004 } catch (RemoteException e) {
1005 Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
1006 }
1007
1008 // Finally, finish() ourselves so that we don't stay on the
1009 // activity stack.
1010 // Note that we do this whether or not the showCallScreenWithDialpad()
1011 // call above had any effect or not! (That call is a no-op if the
1012 // phone is idle, which can happen if the current call ends while
1013 // the dialpad chooser is up. In this case we can't show the
1014 // InCallScreen, and there's no point staying here in the Dialer,
1015 // so we just take the user back where he came from...)
1016 finish();
1017 }
1018
1019 /**
1020 * @return true if the phone is "in use", meaning that at least one line
1021 * is active (ie. off hook or ringing or dialing).
1022 */
1023 private boolean phoneIsInUse() {
1024 boolean phoneInUse = false;
1025 try {
1026 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1027 if (phone != null) phoneInUse = !phone.isIdle();
1028 } catch (RemoteException e) {
1029 Log.w(TAG, "phone.isIdle() failed", e);
1030 }
1031 return phoneInUse;
1032 }
David Brownc29c7ab2009-07-07 16:00:18 -07001033
1034 /**
Paul Bermandbdcde22009-10-09 12:04:10 -04001035 * @return true if the phone is a CDMA phone type
1036 */
1037 private boolean phoneIsCdma() {
1038 boolean isCdma = false;
1039 try {
1040 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1041 if (phone != null) {
1042 isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA);
1043 }
1044 } catch (RemoteException e) {
1045 Log.w(TAG, "phone.getActivePhoneType() failed", e);
1046 }
1047 return isCdma;
1048 }
1049
1050 /**
1051 * @return true if the phone state is OFFHOOK
1052 */
1053 private boolean phoneIsOffhook() {
1054 boolean phoneOffhook = false;
1055 try {
1056 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1057 if (phone != null) phoneOffhook = phone.isOffhook();
1058 } catch (RemoteException e) {
1059 Log.w(TAG, "phone.isOffhook() failed", e);
1060 }
1061 return phoneOffhook;
1062 }
1063
Reli Talc2a2a512009-06-10 16:48:00 -04001064
1065 /**
1066 * Returns true whenever any one of the options from the menu is selected.
1067 * Code changes to support dialpad options
1068 */
1069 @Override
1070 public boolean onOptionsItemSelected(MenuItem item) {
1071 switch (item.getItemId()) {
1072 case MENU_2S_PAUSE:
1073 updateDialString(",");
1074 return true;
1075 case MENU_WAIT:
1076 updateDialString(";");
1077 return true;
1078 }
1079 return false;
1080 }
1081
1082 /**
1083 * Updates the dial string (mDigits) after inserting a Pause character (,)
1084 * or Wait character (;).
1085 */
1086 private void updateDialString(String newDigits) {
1087 int selectionStart;
1088 int selectionEnd;
1089
1090 // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
Eric Fischer686782e2009-09-10 17:57:45 -07001091 int anchor = mDigits.getSelectionStart();
1092 int point = mDigits.getSelectionEnd();
1093
1094 selectionStart = Math.min(anchor, point);
1095 selectionEnd = Math.max(anchor, point);
Reli Talc2a2a512009-06-10 16:48:00 -04001096
1097 Editable digits = mDigits.getText();
1098 if (selectionStart != -1 ) {
1099 if (selectionStart == selectionEnd) {
1100 // then there is no selection. So insert the pause at this
1101 // position and update the mDigits.
1102 digits.replace(selectionStart, selectionStart, newDigits);
1103 } else {
Eric Fischer1e2d3a22009-09-17 10:53:10 -07001104 digits.replace(selectionStart, selectionEnd, newDigits);
Nicolas Catania7edbd0c2009-09-28 20:37:33 -07001105 // Unselect: back to a regular cursor, just pass the character inserted.
1106 mDigits.setSelection(selectionStart + 1);
Reli Talc2a2a512009-06-10 16:48:00 -04001107 }
1108 } else {
1109 int len = mDigits.length();
1110 digits.replace(len, len, newDigits);
1111 }
1112 }
1113
1114 /**
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001115 * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001116 */
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001117 private void updateDialAndDeleteButtonStateEnabledAttr() {
1118 final boolean notEmpty = mDigits.length() != 0;
1119
Paul Bermandbdcde22009-10-09 12:04:10 -04001120 // If we're already on a CDMA call, then we want to enable the Call button
1121 if (phoneIsCdma() && phoneIsOffhook()) {
1122 if (mDialButton != null) {
1123 mDialButton.setEnabled(true);
1124 }
1125 } else {
1126 if (mDialButton != null) {
1127 mDialButton.setEnabled(notEmpty);
1128 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001129 }
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001130 mDelete.setEnabled(notEmpty);
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001131 }
1132
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001133
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001134 /**
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001135 * Check if voicemail is enabled/accessible.
1136 */
1137 private void initVoicemailButton() {
1138 boolean hasVoicemail = false;
1139 try {
1140 hasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
1141 } catch (SecurityException se) {
1142 // Possibly no READ_PHONE_STATE privilege.
1143 }
1144
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001145 mVoicemailButton = mVoicemailDialAndDeleteRow.findViewById(R.id.voicemailButton);
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001146 if (hasVoicemail) {
1147 mVoicemailButton.setOnClickListener(this);
1148 } else {
1149 mVoicemailButton.setEnabled(false);
1150 }
1151 }
1152
1153 /**
Reli Talc2a2a512009-06-10 16:48:00 -04001154 * This function return true if Wait menu item can be shown
1155 * otherwise returns false. Assumes the passed string is non-empty
1156 * and the 0th index check is not required.
1157 */
1158 private boolean showWait(int start, int end, String digits) {
1159 if (start == end) {
1160 // visible false in this case
1161 if (start > digits.length()) return false;
1162
1163 // preceding char is ';', so visible should be false
1164 if (digits.charAt(start-1) == ';') return false;
1165
1166 // next char is ';', so visible should be false
1167 if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
1168 } else {
1169 // visible false in this case
1170 if (start > digits.length() || end > digits.length()) return false;
1171
1172 // In this case we need to just check for ';' preceding to start
1173 // or next to end
1174 if (digits.charAt(start-1) == ';') return false;
1175 }
1176 return true;
1177 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001178}