blob: 6f8be1f6e4c3e621f9af5f631d001a498365d4c7 [file] [log] [blame]
Adam Powellbe0a4532010-11-29 17:47:48 -08001/*
2 * Copyright (C) 2010 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 android.preference;
18
Adam Powellbe0a4532010-11-29 17:47:48 -080019import android.content.Context;
20import android.content.SharedPreferences;
21import android.content.res.TypedArray;
22import android.os.Parcel;
23import android.os.Parcelable;
Amith Yamasanib2515882013-09-09 14:46:07 -070024import android.text.TextUtils;
Adam Powellbe0a4532010-11-29 17:47:48 -080025import android.util.AttributeSet;
26import android.view.View;
27import android.view.accessibility.AccessibilityEvent;
28import android.view.accessibility.AccessibilityManager;
29import android.widget.TextView;
30
31/**
32 * Common base class for preferences that have two selectable states, persist a
33 * boolean value in SharedPreferences, and may have dependent preferences that are
34 * enabled/disabled based on the current state.
35 */
36public abstract class TwoStatePreference extends Preference {
37
38 private CharSequence mSummaryOn;
39 private CharSequence mSummaryOff;
40 boolean mChecked;
Adam Powellc1b07212012-08-10 14:20:14 -070041 private boolean mCheckedSet;
Svetoslav Ganov76502592011-07-29 10:44:59 -070042 private boolean mSendClickAccessibilityEvent;
Adam Powellbe0a4532010-11-29 17:47:48 -080043 private boolean mDisableDependentsState;
44
Alan Viverette617feb92013-09-09 18:09:13 -070045 public TwoStatePreference(
46 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
47 super(context, attrs, defStyleAttr, defStyleRes);
48 }
Svetoslav Ganov5c3ea062011-07-22 18:52:56 -070049
Alan Viverette617feb92013-09-09 18:09:13 -070050 public TwoStatePreference(Context context, AttributeSet attrs, int defStyleAttr) {
51 this(context, attrs, defStyleAttr, 0);
Adam Powellbe0a4532010-11-29 17:47:48 -080052 }
53
54 public TwoStatePreference(Context context, AttributeSet attrs) {
Svetoslav Ganov5c3ea062011-07-22 18:52:56 -070055 this(context, attrs, 0);
Adam Powellbe0a4532010-11-29 17:47:48 -080056 }
57
58 public TwoStatePreference(Context context) {
Svetoslav Ganov5c3ea062011-07-22 18:52:56 -070059 this(context, null);
Adam Powellbe0a4532010-11-29 17:47:48 -080060 }
61
62 @Override
63 protected void onClick() {
64 super.onClick();
65
66 boolean newValue = !isChecked();
67
Svetoslav Ganov76502592011-07-29 10:44:59 -070068 mSendClickAccessibilityEvent = true;
Adam Powellbe0a4532010-11-29 17:47:48 -080069
70 if (!callChangeListener(newValue)) {
71 return;
72 }
73
74 setChecked(newValue);
75 }
76
77 /**
78 * Sets the checked state and saves it to the {@link SharedPreferences}.
79 *
80 * @param checked The checked state.
81 */
82 public void setChecked(boolean checked) {
Adam Powellc1b07212012-08-10 14:20:14 -070083 // Always persist/notify the first time; don't assume the field's default of false.
84 final boolean changed = mChecked != checked;
85 if (changed || !mCheckedSet) {
Adam Powellbe0a4532010-11-29 17:47:48 -080086 mChecked = checked;
Adam Powellc1b07212012-08-10 14:20:14 -070087 mCheckedSet = true;
Adam Powellbe0a4532010-11-29 17:47:48 -080088 persistBoolean(checked);
Adam Powellc1b07212012-08-10 14:20:14 -070089 if (changed) {
90 notifyDependencyChange(shouldDisableDependents());
91 notifyChanged();
92 }
Adam Powellbe0a4532010-11-29 17:47:48 -080093 }
94 }
95
96 /**
97 * Returns the checked state.
98 *
99 * @return The checked state.
100 */
101 public boolean isChecked() {
102 return mChecked;
103 }
104
105 @Override
106 public boolean shouldDisableDependents() {
107 boolean shouldDisable = mDisableDependentsState ? mChecked : !mChecked;
108 return shouldDisable || super.shouldDisableDependents();
109 }
110
111 /**
112 * Sets the summary to be shown when checked.
113 *
114 * @param summary The summary to be shown when checked.
115 */
116 public void setSummaryOn(CharSequence summary) {
117 mSummaryOn = summary;
118 if (isChecked()) {
119 notifyChanged();
120 }
121 }
122
123 /**
124 * @see #setSummaryOn(CharSequence)
125 * @param summaryResId The summary as a resource.
126 */
127 public void setSummaryOn(int summaryResId) {
128 setSummaryOn(getContext().getString(summaryResId));
129 }
130
131 /**
132 * Returns the summary to be shown when checked.
133 * @return The summary.
134 */
135 public CharSequence getSummaryOn() {
136 return mSummaryOn;
137 }
138
139 /**
140 * Sets the summary to be shown when unchecked.
141 *
142 * @param summary The summary to be shown when unchecked.
143 */
144 public void setSummaryOff(CharSequence summary) {
145 mSummaryOff = summary;
146 if (!isChecked()) {
147 notifyChanged();
148 }
149 }
150
151 /**
152 * @see #setSummaryOff(CharSequence)
153 * @param summaryResId The summary as a resource.
154 */
155 public void setSummaryOff(int summaryResId) {
156 setSummaryOff(getContext().getString(summaryResId));
157 }
158
159 /**
160 * Returns the summary to be shown when unchecked.
161 * @return The summary.
162 */
163 public CharSequence getSummaryOff() {
164 return mSummaryOff;
165 }
166
167 /**
168 * Returns whether dependents are disabled when this preference is on ({@code true})
169 * or when this preference is off ({@code false}).
170 *
171 * @return Whether dependents are disabled when this preference is on ({@code true})
172 * or when this preference is off ({@code false}).
173 */
174 public boolean getDisableDependentsState() {
175 return mDisableDependentsState;
176 }
177
178 /**
179 * Sets whether dependents are disabled when this preference is on ({@code true})
180 * or when this preference is off ({@code false}).
181 *
182 * @param disableDependentsState The preference state that should disable dependents.
183 */
184 public void setDisableDependentsState(boolean disableDependentsState) {
185 mDisableDependentsState = disableDependentsState;
186 }
187
188 @Override
189 protected Object onGetDefaultValue(TypedArray a, int index) {
190 return a.getBoolean(index, false);
191 }
192
193 @Override
194 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
195 setChecked(restoreValue ? getPersistedBoolean(mChecked)
196 : (Boolean) defaultValue);
197 }
198
Svetoslav Ganov76502592011-07-29 10:44:59 -0700199 void sendAccessibilityEvent(View view) {
200 // Since the view is still not attached we create, populate,
201 // and send the event directly since we do not know when it
202 // will be attached and posting commands is not as clean.
203 AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getContext());
204 if (mSendClickAccessibilityEvent && accessibilityManager.isEnabled()) {
205 AccessibilityEvent event = AccessibilityEvent.obtain();
206 event.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED);
207 view.onInitializeAccessibilityEvent(event);
208 view.dispatchPopulateAccessibilityEvent(event);
209 accessibilityManager.sendAccessibilityEvent(event);
Adam Powellbe0a4532010-11-29 17:47:48 -0800210 }
Svetoslav Ganov76502592011-07-29 10:44:59 -0700211 mSendClickAccessibilityEvent = false;
Adam Powellbe0a4532010-11-29 17:47:48 -0800212 }
213
214 /**
215 * Sync a summary view contained within view's subhierarchy with the correct summary text.
216 * @param view View where a summary should be located
217 */
218 void syncSummaryView(View view) {
219 // Sync the summary view
220 TextView summaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);
221 if (summaryView != null) {
222 boolean useDefaultSummary = true;
Amith Yamasanib2515882013-09-09 14:46:07 -0700223 if (mChecked && !TextUtils.isEmpty(mSummaryOn)) {
Adam Powellbe0a4532010-11-29 17:47:48 -0800224 summaryView.setText(mSummaryOn);
225 useDefaultSummary = false;
Amith Yamasanib2515882013-09-09 14:46:07 -0700226 } else if (!mChecked && !TextUtils.isEmpty(mSummaryOff)) {
Adam Powellbe0a4532010-11-29 17:47:48 -0800227 summaryView.setText(mSummaryOff);
228 useDefaultSummary = false;
229 }
230
231 if (useDefaultSummary) {
232 final CharSequence summary = getSummary();
Amith Yamasanib2515882013-09-09 14:46:07 -0700233 if (!TextUtils.isEmpty(summary)) {
Adam Powellbe0a4532010-11-29 17:47:48 -0800234 summaryView.setText(summary);
235 useDefaultSummary = false;
236 }
237 }
238
239 int newVisibility = View.GONE;
240 if (!useDefaultSummary) {
241 // Someone has written to it
242 newVisibility = View.VISIBLE;
243 }
244 if (newVisibility != summaryView.getVisibility()) {
245 summaryView.setVisibility(newVisibility);
246 }
247 }
248 }
249
250 @Override
251 protected Parcelable onSaveInstanceState() {
252 final Parcelable superState = super.onSaveInstanceState();
253 if (isPersistent()) {
254 // No need to save instance state since it's persistent
255 return superState;
256 }
257
258 final SavedState myState = new SavedState(superState);
259 myState.checked = isChecked();
260 return myState;
261 }
262
263 @Override
264 protected void onRestoreInstanceState(Parcelable state) {
265 if (state == null || !state.getClass().equals(SavedState.class)) {
266 // Didn't save state for us in onSaveInstanceState
267 super.onRestoreInstanceState(state);
268 return;
269 }
270
271 SavedState myState = (SavedState) state;
272 super.onRestoreInstanceState(myState.getSuperState());
273 setChecked(myState.checked);
274 }
275
276 static class SavedState extends BaseSavedState {
277 boolean checked;
278
279 public SavedState(Parcel source) {
280 super(source);
281 checked = source.readInt() == 1;
282 }
283
284 @Override
285 public void writeToParcel(Parcel dest, int flags) {
286 super.writeToParcel(dest, flags);
287 dest.writeInt(checked ? 1 : 0);
288 }
289
290 public SavedState(Parcelable superState) {
291 super(superState);
292 }
293
294 public static final Parcelable.Creator<SavedState> CREATOR =
295 new Parcelable.Creator<SavedState>() {
296 public SavedState createFromParcel(Parcel in) {
297 return new SavedState(in);
298 }
299
300 public SavedState[] newArray(int size) {
301 return new SavedState[size];
302 }
303 };
304 }
Adam Powellbe0a4532010-11-29 17:47:48 -0800305}