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