EditPhoneNumberPreference.java revision 153b94130a5e7a0e7bb17f17ce8f1ed294453d9e
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.phone;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.Intent;
24import android.content.res.TypedArray;
25import android.preference.EditTextPreference;
26import android.provider.ContactsContract.CommonDataKinds.Phone;
27import android.telephony.PhoneNumberUtils;
28import android.text.TextUtils;
29import android.text.method.ArrowKeyMovementMethod;
30import android.text.method.DialerKeyListener;
31import android.util.AttributeSet;
32import android.view.View;
33import android.view.ViewGroup;
34import android.widget.EditText;
35import android.widget.ImageButton;
36import android.widget.TextView;
37
38public class EditPhoneNumberPreference extends EditTextPreference {
39
40    //allowed modes for this preference.
41    /** simple confirmation (OK / CANCEL) */
42    private static final int CM_CONFIRM = 0;
43    /** toggle [(ENABLE / CANCEL) or (DISABLE / CANCEL)], use isToggled() to see requested state.*/
44    private static final int CM_ACTIVATION = 1;
45
46    private int mConfirmationMode;
47
48    //String constants used in storing the value of the preference
49    // The preference is backed by a string that holds the encoded value, which reads:
50    //  <VALUE_ON | VALUE_OFF><VALUE_SEPARATOR><mPhoneNumber>
51    // for example, an enabled preference with a number of 6502345678 would read:
52    //  "1:6502345678"
53    private static final String VALUE_SEPARATOR = ":";
54    private static final String VALUE_OFF = "0";
55    private static final String VALUE_ON = "1";
56
57    //UI layout
58    private ImageButton mContactPickButton;
59
60    //Listeners
61    /** Called when focus is changed between fields */
62    private View.OnFocusChangeListener mDialogFocusChangeListener;
63    /** Called when the Dialog is closed. */
64    private OnDialogClosedListener mDialogOnClosedListener;
65    /**
66     * Used to indicate that we are going to request for a
67     * default number. for the dialog.
68     */
69    private GetDefaultNumberListener mGetDefaultNumberListener;
70
71    //Activity values
72    private Activity mParentActivity;
73    private Intent mContactListIntent;
74    /** Arbitrary activity-assigned preference id value */
75    private int mPrefId;
76
77    //similar to toggle preference
78    private CharSequence mEnableText;
79    private CharSequence mDisableText;
80    private CharSequence mChangeNumberText;
81    private CharSequence mSummaryOn;
82    private CharSequence mSummaryOff;
83
84    // button that was clicked on dialog close.
85    private int mButtonClicked;
86
87    //relevant (parsed) value of the mText
88    private String mPhoneNumber;
89    private boolean mChecked;
90
91
92    /**
93     * Interface for the dialog closed listener, related to
94     * DialogPreference.onDialogClosed(), except we also pass in a buttonClicked
95     * value indicating which of the three possible buttons were pressed.
96     */
97    interface OnDialogClosedListener {
98        void onDialogClosed(EditPhoneNumberPreference preference, int buttonClicked);
99    }
100
101    /**
102     * Interface for the default number setting listener.  Handles requests for
103     * the default display number for the dialog.
104     */
105    interface GetDefaultNumberListener {
106        /**
107         * Notify that we are looking for a default display value.
108         * @return null if there is no contribution from this interface,
109         *  indicating that the orignal value of mPhoneNumber should be
110         *  displayed unchanged.
111         */
112        String onGetDefaultNumber(EditPhoneNumberPreference preference);
113    }
114
115    /*
116     * Constructors
117     */
118    public EditPhoneNumberPreference(Context context, AttributeSet attrs) {
119        super(context, attrs);
120
121        setDialogLayoutResource(R.layout.pref_dialog_editphonenumber);
122
123        //create intent to bring up contact list
124        mContactListIntent = new Intent(Intent.ACTION_GET_CONTENT);
125        mContactListIntent.setType(Phone.CONTENT_ITEM_TYPE);
126
127        //get the edit phone number default settings
128        TypedArray a = context.obtainStyledAttributes(attrs,
129                R.styleable.EditPhoneNumberPreference, 0, R.style.EditPhoneNumberPreference);
130        mEnableText = a.getString(R.styleable.EditPhoneNumberPreference_enableButtonText);
131        mDisableText = a.getString(R.styleable.EditPhoneNumberPreference_disableButtonText);
132        mChangeNumberText = a.getString(R.styleable.EditPhoneNumberPreference_changeNumButtonText);
133        mConfirmationMode = a.getInt(R.styleable.EditPhoneNumberPreference_confirmMode, 0);
134        a.recycle();
135
136        //get the summary settings, use CheckBoxPreference as the standard.
137        a = context.obtainStyledAttributes(attrs, android.R.styleable.CheckBoxPreference, 0, 0);
138        mSummaryOn = a.getString(android.R.styleable.CheckBoxPreference_summaryOn);
139        mSummaryOff = a.getString(android.R.styleable.CheckBoxPreference_summaryOff);
140        a.recycle();
141    }
142
143    public EditPhoneNumberPreference(Context context) {
144        this(context, null);
145    }
146
147
148    /*
149     * Methods called on UI bindings
150     */
151    @Override
152    //called when we're binding the view to the preference.
153    protected void onBindView(View view) {
154        super.onBindView(view);
155
156        // Sync the summary view
157        TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
158        if (summaryView != null) {
159            CharSequence sum;
160            int vis;
161
162            //set summary depending upon mode
163            if (mConfirmationMode == CM_ACTIVATION) {
164                if (mChecked) {
165                    sum = (mSummaryOn == null) ? getSummary() : mSummaryOn;
166                } else {
167                    sum = (mSummaryOff == null) ? getSummary() : mSummaryOff;
168                }
169            } else {
170                sum = getSummary();
171            }
172
173            if (sum != null) {
174                summaryView.setText(sum);
175                vis = View.VISIBLE;
176            } else {
177                vis = View.GONE;
178            }
179
180            if (vis != summaryView.getVisibility()) {
181                summaryView.setVisibility(vis);
182            }
183        }
184    }
185
186    //called when we're binding the dialog to the preference's view.
187    @Override
188    protected void onBindDialogView(View view) {
189        // default the button clicked to be the cancel button.
190        mButtonClicked = DialogInterface.BUTTON2;
191
192        super.onBindDialogView(view);
193
194        //get the edittext component within the number field
195        EditText editText = getEditText();
196        //get the contact pick button within the number field
197        mContactPickButton = (ImageButton) view.findViewById(R.id.select_contact);
198
199        //setup number entry
200        if (editText != null) {
201            // see if there is a means to get a default number,
202            // and set it accordingly.
203            if (mGetDefaultNumberListener != null) {
204                String defaultNumber = mGetDefaultNumberListener.onGetDefaultNumber(this);
205                if (defaultNumber != null) {
206                    mPhoneNumber = defaultNumber;
207                }
208            }
209            editText.setText(mPhoneNumber);
210            editText.setMovementMethod(ArrowKeyMovementMethod.getInstance());
211            editText.setKeyListener(DialerKeyListener.getInstance());
212            editText.setOnFocusChangeListener(mDialogFocusChangeListener);
213        }
214
215        //set contact picker
216        if (mContactPickButton != null) {
217            mContactPickButton.setOnClickListener(new View.OnClickListener() {
218                public void onClick(View v) {
219                    if (mParentActivity != null) {
220                        mParentActivity.startActivityForResult(mContactListIntent, mPrefId);
221                    }
222                }
223            });
224        }
225    }
226
227    /**
228     * Overriding EditTextPreference's onAddEditTextToDialogView.
229     *
230     * This method attaches the EditText to the container specific to this
231     * preference's dialog layout.
232     */
233    @Override
234    protected void onAddEditTextToDialogView(View dialogView, EditText editText) {
235
236        // look for the container object
237        ViewGroup container = (ViewGroup) dialogView
238                .findViewById(R.id.edit_container);
239
240        // add the edittext to the container.
241        if (container != null) {
242            container.addView(editText, ViewGroup.LayoutParams.FILL_PARENT,
243                    ViewGroup.LayoutParams.WRAP_CONTENT);
244        }
245    }
246
247    //control the appearance of the dialog depending upon the mode.
248    @Override
249    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
250        // modified so that we just worry about the buttons being
251        // displayed, since there is no need to hide the edittext
252        // field anymore.
253        if (mConfirmationMode == CM_ACTIVATION) {
254            if (mChecked) {
255                builder.setPositiveButton(mChangeNumberText, this);
256                builder.setNeutralButton(mDisableText, this);
257            } else {
258                builder.setPositiveButton(null, null);
259                builder.setNeutralButton(mEnableText, this);
260            }
261        }
262        // set the call icon on the title.
263        builder.setIcon(R.drawable.ic_dialog_call);
264    }
265
266
267    /*
268     * Listeners and other state setting methods
269     */
270    //set the on focus change listener to be assigned to the Dialog's edittext field.
271    public void setDialogOnFocusChangeListener(View.OnFocusChangeListener l) {
272        mDialogFocusChangeListener = l;
273    }
274
275    //set the listener to be called wht the dialog is closed.
276    public void setDialogOnClosedListener(OnDialogClosedListener l) {
277        mDialogOnClosedListener = l;
278    }
279
280    //set the link back to the parent activity, so that we may run the contact picker.
281    public void setParentActivity(Activity parent, int identifier) {
282        mParentActivity = parent;
283        mPrefId = identifier;
284        mGetDefaultNumberListener = null;
285    }
286
287    //set the link back to the parent activity, so that we may run the contact picker.
288    //also set the default number listener.
289    public void setParentActivity(Activity parent, int identifier, GetDefaultNumberListener l) {
290        mParentActivity = parent;
291        mPrefId = identifier;
292        mGetDefaultNumberListener = l;
293    }
294
295    /*
296     * Notification handlers
297     */
298    //Notify the preference that the pick activity is complete.
299    public void onPickActivityResult(String pickedValue) {
300        EditText editText = getEditText();
301        if (editText != null) {
302            editText.setText(pickedValue);
303        }
304    }
305
306    //called when the dialog is clicked.
307    @Override
308    public void onClick(DialogInterface dialog, int which) {
309        // The neutral button (button3) is always the toggle.
310        if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON3)) {
311            //flip the toggle if we are in the correct mode.
312            setToggled(!isToggled());
313        }
314        // record the button that was clicked.
315        mButtonClicked = which;
316        super.onClick(dialog, which);
317    }
318
319    @Override
320    //When the dialog is closed, perform the relevant actions, including setting
321    // phone numbers and calling the close action listener.
322    protected void onDialogClosed(boolean positiveResult) {
323        // A positive result is technically either button1 or button3.
324        if ((mButtonClicked == DialogInterface.BUTTON1) ||
325                (mButtonClicked == DialogInterface.BUTTON3)){
326            setPhoneNumber(getEditText().getText().toString());
327            super.onDialogClosed(positiveResult);
328            setText(getStringValue());
329        } else {
330            super.onDialogClosed(positiveResult);
331        }
332
333        // send the clicked button over to the listener.
334        if (mDialogOnClosedListener != null) {
335            mDialogOnClosedListener.onDialogClosed(this, mButtonClicked);
336        }
337    }
338
339
340    /*
341     * Toggle handling code.
342     */
343    //return the toggle value.
344    public boolean isToggled() {
345        return mChecked;
346    }
347
348    //set the toggle value.
349    // return the current preference to allow for chaining preferences.
350    public EditPhoneNumberPreference setToggled(boolean checked) {
351        mChecked = checked;
352        setText(getStringValue());
353        notifyChanged();
354
355        return this;
356    }
357
358
359    /**
360     * Phone number handling code
361     */
362    public String getPhoneNumber() {
363        // return the phone number, after it has been stripped of all
364        // irrelevant text.
365        return PhoneNumberUtils.stripSeparators(mPhoneNumber);
366    }
367
368    //set the phone number value.
369    // return the current preference to allow for chaining preferences.
370    public EditPhoneNumberPreference setPhoneNumber(String number) {
371        mPhoneNumber = number;
372        setText(getStringValue());
373        notifyChanged();
374
375        return this;
376    }
377
378
379    /*
380     * Other code relevant to preference framework
381     */
382    //when setting default / initial values, make sure we're setting things correctly.
383    @Override
384    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
385        setValueFromString(restoreValue ? getPersistedString(getStringValue())
386                : (String) defaultValue);
387    }
388
389    /**
390     * Decides how to disable dependents.
391     */
392    @Override
393    public boolean shouldDisableDependents() {
394        // There is really only one case we care about, but for consistency
395        // we fill out the dependency tree for all of the cases.  If this
396        // is in activation mode (CF), we look for the encoded toggle value
397        // in the string.  If this in confirm mode (VM), then we just
398        // examine the number field.
399        // Note: The toggle value is stored in the string in an encoded
400        // manner (refer to setValueFromString and getStringValue below).
401        boolean shouldDisable = false;
402        if ((mConfirmationMode == CM_ACTIVATION) && (mEncodedText != null)) {
403            String[] inValues = mEncodedText.split(":", 2);
404            shouldDisable = inValues[0].equals(VALUE_ON);
405        } else {
406            shouldDisable = (TextUtils.isEmpty(mPhoneNumber) && (mConfirmationMode == CM_CONFIRM));
407        }
408        return shouldDisable;
409    }
410
411    /**
412     * Override persistString so that we can get a hold of the EditTextPreference's
413     * text field.
414     */
415    private String mEncodedText = null;
416    @Override
417    protected boolean persistString(String value) {
418        mEncodedText = value;
419        return super.persistString(value);
420    }
421
422
423    /*
424     * Summary On handling code
425     */
426    //set the Summary for the on state (relevant only in CM_ACTIVATION mode)
427    public EditPhoneNumberPreference setSummaryOn(CharSequence summary) {
428        mSummaryOn = summary;
429        if (isToggled()) {
430            notifyChanged();
431        }
432        return this;
433    }
434
435    //set the Summary for the on state, given a string resource id
436    // (relevant only in CM_ACTIVATION mode)
437    public EditPhoneNumberPreference setSummaryOn(int summaryResId) {
438        return setSummaryOn(getContext().getString(summaryResId));
439    }
440
441    //get the summary string for the on state
442    public CharSequence getSummaryOn() {
443        return mSummaryOn;
444    }
445
446
447    /*
448     * Summary Off handling code
449     */
450    //set the Summary for the off state (relevant only in CM_ACTIVATION mode)
451    public EditPhoneNumberPreference setSummaryOff(CharSequence summary) {
452        mSummaryOff = summary;
453        if (!isToggled()) {
454            notifyChanged();
455        }
456        return this;
457    }
458
459    //set the Summary for the off state, given a string resource id
460    // (relevant only in CM_ACTIVATION mode)
461    public EditPhoneNumberPreference setSummaryOff(int summaryResId) {
462        return setSummaryOff(getContext().getString(summaryResId));
463    }
464
465    //get the summary string for the off state
466    public CharSequence getSummaryOff() {
467        return mSummaryOff;
468    }
469
470
471    /*
472     * Methods to get and set from encoded strings.
473     */
474    //set the values given an encoded string.
475    protected void setValueFromString(String value) {
476        String[] inValues = value.split(":", 2);
477        setToggled(inValues[0].equals(VALUE_ON));
478        setPhoneNumber(inValues[1]);
479    }
480
481    //retrieve the state of this preference in the form of an encoded string
482    protected String getStringValue() {
483        return ((isToggled() ? VALUE_ON : VALUE_OFF) + VALUE_SEPARATOR + getPhoneNumber());
484    }
485
486    /**
487     * Externally visible method to bring up the dialog.
488     *
489     * Generally used when we are navigating the user to this preference.
490     */
491    public void showPhoneNumberDialog() {
492        showDialog(null);
493    }
494}
495