1 /*
2  * Copyright (C) 2013 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 
17 package com.android.settings;
18 
19 import android.app.Dialog;
20 import android.app.settings.SettingsEnums;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.DialogInterface.OnClickListener;
24 import android.content.Intent;
25 import android.os.Bundle;
26 import android.util.AttributeSet;
27 
28 import androidx.appcompat.app.AlertDialog.Builder;
29 import androidx.fragment.app.Fragment;
30 import androidx.fragment.app.FragmentTransaction;
31 import androidx.preference.ListPreference;
32 import androidx.preference.ListPreferenceDialogFragmentCompat;
33 
34 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
35 
36 public class CustomListPreference extends ListPreference {
37 
CustomListPreference(Context context, AttributeSet attrs)38     public CustomListPreference(Context context, AttributeSet attrs) {
39         super(context, attrs);
40     }
41 
CustomListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)42     public CustomListPreference(Context context, AttributeSet attrs, int defStyleAttr,
43             int defStyleRes) {
44         super(context, attrs, defStyleAttr, defStyleRes);
45     }
46 
onPrepareDialogBuilder(Builder builder, DialogInterface.OnClickListener listener)47     protected void onPrepareDialogBuilder(Builder builder,
48             DialogInterface.OnClickListener listener) {
49     }
50 
onDialogClosed(boolean positiveResult)51     protected void onDialogClosed(boolean positiveResult) {
52     }
53 
onDialogCreated(Dialog dialog)54     protected void onDialogCreated(Dialog dialog) {
55     }
56 
isAutoClosePreference()57     protected boolean isAutoClosePreference() {
58         return true;
59     }
60 
61     /**
62      * Called when a user is about to choose the given value, to determine if we
63      * should show a confirmation dialog.
64      *
65      * @param value the value the user is about to choose
66      * @return the message to show in a confirmation dialog, or {@code null} to
67      * not request confirmation
68      */
getConfirmationMessage(String value)69     protected CharSequence getConfirmationMessage(String value) {
70         return null;
71     }
72 
onDialogStateRestored(Dialog dialog, Bundle savedInstanceState)73     protected void onDialogStateRestored(Dialog dialog, Bundle savedInstanceState) {
74     }
75 
76     public static class CustomListPreferenceDialogFragment extends
77             ListPreferenceDialogFragmentCompat {
78 
79         private static final java.lang.String KEY_CLICKED_ENTRY_INDEX
80                 = "settings.CustomListPrefDialog.KEY_CLICKED_ENTRY_INDEX";
81 
82         private int mClickedDialogEntryIndex;
83 
newInstance(String key)84         public static ListPreferenceDialogFragmentCompat newInstance(String key) {
85             final ListPreferenceDialogFragmentCompat fragment =
86                     new CustomListPreferenceDialogFragment();
87             final Bundle b = new Bundle(1);
88             b.putString(ARG_KEY, key);
89             fragment.setArguments(b);
90             return fragment;
91         }
92 
getCustomizablePreference()93         private CustomListPreference getCustomizablePreference() {
94             return (CustomListPreference) getPreference();
95         }
96 
97         @Override
onPrepareDialogBuilder(Builder builder)98         protected void onPrepareDialogBuilder(Builder builder) {
99             super.onPrepareDialogBuilder(builder);
100             mClickedDialogEntryIndex = getCustomizablePreference()
101                     .findIndexOfValue(getCustomizablePreference().getValue());
102             getCustomizablePreference().onPrepareDialogBuilder(builder, getOnItemClickListener());
103             if (!getCustomizablePreference().isAutoClosePreference()) {
104                 builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
105                     @Override
106                     public void onClick(DialogInterface dialog, int which) {
107                         onItemChosen();
108                     }
109                 });
110             }
111         }
112 
113         @Override
onCreateDialog(Bundle savedInstanceState)114         public Dialog onCreateDialog(Bundle savedInstanceState) {
115             Dialog dialog = super.onCreateDialog(savedInstanceState);
116             if (savedInstanceState != null) {
117                 mClickedDialogEntryIndex = savedInstanceState.getInt(KEY_CLICKED_ENTRY_INDEX,
118                         mClickedDialogEntryIndex);
119             }
120             getCustomizablePreference().onDialogCreated(dialog);
121             return dialog;
122         }
123 
124         @Override
onSaveInstanceState(Bundle outState)125         public void onSaveInstanceState(Bundle outState) {
126             super.onSaveInstanceState(outState);
127             outState.putInt(KEY_CLICKED_ENTRY_INDEX, mClickedDialogEntryIndex);
128         }
129 
130         @Override
onActivityCreated(Bundle savedInstanceState)131         public void onActivityCreated(Bundle savedInstanceState) {
132             super.onActivityCreated(savedInstanceState);
133             getCustomizablePreference().onDialogStateRestored(getDialog(), savedInstanceState);
134         }
135 
getOnItemClickListener()136         protected DialogInterface.OnClickListener getOnItemClickListener() {
137             return new DialogInterface.OnClickListener() {
138                 @Override
139                 public void onClick(DialogInterface dialog, int which) {
140                     setClickedDialogEntryIndex(which);
141                     if (getCustomizablePreference().isAutoClosePreference()) {
142                         onItemChosen();
143                     }
144                 }
145             };
146         }
147 
setClickedDialogEntryIndex(int which)148         protected void setClickedDialogEntryIndex(int which) {
149             mClickedDialogEntryIndex = which;
150         }
151 
getValue()152         private String getValue() {
153             final ListPreference preference = getCustomizablePreference();
154             if (mClickedDialogEntryIndex >= 0 && preference.getEntryValues() != null) {
155                 return preference.getEntryValues()[mClickedDialogEntryIndex].toString();
156             } else {
157                 return null;
158             }
159         }
160 
161         /**
162          * Called when user has made a concrete item choice, but we might need
163          * to make a quick detour to confirm that choice with a second dialog.
164          */
onItemChosen()165         protected void onItemChosen() {
166             final CharSequence message = getCustomizablePreference()
167                     .getConfirmationMessage(getValue());
168             if (message != null) {
169                 final Fragment f = new ConfirmDialogFragment();
170                 final Bundle args = new Bundle();
171                 args.putCharSequence(Intent.EXTRA_TEXT, message);
172                 f.setArguments(args);
173                 f.setTargetFragment(CustomListPreferenceDialogFragment.this, 0);
174                 final FragmentTransaction ft = getFragmentManager().beginTransaction();
175                 ft.add(f, getTag() + "-Confirm");
176                 ft.commitAllowingStateLoss();
177             } else {
178                 onItemConfirmed();
179             }
180         }
181 
182         /**
183          * Called when user has made a concrete item choice and we've fully
184          * confirmed they want to move forward (if we took a detour above).
185          */
onItemConfirmed()186         protected void onItemConfirmed() {
187             onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
188             getDialog().dismiss();
189         }
190 
191         @Override
onDialogClosed(boolean positiveResult)192         public void onDialogClosed(boolean positiveResult) {
193             getCustomizablePreference().onDialogClosed(positiveResult);
194             final ListPreference preference = getCustomizablePreference();
195             final String value = getValue();
196             if (positiveResult && value != null) {
197                 if (preference.callChangeListener(value)) {
198                     preference.setValue(value);
199                 }
200             }
201         }
202     }
203 
204     public static class ConfirmDialogFragment extends InstrumentedDialogFragment {
205         @Override
206         public Dialog onCreateDialog(Bundle savedInstanceState) {
207             return new Builder(getActivity())
208                     .setMessage(getArguments().getCharSequence(Intent.EXTRA_TEXT))
209                     .setPositiveButton(android.R.string.ok, new OnClickListener() {
210                         @Override
211                         public void onClick(DialogInterface dialog, int which) {
212                             final Fragment f = getTargetFragment();
213                             if (f != null) {
214                                 ((CustomListPreferenceDialogFragment) f).onItemConfirmed();
215                             }
216                         }
217                     })
218                     .setNegativeButton(android.R.string.cancel, null)
219                     .create();
220         }
221 
222         @Override
223         public int getMetricsCategory() {
224             return SettingsEnums.DIALOG_CUSTOM_LIST_CONFIRMATION;
225         }
226     }
227 }
228