blob: de7499cccdbe08fbf3542ca400b03005908d671d [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
Jang Hayeong43bd1402019-07-16 15:40:47 +090040import com.android.internal.telephony.CommandsInterface;
41
Santos Cordon7d4ddf62013-07-10 11:58:08 -070042public class EditPhoneNumberPreference extends EditTextPreference {
43
44 //allowed modes for this preference.
45 /** simple confirmation (OK / CANCEL) */
46 private static final int CM_CONFIRM = 0;
47 /** toggle [(ENABLE / CANCEL) or (DISABLE / CANCEL)], use isToggled() to see requested state.*/
48 private static final int CM_ACTIVATION = 1;
49
50 private int mConfirmationMode;
51
52 //String constants used in storing the value of the preference
53 // The preference is backed by a string that holds the encoded value, which reads:
54 // <VALUE_ON | VALUE_OFF><VALUE_SEPARATOR><mPhoneNumber>
55 // for example, an enabled preference with a number of 6502345678 would read:
56 // "1:6502345678"
57 private static final String VALUE_SEPARATOR = ":";
58 private static final String VALUE_OFF = "0";
59 private static final String VALUE_ON = "1";
60
61 //UI layout
62 private ImageButton mContactPickButton;
63
64 //Listeners
65 /** Called when focus is changed between fields */
66 private View.OnFocusChangeListener mDialogFocusChangeListener;
67 /** Called when the Dialog is closed. */
68 private OnDialogClosedListener mDialogOnClosedListener;
69 /**
70 * Used to indicate that we are going to request for a
71 * default number. for the dialog.
72 */
73 private GetDefaultNumberListener mGetDefaultNumberListener;
74
75 //Activity values
76 private Activity mParentActivity;
77 private Intent mContactListIntent;
78 /** Arbitrary activity-assigned preference id value */
79 private int mPrefId;
80
81 //similar to toggle preference
82 private CharSequence mEnableText;
83 private CharSequence mDisableText;
84 private CharSequence mChangeNumberText;
85 private CharSequence mSummaryOn;
86 private CharSequence mSummaryOff;
87
88 // button that was clicked on dialog close.
89 private int mButtonClicked;
90
91 //relevant (parsed) value of the mText
92 private String mPhoneNumber;
93 private boolean mChecked;
94
Jang Hayeong43bd1402019-07-16 15:40:47 +090095 private boolean mIsUnknownStatus;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070096
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(
215 mPhoneNumber, TextDirectionHeuristics.LTR));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700216 editText.setMovementMethod(ArrowKeyMovementMethod.getInstance());
217 editText.setKeyListener(DialerKeyListener.getInstance());
218 editText.setOnFocusChangeListener(mDialogFocusChangeListener);
219 }
220
221 //set contact picker
222 if (mContactPickButton != null) {
223 mContactPickButton.setOnClickListener(new View.OnClickListener() {
224 public void onClick(View v) {
225 if (mParentActivity != null) {
226 mParentActivity.startActivityForResult(mContactListIntent, mPrefId);
227 }
228 }
229 });
230 }
231 }
232
233 /**
234 * Overriding EditTextPreference's onAddEditTextToDialogView.
235 *
236 * This method attaches the EditText to the container specific to this
237 * preference's dialog layout.
238 */
239 @Override
240 protected void onAddEditTextToDialogView(View dialogView, EditText editText) {
241
242 // look for the container object
243 ViewGroup container = (ViewGroup) dialogView
244 .findViewById(R.id.edit_container);
245
246 // add the edittext to the container.
247 if (container != null) {
248 container.addView(editText, ViewGroup.LayoutParams.MATCH_PARENT,
249 ViewGroup.LayoutParams.WRAP_CONTENT);
250 }
251 }
252
253 //control the appearance of the dialog depending upon the mode.
254 @Override
255 protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
256 // modified so that we just worry about the buttons being
257 // displayed, since there is no need to hide the edittext
258 // field anymore.
259 if (mConfirmationMode == CM_ACTIVATION) {
Jang Hayeong43bd1402019-07-16 15:40:47 +0900260 if (mIsUnknownStatus) {
261 builder.setPositiveButton(mEnableText, this);
262 builder.setNeutralButton(mDisableText, this);
263 if (mPrefId == CommandsInterface.CF_REASON_ALL) {
264 builder.setPositiveButton(null, null);
265 }
266 } else if (mChecked) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700267 builder.setPositiveButton(mChangeNumberText, this);
268 builder.setNeutralButton(mDisableText, this);
269 } else {
Hall Liu80964692017-08-14 14:59:14 -0700270 builder.setPositiveButton(mEnableText, this);
271 builder.setNeutralButton(null, null);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700272 }
273 }
274 // set the call icon on the title.
275 builder.setIcon(R.mipmap.ic_launcher_phone);
276 }
277
278
279 /*
280 * Listeners and other state setting methods
281 */
282 //set the on focus change listener to be assigned to the Dialog's edittext field.
283 public void setDialogOnFocusChangeListener(View.OnFocusChangeListener l) {
284 mDialogFocusChangeListener = l;
285 }
286
287 //set the listener to be called wht the dialog is closed.
288 public void setDialogOnClosedListener(OnDialogClosedListener l) {
289 mDialogOnClosedListener = l;
290 }
291
292 //set the link back to the parent activity, so that we may run the contact picker.
293 public void setParentActivity(Activity parent, int identifier) {
294 mParentActivity = parent;
295 mPrefId = identifier;
296 mGetDefaultNumberListener = null;
297 }
298
299 //set the link back to the parent activity, so that we may run the contact picker.
300 //also set the default number listener.
301 public void setParentActivity(Activity parent, int identifier, GetDefaultNumberListener l) {
302 mParentActivity = parent;
303 mPrefId = identifier;
304 mGetDefaultNumberListener = l;
305 }
306
307 /*
308 * Notification handlers
309 */
310 //Notify the preference that the pick activity is complete.
311 public void onPickActivityResult(String pickedValue) {
312 EditText editText = getEditText();
313 if (editText != null) {
314 editText.setText(pickedValue);
315 }
316 }
317
318 //called when the dialog is clicked.
319 @Override
320 public void onClick(DialogInterface dialog, int which) {
321 // The neutral button (button3) is always the toggle.
Jang Hayeong43bd1402019-07-16 15:40:47 +0900322 if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL)
323 && !mIsUnknownStatus) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700324 //flip the toggle if we are in the correct mode.
325 setToggled(!isToggled());
326 }
327 // record the button that was clicked.
328 mButtonClicked = which;
329 super.onClick(dialog, which);
330 }
331
332 @Override
333 //When the dialog is closed, perform the relevant actions, including setting
334 // phone numbers and calling the close action listener.
335 protected void onDialogClosed(boolean positiveResult) {
336 // A positive result is technically either button1 or button3.
337 if ((mButtonClicked == DialogInterface.BUTTON_POSITIVE) ||
338 (mButtonClicked == DialogInterface.BUTTON_NEUTRAL)){
339 setPhoneNumber(getEditText().getText().toString());
340 super.onDialogClosed(positiveResult);
341 setText(getStringValue());
342 } else {
343 super.onDialogClosed(positiveResult);
344 }
345
346 // send the clicked button over to the listener.
347 if (mDialogOnClosedListener != null) {
348 mDialogOnClosedListener.onDialogClosed(this, mButtonClicked);
349 }
350 }
351
352
353 /*
354 * Toggle handling code.
355 */
356 //return the toggle value.
357 public boolean isToggled() {
358 return mChecked;
359 }
360
361 //set the toggle value.
362 // return the current preference to allow for chaining preferences.
363 public EditPhoneNumberPreference setToggled(boolean checked) {
364 mChecked = checked;
365 setText(getStringValue());
366 notifyChanged();
367
368 return this;
369 }
370
371
372 /**
373 * Phone number handling code
374 */
375 public String getPhoneNumber() {
376 // return the phone number, after it has been stripped of all
377 // irrelevant text.
378 return PhoneNumberUtils.stripSeparators(mPhoneNumber);
379 }
380
381 /** The phone number including any formatting characters */
382 protected String getRawPhoneNumber() {
383 return mPhoneNumber;
384 }
385
386 //set the phone number value.
387 // return the current preference to allow for chaining preferences.
388 public EditPhoneNumberPreference setPhoneNumber(String number) {
389 mPhoneNumber = number;
390 setText(getStringValue());
391 notifyChanged();
392
393 return this;
394 }
395
396
397 /*
398 * Other code relevant to preference framework
399 */
400 //when setting default / initial values, make sure we're setting things correctly.
401 @Override
402 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
403 setValueFromString(restoreValue ? getPersistedString(getStringValue())
404 : (String) defaultValue);
405 }
406
407 /**
408 * Decides how to disable dependents.
409 */
410 @Override
411 public boolean shouldDisableDependents() {
412 // There is really only one case we care about, but for consistency
413 // we fill out the dependency tree for all of the cases. If this
414 // is in activation mode (CF), we look for the encoded toggle value
415 // in the string. If this in confirm mode (VM), then we just
416 // examine the number field.
417 // Note: The toggle value is stored in the string in an encoded
418 // manner (refer to setValueFromString and getStringValue below).
419 boolean shouldDisable = false;
420 if ((mConfirmationMode == CM_ACTIVATION) && (mEncodedText != null)) {
421 String[] inValues = mEncodedText.split(":", 2);
422 shouldDisable = inValues[0].equals(VALUE_ON);
423 } else {
424 shouldDisable = (TextUtils.isEmpty(mPhoneNumber) && (mConfirmationMode == CM_CONFIRM));
425 }
426 return shouldDisable;
427 }
428
429 /**
430 * Override persistString so that we can get a hold of the EditTextPreference's
431 * text field.
432 */
433 private String mEncodedText = null;
434 @Override
435 protected boolean persistString(String value) {
436 mEncodedText = value;
437 return super.persistString(value);
438 }
439
440
441 /*
442 * Summary On handling code
443 */
444 //set the Summary for the on state (relevant only in CM_ACTIVATION mode)
445 public EditPhoneNumberPreference setSummaryOn(CharSequence summary) {
446 mSummaryOn = summary;
447 if (isToggled()) {
448 notifyChanged();
449 }
450 return this;
451 }
452
453 //set the Summary for the on state, given a string resource id
454 // (relevant only in CM_ACTIVATION mode)
455 public EditPhoneNumberPreference setSummaryOn(int summaryResId) {
456 return setSummaryOn(getContext().getString(summaryResId));
457 }
458
459 //get the summary string for the on state
460 public CharSequence getSummaryOn() {
461 return mSummaryOn;
462 }
463
464
465 /*
466 * Summary Off handling code
467 */
468 //set the Summary for the off state (relevant only in CM_ACTIVATION mode)
469 public EditPhoneNumberPreference setSummaryOff(CharSequence summary) {
470 mSummaryOff = summary;
471 if (!isToggled()) {
472 notifyChanged();
473 }
474 return this;
475 }
476
477 //set the Summary for the off state, given a string resource id
478 // (relevant only in CM_ACTIVATION mode)
479 public EditPhoneNumberPreference setSummaryOff(int summaryResId) {
480 return setSummaryOff(getContext().getString(summaryResId));
481 }
482
483 //get the summary string for the off state
484 public CharSequence getSummaryOff() {
485 return mSummaryOff;
486 }
487
488
489 /*
490 * Methods to get and set from encoded strings.
491 */
492 //set the values given an encoded string.
493 protected void setValueFromString(String value) {
494 String[] inValues = value.split(":", 2);
495 setToggled(inValues[0].equals(VALUE_ON));
496 setPhoneNumber(inValues[1]);
497 }
498
499 //retrieve the state of this preference in the form of an encoded string
500 protected String getStringValue() {
501 return ((isToggled() ? VALUE_ON : VALUE_OFF) + VALUE_SEPARATOR + getPhoneNumber());
502 }
503
504 /**
505 * Externally visible method to bring up the dialog.
506 *
507 * Generally used when we are navigating the user to this preference.
508 */
509 public void showPhoneNumberDialog() {
510 showDialog(null);
511 }
Jang Hayeong43bd1402019-07-16 15:40:47 +0900512
513 public void setUnknownStatus(boolean isUnknown) {
514 mIsUnknownStatus = isUnknown;
515 }
516
517 public boolean isUnknownStatus() {
518 return mIsUnknownStatus;
519 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700520}