/* * 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 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; 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: // // 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; /** * 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_GET_CONTENT); mContactListIntent.setType(Phone.CONTENT_ITEM_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.LTR)); 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 (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)) { //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); } }