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