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