FileDocCategorySizeDatePackage
EditPhoneNumberPreference.javaAPI DocAndroid 1.5 API17631Wed May 06 22:42:46 BST 2009com.android.phone

EditPhoneNumberPreference.java

/*
 * 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.telephony.PhoneNumberUtils;
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.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.Map;

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;
    
    
    /**
     * 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.
     */
    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.
     */
    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(android.provider.Contacts.Phones.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.
    protected void onBindDialogView(View view) {
        // default the button clicked to be the cancel button.
        mButtonClicked = DialogInterface.BUTTON2;
        
        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(mPhoneNumber);
            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.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }
    }
    
    //control the appearance of the dialog depending upon the mode.
    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(null, null);
                builder.setNeutralButton(mEnableText, this);
            }
        }
        // set the call icon on the title.
        builder.setIcon(R.drawable.ic_dialog_call);
    }

    
    /*
     * 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.  
    public void onClick(DialogInterface dialog, int which) {
        // The neutral button (button3) is always the toggle.
        if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON3)) {
            //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.BUTTON1) || 
                (mButtonClicked == DialogInterface.BUTTON3)){
            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);
    }

    //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.
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        setValueFromString(restoreValue ? getPersistedString(getStringValue())
                : (String) defaultValue);
    }

    /**
     * Decides how to disable dependents.
     */
    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);
    }
}