blob: 78e8439d4f34aed492bf5af4e75c49ce8f36f158 [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;
Andrew Lee4b70f892015-08-05 18:14:57 -070028import android.text.BidiFormatter;
29import android.text.TextDirectionHeuristics;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070030import android.text.TextUtils;
31import android.text.method.ArrowKeyMovementMethod;
32import android.text.method.DialerKeyListener;
33import android.util.AttributeSet;
34import android.view.View;
35import android.view.ViewGroup;
36import android.widget.EditText;
37import android.widget.ImageButton;
38import android.widget.TextView;
39
40public 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 */
Andrew Leebf07f762015-04-07 19:05:50 -070099 public interface OnDialogClosedListener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700100 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 */
Andrew Leebf07f762015-04-07 19:05:50 -0700107 public interface GetDefaultNumberListener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700108 /**
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 */
114 String onGetDefaultNumber(EditPhoneNumberPreference preference);
115 }
116
117 /*
118 * Constructors
119 */
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_GET_CONTENT);
127 mContactListIntent.setType(Phone.CONTENT_ITEM_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
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.
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
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 }
Andrew Lee4b70f892015-08-05 18:14:57 -0700211 editText.setText(BidiFormatter.getInstance().unicodeWrap(
212 mPhoneNumber, TextDirectionHeuristics.LTR));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700213 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
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
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(null, null);
262 builder.setNeutralButton(mEnableText, this);
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.
274 public void setDialogOnFocusChangeListener(View.OnFocusChangeListener l) {
275 mDialogFocusChangeListener = l;
276 }
277
278 //set the listener to be called wht the dialog is closed.
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.
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.
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.
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
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.
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.
347 public boolean isToggled() {
348 return mChecked;
349 }
350
351 //set the toggle value.
352 // return the current preference to allow for chaining preferences.
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 */
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 */
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.
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
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
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
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)
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)
445 public EditPhoneNumberPreference setSummaryOn(int summaryResId) {
446 return setSummaryOn(getContext().getString(summaryResId));
447 }
448
449 //get the summary string for the on state
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)
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)
469 public EditPhoneNumberPreference setSummaryOff(int summaryResId) {
470 return setSummaryOff(getContext().getString(summaryResId));
471 }
472
473 //get the summary string for the off state
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.
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
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 */
499 public void showPhoneNumberDialog() {
500 showDialog(null);
501 }
502}