blob: 1ec03609e9c721beba9b672059e8636f2307f52e [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;
David Brownc29c7ab2009-07-07 16:00:18 -070037import android.os.Vibrator;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080038import android.provider.Contacts.Intents.Insert;
39import android.provider.Contacts.People;
40import android.provider.Contacts.Phones;
41import android.provider.Contacts.PhonesColumns;
42import android.provider.Settings;
43import android.telephony.PhoneNumberFormattingTextWatcher;
44import android.telephony.PhoneNumberUtils;
45import android.telephony.PhoneStateListener;
46import android.telephony.TelephonyManager;
47import android.text.Editable;
Reli Talc2a2a512009-06-10 16:48:00 -040048import android.text.SpannableStringBuilder;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080049import 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
68import com.android.internal.telephony.ITelephony;
69
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 */
83 private static final int TONE_RELATIVE_VOLUME = 50;
84
Nicolas Catania4c0704a2009-09-23 11:42:00 -070085 /** Play the vibrate pattern only once. */
86 private static final int VIBRATE_NO_REPEAT = -1;
87
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080088 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.
112 private Vibrator mVibrator;
113 private boolean mVibrateOn;
Nicolas Catania4c0704a2009-09-23 11:42:00 -0700114 private long[] mVibratePattern;
David Brownc29c7ab2009-07-07 16:00:18 -0700115
Eric Laurentd9efc872009-07-17 11:52:06 -0700116
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800117 /** Identifier for the "Add Call" intent extra. */
118 static final String ADD_CALL_MODE_KEY = "add_call_mode";
Paul Bermandbdcde22009-10-09 12:04:10 -0400119
120 /**
121 * Identifier for intent extra for sending an empty Flash message for
122 * CDMA networks. This message is used by the network to simulate a
123 * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
124 *
125 * TODO: Using an intent extra to tell the phone to send this flash is a
126 * temporary measure. To be replaced with an ITelephony call in the future.
127 * TODO: Keep in sync with the string defined in OutgoingCallBroadcaster.java
128 * in Phone app until this is replaced with the ITelephony API.
129 */
130 static final String EXTRA_SEND_EMPTY_FLASH
131 = "com.android.phone.extra.SEND_EMPTY_FLASH";
132
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800133 /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
134 private boolean mIsAddCallMode;
135
136 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
137 /**
138 * Listen for phone state changes so that we can take down the
139 * "dialpad chooser" if the phone becomes idle while the
140 * chooser UI is visible.
141 */
142 @Override
143 public void onCallStateChanged(int state, String incomingNumber) {
144 // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
145 // + state + ", '" + incomingNumber + "'");
146 if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
147 // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
148 // Note there's a race condition in the UI here: the
149 // dialpad chooser could conceivably disappear (on its
150 // own) at the exact moment the user was trying to select
151 // one of the choices, which would be confusing. (But at
152 // least that's better than leaving the dialpad chooser
153 // onscreen, but useless...)
154 showDialpadChooser(false);
155 }
156 }
157 };
158
159 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
160 // Do nothing
161 }
162
163 public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
164 // Do nothing
Eric Laurentd9efc872009-07-17 11:52:06 -0700165 // DTMF Tones do not need to be played here any longer -
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800166 // the DTMF dialer handles that functionality now.
167 }
168
169 public void afterTextChanged(Editable input) {
170 if (SpecialCharSequenceMgr.handleChars(this, input.toString(), mDigits)) {
171 // A special sequence was entered, clear the digits
172 mDigits.getText().clear();
173 }
174
Nicolas Catania75993762009-09-21 16:42:00 -0700175 final boolean notEmpty = mDigits.length() != 0;
176 if (notEmpty) {
177 mDigits.setBackgroundDrawable(mDigitsBackground);
178 } else {
Nicolas Catania3040fa32009-10-01 13:00:53 -0700179 mDigits.setCursorVisible(false);
Nicolas Catania75993762009-09-21 16:42:00 -0700180 mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
181 }
182
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700183 updateDialAndDeleteButtonStateEnabledAttr();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800184 }
185
186 @Override
187 protected void onCreate(Bundle icicle) {
188 super.onCreate(icicle);
189
190 // Set the content view
191 setContentView(getContentViewResource());
192
Nicolas Catania75993762009-09-21 16:42:00 -0700193 // Load up the resources for the text field.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800194 Resources r = getResources();
195 mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800196 mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800197
198 mDigits = (EditText) findViewById(R.id.digits);
199 mDigits.setKeyListener(DialerKeyListener.getInstance());
200 mDigits.setOnClickListener(this);
201 mDigits.setOnKeyListener(this);
Nicolas Catania3040fa32009-10-01 13:00:53 -0700202
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800203 maybeAddNumberFormatting();
204
205 // Check for the presence of the keypad
206 View view = findViewById(R.id.one);
207 if (view != null) {
208 setupKeypad();
209 }
210
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700211 mVoicemailDialAndDeleteRow = findViewById(R.id.voicemailAndDialAndDelete);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700212
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700213 initVoicemailButton();
214
David Brown3d07e6d2009-08-04 20:30:09 -0700215 // Check whether we should show the onscreen "Dial" button.
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700216 mDialButton = mVoicemailDialAndDeleteRow.findViewById(R.id.dialButton);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700217
David Brown3d07e6d2009-08-04 20:30:09 -0700218 if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
David Brown3d07e6d2009-08-04 20:30:09 -0700219 mDialButton.setOnClickListener(this);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700220 } else {
221 mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
222 mDialButton = null;
David Brown3d07e6d2009-08-04 20:30:09 -0700223 }
224
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700225 view = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800226 view.setOnClickListener(this);
227 view.setOnLongClickListener(this);
228 mDelete = view;
229
Nicolas Catania901f8562009-10-09 11:09:45 -0700230 mDialpad = findViewById(R.id.dialpad); // This is null in landscape mode.
231
232 // In landscape we put the keyboard in phone mode.
233 // In portrait we prevent the soft keyboard to show since the
234 // dialpad acts as one already.
235 if (null == mDialpad) {
236 mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
237 } else {
238 mDigits.setInputType(android.text.InputType.TYPE_NULL);
239 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800240
241 // Set up the "dialpad chooser" UI; see showDialpadChooser().
242 mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
243 mDialpadChooser.setOnItemClickListener(this);
244
245 if (!resolveIntent() && icicle != null) {
246 super.onRestoreInstanceState(icicle);
247 }
248
David Brownc29c7ab2009-07-07 16:00:18 -0700249 // TODO: We might eventually need to make mVibrateOn come from a
250 // user preference rather than a per-platform resource, in which
251 // case we would need to update it in onResume() rather than here.
Nicolas Catania4c0704a2009-09-23 11:42:00 -0700252 initVibrationPattern(r);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800253 }
254
255 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800256 protected void onRestoreInstanceState(Bundle icicle) {
257 // Do nothing, state is restored in onCreate() if needed
258 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700259
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800260 protected void maybeAddNumberFormatting() {
261 mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
262 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700263
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800264 /**
Eric Laurentd9efc872009-07-17 11:52:06 -0700265 * Overridden by subclasses to control the resource used by the content view.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800266 */
267 protected int getContentViewResource() {
268 return R.layout.twelve_key_dialer;
269 }
270
271 private boolean resolveIntent() {
272 boolean ignoreState = false;
273
274 // Find the proper intent
275 final Intent intent;
276 if (isChild()) {
277 intent = getParent().getIntent();
278 ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
279 } else {
280 intent = getIntent();
281 }
282 // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
283
284 // by default we are not adding a call.
285 mIsAddCallMode = false;
286
287 // By default we don't show the "dialpad chooser" UI.
288 boolean needToShowDialpadChooser = false;
289
290 // Resolve the intent
291 final String action = intent.getAction();
292 if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
293 // see if we are "adding a call" from the InCallScreen; false by default.
294 mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
295 Uri uri = intent.getData();
296 if (uri != null) {
297 if ("tel".equals(uri.getScheme())) {
298 // Put the requested number into the input area
299 String data = uri.getSchemeSpecificPart();
300 setFormattedDigits(data);
301 } else {
302 String type = intent.getType();
303 if (People.CONTENT_ITEM_TYPE.equals(type)
304 || Phones.CONTENT_ITEM_TYPE.equals(type)) {
305 // Query the phone number
306 Cursor c = getContentResolver().query(intent.getData(),
307 new String[] {PhonesColumns.NUMBER}, null, null, null);
308 if (c != null) {
309 if (c.moveToFirst()) {
310 // Put the number into the input area
311 setFormattedDigits(c.getString(0));
312 }
313 c.close();
314 }
315 }
316 }
317 }
318 } else if (Intent.ACTION_MAIN.equals(action)) {
319 // The MAIN action means we're bringing up a blank dialer
320 // (e.g. by selecting the Home shortcut, or tabbing over from
321 // Contacts or Call log.)
322 //
323 // At this point, IF there's already an active call, there's a
324 // good chance that the user got here accidentally (but really
325 // wanted the in-call dialpad instead). So we bring up an
326 // intermediate UI to make the user confirm what they really
327 // want to do.
328 if (phoneIsInUse()) {
329 // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
330 needToShowDialpadChooser = true;
331 }
332 }
333
334 // Bring up the "dialpad chooser" IFF we need to make the user
335 // confirm which dialpad they really want.
336 showDialpadChooser(needToShowDialpadChooser);
337
338 return ignoreState;
339 }
340
341 protected void setFormattedDigits(String data) {
342 // strip the non-dialable numbers out of the data string.
343 String dialString = PhoneNumberUtils.extractNetworkPortion(data);
344 dialString = PhoneNumberUtils.formatNumber(dialString);
345 if (!TextUtils.isEmpty(dialString)) {
346 Editable digits = mDigits.getText();
347 digits.replace(0, digits.length(), dialString);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700348 // for some reason this isn't getting called in the digits.replace call above..
349 // but in any case, this will make sure the background drawable looks right
350 afterTextChanged(digits);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800351 }
352 }
353
354 @Override
355 protected void onNewIntent(Intent newIntent) {
356 setIntent(newIntent);
357 resolveIntent();
358 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700359
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800360 @Override
361 protected void onPostCreate(Bundle savedInstanceState) {
362 super.onPostCreate(savedInstanceState);
363
364 // This can't be done in onCreate(), since the auto-restoring of the digits
365 // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
366 // is called. This method will be called every time the activity is created, and
367 // will always happen after onRestoreSavedInstanceState().
368 mDigits.addTextChangedListener(this);
369 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700370
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800371 private void setupKeypad() {
372 // Setup the listeners for the buttons
373 View view = findViewById(R.id.one);
374 view.setOnClickListener(this);
375 view.setOnLongClickListener(this);
376
377 findViewById(R.id.two).setOnClickListener(this);
378 findViewById(R.id.three).setOnClickListener(this);
379 findViewById(R.id.four).setOnClickListener(this);
380 findViewById(R.id.five).setOnClickListener(this);
381 findViewById(R.id.six).setOnClickListener(this);
382 findViewById(R.id.seven).setOnClickListener(this);
383 findViewById(R.id.eight).setOnClickListener(this);
384 findViewById(R.id.nine).setOnClickListener(this);
385 findViewById(R.id.star).setOnClickListener(this);
386
387 view = findViewById(R.id.zero);
388 view.setOnClickListener(this);
389 view.setOnLongClickListener(this);
390
391 findViewById(R.id.pound).setOnClickListener(this);
392 }
393
394 @Override
395 protected void onResume() {
396 super.onResume();
David Brownc29c7ab2009-07-07 16:00:18 -0700397
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800398 // retrieve the DTMF tone play back setting.
399 mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
400 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
401
Eric Laurentd9efc872009-07-17 11:52:06 -0700402 // if the mToneGenerator creation fails, just continue without it. It is
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800403 // a local audio signal, and is not as important as the dtmf tone itself.
404 synchronized(mToneGeneratorLock) {
405 if (mToneGenerator == null) {
406 try {
Eric Laurentd9efc872009-07-17 11:52:06 -0700407 mToneGenerator = new ToneGenerator(AudioManager.STREAM_DTMF,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800408 TONE_RELATIVE_VOLUME);
409 } catch (RuntimeException e) {
410 Log.w(TAG, "Exception caught while creating local tone generator: " + e);
411 mToneGenerator = null;
412 }
413 }
414 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700415
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800416 Activity parent = getParent();
417 // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
418 // digits in the dialer field.
419 if (parent != null && parent instanceof DialtactsActivity) {
420 Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
421 if (dialUri != null) {
422 resolveIntent();
423 }
424 }
425
426 // While we're in the foreground, listen for phone state changes,
427 // purely so that we can take down the "dialpad chooser" if the
428 // phone becomes idle while the chooser UI is visible.
429 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
430 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
431
432 // Potentially show hint text in the mDigits field when the user
433 // hasn't typed any digits yet. (If there's already an active call,
434 // this hint text will remind the user that he's about to add a new
435 // call.)
436 //
437 // TODO: consider adding better UI for the case where *both* lines
438 // are currently in use. (Right now we let the user try to add
439 // another call, but that call is guaranteed to fail. Perhaps the
440 // entire dialer UI should be disabled instead.)
441 if (phoneIsInUse()) {
442 mDigits.setHint(R.string.dialerDialpadHintText);
443 } else {
444 // Common case; no hint necessary.
445 mDigits.setHint(null);
446
447 // Also, a sanity-check: the "dialpad chooser" UI should NEVER
448 // be visible if the phone is idle!
449 showDialpadChooser(false);
450 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700451
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700452 updateDialAndDeleteButtonStateEnabledAttr();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800453 }
454
455 @Override
Karl Rosaenf46bc312009-03-24 18:20:48 -0700456 public void onWindowFocusChanged(boolean hasFocus) {
457 if (hasFocus) {
458 // Hide soft keyboard, if visible (it's fugly over button dialer).
459 // The only known case where this will be true is when launching the dialer with
460 // ACTION_DIAL via a soft keyboard. we dismiss it here because we don't
461 // have a window token yet in onCreate / onNewIntent
462 InputMethodManager inputMethodManager = (InputMethodManager)
463 getSystemService(Context.INPUT_METHOD_SERVICE);
Eric Laurentd9efc872009-07-17 11:52:06 -0700464 inputMethodManager.hideSoftInputFromWindow(mDigits.getWindowToken(), 0);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700465 }
466 }
467
468 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800469 protected void onPause() {
470 super.onPause();
471
472 // Stop listening for phone state changes.
473 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
474 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
475
476 synchronized(mToneGeneratorLock) {
477 if (mToneGenerator != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800478 mToneGenerator.release();
479 mToneGenerator = null;
480 }
481 }
482 }
483
484 @Override
485 public boolean onCreateOptionsMenu(Menu menu) {
Reli Talc2a2a512009-06-10 16:48:00 -0400486 mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800487 .setIcon(android.R.drawable.ic_menu_add);
Reli Talc2a2a512009-06-10 16:48:00 -0400488 m2SecPauseMenuItem = menu.add(0, MENU_2S_PAUSE, 0, R.string.add_2sec_pause)
489 .setIcon(R.drawable.ic_menu_2sec_pause);
490 mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
491 .setIcon(R.drawable.ic_menu_wait);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800492 return true;
493 }
494
495 @Override
496 public boolean onPrepareOptionsMenu(Menu menu) {
497 // We never show a menu if the "choose dialpad" UI is up.
498 if (dialpadChooserVisible()) {
499 return false;
500 }
501
502 CharSequence digits = mDigits.getText();
503 if (digits == null || !TextUtils.isGraphic(digits)) {
504 mAddToContactMenuItem.setVisible(false);
Reli Talc2a2a512009-06-10 16:48:00 -0400505 m2SecPauseMenuItem.setVisible(false);
506 mWaitMenuItem.setVisible(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800507 } else {
508 // Put the current digits string into an intent
509 Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
510 intent.putExtra(Insert.PHONE, mDigits.getText());
511 intent.setType(People.CONTENT_ITEM_TYPE);
512 mAddToContactMenuItem.setIntent(intent);
513 mAddToContactMenuItem.setVisible(true);
Reli Talc2a2a512009-06-10 16:48:00 -0400514
515 // Check out whether to show Pause & Wait option menu items
516 int selectionStart;
517 int selectionEnd;
518 String strDigits = digits.toString();
519
520 selectionStart = mDigits.getSelectionStart();
521 selectionEnd = mDigits.getSelectionEnd();
522
523 if (selectionStart != -1) {
524 if (selectionStart > selectionEnd) {
525 // swap it as we want start to be less then end
526 int tmp = selectionStart;
527 selectionStart = selectionEnd;
528 selectionEnd = tmp;
529 }
530
531 if (selectionStart != 0) {
532 // Pause can be visible if cursor is not in the begining
533 m2SecPauseMenuItem.setVisible(true);
534
535 // For Wait to be visible set of condition to meet
536 mWaitMenuItem.setVisible(showWait(selectionStart,
537 selectionEnd, strDigits));
538 } else {
539 // cursor in the beginning both pause and wait to be invisible
540 m2SecPauseMenuItem.setVisible(false);
541 mWaitMenuItem.setVisible(false);
542 }
543 } else {
544 // cursor is not selected so assume new digit is added to the end
545 int strLength = strDigits.length();
546 mWaitMenuItem.setVisible(showWait(strLength,
547 strLength, strDigits));
548 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800549 }
550 return true;
551 }
552
553 @Override
554 public boolean onKeyDown(int keyCode, KeyEvent event) {
555 switch (keyCode) {
556 case KeyEvent.KEYCODE_CALL: {
557 long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
558 if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
559 // Launch voice dialer
560 Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
561 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
562 try {
563 startActivity(intent);
564 } catch (ActivityNotFoundException e) {
565 }
566 }
567 return true;
568 }
569 case KeyEvent.KEYCODE_1: {
Eric Laurentd9efc872009-07-17 11:52:06 -0700570 long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800571 if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
572 // Long press detected, call voice mail
573 callVoicemail();
574 }
575 return true;
576 }
577 }
578 return super.onKeyDown(keyCode, event);
579 }
580
581 @Override
582 public boolean onKeyUp(int keyCode, KeyEvent event) {
583 switch (keyCode) {
584 case KeyEvent.KEYCODE_CALL: {
Paul Bermandbdcde22009-10-09 12:04:10 -0400585 if (phoneIsCdma()) {
586 // If we're CDMA, regardless of where we are adding a call from (either
587 // InCallScreen or Dialtacts), the user may need to send an empty
588 // flash command to the network. So let's call placeCall() regardless
589 // and placeCall will handle this functionality for us.
590 placeCall();
591 } else if (mIsAddCallMode && (TextUtils.isEmpty(mDigits.getText().toString()))) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800592 // if we are adding a call from the InCallScreen and the phone
593 // number entered is empty, we just close the dialer to expose
594 // the InCallScreen under it.
595 finish();
596 } else {
597 // otherwise, we place the call.
598 placeCall();
599 }
600 return true;
601 }
602 }
603 return super.onKeyUp(keyCode, event);
604 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700605
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800606 private void keyPressed(int keyCode) {
David Brownc29c7ab2009-07-07 16:00:18 -0700607 vibrate();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800608 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
609 mDigits.onKeyDown(keyCode, event);
610 }
611
612 public boolean onKey(View view, int keyCode, KeyEvent event) {
613 switch (view.getId()) {
614 case R.id.digits:
615 if (keyCode == KeyEvent.KEYCODE_ENTER) {
616 placeCall();
617 return true;
618 }
619 break;
620 }
621 return false;
622 }
623
624 public void onClick(View view) {
625 switch (view.getId()) {
626 case R.id.one: {
627 playTone(ToneGenerator.TONE_DTMF_1);
628 keyPressed(KeyEvent.KEYCODE_1);
629 return;
630 }
631 case R.id.two: {
632 playTone(ToneGenerator.TONE_DTMF_2);
633 keyPressed(KeyEvent.KEYCODE_2);
634 return;
635 }
636 case R.id.three: {
637 playTone(ToneGenerator.TONE_DTMF_3);
638 keyPressed(KeyEvent.KEYCODE_3);
639 return;
640 }
641 case R.id.four: {
642 playTone(ToneGenerator.TONE_DTMF_4);
643 keyPressed(KeyEvent.KEYCODE_4);
644 return;
645 }
646 case R.id.five: {
647 playTone(ToneGenerator.TONE_DTMF_5);
648 keyPressed(KeyEvent.KEYCODE_5);
649 return;
650 }
651 case R.id.six: {
652 playTone(ToneGenerator.TONE_DTMF_6);
653 keyPressed(KeyEvent.KEYCODE_6);
654 return;
655 }
656 case R.id.seven: {
657 playTone(ToneGenerator.TONE_DTMF_7);
658 keyPressed(KeyEvent.KEYCODE_7);
659 return;
660 }
661 case R.id.eight: {
662 playTone(ToneGenerator.TONE_DTMF_8);
663 keyPressed(KeyEvent.KEYCODE_8);
664 return;
665 }
666 case R.id.nine: {
667 playTone(ToneGenerator.TONE_DTMF_9);
668 keyPressed(KeyEvent.KEYCODE_9);
669 return;
670 }
671 case R.id.zero: {
672 playTone(ToneGenerator.TONE_DTMF_0);
673 keyPressed(KeyEvent.KEYCODE_0);
674 return;
675 }
676 case R.id.pound: {
677 playTone(ToneGenerator.TONE_DTMF_P);
678 keyPressed(KeyEvent.KEYCODE_POUND);
679 return;
680 }
681 case R.id.star: {
682 playTone(ToneGenerator.TONE_DTMF_S);
683 keyPressed(KeyEvent.KEYCODE_STAR);
684 return;
685 }
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700686 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800687 keyPressed(KeyEvent.KEYCODE_DEL);
688 return;
689 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700690 case R.id.dialButton: {
David Brownc29c7ab2009-07-07 16:00:18 -0700691 vibrate(); // Vibrate here too, just like we do for the regular keys
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800692 placeCall();
693 return;
694 }
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700695 case R.id.voicemailButton: {
696 callVoicemail();
697 vibrate();
698 return;
699 }
Nicolas Catania3040fa32009-10-01 13:00:53 -0700700 case R.id.digits: {
701 if (mDigits.length() != 0) {
702 mDigits.setCursorVisible(true);
703 }
704 return;
705 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800706 }
707 }
708
709 public boolean onLongClick(View view) {
710 final Editable digits = mDigits.getText();
711 int id = view.getId();
712 switch (id) {
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700713 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800714 digits.clear();
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700715 // TODO: The framework forgets to clear the pressed
716 // status of disabled button. Until this is fixed,
717 // clear manually the pressed status. b/2133127
718 mDelete.setPressed(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800719 return true;
720 }
721 case R.id.one: {
722 if (digits.length() == 0) {
723 callVoicemail();
724 return true;
725 }
726 return false;
727 }
728 case R.id.zero: {
729 keyPressed(KeyEvent.KEYCODE_PLUS);
730 return true;
731 }
732 }
733 return false;
734 }
735
736 void callVoicemail() {
737 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
738 Uri.fromParts("voicemail", "", null));
739 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
740 startActivity(intent);
741 mDigits.getText().clear();
742 finish();
743 }
744
745 void placeCall() {
746 final String number = mDigits.getText().toString();
Paul Bermandbdcde22009-10-09 12:04:10 -0400747 boolean sendEmptyFlash = false;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800748 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
749 Uri.fromParts("tel", number, null));
Paul Bermandbdcde22009-10-09 12:04:10 -0400750 if (number == null || !TextUtils.isGraphic(number)) {
751 // There is no number entered.
752 if (phoneIsCdma() && phoneIsOffhook()) {
753 // We only want to send this empty flash extra if we're CDMA and the
754 // phone is offhook (don't want to send if ringing or dialing)
755 intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
756 sendEmptyFlash = true;
757 } else {
758 playTone(ToneGenerator.TONE_PROP_NACK);
759 return;
760 }
761 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800762 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
763 startActivity(intent);
764 mDigits.getText().clear();
Paul Bermandbdcde22009-10-09 12:04:10 -0400765 // Don't finish TwelveKeyDialer yet if we're sending a blank flash for CDMA. CDMA
766 // networks use Flash messages when special processing needs to be done, mainly for
767 // 3-way or call waiting scenarios. Presumably, here we're in a special 3-way scenario
768 // where the network needs a blank flash before being able to add the new participant.
769 // (This is not the case with all 3-way calls, just certain CDMA infrastructures.)
770 if (!sendEmptyFlash) {
771 finish();
772 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800773 }
774
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800775
776 /**
David Brown22f615f2009-06-25 16:19:19 -0700777 * Plays the specified tone for TONE_LENGTH_MS milliseconds.
778 *
779 * The tone is played locally, using the audio stream for phone calls.
780 * Tones are played only if the "Audible touch tones" user preference
781 * is checked, and are NOT played if the device is in silent mode.
782 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800783 * @param tone a tone code from {@link ToneGenerator}
784 */
785 void playTone(int tone) {
786 // if local tone playback is disabled, just return.
787 if (!mDTMFToneEnabled) {
788 return;
789 }
David Brown22f615f2009-06-25 16:19:19 -0700790
791 // Also do nothing if the phone is in silent mode.
792 // We need to re-check the ringer mode for *every* playTone()
793 // call, rather than keeping a local flag that's updated in
794 // onResume(), since it's possible to toggle silent mode without
795 // leaving the current activity (via the ENDCALL-longpress menu.)
796 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
David Brownd5a15302009-07-20 16:39:47 -0700797 int ringerMode = audioManager.getRingerMode();
798 if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
799 || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
David Brown22f615f2009-06-25 16:19:19 -0700800 return;
801 }
802
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800803 synchronized(mToneGeneratorLock) {
804 if (mToneGenerator == null) {
805 Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
806 return;
807 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700808
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800809 // Start the new tone (will stop any playing tone)
Eric Laurent8487fed2009-09-07 08:45:14 -0700810 mToneGenerator.startTone(tone, TONE_LENGTH_MS);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800811 }
812 }
813
814 /**
815 * Brings up the "dialpad chooser" UI in place of the usual Dialer
816 * elements (the textfield/button and the dialpad underneath).
817 *
818 * We show this UI if the user brings up the Dialer while a call is
819 * already in progress, since there's a good chance we got here
820 * accidentally (and the user really wanted the in-call dialpad instead).
821 * So in this situation we display an intermediate UI that lets the user
822 * explicitly choose between the in-call dialpad ("Use touch tone
823 * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
824 * to call in progress" just goes back to the in-call UI with no dialpad
825 * at all.)
826 *
827 * @param enabled If true, show the "dialpad chooser" instead
828 * of the regular Dialer UI
829 */
830 private void showDialpadChooser(boolean enabled) {
831 if (enabled) {
832 // Log.i(TAG, "Showing dialpad chooser!");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700833 mDigits.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800834 if (mDialpad != null) mDialpad.setVisibility(View.GONE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700835 mVoicemailDialAndDeleteRow.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800836 mDialpadChooser.setVisibility(View.VISIBLE);
837
838 // Instantiate the DialpadChooserAdapter and hook it up to the
839 // ListView. We do this only once.
840 if (mDialpadChooserAdapter == null) {
841 mDialpadChooserAdapter = new DialpadChooserAdapter(this);
842 mDialpadChooser.setAdapter(mDialpadChooserAdapter);
843 }
844 } else {
845 // Log.i(TAG, "Displaying normal Dialer UI.");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700846 mDigits.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800847 if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700848 mVoicemailDialAndDeleteRow.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800849 mDialpadChooser.setVisibility(View.GONE);
850 }
851 }
852
853 /**
854 * @return true if we're currently showing the "dialpad chooser" UI.
855 */
856 private boolean dialpadChooserVisible() {
857 return mDialpadChooser.getVisibility() == View.VISIBLE;
858 }
859
860 /**
861 * Simple list adapter, binding to an icon + text label
862 * for each item in the "dialpad chooser" list.
863 */
864 private static class DialpadChooserAdapter extends BaseAdapter {
865 private LayoutInflater mInflater;
866
867 // Simple struct for a single "choice" item.
868 static class ChoiceItem {
869 String text;
870 Bitmap icon;
871 int id;
872
873 public ChoiceItem(String s, Bitmap b, int i) {
874 text = s;
875 icon = b;
876 id = i;
877 }
878 }
879
880 // IDs for the possible "choices":
881 static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
882 static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
883 static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
884
885 private static final int NUM_ITEMS = 3;
886 private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
887
888 public DialpadChooserAdapter(Context context) {
889 // Cache the LayoutInflate to avoid asking for a new one each time.
890 mInflater = LayoutInflater.from(context);
891
892 // Initialize the possible choices.
893 // TODO: could this be specified entirely in XML?
894
895 // - "Use touch tone keypad"
896 mChoiceItems[0] = new ChoiceItem(
897 context.getString(R.string.dialer_useDtmfDialpad),
898 BitmapFactory.decodeResource(context.getResources(),
899 R.drawable.ic_dialer_fork_tt_keypad),
900 DIALPAD_CHOICE_USE_DTMF_DIALPAD);
901
902 // - "Return to call in progress"
903 mChoiceItems[1] = new ChoiceItem(
904 context.getString(R.string.dialer_returnToInCallScreen),
905 BitmapFactory.decodeResource(context.getResources(),
906 R.drawable.ic_dialer_fork_current_call),
907 DIALPAD_CHOICE_RETURN_TO_CALL);
908
909 // - "Add call"
910 mChoiceItems[2] = new ChoiceItem(
911 context.getString(R.string.dialer_addAnotherCall),
912 BitmapFactory.decodeResource(context.getResources(),
913 R.drawable.ic_dialer_fork_add_call),
914 DIALPAD_CHOICE_ADD_NEW_CALL);
915 }
916
917 public int getCount() {
918 return NUM_ITEMS;
919 }
920
921 /**
922 * Return the ChoiceItem for a given position.
923 */
924 public Object getItem(int position) {
925 return mChoiceItems[position];
926 }
927
928 /**
929 * Return a unique ID for each possible choice.
930 */
931 public long getItemId(int position) {
932 return position;
933 }
934
935 /**
936 * Make a view for each row.
937 */
938 public View getView(int position, View convertView, ViewGroup parent) {
939 // When convertView is non-null, we can reuse it (there's no need
940 // to reinflate it.)
941 if (convertView == null) {
942 convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
943 }
944
945 TextView text = (TextView) convertView.findViewById(R.id.text);
946 text.setText(mChoiceItems[position].text);
947
948 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
949 icon.setImageBitmap(mChoiceItems[position].icon);
950
951 return convertView;
952 }
953 }
954
955 /**
956 * Handle clicks from the dialpad chooser.
957 */
958 public void onItemClick(AdapterView parent, View v, int position, long id) {
959 DialpadChooserAdapter.ChoiceItem item =
960 (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
961 int itemId = item.id;
962 switch (itemId) {
963 case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
964 // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
965 // Fire off an intent to go back to the in-call UI
966 // with the dialpad visible.
967 returnToInCallScreen(true);
968 break;
969
970 case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
971 // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
972 // Fire off an intent to go back to the in-call UI
973 // (with the dialpad hidden).
974 returnToInCallScreen(false);
975 break;
976
977 case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
978 // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
979 // Ok, guess the user really did want to be here (in the
980 // regular Dialer) after all. Bring back the normal Dialer UI.
981 showDialpadChooser(false);
982 break;
983
984 default:
985 Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
986 break;
987 }
988 }
989
990 /**
991 * Returns to the in-call UI (where there's presumably a call in
992 * progress) in response to the user selecting "use touch tone keypad"
993 * or "return to call" from the dialpad chooser.
994 */
995 private void returnToInCallScreen(boolean showDialpad) {
996 try {
997 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
998 if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
999 } catch (RemoteException e) {
1000 Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
1001 }
1002
1003 // Finally, finish() ourselves so that we don't stay on the
1004 // activity stack.
1005 // Note that we do this whether or not the showCallScreenWithDialpad()
1006 // call above had any effect or not! (That call is a no-op if the
1007 // phone is idle, which can happen if the current call ends while
1008 // the dialpad chooser is up. In this case we can't show the
1009 // InCallScreen, and there's no point staying here in the Dialer,
1010 // so we just take the user back where he came from...)
1011 finish();
1012 }
1013
1014 /**
1015 * @return true if the phone is "in use", meaning that at least one line
1016 * is active (ie. off hook or ringing or dialing).
1017 */
1018 private boolean phoneIsInUse() {
1019 boolean phoneInUse = false;
1020 try {
1021 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1022 if (phone != null) phoneInUse = !phone.isIdle();
1023 } catch (RemoteException e) {
1024 Log.w(TAG, "phone.isIdle() failed", e);
1025 }
1026 return phoneInUse;
1027 }
David Brownc29c7ab2009-07-07 16:00:18 -07001028
1029 /**
Paul Bermandbdcde22009-10-09 12:04:10 -04001030 * @return true if the phone is a CDMA phone type
1031 */
1032 private boolean phoneIsCdma() {
1033 boolean isCdma = false;
1034 try {
1035 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1036 if (phone != null) {
1037 isCdma = (phone.getActivePhoneType() == TelephonyManager.PHONE_TYPE_CDMA);
1038 }
1039 } catch (RemoteException e) {
1040 Log.w(TAG, "phone.getActivePhoneType() failed", e);
1041 }
1042 return isCdma;
1043 }
1044
1045 /**
1046 * @return true if the phone state is OFFHOOK
1047 */
1048 private boolean phoneIsOffhook() {
1049 boolean phoneOffhook = false;
1050 try {
1051 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1052 if (phone != null) phoneOffhook = phone.isOffhook();
1053 } catch (RemoteException e) {
1054 Log.w(TAG, "phone.isOffhook() failed", e);
1055 }
1056 return phoneOffhook;
1057 }
1058
1059 /**
David Brownc29c7ab2009-07-07 16:00:18 -07001060 * Triggers haptic feedback (if enabled) for dialer key presses.
1061 */
1062 private synchronized void vibrate() {
1063 if (!mVibrateOn) {
1064 return;
1065 }
1066 if (mVibrator == null) {
1067 mVibrator = new Vibrator();
1068 }
Nicolas Catania4c0704a2009-09-23 11:42:00 -07001069 mVibrator.vibrate(mVibratePattern, VIBRATE_NO_REPEAT);
David Brownc29c7ab2009-07-07 16:00:18 -07001070 }
Reli Talc2a2a512009-06-10 16:48:00 -04001071
1072 /**
1073 * Returns true whenever any one of the options from the menu is selected.
1074 * Code changes to support dialpad options
1075 */
1076 @Override
1077 public boolean onOptionsItemSelected(MenuItem item) {
1078 switch (item.getItemId()) {
1079 case MENU_2S_PAUSE:
1080 updateDialString(",");
1081 return true;
1082 case MENU_WAIT:
1083 updateDialString(";");
1084 return true;
1085 }
1086 return false;
1087 }
1088
1089 /**
1090 * Updates the dial string (mDigits) after inserting a Pause character (,)
1091 * or Wait character (;).
1092 */
1093 private void updateDialString(String newDigits) {
1094 int selectionStart;
1095 int selectionEnd;
1096
1097 // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
Eric Fischer686782e2009-09-10 17:57:45 -07001098 int anchor = mDigits.getSelectionStart();
1099 int point = mDigits.getSelectionEnd();
1100
1101 selectionStart = Math.min(anchor, point);
1102 selectionEnd = Math.max(anchor, point);
Reli Talc2a2a512009-06-10 16:48:00 -04001103
1104 Editable digits = mDigits.getText();
1105 if (selectionStart != -1 ) {
1106 if (selectionStart == selectionEnd) {
1107 // then there is no selection. So insert the pause at this
1108 // position and update the mDigits.
1109 digits.replace(selectionStart, selectionStart, newDigits);
1110 } else {
Eric Fischer1e2d3a22009-09-17 10:53:10 -07001111 digits.replace(selectionStart, selectionEnd, newDigits);
Nicolas Catania7edbd0c2009-09-28 20:37:33 -07001112 // Unselect: back to a regular cursor, just pass the character inserted.
1113 mDigits.setSelection(selectionStart + 1);
Reli Talc2a2a512009-06-10 16:48:00 -04001114 }
1115 } else {
1116 int len = mDigits.length();
1117 digits.replace(len, len, newDigits);
1118 }
1119 }
1120
1121 /**
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001122 * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001123 */
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001124 private void updateDialAndDeleteButtonStateEnabledAttr() {
1125 final boolean notEmpty = mDigits.length() != 0;
1126
Paul Bermandbdcde22009-10-09 12:04:10 -04001127 // If we're already on a CDMA call, then we want to enable the Call button
1128 if (phoneIsCdma() && phoneIsOffhook()) {
1129 if (mDialButton != null) {
1130 mDialButton.setEnabled(true);
1131 }
1132 } else {
1133 if (mDialButton != null) {
1134 mDialButton.setEnabled(notEmpty);
1135 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001136 }
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001137 mDelete.setEnabled(notEmpty);
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001138 }
1139
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001140
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001141 /**
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001142 * Check if voicemail is enabled/accessible.
1143 */
1144 private void initVoicemailButton() {
1145 boolean hasVoicemail = false;
1146 try {
1147 hasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
1148 } catch (SecurityException se) {
1149 // Possibly no READ_PHONE_STATE privilege.
1150 }
1151
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001152 mVoicemailButton = mVoicemailDialAndDeleteRow.findViewById(R.id.voicemailButton);
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001153 if (hasVoicemail) {
1154 mVoicemailButton.setOnClickListener(this);
1155 } else {
1156 mVoicemailButton.setEnabled(false);
1157 }
1158 }
1159
1160 /**
Nicolas Catania4c0704a2009-09-23 11:42:00 -07001161 * Initialize the vibration parameters.
1162 * @param r The Resources with the vibration parameters.
1163 */
1164 private void initVibrationPattern(Resources r) {
1165 int[] pattern = null;
1166 try {
1167 mVibrateOn = r.getBoolean(R.bool.config_enable_dialer_key_vibration);
1168 pattern = r.getIntArray(com.android.internal.R.array.config_virtualKeyVibePattern);
1169 if (null == pattern) {
1170 Log.e(TAG, "Vibrate pattern is null.");
1171 mVibrateOn = false;
1172 }
1173 } catch (Resources.NotFoundException nfe) {
1174 Log.e(TAG, "Vibrate control bool or pattern missing.", nfe);
1175 mVibrateOn = false;
1176 }
1177
1178 if (!mVibrateOn) {
1179 return;
1180 }
1181
1182 // int[] to long[] conversion.
1183 mVibratePattern = new long[pattern.length];
1184 for (int i = 0; i < pattern.length; i++) {
1185 mVibratePattern[i] = pattern[i];
1186 }
1187 }
1188
1189 /**
Reli Talc2a2a512009-06-10 16:48:00 -04001190 * This function return true if Wait menu item can be shown
1191 * otherwise returns false. Assumes the passed string is non-empty
1192 * and the 0th index check is not required.
1193 */
1194 private boolean showWait(int start, int end, String digits) {
1195 if (start == end) {
1196 // visible false in this case
1197 if (start > digits.length()) return false;
1198
1199 // preceding char is ';', so visible should be false
1200 if (digits.charAt(start-1) == ';') return false;
1201
1202 // next char is ';', so visible should be false
1203 if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
1204 } else {
1205 // visible false in this case
1206 if (start > digits.length() || end > digits.length()) return false;
1207
1208 // In this case we need to just check for ';' preceding to start
1209 // or next to end
1210 if (digits.charAt(start-1) == ';') return false;
1211 }
1212 return true;
1213 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001214}