/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dialer.app.dialpad; import android.Manifest.permission; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.app.Fragment; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.AudioManager; import android.media.ToneGenerator; import android.net.Uri; import android.os.Bundle; import android.os.Trace; import android.provider.Contacts.People; import android.provider.Contacts.Phones; import android.provider.Contacts.PhonesColumns; import android.provider.Settings; import android.support.annotation.VisibleForTesting; import android.support.v4.content.ContextCompat; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telephony.PhoneNumberFormattingTextWatcher; import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ListView; import android.widget.PopupMenu; import android.widget.RelativeLayout; import android.widget.TextView; import com.android.contacts.common.GeoUtil; import com.android.contacts.common.dialog.CallSubjectDialog; import com.android.contacts.common.util.StopWatch; import com.android.contacts.common.widget.FloatingActionButtonController; import com.android.dialer.animation.AnimUtils; import com.android.dialer.app.DialtactsActivity; import com.android.dialer.app.R; import com.android.dialer.app.SpecialCharSequenceMgr; import com.android.dialer.app.calllog.CallLogAsync; import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.callintent.nano.CallInitiationType; import com.android.dialer.calllogutils.PhoneAccountUtils; import com.android.dialer.common.LogUtil; import com.android.dialer.dialpadview.DialpadKeyButton; import com.android.dialer.dialpadview.DialpadView; import com.android.dialer.proguard.UsedByReflection; import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.util.CallUtil; import com.android.dialer.util.DialerUtils; import com.android.dialer.util.PermissionsUtil; import java.util.HashSet; import java.util.List; /** Fragment that displays a twelve-key phone dialpad. */ public class DialpadFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener, View.OnKeyListener, AdapterView.OnItemClickListener, TextWatcher, PopupMenu.OnMenuItemClickListener, DialpadKeyButton.OnPressedListener { private static final String TAG = "DialpadFragment"; private static final boolean DEBUG = DialtactsActivity.DEBUG; private static final String EMPTY_NUMBER = ""; private static final char PAUSE = ','; private static final char WAIT = ';'; /** The length of DTMF tones in milliseconds */ private static final int TONE_LENGTH_MS = 150; private static final int TONE_LENGTH_INFINITE = -1; /** The DTMF tone volume relative to other sounds in the stream */ private static final int TONE_RELATIVE_VOLUME = 80; /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */ private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF; /** Identifier for the "Add Call" intent extra. */ private static final String ADD_CALL_MODE_KEY = "add_call_mode"; /** * Identifier for intent extra for sending an empty Flash message for CDMA networks. This message * is used by the network to simulate a press/depress of the "hookswitch" of a landline phone. Aka * "empty flash". * *
TODO: Using an intent extra to tell the phone to send this flash is a temporary measure. To
* be replaced with an Telephony/TelecomManager call in the future. TODO: Keep in sync with the
* string defined in OutgoingCallBroadcaster.java in Phone app until this is replaced with the
* Telephony/Telecom API.
*/
private static final String EXTRA_SEND_EMPTY_FLASH = "com.android.phone.extra.SEND_EMPTY_FLASH";
private static final String PREF_DIGITS_FILLED_BY_INTENT = "pref_digits_filled_by_intent";
private final Object mToneGeneratorLock = new Object();
/** Set of dialpad keys that are currently being pressed */
private final HashSet When there is no digit and the phone is CDMA and off hook, we're sending a blank flash for
* CDMA. CDMA networks use Flash messages when special processing needs to be done, mainly for
* 3-way or call waiting scenarios. Presumably, here we're in a special 3-way scenario where the
* network needs a blank flash before being able to add the new participant. (This is not the case
* with all 3-way calls, just certain CDMA infrastructures.)
*
* Otherwise, there is no digit, display the last dialed number. Don't finish since the user
* may want to edit it. The user needs to press the dial button again, to dial it (general case
* described above).
*/
private void handleDialButtonPressed() {
if (isDigitsEmpty()) { // No number entered.
handleDialButtonClickWithEmptyDigits();
} else {
final String number = mDigits.getText().toString();
// "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
// test equipment.
// TODO: clean it up.
if (number != null
&& !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
&& number.matches(mProhibitedPhoneNumberRegexp)) {
LogUtil.i(
"DialpadFragment.handleDialButtonPressed",
"The phone number is prohibited explicitly by a rule.");
if (getActivity() != null) {
DialogFragment dialogFragment =
ErrorDialogFragment.newInstance(R.string.dialog_phone_call_prohibited_message);
dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
}
// Clear the digits just in case.
clearDialpad();
} else {
final Intent intent =
new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD).build();
DialerUtils.startActivityWithErrorToast(getActivity(), intent);
hideAndClearDialpad(false);
}
}
}
public void clearDialpad() {
if (mDigits != null) {
mDigits.getText().clear();
}
}
private void handleDialButtonClickWithEmptyDigits() {
if (phoneIsCdma() && isPhoneInUse()) {
// TODO: Move this logic into services/Telephony
//
// This is really CDMA specific. On GSM is it possible
// to be off hook and wanted to add a 3rd party using
// the redial feature.
startActivity(newFlashIntent());
} else {
if (!TextUtils.isEmpty(mLastNumberDialed)) {
// Recall the last number dialed.
mDigits.setText(mLastNumberDialed);
// ...and move the cursor to the end of the digits string,
// so you'll be able to delete digits using the Delete
// button (just as if you had typed the number manually.)
//
// Note we use mDigits.getText().length() here, not
// mLastNumberDialed.length(), since the EditText widget now
// contains a *formatted* version of mLastNumberDialed (due to
// mTextWatcher) and its length may have changed.
mDigits.setSelection(mDigits.getText().length());
} else {
// There's no "last number dialed" or the
// background query is still running. There's
// nothing useful for the Dial button to do in
// this case. Note: with a soft dial button, this
// can never happens since the dial button is
// disabled under these conditons.
playTone(ToneGenerator.TONE_PROP_NACK);
}
}
}
/** Plays the specified tone for TONE_LENGTH_MS milliseconds. */
private void playTone(int tone) {
playTone(tone, TONE_LENGTH_MS);
}
/**
* Play the specified tone for the specified milliseconds
*
* The tone is played locally, using the audio stream for phone calls. Tones are played only if
* the "Audible touch tones" user preference is checked, and are NOT played if the device is in
* silent mode.
*
* The tone length can be -1, meaning "keep playing the tone." If the caller does so, it should
* call stopTone() afterward.
*
* @param tone a tone code from {@link ToneGenerator}
* @param durationMs tone length.
*/
private void playTone(int tone, int durationMs) {
// if local tone playback is disabled, just return.
if (!mDTMFToneEnabled) {
return;
}
// Also do nothing if the phone is in silent mode.
// We need to re-check the ringer mode for *every* playTone()
// call, rather than keeping a local flag that's updated in
// onResume(), since it's possible to toggle silent mode without
// leaving the current activity (via the ENDCALL-longpress menu.)
AudioManager audioManager =
(AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
int ringerMode = audioManager.getRingerMode();
if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
|| (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
return;
}
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
LogUtil.w("DialpadFragment.playTone", "mToneGenerator == null, tone: " + tone);
return;
}
// Start the new tone (will stop any playing tone)
mToneGenerator.startTone(tone, durationMs);
}
}
/** Stop the tone if it is played. */
private void stopTone() {
// if local tone playback is disabled, just return.
if (!mDTMFToneEnabled) {
return;
}
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
LogUtil.w("DialpadFragment.stopTone", "mToneGenerator == null");
return;
}
mToneGenerator.stopTone();
}
}
/**
* Brings up the "dialpad chooser" UI in place of the usual Dialer elements (the textfield/button
* and the dialpad underneath).
*
* We show this UI if the user brings up the Dialer while a call is already in progress, since
* there's a good chance we got here accidentally (and the user really wanted the in-call dialpad
* instead). So in this situation we display an intermediate UI that lets the user explicitly
* choose between the in-call dialpad ("Use touch tone keypad") and the regular Dialer ("Add
* call"). (Or, the option "Return to call in progress" just goes back to the in-call UI with no
* dialpad at all.)
*
* @param enabled If true, show the "dialpad chooser" instead of the regular Dialer UI
*/
private void showDialpadChooser(boolean enabled) {
if (getActivity() == null) {
return;
}
// Check if onCreateView() is already called by checking one of View objects.
if (!isLayoutReady()) {
return;
}
if (enabled) {
LogUtil.i("DialpadFragment.showDialpadChooser", "Showing dialpad chooser!");
if (mDialpadView != null) {
mDialpadView.setVisibility(View.GONE);
}
mFloatingActionButtonController.setVisible(false);
mDialpadChooser.setVisibility(View.VISIBLE);
// Instantiate the DialpadChooserAdapter and hook it up to the
// ListView. We do this only once.
if (mDialpadChooserAdapter == null) {
mDialpadChooserAdapter = new DialpadChooserAdapter(getActivity());
}
mDialpadChooser.setAdapter(mDialpadChooserAdapter);
} else {
LogUtil.i("DialpadFragment.showDialpadChooser", "Displaying normal Dialer UI.");
if (mDialpadView != null) {
mDialpadView.setVisibility(View.VISIBLE);
} else {
mDigits.setVisibility(View.VISIBLE);
}
// mFloatingActionButtonController must also be 'scaled in', in order to be visible after
// 'scaleOut()' hidden method.
if (!mFloatingActionButtonController.isVisible()) {
// Just call 'scaleIn()' method if the mFloatingActionButtonController was not already
// previously visible.
mFloatingActionButtonController.scaleIn(0);
mFloatingActionButtonController.setVisible(true);
}
mDialpadChooser.setVisibility(View.GONE);
}
}
/** @return true if we're currently showing the "dialpad chooser" UI. */
private boolean isDialpadChooserVisible() {
return mDialpadChooser.getVisibility() == View.VISIBLE;
}
/** Handle clicks from the dialpad chooser. */
@Override
public void onItemClick(AdapterView> parent, View v, int position, long id) {
DialpadChooserAdapter.ChoiceItem item =
(DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
int itemId = item.id;
if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD) {
// Fire off an intent to go back to the in-call UI
// with the dialpad visible.
returnToInCallScreen(true);
} else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL) {
// Fire off an intent to go back to the in-call UI
// (with the dialpad hidden).
returnToInCallScreen(false);
} else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL) {
// Ok, guess the user really did want to be here (in the
// regular Dialer) after all. Bring back the normal Dialer UI.
showDialpadChooser(false);
} else {
LogUtil.w("DialpadFragment.onItemClick", "Unexpected itemId: " + itemId);
}
}
/**
* Returns to the in-call UI (where there's presumably a call in progress) in response to the user
* selecting "use touch tone keypad" or "return to call" from the dialpad chooser.
*/
private void returnToInCallScreen(boolean showDialpad) {
TelecomUtil.showInCallScreen(getActivity(), showDialpad);
// Finally, finish() ourselves so that we don't stay on the
// activity stack.
// Note that we do this whether or not the showCallScreenWithDialpad()
// call above had any effect or not! (That call is a no-op if the
// phone is idle, which can happen if the current call ends while
// the dialpad chooser is up. In this case we can't show the
// InCallScreen, and there's no point staying here in the Dialer,
// so we just take the user back where he came from...)
getActivity().finish();
}
/**
* @return true if the phone is "in use", meaning that at least one line is active (ie. off hook
* or ringing or dialing, or on hold).
*/
private boolean isPhoneInUse() {
final Context context = getActivity();
if (context != null) {
return TelecomUtil.isInCall(context);
}
return false;
}
/** @return true if the phone is a CDMA phone type */
private boolean phoneIsCdma() {
return getTelephonyManager().getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
int resId = item.getItemId();
if (resId == R.id.menu_2s_pause) {
updateDialString(PAUSE);
return true;
} else if (resId == R.id.menu_add_wait) {
updateDialString(WAIT);
return true;
} else if (resId == R.id.menu_call_with_note) {
CallSubjectDialog.start(getActivity(), mDigits.getText().toString());
hideAndClearDialpad(false);
return true;
} else {
return false;
}
}
/**
* Updates the dial string (mDigits) after inserting a Pause character (,) or Wait character (;).
*/
private void updateDialString(char newDigit) {
if (newDigit != WAIT && newDigit != PAUSE) {
throw new IllegalArgumentException("Not expected for anything other than PAUSE & WAIT");
}
int selectionStart;
int selectionEnd;
// SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
int anchor = mDigits.getSelectionStart();
int point = mDigits.getSelectionEnd();
selectionStart = Math.min(anchor, point);
selectionEnd = Math.max(anchor, point);
if (selectionStart == -1) {
selectionStart = selectionEnd = mDigits.length();
}
Editable digits = mDigits.getText();
if (canAddDigit(digits, selectionStart, selectionEnd, newDigit)) {
digits.replace(selectionStart, selectionEnd, Character.toString(newDigit));
if (selectionStart != selectionEnd) {
// Unselect: back to a regular cursor, just pass the character inserted.
mDigits.setSelection(selectionStart + 1);
}
}
}
/** Update the enabledness of the "Dial" and "Backspace" buttons if applicable. */
private void updateDeleteButtonEnabledState() {
if (getActivity() == null) {
return;
}
final boolean digitsNotEmpty = !isDigitsEmpty();
mDelete.setEnabled(digitsNotEmpty);
}
/**
* Handle transitions for the menu button depending on the state of the digits edit text.
* Transition out when going from digits to no digits and transition in when the first digit is
* pressed.
*
* @param transitionIn True if transitioning in, False if transitioning out
*/
private void updateMenuOverflowButton(boolean transitionIn) {
mOverflowMenuButton = mDialpadView.getOverflowMenuButton();
if (transitionIn) {
AnimUtils.fadeIn(mOverflowMenuButton, AnimUtils.DEFAULT_DURATION);
} else {
AnimUtils.fadeOut(mOverflowMenuButton, AnimUtils.DEFAULT_DURATION);
}
}
/**
* Check if voicemail is enabled/accessible.
*
* @return true if voicemail is enabled and accessible. Note that this can be false "temporarily"
* after the app boot.
*/
private boolean isVoicemailAvailable() {
try {
PhoneAccountHandle defaultUserSelectedAccount =
TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(), PhoneAccount.SCHEME_VOICEMAIL);
if (defaultUserSelectedAccount == null) {
// In a single-SIM phone, there is no default outgoing phone account selected by
// the user, so just call TelephonyManager#getVoicemailNumber directly.
return !TextUtils.isEmpty(getTelephonyManager().getVoiceMailNumber());
} else {
return !TextUtils.isEmpty(
TelecomUtil.getVoicemailNumber(getActivity(), defaultUserSelectedAccount));
}
} catch (SecurityException se) {
// Possibly no READ_PHONE_STATE privilege.
LogUtil.w(
"DialpadFragment.isVoicemailAvailable",
"SecurityException is thrown. Maybe privilege isn't sufficient.");
}
return false;
}
/** @return true if the widget with the phone number digits is empty. */
private boolean isDigitsEmpty() {
return mDigits.length() == 0;
}
/**
* Starts the asyn query to get the last dialed/outgoing number. When the background query
* finishes, mLastNumberDialed is set to the last dialed number or an empty string if none exists
* yet.
*/
private void queryLastOutgoingCall() {
mLastNumberDialed = EMPTY_NUMBER;
if (ContextCompat.checkSelfPermission(getActivity(), permission.READ_CALL_LOG)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
new CallLogAsync.GetLastOutgoingCallArgs(
getActivity(),
new CallLogAsync.OnLastOutgoingCallComplete() {
@Override
public void lastOutgoingCall(String number) {
// TODO: Filter out emergency numbers if
// the carrier does not want redial for
// these.
// If the fragment has already been detached since the last time
// we called queryLastOutgoingCall in onResume there is no point
// doing anything here.
if (getActivity() == null) {
return;
}
mLastNumberDialed = number;
updateDeleteButtonEnabledState();
}
});
mCallLog.getLastOutgoingCall(lastCallArgs);
}
private Intent newFlashIntent() {
Intent intent = new CallIntentBuilder(EMPTY_NUMBER, CallInitiationType.Type.DIALPAD).build();
intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
return intent;
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
final DialtactsActivity activity = (DialtactsActivity) getActivity();
final DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
if (activity == null) {
return;
}
if (!hidden && !isDialpadChooserVisible()) {
if (mAnimate) {
dialpadView.animateShow();
}
mFloatingActionButtonController.setVisible(false);
mFloatingActionButtonController.scaleIn(mAnimate ? mDialpadSlideInDuration : 0);
activity.onDialpadShown();
mDigits.requestFocus();
}
if (hidden) {
if (mAnimate) {
mFloatingActionButtonController.scaleOut();
} else {
mFloatingActionButtonController.setVisible(false);
}
}
}
public boolean getAnimate() {
return mAnimate;
}
public void setAnimate(boolean value) {
mAnimate = value;
}
public void setYFraction(float yFraction) {
((DialpadSlidingRelativeLayout) getView()).setYFraction(yFraction);
}
public int getDialpadHeight() {
if (mDialpadView == null) {
return 0;
}
return mDialpadView.getHeight();
}
public void process_quote_emergency_unquote(String query) {
if (PseudoEmergencyAnimator.PSEUDO_EMERGENCY_NUMBER.equals(query)) {
if (mPseudoEmergencyAnimator == null) {
mPseudoEmergencyAnimator =
new PseudoEmergencyAnimator(
new PseudoEmergencyAnimator.ViewProvider() {
@Override
public View getView() {
return DialpadFragment.this.getView();
}
});
}
mPseudoEmergencyAnimator.start();
} else {
if (mPseudoEmergencyAnimator != null) {
mPseudoEmergencyAnimator.end();
}
}
}
public interface OnDialpadQueryChangedListener {
void onDialpadQueryChanged(String query);
}
public interface HostInterface {
/**
* Notifies the parent activity that the space above the dialpad has been tapped with no query
* in the dialpad present. In most situations this will cause the dialpad to be dismissed,
* unless there happens to be content showing.
*/
boolean onDialpadSpacerTouchWithEmptyQuery();
}
/**
* LinearLayout with getter and setter methods for the translationY property using floats, for
* animation purposes.
*/
public static class DialpadSlidingRelativeLayout extends RelativeLayout {
public DialpadSlidingRelativeLayout(Context context) {
super(context);
}
public DialpadSlidingRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DialpadSlidingRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@UsedByReflection(value = "dialpad_fragment.xml")
public float getYFraction() {
final int height = getHeight();
if (height == 0) {
return 0;
}
return getTranslationY() / height;
}
@UsedByReflection(value = "dialpad_fragment.xml")
public void setYFraction(float yFraction) {
setTranslationY(yFraction * getHeight());
}
}
public static class ErrorDialogFragment extends DialogFragment {
private static final String ARG_TITLE_RES_ID = "argTitleResId";
private static final String ARG_MESSAGE_RES_ID = "argMessageResId";
private int mTitleResId;
private int mMessageResId;
public static ErrorDialogFragment newInstance(int messageResId) {
return newInstance(0, messageResId);
}
public static ErrorDialogFragment newInstance(int titleResId, int messageResId) {
final ErrorDialogFragment fragment = new ErrorDialogFragment();
final Bundle args = new Bundle();
args.putInt(ARG_TITLE_RES_ID, titleResId);
args.putInt(ARG_MESSAGE_RES_ID, messageResId);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTitleResId = getArguments().getInt(ARG_TITLE_RES_ID);
mMessageResId = getArguments().getInt(ARG_MESSAGE_RES_ID);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
if (mTitleResId != 0) {
builder.setTitle(mTitleResId);
}
if (mMessageResId != 0) {
builder.setMessage(mMessageResId);
}
builder.setPositiveButton(
android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
});
return builder.create();
}
}
/**
* Simple list adapter, binding to an icon + text label for each item in the "dialpad chooser"
* list.
*/
private static class DialpadChooserAdapter extends BaseAdapter {
// IDs for the possible "choices":
static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
private static final int NUM_ITEMS = 3;
private LayoutInflater mInflater;
private ChoiceItem[] mChoiceItems = new ChoiceItem[NUM_ITEMS];
public DialpadChooserAdapter(Context context) {
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
// Initialize the possible choices.
// TODO: could this be specified entirely in XML?
// - "Use touch tone keypad"
mChoiceItems[0] =
new ChoiceItem(
context.getString(R.string.dialer_useDtmfDialpad),
BitmapFactory.decodeResource(
context.getResources(), R.drawable.ic_dialer_fork_tt_keypad),
DIALPAD_CHOICE_USE_DTMF_DIALPAD);
// - "Return to call in progress"
mChoiceItems[1] =
new ChoiceItem(
context.getString(R.string.dialer_returnToInCallScreen),
BitmapFactory.decodeResource(
context.getResources(), R.drawable.ic_dialer_fork_current_call),
DIALPAD_CHOICE_RETURN_TO_CALL);
// - "Add call"
mChoiceItems[2] =
new ChoiceItem(
context.getString(R.string.dialer_addAnotherCall),
BitmapFactory.decodeResource(
context.getResources(), R.drawable.ic_dialer_fork_add_call),
DIALPAD_CHOICE_ADD_NEW_CALL);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
/** Return the ChoiceItem for a given position. */
@Override
public Object getItem(int position) {
return mChoiceItems[position];
}
/** Return a unique ID for each possible choice. */
@Override
public long getItemId(int position) {
return position;
}
/** Make a view for each row. */
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// When convertView is non-null, we can reuse it (there's no need
// to reinflate it.)
if (convertView == null) {
convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
}
TextView text = (TextView) convertView.findViewById(R.id.text);
text.setText(mChoiceItems[position].text);
ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
icon.setImageBitmap(mChoiceItems[position].icon);
return convertView;
}
// Simple struct for a single "choice" item.
static class ChoiceItem {
String text;
Bitmap icon;
int id;
public ChoiceItem(String s, Bitmap b, int i) {
text = s;
icon = b;
id = i;
}
}
}
private class CallStateReceiver extends BroadcastReceiver {
/**
* Receive call state changes so that we can take down the "dialpad chooser" if the phone
* becomes idle while the chooser UI is visible.
*/
@Override
public void onReceive(Context context, Intent intent) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if ((TextUtils.equals(state, TelephonyManager.EXTRA_STATE_IDLE)
|| TextUtils.equals(state, TelephonyManager.EXTRA_STATE_OFFHOOK))
&& isDialpadChooserVisible()) {
// Note there's a race condition in the UI here: the
// dialpad chooser could conceivably disappear (on its
// own) at the exact moment the user was trying to select
// one of the choices, which would be confusing. (But at
// least that's better than leaving the dialpad chooser
// onscreen, but useless...)
showDialpadChooser(false);
}
}
}
}