Blanket copy of PhoneApp to services/Telephony.

First phase of splitting out InCallUI from PhoneApp.

Change-Id: I237341c4ff00e96c677caa4580b251ef3432931b
diff --git a/src/com/android/phone/EditPhoneNumberPreference.java b/src/com/android/phone/EditPhoneNumberPreference.java
new file mode 100644
index 0000000..86671a8
--- /dev/null
+++ b/src/com/android/phone/EditPhoneNumberPreference.java
@@ -0,0 +1,499 @@
+/*
+ * 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.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:
+    //  <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(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(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.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(null, null);
+                builder.setNeutralButton(mEnableText, this);
+            }
+        }
+        // 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);
+    }
+}