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