|  | /* | 
|  | * Copyright (C) 2008 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.phone; | 
|  |  | 
|  | import static android.view.View.LAYOUT_DIRECTION_LOCALE; | 
|  | import static android.view.View.TEXT_DIRECTION_LOCALE; | 
|  |  | 
|  | import android.app.Activity; | 
|  | import android.app.AlertDialog; | 
|  | import android.content.Context; | 
|  | import android.content.DialogInterface; | 
|  | import android.content.Intent; | 
|  | import android.content.res.TypedArray; | 
|  | import android.preference.EditTextPreference; | 
|  | import android.provider.ContactsContract.CommonDataKinds.Phone; | 
|  | import android.telephony.PhoneNumberUtils; | 
|  | import android.text.BidiFormatter; | 
|  | import android.text.TextDirectionHeuristics; | 
|  | import android.text.TextUtils; | 
|  | import android.text.method.ArrowKeyMovementMethod; | 
|  | import android.text.method.DialerKeyListener; | 
|  | import android.util.AttributeSet; | 
|  | import android.view.View; | 
|  | import android.view.ViewGroup; | 
|  | import android.widget.EditText; | 
|  | import android.widget.ImageButton; | 
|  | import android.widget.TextView; | 
|  |  | 
|  | import com.android.internal.telephony.CommandsInterface; | 
|  |  | 
|  | public class EditPhoneNumberPreference extends EditTextPreference { | 
|  |  | 
|  | //allowed modes for this preference. | 
|  | /** simple confirmation (OK / CANCEL) */ | 
|  | private static final int CM_CONFIRM = 0; | 
|  | /** toggle [(ENABLE / CANCEL) or (DISABLE / CANCEL)], use isToggled() to see requested state.*/ | 
|  | private static final int CM_ACTIVATION = 1; | 
|  |  | 
|  | private int mConfirmationMode; | 
|  |  | 
|  | //String constants used in storing the value of the preference | 
|  | // The preference is backed by a string that holds the encoded value, which reads: | 
|  | //  <VALUE_ON | VALUE_OFF><VALUE_SEPARATOR><mPhoneNumber> | 
|  | // for example, an enabled preference with a number of 6502345678 would read: | 
|  | //  "1:6502345678" | 
|  | private static final String VALUE_SEPARATOR = ":"; | 
|  | private static final String VALUE_OFF = "0"; | 
|  | private static final String VALUE_ON = "1"; | 
|  |  | 
|  | //UI layout | 
|  | private ImageButton mContactPickButton; | 
|  |  | 
|  | //Listeners | 
|  | /** Called when focus is changed between fields */ | 
|  | private View.OnFocusChangeListener mDialogFocusChangeListener; | 
|  | /** Called when the Dialog is closed. */ | 
|  | private OnDialogClosedListener mDialogOnClosedListener; | 
|  | /** | 
|  | * Used to indicate that we are going to request for a | 
|  | * default number. for the dialog. | 
|  | */ | 
|  | private GetDefaultNumberListener mGetDefaultNumberListener; | 
|  |  | 
|  | //Activity values | 
|  | private Activity mParentActivity; | 
|  | private Intent mContactListIntent; | 
|  | /** Arbitrary activity-assigned preference id value */ | 
|  | private int mPrefId; | 
|  |  | 
|  | //similar to toggle preference | 
|  | private CharSequence mEnableText; | 
|  | private CharSequence mDisableText; | 
|  | private CharSequence mChangeNumberText; | 
|  | private CharSequence mSummaryOn; | 
|  | private CharSequence mSummaryOff; | 
|  |  | 
|  | // button that was clicked on dialog close. | 
|  | private int mButtonClicked; | 
|  |  | 
|  | //relevant (parsed) value of the mText | 
|  | private String mPhoneNumber; | 
|  | private boolean mChecked; | 
|  |  | 
|  | private boolean mIsUnknownStatus; | 
|  |  | 
|  | /** | 
|  | * Interface for the dialog closed listener, related to | 
|  | * DialogPreference.onDialogClosed(), except we also pass in a buttonClicked | 
|  | * value indicating which of the three possible buttons were pressed. | 
|  | */ | 
|  | public interface OnDialogClosedListener { | 
|  | void onDialogClosed(EditPhoneNumberPreference preference, int buttonClicked); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Interface for the default number setting listener.  Handles requests for | 
|  | * the default display number for the dialog. | 
|  | */ | 
|  | public interface GetDefaultNumberListener { | 
|  | /** | 
|  | * Notify that we are looking for a default display value. | 
|  | * @return null if there is no contribution from this interface, | 
|  | *  indicating that the orignal value of mPhoneNumber should be | 
|  | *  displayed unchanged. | 
|  | */ | 
|  | String onGetDefaultNumber(EditPhoneNumberPreference preference); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Constructors | 
|  | */ | 
|  | public EditPhoneNumberPreference(Context context, AttributeSet attrs) { | 
|  | super(context, attrs); | 
|  |  | 
|  | setDialogLayoutResource(R.layout.pref_dialog_editphonenumber); | 
|  |  | 
|  | //create intent to bring up contact list | 
|  | mContactListIntent = new Intent(Intent.ACTION_PICK); | 
|  | mContactListIntent.setType(Phone.CONTENT_TYPE); | 
|  |  | 
|  | //get the edit phone number default settings | 
|  | TypedArray a = context.obtainStyledAttributes(attrs, | 
|  | R.styleable.EditPhoneNumberPreference, 0, R.style.EditPhoneNumberPreference); | 
|  | mEnableText = a.getString(R.styleable.EditPhoneNumberPreference_enableButtonText); | 
|  | mDisableText = a.getString(R.styleable.EditPhoneNumberPreference_disableButtonText); | 
|  | mChangeNumberText = a.getString(R.styleable.EditPhoneNumberPreference_changeNumButtonText); | 
|  | mConfirmationMode = a.getInt(R.styleable.EditPhoneNumberPreference_confirmMode, 0); | 
|  | a.recycle(); | 
|  |  | 
|  | //get the summary settings, use CheckBoxPreference as the standard. | 
|  | a = context.obtainStyledAttributes(attrs, android.R.styleable.CheckBoxPreference, 0, 0); | 
|  | mSummaryOn = a.getString(android.R.styleable.CheckBoxPreference_summaryOn); | 
|  | mSummaryOff = a.getString(android.R.styleable.CheckBoxPreference_summaryOff); | 
|  | a.recycle(); | 
|  | } | 
|  |  | 
|  | public EditPhoneNumberPreference(Context context) { | 
|  | this(context, null); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Methods called on UI bindings | 
|  | */ | 
|  | @Override | 
|  | //called when we're binding the view to the preference. | 
|  | protected void onBindView(View view) { | 
|  | super.onBindView(view); | 
|  |  | 
|  | // Sync the summary view | 
|  | TextView summaryView = (TextView) view.findViewById(android.R.id.summary); | 
|  | if (summaryView != null) { | 
|  | CharSequence sum; | 
|  | int vis; | 
|  |  | 
|  | //set summary depending upon mode | 
|  | if (mConfirmationMode == CM_ACTIVATION) { | 
|  | if (mChecked) { | 
|  | sum = (mSummaryOn == null) ? getSummary() : mSummaryOn; | 
|  | } else { | 
|  | sum = (mSummaryOff == null) ? getSummary() : mSummaryOff; | 
|  | } | 
|  | } else { | 
|  | sum = getSummary(); | 
|  | } | 
|  |  | 
|  | if (sum != null) { | 
|  | summaryView.setText(sum); | 
|  | vis = View.VISIBLE; | 
|  | } else { | 
|  | vis = View.GONE; | 
|  | } | 
|  |  | 
|  | if (vis != summaryView.getVisibility()) { | 
|  | summaryView.setVisibility(vis); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //called when we're binding the dialog to the preference's view. | 
|  | @Override | 
|  | protected void onBindDialogView(View view) { | 
|  | // default the button clicked to be the cancel button. | 
|  | mButtonClicked = DialogInterface.BUTTON_NEGATIVE; | 
|  |  | 
|  | super.onBindDialogView(view); | 
|  |  | 
|  | //get the edittext component within the number field | 
|  | EditText editText = getEditText(); | 
|  | //get the contact pick button within the number field | 
|  | mContactPickButton = (ImageButton) view.findViewById(R.id.select_contact); | 
|  |  | 
|  | //setup number entry | 
|  | if (editText != null) { | 
|  | // see if there is a means to get a default number, | 
|  | // and set it accordingly. | 
|  | if (mGetDefaultNumberListener != null) { | 
|  | String defaultNumber = mGetDefaultNumberListener.onGetDefaultNumber(this); | 
|  | if (defaultNumber != null) { | 
|  | mPhoneNumber = defaultNumber; | 
|  | } | 
|  | } | 
|  | editText.setText(BidiFormatter.getInstance().unicodeWrap( | 
|  | mPhoneNumber, TextDirectionHeuristics.LOCALE)); | 
|  | editText.setTextDirection(TEXT_DIRECTION_LOCALE); | 
|  | editText.setLayoutDirection(LAYOUT_DIRECTION_LOCALE); | 
|  | editText.setMovementMethod(ArrowKeyMovementMethod.getInstance()); | 
|  | editText.setKeyListener(DialerKeyListener.getInstance()); | 
|  | editText.setOnFocusChangeListener(mDialogFocusChangeListener); | 
|  | } | 
|  |  | 
|  | //set contact picker | 
|  | if (mContactPickButton != null) { | 
|  | mContactPickButton.setOnClickListener(new View.OnClickListener() { | 
|  | public void onClick(View v) { | 
|  | if (mParentActivity != null) { | 
|  | mParentActivity.startActivityForResult(mContactListIntent, mPrefId); | 
|  | } | 
|  | } | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Overriding EditTextPreference's onAddEditTextToDialogView. | 
|  | * | 
|  | * This method attaches the EditText to the container specific to this | 
|  | * preference's dialog layout. | 
|  | */ | 
|  | @Override | 
|  | protected void onAddEditTextToDialogView(View dialogView, EditText editText) { | 
|  |  | 
|  | // look for the container object | 
|  | ViewGroup container = (ViewGroup) dialogView | 
|  | .findViewById(R.id.edit_container); | 
|  |  | 
|  | // add the edittext to the container. | 
|  | if (container != null) { | 
|  | container.addView(editText, ViewGroup.LayoutParams.MATCH_PARENT, | 
|  | ViewGroup.LayoutParams.WRAP_CONTENT); | 
|  | } | 
|  | } | 
|  |  | 
|  | //control the appearance of the dialog depending upon the mode. | 
|  | @Override | 
|  | protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { | 
|  | // modified so that we just worry about the buttons being | 
|  | // displayed, since there is no need to hide the edittext | 
|  | // field anymore. | 
|  | if (mConfirmationMode == CM_ACTIVATION) { | 
|  | if (mIsUnknownStatus) { | 
|  | builder.setPositiveButton(mEnableText, this); | 
|  | builder.setNeutralButton(mDisableText, this); | 
|  | if (mPrefId == CommandsInterface.CF_REASON_ALL) { | 
|  | builder.setPositiveButton(null, null); | 
|  | } | 
|  | } else if (mChecked) { | 
|  | builder.setPositiveButton(mChangeNumberText, this); | 
|  | builder.setNeutralButton(mDisableText, this); | 
|  | } else { | 
|  | builder.setPositiveButton(mEnableText, this); | 
|  | builder.setNeutralButton(null, null); | 
|  | } | 
|  | } | 
|  | // set the call icon on the title. | 
|  | builder.setIcon(R.mipmap.ic_launcher_phone); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Listeners and other state setting methods | 
|  | */ | 
|  | //set the on focus change listener to be assigned to the Dialog's edittext field. | 
|  | public void setDialogOnFocusChangeListener(View.OnFocusChangeListener l) { | 
|  | mDialogFocusChangeListener = l; | 
|  | } | 
|  |  | 
|  | //set the listener to be called wht the dialog is closed. | 
|  | public void setDialogOnClosedListener(OnDialogClosedListener l) { | 
|  | mDialogOnClosedListener = l; | 
|  | } | 
|  |  | 
|  | //set the link back to the parent activity, so that we may run the contact picker. | 
|  | public void setParentActivity(Activity parent, int identifier) { | 
|  | mParentActivity = parent; | 
|  | mPrefId = identifier; | 
|  | mGetDefaultNumberListener = null; | 
|  | } | 
|  |  | 
|  | //set the link back to the parent activity, so that we may run the contact picker. | 
|  | //also set the default number listener. | 
|  | public void setParentActivity(Activity parent, int identifier, GetDefaultNumberListener l) { | 
|  | mParentActivity = parent; | 
|  | mPrefId = identifier; | 
|  | mGetDefaultNumberListener = l; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Notification handlers | 
|  | */ | 
|  | //Notify the preference that the pick activity is complete. | 
|  | public void onPickActivityResult(String pickedValue) { | 
|  | EditText editText = getEditText(); | 
|  | if (editText != null) { | 
|  | editText.setText(pickedValue); | 
|  | } | 
|  | } | 
|  |  | 
|  | //called when the dialog is clicked. | 
|  | @Override | 
|  | public void onClick(DialogInterface dialog, int which) { | 
|  | // The neutral button (button3) is always the toggle. | 
|  | if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL) | 
|  | && !mIsUnknownStatus) { | 
|  | //flip the toggle if we are in the correct mode. | 
|  | setToggled(!isToggled()); | 
|  | } | 
|  | // record the button that was clicked. | 
|  | mButtonClicked = which; | 
|  | super.onClick(dialog, which); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | //When the dialog is closed, perform the relevant actions, including setting | 
|  | // phone numbers and calling the close action listener. | 
|  | protected void onDialogClosed(boolean positiveResult) { | 
|  | // A positive result is technically either button1 or button3. | 
|  | if ((mButtonClicked == DialogInterface.BUTTON_POSITIVE) || | 
|  | (mButtonClicked == DialogInterface.BUTTON_NEUTRAL)){ | 
|  | setPhoneNumber(getEditText().getText().toString()); | 
|  | super.onDialogClosed(positiveResult); | 
|  | setText(getStringValue()); | 
|  | } else { | 
|  | super.onDialogClosed(positiveResult); | 
|  | } | 
|  |  | 
|  | // send the clicked button over to the listener. | 
|  | if (mDialogOnClosedListener != null) { | 
|  | mDialogOnClosedListener.onDialogClosed(this, mButtonClicked); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Toggle handling code. | 
|  | */ | 
|  | //return the toggle value. | 
|  | public boolean isToggled() { | 
|  | return mChecked; | 
|  | } | 
|  |  | 
|  | //set the toggle value. | 
|  | // return the current preference to allow for chaining preferences. | 
|  | public EditPhoneNumberPreference setToggled(boolean checked) { | 
|  | mChecked = checked; | 
|  | setText(getStringValue()); | 
|  | notifyChanged(); | 
|  |  | 
|  | return this; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Phone number handling code | 
|  | */ | 
|  | public String getPhoneNumber() { | 
|  | // return the phone number, after it has been stripped of all | 
|  | // irrelevant text. | 
|  | return PhoneNumberUtils.stripSeparators(mPhoneNumber); | 
|  | } | 
|  |  | 
|  | /** The phone number including any formatting characters */ | 
|  | protected String getRawPhoneNumber() { | 
|  | return mPhoneNumber; | 
|  | } | 
|  |  | 
|  | //set the phone number value. | 
|  | // return the current preference to allow for chaining preferences. | 
|  | public EditPhoneNumberPreference setPhoneNumber(String number) { | 
|  | mPhoneNumber = number; | 
|  | setText(getStringValue()); | 
|  | notifyChanged(); | 
|  |  | 
|  | return this; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Other code relevant to preference framework | 
|  | */ | 
|  | //when setting default / initial values, make sure we're setting things correctly. | 
|  | @Override | 
|  | protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { | 
|  | setValueFromString(restoreValue ? getPersistedString(getStringValue()) | 
|  | : (String) defaultValue); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Decides how to disable dependents. | 
|  | */ | 
|  | @Override | 
|  | public boolean shouldDisableDependents() { | 
|  | // There is really only one case we care about, but for consistency | 
|  | // we fill out the dependency tree for all of the cases.  If this | 
|  | // is in activation mode (CF), we look for the encoded toggle value | 
|  | // in the string.  If this in confirm mode (VM), then we just | 
|  | // examine the number field. | 
|  | // Note: The toggle value is stored in the string in an encoded | 
|  | // manner (refer to setValueFromString and getStringValue below). | 
|  | boolean shouldDisable = false; | 
|  | if ((mConfirmationMode == CM_ACTIVATION) && (mEncodedText != null)) { | 
|  | String[] inValues = mEncodedText.split(":", 2); | 
|  | shouldDisable = inValues[0].equals(VALUE_ON); | 
|  | } else { | 
|  | shouldDisable = (TextUtils.isEmpty(mPhoneNumber) && (mConfirmationMode == CM_CONFIRM)); | 
|  | } | 
|  | return shouldDisable; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Override persistString so that we can get a hold of the EditTextPreference's | 
|  | * text field. | 
|  | */ | 
|  | private String mEncodedText = null; | 
|  | @Override | 
|  | protected boolean persistString(String value) { | 
|  | mEncodedText = value; | 
|  | return super.persistString(value); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Summary On handling code | 
|  | */ | 
|  | //set the Summary for the on state (relevant only in CM_ACTIVATION mode) | 
|  | public EditPhoneNumberPreference setSummaryOn(CharSequence summary) { | 
|  | mSummaryOn = summary; | 
|  | if (isToggled()) { | 
|  | notifyChanged(); | 
|  | } | 
|  | return this; | 
|  | } | 
|  |  | 
|  | //set the Summary for the on state, given a string resource id | 
|  | // (relevant only in CM_ACTIVATION mode) | 
|  | public EditPhoneNumberPreference setSummaryOn(int summaryResId) { | 
|  | return setSummaryOn(getContext().getString(summaryResId)); | 
|  | } | 
|  |  | 
|  | //get the summary string for the on state | 
|  | public CharSequence getSummaryOn() { | 
|  | return mSummaryOn; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Summary Off handling code | 
|  | */ | 
|  | //set the Summary for the off state (relevant only in CM_ACTIVATION mode) | 
|  | public EditPhoneNumberPreference setSummaryOff(CharSequence summary) { | 
|  | mSummaryOff = summary; | 
|  | if (!isToggled()) { | 
|  | notifyChanged(); | 
|  | } | 
|  | return this; | 
|  | } | 
|  |  | 
|  | //set the Summary for the off state, given a string resource id | 
|  | // (relevant only in CM_ACTIVATION mode) | 
|  | public EditPhoneNumberPreference setSummaryOff(int summaryResId) { | 
|  | return setSummaryOff(getContext().getString(summaryResId)); | 
|  | } | 
|  |  | 
|  | //get the summary string for the off state | 
|  | public CharSequence getSummaryOff() { | 
|  | return mSummaryOff; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Methods to get and set from encoded strings. | 
|  | */ | 
|  | //set the values given an encoded string. | 
|  | protected void setValueFromString(String value) { | 
|  | String[] inValues = value.split(":", 2); | 
|  | setToggled(inValues[0].equals(VALUE_ON)); | 
|  | setPhoneNumber(inValues[1]); | 
|  | } | 
|  |  | 
|  | //retrieve the state of this preference in the form of an encoded string | 
|  | protected String getStringValue() { | 
|  | return ((isToggled() ? VALUE_ON : VALUE_OFF) + VALUE_SEPARATOR + getPhoneNumber()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Externally visible method to bring up the dialog. | 
|  | * | 
|  | * Generally used when we are navigating the user to this preference. | 
|  | */ | 
|  | public void showPhoneNumberDialog() { | 
|  | showDialog(null); | 
|  | } | 
|  |  | 
|  | public void setUnknownStatus(boolean isUnknown) { | 
|  | mIsUnknownStatus = isUnknown; | 
|  | } | 
|  |  | 
|  | public boolean isUnknownStatus() { | 
|  | return mIsUnknownStatus; | 
|  | } | 
|  | } |