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