blob: bdc6b6f91fb305f7801351bb9a79933774deda94 [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
85 private EditText mDigits;
86 private View mDelete;
87 private MenuItem mAddToContactMenuItem;
88 private ToneGenerator mToneGenerator;
89 private Object mToneGeneratorLock = new Object();
90 private Drawable mDigitsBackground;
91 private Drawable mDigitsEmptyBackground;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080092 private View mDialpad;
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -070093 private View mVoicemailDialAndDeleteRow;
Nicolas Catania80bda0f2009-09-19 09:17:14 -070094 private View mVoicemailButton;
95 private View mDialButton;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -080096 private ListView mDialpadChooser;
97 private DialpadChooserAdapter mDialpadChooserAdapter;
Reli Talc2a2a512009-06-10 16:48:00 -040098 //Member variables for dialpad options
99 private MenuItem m2SecPauseMenuItem;
100 private MenuItem mWaitMenuItem;
101 private static final int MENU_ADD_CONTACTS = 1;
102 private static final int MENU_2S_PAUSE = 2;
103 private static final int MENU_WAIT = 3;
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800104
105 // determines if we want to playback local DTMF tones.
106 private boolean mDTMFToneEnabled;
David Brownc29c7ab2009-07-07 16:00:18 -0700107
108 // Vibration (haptic feedback) for dialer key presses.
109 private Vibrator mVibrator;
110 private boolean mVibrateOn;
111 private long mVibrateDuration;
112
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";
116 /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
117 private boolean mIsAddCallMode;
118
119 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
120 /**
121 * Listen for phone state changes so that we can take down the
122 * "dialpad chooser" if the phone becomes idle while the
123 * chooser UI is visible.
124 */
125 @Override
126 public void onCallStateChanged(int state, String incomingNumber) {
127 // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
128 // + state + ", '" + incomingNumber + "'");
129 if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
130 // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
131 // Note there's a race condition in the UI here: the
132 // dialpad chooser could conceivably disappear (on its
133 // own) at the exact moment the user was trying to select
134 // one of the choices, which would be confusing. (But at
135 // least that's better than leaving the dialpad chooser
136 // onscreen, but useless...)
137 showDialpadChooser(false);
138 }
139 }
140 };
141
142 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
143 // Do nothing
144 }
145
146 public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
147 // Do nothing
Eric Laurentd9efc872009-07-17 11:52:06 -0700148 // DTMF Tones do not need to be played here any longer -
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800149 // the DTMF dialer handles that functionality now.
150 }
151
152 public void afterTextChanged(Editable input) {
153 if (SpecialCharSequenceMgr.handleChars(this, input.toString(), mDigits)) {
154 // A special sequence was entered, clear the digits
155 mDigits.getText().clear();
156 }
157
Nicolas Catania75993762009-09-21 16:42:00 -0700158 final boolean notEmpty = mDigits.length() != 0;
159 if (notEmpty) {
160 mDigits.setBackgroundDrawable(mDigitsBackground);
161 } else {
162 mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
163 }
164
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700165 updateDialAndDeleteButtonStateEnabledAttr();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800166 }
167
168 @Override
169 protected void onCreate(Bundle icicle) {
170 super.onCreate(icicle);
171
172 // Set the content view
173 setContentView(getContentViewResource());
174
Nicolas Catania75993762009-09-21 16:42:00 -0700175 // Load up the resources for the text field.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800176 Resources r = getResources();
177 mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800178 mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800179
180 mDigits = (EditText) findViewById(R.id.digits);
181 mDigits.setKeyListener(DialerKeyListener.getInstance());
182 mDigits.setOnClickListener(this);
183 mDigits.setOnKeyListener(this);
184 maybeAddNumberFormatting();
185
186 // Check for the presence of the keypad
187 View view = findViewById(R.id.one);
188 if (view != null) {
189 setupKeypad();
190 }
191
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700192 mVoicemailDialAndDeleteRow = findViewById(R.id.voicemailAndDialAndDelete);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700193
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700194 initVoicemailButton();
195
David Brown3d07e6d2009-08-04 20:30:09 -0700196 // Check whether we should show the onscreen "Dial" button.
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700197 mDialButton = mVoicemailDialAndDeleteRow.findViewById(R.id.dialButton);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700198
David Brown3d07e6d2009-08-04 20:30:09 -0700199 if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
David Brown3d07e6d2009-08-04 20:30:09 -0700200 mDialButton.setOnClickListener(this);
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700201 } else {
202 mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
203 mDialButton = null;
David Brown3d07e6d2009-08-04 20:30:09 -0700204 }
205
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700206 view = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800207 view.setOnClickListener(this);
208 view.setOnLongClickListener(this);
209 mDelete = view;
210
Dmitri Plotnikov032bb362009-05-06 17:05:39 -0700211 mDialpad = findViewById(R.id.dialpad); // This is null in landscape mode
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800212
213 // Set up the "dialpad chooser" UI; see showDialpadChooser().
214 mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
215 mDialpadChooser.setOnItemClickListener(this);
216
217 if (!resolveIntent() && icicle != null) {
218 super.onRestoreInstanceState(icicle);
219 }
220
David Brownc29c7ab2009-07-07 16:00:18 -0700221 // Initialize vibration parameters.
222 // TODO: We might eventually need to make mVibrateOn come from a
223 // user preference rather than a per-platform resource, in which
224 // case we would need to update it in onResume() rather than here.
225 mVibrateOn = r.getBoolean(R.bool.config_enable_dialer_key_vibration);
226 mVibrateDuration = (long) r.getInteger(R.integer.config_dialer_key_vibrate_duration);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800227 }
228
229 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800230 protected void onRestoreInstanceState(Bundle icicle) {
231 // Do nothing, state is restored in onCreate() if needed
232 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700233
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800234 protected void maybeAddNumberFormatting() {
235 mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
236 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700237
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800238 /**
Eric Laurentd9efc872009-07-17 11:52:06 -0700239 * Overridden by subclasses to control the resource used by the content view.
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800240 */
241 protected int getContentViewResource() {
242 return R.layout.twelve_key_dialer;
243 }
244
245 private boolean resolveIntent() {
246 boolean ignoreState = false;
247
248 // Find the proper intent
249 final Intent intent;
250 if (isChild()) {
251 intent = getParent().getIntent();
252 ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
253 } else {
254 intent = getIntent();
255 }
256 // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
257
258 // by default we are not adding a call.
259 mIsAddCallMode = false;
260
261 // By default we don't show the "dialpad chooser" UI.
262 boolean needToShowDialpadChooser = false;
263
264 // Resolve the intent
265 final String action = intent.getAction();
266 if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
267 // see if we are "adding a call" from the InCallScreen; false by default.
268 mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
269 Uri uri = intent.getData();
270 if (uri != null) {
271 if ("tel".equals(uri.getScheme())) {
272 // Put the requested number into the input area
273 String data = uri.getSchemeSpecificPart();
274 setFormattedDigits(data);
275 } else {
276 String type = intent.getType();
277 if (People.CONTENT_ITEM_TYPE.equals(type)
278 || Phones.CONTENT_ITEM_TYPE.equals(type)) {
279 // Query the phone number
280 Cursor c = getContentResolver().query(intent.getData(),
281 new String[] {PhonesColumns.NUMBER}, null, null, null);
282 if (c != null) {
283 if (c.moveToFirst()) {
284 // Put the number into the input area
285 setFormattedDigits(c.getString(0));
286 }
287 c.close();
288 }
289 }
290 }
291 }
292 } else if (Intent.ACTION_MAIN.equals(action)) {
293 // The MAIN action means we're bringing up a blank dialer
294 // (e.g. by selecting the Home shortcut, or tabbing over from
295 // Contacts or Call log.)
296 //
297 // At this point, IF there's already an active call, there's a
298 // good chance that the user got here accidentally (but really
299 // wanted the in-call dialpad instead). So we bring up an
300 // intermediate UI to make the user confirm what they really
301 // want to do.
302 if (phoneIsInUse()) {
303 // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
304 needToShowDialpadChooser = true;
305 }
306 }
307
308 // Bring up the "dialpad chooser" IFF we need to make the user
309 // confirm which dialpad they really want.
310 showDialpadChooser(needToShowDialpadChooser);
311
312 return ignoreState;
313 }
314
315 protected void setFormattedDigits(String data) {
316 // strip the non-dialable numbers out of the data string.
317 String dialString = PhoneNumberUtils.extractNetworkPortion(data);
318 dialString = PhoneNumberUtils.formatNumber(dialString);
319 if (!TextUtils.isEmpty(dialString)) {
320 Editable digits = mDigits.getText();
321 digits.replace(0, digits.length(), dialString);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700322 // for some reason this isn't getting called in the digits.replace call above..
323 // but in any case, this will make sure the background drawable looks right
324 afterTextChanged(digits);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800325 }
326 }
327
328 @Override
329 protected void onNewIntent(Intent newIntent) {
330 setIntent(newIntent);
331 resolveIntent();
332 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700333
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800334 @Override
335 protected void onPostCreate(Bundle savedInstanceState) {
336 super.onPostCreate(savedInstanceState);
337
338 // This can't be done in onCreate(), since the auto-restoring of the digits
339 // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
340 // is called. This method will be called every time the activity is created, and
341 // will always happen after onRestoreSavedInstanceState().
342 mDigits.addTextChangedListener(this);
343 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700344
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800345 private void setupKeypad() {
346 // Setup the listeners for the buttons
347 View view = findViewById(R.id.one);
348 view.setOnClickListener(this);
349 view.setOnLongClickListener(this);
350
351 findViewById(R.id.two).setOnClickListener(this);
352 findViewById(R.id.three).setOnClickListener(this);
353 findViewById(R.id.four).setOnClickListener(this);
354 findViewById(R.id.five).setOnClickListener(this);
355 findViewById(R.id.six).setOnClickListener(this);
356 findViewById(R.id.seven).setOnClickListener(this);
357 findViewById(R.id.eight).setOnClickListener(this);
358 findViewById(R.id.nine).setOnClickListener(this);
359 findViewById(R.id.star).setOnClickListener(this);
360
361 view = findViewById(R.id.zero);
362 view.setOnClickListener(this);
363 view.setOnLongClickListener(this);
364
365 findViewById(R.id.pound).setOnClickListener(this);
366 }
367
368 @Override
369 protected void onResume() {
370 super.onResume();
David Brownc29c7ab2009-07-07 16:00:18 -0700371
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800372 // retrieve the DTMF tone play back setting.
373 mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
374 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
375
Eric Laurentd9efc872009-07-17 11:52:06 -0700376 // if the mToneGenerator creation fails, just continue without it. It is
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800377 // a local audio signal, and is not as important as the dtmf tone itself.
378 synchronized(mToneGeneratorLock) {
379 if (mToneGenerator == null) {
380 try {
Eric Laurentd9efc872009-07-17 11:52:06 -0700381 mToneGenerator = new ToneGenerator(AudioManager.STREAM_DTMF,
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800382 TONE_RELATIVE_VOLUME);
383 } catch (RuntimeException e) {
384 Log.w(TAG, "Exception caught while creating local tone generator: " + e);
385 mToneGenerator = null;
386 }
387 }
388 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700389
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800390 Activity parent = getParent();
391 // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
392 // digits in the dialer field.
393 if (parent != null && parent instanceof DialtactsActivity) {
394 Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
395 if (dialUri != null) {
396 resolveIntent();
397 }
398 }
399
400 // While we're in the foreground, listen for phone state changes,
401 // purely so that we can take down the "dialpad chooser" if the
402 // phone becomes idle while the chooser UI is visible.
403 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
404 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
405
406 // Potentially show hint text in the mDigits field when the user
407 // hasn't typed any digits yet. (If there's already an active call,
408 // this hint text will remind the user that he's about to add a new
409 // call.)
410 //
411 // TODO: consider adding better UI for the case where *both* lines
412 // are currently in use. (Right now we let the user try to add
413 // another call, but that call is guaranteed to fail. Perhaps the
414 // entire dialer UI should be disabled instead.)
415 if (phoneIsInUse()) {
416 mDigits.setHint(R.string.dialerDialpadHintText);
417 } else {
418 // Common case; no hint necessary.
419 mDigits.setHint(null);
420
421 // Also, a sanity-check: the "dialpad chooser" UI should NEVER
422 // be visible if the phone is idle!
423 showDialpadChooser(false);
424 }
Nicolas Cataniadea164e2009-09-18 06:26:16 -0700425
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700426 updateDialAndDeleteButtonStateEnabledAttr();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800427 }
428
429 @Override
Karl Rosaenf46bc312009-03-24 18:20:48 -0700430 public void onWindowFocusChanged(boolean hasFocus) {
431 if (hasFocus) {
432 // Hide soft keyboard, if visible (it's fugly over button dialer).
433 // The only known case where this will be true is when launching the dialer with
434 // ACTION_DIAL via a soft keyboard. we dismiss it here because we don't
435 // have a window token yet in onCreate / onNewIntent
436 InputMethodManager inputMethodManager = (InputMethodManager)
437 getSystemService(Context.INPUT_METHOD_SERVICE);
Eric Laurentd9efc872009-07-17 11:52:06 -0700438 inputMethodManager.hideSoftInputFromWindow(mDigits.getWindowToken(), 0);
Karl Rosaenf46bc312009-03-24 18:20:48 -0700439 }
440 }
441
442 @Override
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800443 protected void onPause() {
444 super.onPause();
445
446 // Stop listening for phone state changes.
447 TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
448 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
449
450 synchronized(mToneGeneratorLock) {
451 if (mToneGenerator != null) {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800452 mToneGenerator.release();
453 mToneGenerator = null;
454 }
455 }
456 }
457
458 @Override
459 public boolean onCreateOptionsMenu(Menu menu) {
Reli Talc2a2a512009-06-10 16:48:00 -0400460 mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800461 .setIcon(android.R.drawable.ic_menu_add);
Reli Talc2a2a512009-06-10 16:48:00 -0400462 m2SecPauseMenuItem = menu.add(0, MENU_2S_PAUSE, 0, R.string.add_2sec_pause)
463 .setIcon(R.drawable.ic_menu_2sec_pause);
464 mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
465 .setIcon(R.drawable.ic_menu_wait);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800466 return true;
467 }
468
469 @Override
470 public boolean onPrepareOptionsMenu(Menu menu) {
471 // We never show a menu if the "choose dialpad" UI is up.
472 if (dialpadChooserVisible()) {
473 return false;
474 }
475
476 CharSequence digits = mDigits.getText();
477 if (digits == null || !TextUtils.isGraphic(digits)) {
478 mAddToContactMenuItem.setVisible(false);
Reli Talc2a2a512009-06-10 16:48:00 -0400479 m2SecPauseMenuItem.setVisible(false);
480 mWaitMenuItem.setVisible(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800481 } else {
482 // Put the current digits string into an intent
483 Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
484 intent.putExtra(Insert.PHONE, mDigits.getText());
485 intent.setType(People.CONTENT_ITEM_TYPE);
486 mAddToContactMenuItem.setIntent(intent);
487 mAddToContactMenuItem.setVisible(true);
Reli Talc2a2a512009-06-10 16:48:00 -0400488
489 // Check out whether to show Pause & Wait option menu items
490 int selectionStart;
491 int selectionEnd;
492 String strDigits = digits.toString();
493
494 selectionStart = mDigits.getSelectionStart();
495 selectionEnd = mDigits.getSelectionEnd();
496
497 if (selectionStart != -1) {
498 if (selectionStart > selectionEnd) {
499 // swap it as we want start to be less then end
500 int tmp = selectionStart;
501 selectionStart = selectionEnd;
502 selectionEnd = tmp;
503 }
504
505 if (selectionStart != 0) {
506 // Pause can be visible if cursor is not in the begining
507 m2SecPauseMenuItem.setVisible(true);
508
509 // For Wait to be visible set of condition to meet
510 mWaitMenuItem.setVisible(showWait(selectionStart,
511 selectionEnd, strDigits));
512 } else {
513 // cursor in the beginning both pause and wait to be invisible
514 m2SecPauseMenuItem.setVisible(false);
515 mWaitMenuItem.setVisible(false);
516 }
517 } else {
518 // cursor is not selected so assume new digit is added to the end
519 int strLength = strDigits.length();
520 mWaitMenuItem.setVisible(showWait(strLength,
521 strLength, strDigits));
522 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800523 }
524 return true;
525 }
526
527 @Override
528 public boolean onKeyDown(int keyCode, KeyEvent event) {
529 switch (keyCode) {
530 case KeyEvent.KEYCODE_CALL: {
531 long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
532 if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
533 // Launch voice dialer
534 Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
535 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
536 try {
537 startActivity(intent);
538 } catch (ActivityNotFoundException e) {
539 }
540 }
541 return true;
542 }
543 case KeyEvent.KEYCODE_1: {
Eric Laurentd9efc872009-07-17 11:52:06 -0700544 long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800545 if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
546 // Long press detected, call voice mail
547 callVoicemail();
548 }
549 return true;
550 }
551 }
552 return super.onKeyDown(keyCode, event);
553 }
554
555 @Override
556 public boolean onKeyUp(int keyCode, KeyEvent event) {
557 switch (keyCode) {
558 case KeyEvent.KEYCODE_CALL: {
559 if (mIsAddCallMode && (TextUtils.isEmpty(mDigits.getText().toString()))) {
560 // if we are adding a call from the InCallScreen and the phone
561 // number entered is empty, we just close the dialer to expose
562 // the InCallScreen under it.
563 finish();
564 } else {
565 // otherwise, we place the call.
566 placeCall();
567 }
568 return true;
569 }
570 }
571 return super.onKeyUp(keyCode, event);
572 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700573
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800574 private void keyPressed(int keyCode) {
David Brownc29c7ab2009-07-07 16:00:18 -0700575 vibrate();
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800576 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
577 mDigits.onKeyDown(keyCode, event);
578 }
579
580 public boolean onKey(View view, int keyCode, KeyEvent event) {
581 switch (view.getId()) {
582 case R.id.digits:
583 if (keyCode == KeyEvent.KEYCODE_ENTER) {
584 placeCall();
585 return true;
586 }
587 break;
588 }
589 return false;
590 }
591
592 public void onClick(View view) {
593 switch (view.getId()) {
594 case R.id.one: {
595 playTone(ToneGenerator.TONE_DTMF_1);
596 keyPressed(KeyEvent.KEYCODE_1);
597 return;
598 }
599 case R.id.two: {
600 playTone(ToneGenerator.TONE_DTMF_2);
601 keyPressed(KeyEvent.KEYCODE_2);
602 return;
603 }
604 case R.id.three: {
605 playTone(ToneGenerator.TONE_DTMF_3);
606 keyPressed(KeyEvent.KEYCODE_3);
607 return;
608 }
609 case R.id.four: {
610 playTone(ToneGenerator.TONE_DTMF_4);
611 keyPressed(KeyEvent.KEYCODE_4);
612 return;
613 }
614 case R.id.five: {
615 playTone(ToneGenerator.TONE_DTMF_5);
616 keyPressed(KeyEvent.KEYCODE_5);
617 return;
618 }
619 case R.id.six: {
620 playTone(ToneGenerator.TONE_DTMF_6);
621 keyPressed(KeyEvent.KEYCODE_6);
622 return;
623 }
624 case R.id.seven: {
625 playTone(ToneGenerator.TONE_DTMF_7);
626 keyPressed(KeyEvent.KEYCODE_7);
627 return;
628 }
629 case R.id.eight: {
630 playTone(ToneGenerator.TONE_DTMF_8);
631 keyPressed(KeyEvent.KEYCODE_8);
632 return;
633 }
634 case R.id.nine: {
635 playTone(ToneGenerator.TONE_DTMF_9);
636 keyPressed(KeyEvent.KEYCODE_9);
637 return;
638 }
639 case R.id.zero: {
640 playTone(ToneGenerator.TONE_DTMF_0);
641 keyPressed(KeyEvent.KEYCODE_0);
642 return;
643 }
644 case R.id.pound: {
645 playTone(ToneGenerator.TONE_DTMF_P);
646 keyPressed(KeyEvent.KEYCODE_POUND);
647 return;
648 }
649 case R.id.star: {
650 playTone(ToneGenerator.TONE_DTMF_S);
651 keyPressed(KeyEvent.KEYCODE_STAR);
652 return;
653 }
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700654 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800655 keyPressed(KeyEvent.KEYCODE_DEL);
656 return;
657 }
David Brown3d07e6d2009-08-04 20:30:09 -0700658 case R.id.dialButton:
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800659 case R.id.digits: {
David Brownc29c7ab2009-07-07 16:00:18 -0700660 vibrate(); // Vibrate here too, just like we do for the regular keys
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800661 placeCall();
662 return;
663 }
Nicolas Catania80bda0f2009-09-19 09:17:14 -0700664 case R.id.voicemailButton: {
665 callVoicemail();
666 vibrate();
667 return;
668 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800669 }
670 }
671
672 public boolean onLongClick(View view) {
673 final Editable digits = mDigits.getText();
674 int id = view.getId();
675 switch (id) {
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700676 case R.id.deleteButton: {
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800677 digits.clear();
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700678 // TODO: The framework forgets to clear the pressed
679 // status of disabled button. Until this is fixed,
680 // clear manually the pressed status. b/2133127
681 mDelete.setPressed(false);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800682 return true;
683 }
684 case R.id.one: {
685 if (digits.length() == 0) {
686 callVoicemail();
687 return true;
688 }
689 return false;
690 }
691 case R.id.zero: {
692 keyPressed(KeyEvent.KEYCODE_PLUS);
693 return true;
694 }
695 }
696 return false;
697 }
698
699 void callVoicemail() {
700 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
701 Uri.fromParts("voicemail", "", null));
702 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
703 startActivity(intent);
704 mDigits.getText().clear();
705 finish();
706 }
707
708 void placeCall() {
709 final String number = mDigits.getText().toString();
710 if (number == null || !TextUtils.isGraphic(number)) {
711 // There is no number entered.
712 playTone(ToneGenerator.TONE_PROP_NACK);
713 return;
714 }
715 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
716 Uri.fromParts("tel", number, null));
717 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
718 startActivity(intent);
719 mDigits.getText().clear();
720 finish();
721 }
722
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800723
724 /**
David Brown22f615f2009-06-25 16:19:19 -0700725 * Plays the specified tone for TONE_LENGTH_MS milliseconds.
726 *
727 * The tone is played locally, using the audio stream for phone calls.
728 * Tones are played only if the "Audible touch tones" user preference
729 * is checked, and are NOT played if the device is in silent mode.
730 *
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800731 * @param tone a tone code from {@link ToneGenerator}
732 */
733 void playTone(int tone) {
734 // if local tone playback is disabled, just return.
735 if (!mDTMFToneEnabled) {
736 return;
737 }
David Brown22f615f2009-06-25 16:19:19 -0700738
739 // Also do nothing if the phone is in silent mode.
740 // We need to re-check the ringer mode for *every* playTone()
741 // call, rather than keeping a local flag that's updated in
742 // onResume(), since it's possible to toggle silent mode without
743 // leaving the current activity (via the ENDCALL-longpress menu.)
744 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
David Brownd5a15302009-07-20 16:39:47 -0700745 int ringerMode = audioManager.getRingerMode();
746 if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
747 || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
David Brown22f615f2009-06-25 16:19:19 -0700748 return;
749 }
750
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800751 synchronized(mToneGeneratorLock) {
752 if (mToneGenerator == null) {
753 Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
754 return;
755 }
Eric Laurentd9efc872009-07-17 11:52:06 -0700756
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800757 // Start the new tone (will stop any playing tone)
Eric Laurent8487fed2009-09-07 08:45:14 -0700758 mToneGenerator.startTone(tone, TONE_LENGTH_MS);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800759 }
760 }
761
762 /**
763 * Brings up the "dialpad chooser" UI in place of the usual Dialer
764 * elements (the textfield/button and the dialpad underneath).
765 *
766 * We show this UI if the user brings up the Dialer while a call is
767 * already in progress, since there's a good chance we got here
768 * accidentally (and the user really wanted the in-call dialpad instead).
769 * So in this situation we display an intermediate UI that lets the user
770 * explicitly choose between the in-call dialpad ("Use touch tone
771 * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
772 * to call in progress" just goes back to the in-call UI with no dialpad
773 * at all.)
774 *
775 * @param enabled If true, show the "dialpad chooser" instead
776 * of the regular Dialer UI
777 */
778 private void showDialpadChooser(boolean enabled) {
779 if (enabled) {
780 // Log.i(TAG, "Showing dialpad chooser!");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700781 mDigits.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800782 if (mDialpad != null) mDialpad.setVisibility(View.GONE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700783 mVoicemailDialAndDeleteRow.setVisibility(View.GONE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800784 mDialpadChooser.setVisibility(View.VISIBLE);
785
786 // Instantiate the DialpadChooserAdapter and hook it up to the
787 // ListView. We do this only once.
788 if (mDialpadChooserAdapter == null) {
789 mDialpadChooserAdapter = new DialpadChooserAdapter(this);
790 mDialpadChooser.setAdapter(mDialpadChooserAdapter);
791 }
792 } else {
793 // Log.i(TAG, "Displaying normal Dialer UI.");
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700794 mDigits.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800795 if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -0700796 mVoicemailDialAndDeleteRow.setVisibility(View.VISIBLE);
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800797 mDialpadChooser.setVisibility(View.GONE);
798 }
799 }
800
801 /**
802 * @return true if we're currently showing the "dialpad chooser" UI.
803 */
804 private boolean dialpadChooserVisible() {
805 return mDialpadChooser.getVisibility() == View.VISIBLE;
806 }
807
808 /**
809 * Simple list adapter, binding to an icon + text label
810 * for each item in the "dialpad chooser" list.
811 */
812 private static class DialpadChooserAdapter extends BaseAdapter {
813 private LayoutInflater mInflater;
814
815 // Simple struct for a single "choice" item.
816 static class ChoiceItem {
817 String text;
818 Bitmap icon;
819 int id;
820
821 public ChoiceItem(String s, Bitmap b, int i) {
822 text = s;
823 icon = b;
824 id = i;
825 }
826 }
827
828 // IDs for the possible "choices":
829 static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
830 static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
831 static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
832
833 private static final int NUM_ITEMS = 3;
834 private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
835
836 public DialpadChooserAdapter(Context context) {
837 // Cache the LayoutInflate to avoid asking for a new one each time.
838 mInflater = LayoutInflater.from(context);
839
840 // Initialize the possible choices.
841 // TODO: could this be specified entirely in XML?
842
843 // - "Use touch tone keypad"
844 mChoiceItems[0] = new ChoiceItem(
845 context.getString(R.string.dialer_useDtmfDialpad),
846 BitmapFactory.decodeResource(context.getResources(),
847 R.drawable.ic_dialer_fork_tt_keypad),
848 DIALPAD_CHOICE_USE_DTMF_DIALPAD);
849
850 // - "Return to call in progress"
851 mChoiceItems[1] = new ChoiceItem(
852 context.getString(R.string.dialer_returnToInCallScreen),
853 BitmapFactory.decodeResource(context.getResources(),
854 R.drawable.ic_dialer_fork_current_call),
855 DIALPAD_CHOICE_RETURN_TO_CALL);
856
857 // - "Add call"
858 mChoiceItems[2] = new ChoiceItem(
859 context.getString(R.string.dialer_addAnotherCall),
860 BitmapFactory.decodeResource(context.getResources(),
861 R.drawable.ic_dialer_fork_add_call),
862 DIALPAD_CHOICE_ADD_NEW_CALL);
863 }
864
865 public int getCount() {
866 return NUM_ITEMS;
867 }
868
869 /**
870 * Return the ChoiceItem for a given position.
871 */
872 public Object getItem(int position) {
873 return mChoiceItems[position];
874 }
875
876 /**
877 * Return a unique ID for each possible choice.
878 */
879 public long getItemId(int position) {
880 return position;
881 }
882
883 /**
884 * Make a view for each row.
885 */
886 public View getView(int position, View convertView, ViewGroup parent) {
887 // When convertView is non-null, we can reuse it (there's no need
888 // to reinflate it.)
889 if (convertView == null) {
890 convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
891 }
892
893 TextView text = (TextView) convertView.findViewById(R.id.text);
894 text.setText(mChoiceItems[position].text);
895
896 ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
897 icon.setImageBitmap(mChoiceItems[position].icon);
898
899 return convertView;
900 }
901 }
902
903 /**
904 * Handle clicks from the dialpad chooser.
905 */
906 public void onItemClick(AdapterView parent, View v, int position, long id) {
907 DialpadChooserAdapter.ChoiceItem item =
908 (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
909 int itemId = item.id;
910 switch (itemId) {
911 case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
912 // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
913 // Fire off an intent to go back to the in-call UI
914 // with the dialpad visible.
915 returnToInCallScreen(true);
916 break;
917
918 case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
919 // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
920 // Fire off an intent to go back to the in-call UI
921 // (with the dialpad hidden).
922 returnToInCallScreen(false);
923 break;
924
925 case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
926 // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
927 // Ok, guess the user really did want to be here (in the
928 // regular Dialer) after all. Bring back the normal Dialer UI.
929 showDialpadChooser(false);
930 break;
931
932 default:
933 Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
934 break;
935 }
936 }
937
938 /**
939 * Returns to the in-call UI (where there's presumably a call in
940 * progress) in response to the user selecting "use touch tone keypad"
941 * or "return to call" from the dialpad chooser.
942 */
943 private void returnToInCallScreen(boolean showDialpad) {
944 try {
945 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
946 if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
947 } catch (RemoteException e) {
948 Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
949 }
950
951 // Finally, finish() ourselves so that we don't stay on the
952 // activity stack.
953 // Note that we do this whether or not the showCallScreenWithDialpad()
954 // call above had any effect or not! (That call is a no-op if the
955 // phone is idle, which can happen if the current call ends while
956 // the dialpad chooser is up. In this case we can't show the
957 // InCallScreen, and there's no point staying here in the Dialer,
958 // so we just take the user back where he came from...)
959 finish();
960 }
961
962 /**
963 * @return true if the phone is "in use", meaning that at least one line
964 * is active (ie. off hook or ringing or dialing).
965 */
966 private boolean phoneIsInUse() {
967 boolean phoneInUse = false;
968 try {
969 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
970 if (phone != null) phoneInUse = !phone.isIdle();
971 } catch (RemoteException e) {
972 Log.w(TAG, "phone.isIdle() failed", e);
973 }
974 return phoneInUse;
975 }
David Brownc29c7ab2009-07-07 16:00:18 -0700976
977 /**
978 * Triggers haptic feedback (if enabled) for dialer key presses.
979 */
980 private synchronized void vibrate() {
981 if (!mVibrateOn) {
982 return;
983 }
984 if (mVibrator == null) {
985 mVibrator = new Vibrator();
986 }
987 mVibrator.vibrate(mVibrateDuration);
988 }
Reli Talc2a2a512009-06-10 16:48:00 -0400989
990 /**
991 * Returns true whenever any one of the options from the menu is selected.
992 * Code changes to support dialpad options
993 */
994 @Override
995 public boolean onOptionsItemSelected(MenuItem item) {
996 switch (item.getItemId()) {
997 case MENU_2S_PAUSE:
998 updateDialString(",");
999 return true;
1000 case MENU_WAIT:
1001 updateDialString(";");
1002 return true;
1003 }
1004 return false;
1005 }
1006
1007 /**
1008 * Updates the dial string (mDigits) after inserting a Pause character (,)
1009 * or Wait character (;).
1010 */
1011 private void updateDialString(String newDigits) {
1012 int selectionStart;
1013 int selectionEnd;
1014
1015 // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
Eric Fischer686782e2009-09-10 17:57:45 -07001016 int anchor = mDigits.getSelectionStart();
1017 int point = mDigits.getSelectionEnd();
1018
1019 selectionStart = Math.min(anchor, point);
1020 selectionEnd = Math.max(anchor, point);
Reli Talc2a2a512009-06-10 16:48:00 -04001021
1022 Editable digits = mDigits.getText();
1023 if (selectionStart != -1 ) {
1024 if (selectionStart == selectionEnd) {
1025 // then there is no selection. So insert the pause at this
1026 // position and update the mDigits.
1027 digits.replace(selectionStart, selectionStart, newDigits);
1028 } else {
Eric Fischer1e2d3a22009-09-17 10:53:10 -07001029 digits.replace(selectionStart, selectionEnd, newDigits);
Reli Talc2a2a512009-06-10 16:48:00 -04001030 }
1031 } else {
1032 int len = mDigits.length();
1033 digits.replace(len, len, newDigits);
1034 }
1035 }
1036
1037 /**
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001038 * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001039 */
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001040 private void updateDialAndDeleteButtonStateEnabledAttr() {
1041 final boolean notEmpty = mDigits.length() != 0;
1042
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001043 if (mDialButton != null) {
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001044 mDialButton.setEnabled(notEmpty);
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001045 }
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001046 mDelete.setEnabled(notEmpty);
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001047 }
1048
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001049
Nicolas Cataniadea164e2009-09-18 06:26:16 -07001050 /**
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001051 * Check if voicemail is enabled/accessible.
1052 */
1053 private void initVoicemailButton() {
1054 boolean hasVoicemail = false;
1055 try {
1056 hasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
1057 } catch (SecurityException se) {
1058 // Possibly no READ_PHONE_STATE privilege.
1059 }
1060
Nicolas Cataniaa7e5a5b2009-09-20 10:56:40 -07001061 mVoicemailButton = mVoicemailDialAndDeleteRow.findViewById(R.id.voicemailButton);
Nicolas Catania80bda0f2009-09-19 09:17:14 -07001062 if (hasVoicemail) {
1063 mVoicemailButton.setOnClickListener(this);
1064 } else {
1065 mVoicemailButton.setEnabled(false);
1066 }
1067 }
1068
1069 /**
Reli Talc2a2a512009-06-10 16:48:00 -04001070 * This function return true if Wait menu item can be shown
1071 * otherwise returns false. Assumes the passed string is non-empty
1072 * and the 0th index check is not required.
1073 */
1074 private boolean showWait(int start, int end, String digits) {
1075 if (start == end) {
1076 // visible false in this case
1077 if (start > digits.length()) return false;
1078
1079 // preceding char is ';', so visible should be false
1080 if (digits.charAt(start-1) == ';') return false;
1081
1082 // next char is ';', so visible should be false
1083 if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
1084 } else {
1085 // visible false in this case
1086 if (start > digits.length() || end > digits.length()) return false;
1087
1088 // In this case we need to just check for ';' preceding to start
1089 // or next to end
1090 if (digits.charAt(start-1) == ';') return false;
1091 }
1092 return true;
1093 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001094}