blob: 8a672f1428fa9991c3bc3dfd4c8397357158e754 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
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 */
Andrew Leebf07f762015-04-07 19:05:50 -070097 public interface OnDialogClosedListener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070098 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 */
Andrew Leebf07f762015-04-07 19:05:50 -0700105 public interface GetDefaultNumberListener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700106 /**
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.BUTTON_NEGATIVE;
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.MATCH_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.mipmap.ic_launcher_phone);
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.BUTTON_NEUTRAL)) {
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.BUTTON_POSITIVE) ||
325 (mButtonClicked == DialogInterface.BUTTON_NEUTRAL)){
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 /** The phone number including any formatting characters */
369 protected String getRawPhoneNumber() {
370 return mPhoneNumber;
371 }
372
373 //set the phone number value.
374 // return the current preference to allow for chaining preferences.
375 public EditPhoneNumberPreference setPhoneNumber(String number) {
376 mPhoneNumber = number;
377 setText(getStringValue());
378 notifyChanged();
379
380 return this;
381 }
382
383
384 /*
385 * Other code relevant to preference framework
386 */
387 //when setting default / initial values, make sure we're setting things correctly.
388 @Override
389 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
390 setValueFromString(restoreValue ? getPersistedString(getStringValue())
391 : (String) defaultValue);
392 }
393
394 /**
395 * Decides how to disable dependents.
396 */
397 @Override
398 public boolean shouldDisableDependents() {
399 // There is really only one case we care about, but for consistency
400 // we fill out the dependency tree for all of the cases. If this
401 // is in activation mode (CF), we look for the encoded toggle value
402 // in the string. If this in confirm mode (VM), then we just
403 // examine the number field.
404 // Note: The toggle value is stored in the string in an encoded
405 // manner (refer to setValueFromString and getStringValue below).
406 boolean shouldDisable = false;
407 if ((mConfirmationMode == CM_ACTIVATION) && (mEncodedText != null)) {
408 String[] inValues = mEncodedText.split(":", 2);
409 shouldDisable = inValues[0].equals(VALUE_ON);
410 } else {
411 shouldDisable = (TextUtils.isEmpty(mPhoneNumber) && (mConfirmationMode == CM_CONFIRM));
412 }
413 return shouldDisable;
414 }
415
416 /**
417 * Override persistString so that we can get a hold of the EditTextPreference's
418 * text field.
419 */
420 private String mEncodedText = null;
421 @Override
422 protected boolean persistString(String value) {
423 mEncodedText = value;
424 return super.persistString(value);
425 }
426
427
428 /*
429 * Summary On handling code
430 */
431 //set the Summary for the on state (relevant only in CM_ACTIVATION mode)
432 public EditPhoneNumberPreference setSummaryOn(CharSequence summary) {
433 mSummaryOn = summary;
434 if (isToggled()) {
435 notifyChanged();
436 }
437 return this;
438 }
439
440 //set the Summary for the on state, given a string resource id
441 // (relevant only in CM_ACTIVATION mode)
442 public EditPhoneNumberPreference setSummaryOn(int summaryResId) {
443 return setSummaryOn(getContext().getString(summaryResId));
444 }
445
446 //get the summary string for the on state
447 public CharSequence getSummaryOn() {
448 return mSummaryOn;
449 }
450
451
452 /*
453 * Summary Off handling code
454 */
455 //set the Summary for the off state (relevant only in CM_ACTIVATION mode)
456 public EditPhoneNumberPreference setSummaryOff(CharSequence summary) {
457 mSummaryOff = summary;
458 if (!isToggled()) {
459 notifyChanged();
460 }
461 return this;
462 }
463
464 //set the Summary for the off state, given a string resource id
465 // (relevant only in CM_ACTIVATION mode)
466 public EditPhoneNumberPreference setSummaryOff(int summaryResId) {
467 return setSummaryOff(getContext().getString(summaryResId));
468 }
469
470 //get the summary string for the off state
471 public CharSequence getSummaryOff() {
472 return mSummaryOff;
473 }
474
475
476 /*
477 * Methods to get and set from encoded strings.
478 */
479 //set the values given an encoded string.
480 protected void setValueFromString(String value) {
481 String[] inValues = value.split(":", 2);
482 setToggled(inValues[0].equals(VALUE_ON));
483 setPhoneNumber(inValues[1]);
484 }
485
486 //retrieve the state of this preference in the form of an encoded string
487 protected String getStringValue() {
488 return ((isToggled() ? VALUE_ON : VALUE_OFF) + VALUE_SEPARATOR + getPhoneNumber());
489 }
490
491 /**
492 * Externally visible method to bring up the dialog.
493 *
494 * Generally used when we are navigating the user to this preference.
495 */
496 public void showPhoneNumberDialog() {
497 showDialog(null);
498 }
499}