1 /*
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 
17 package android.preference;
18 
19 import android.content.Context;
20 import android.content.SharedPreferences;
21 import android.content.res.TypedArray;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.text.TextUtils;
25 import android.util.AttributeSet;
26 import android.view.View;
27 import android.widget.TextView;
28 
29 /**
30  * Common base class for preferences that have two selectable states, persist a
31  * boolean value in SharedPreferences, and may have dependent preferences that are
32  * enabled/disabled based on the current state.
33  */
34 public abstract class TwoStatePreference extends Preference {
35 
36     private CharSequence mSummaryOn;
37     private CharSequence mSummaryOff;
38     boolean mChecked;
39     private boolean mCheckedSet;
40     private boolean mDisableDependentsState;
41 
TwoStatePreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)42     public TwoStatePreference(
43             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
44         super(context, attrs, defStyleAttr, defStyleRes);
45     }
46 
TwoStatePreference(Context context, AttributeSet attrs, int defStyleAttr)47     public TwoStatePreference(Context context, AttributeSet attrs, int defStyleAttr) {
48         this(context, attrs, defStyleAttr, 0);
49     }
50 
TwoStatePreference(Context context, AttributeSet attrs)51     public TwoStatePreference(Context context, AttributeSet attrs) {
52         this(context, attrs, 0);
53     }
54 
TwoStatePreference(Context context)55     public TwoStatePreference(Context context) {
56         this(context, null);
57     }
58 
59     @Override
onClick()60     protected void onClick() {
61         super.onClick();
62 
63         final boolean newValue = !isChecked();
64         if (callChangeListener(newValue)) {
65             setChecked(newValue);
66         }
67     }
68 
69     /**
70      * Sets the checked state and saves it to the {@link SharedPreferences}.
71      *
72      * @param checked The checked state.
73      */
setChecked(boolean checked)74     public void setChecked(boolean checked) {
75         // Always persist/notify the first time; don't assume the field's default of false.
76         final boolean changed = mChecked != checked;
77         if (changed || !mCheckedSet) {
78             mChecked = checked;
79             mCheckedSet = true;
80             persistBoolean(checked);
81             if (changed) {
82                 notifyDependencyChange(shouldDisableDependents());
83                 notifyChanged();
84             }
85         }
86     }
87 
88     /**
89      * Returns the checked state.
90      *
91      * @return The checked state.
92      */
isChecked()93     public boolean isChecked() {
94         return mChecked;
95     }
96 
97     @Override
shouldDisableDependents()98     public boolean shouldDisableDependents() {
99         boolean shouldDisable = mDisableDependentsState ? mChecked : !mChecked;
100         return shouldDisable || super.shouldDisableDependents();
101     }
102 
103     /**
104      * Sets the summary to be shown when checked.
105      *
106      * @param summary The summary to be shown when checked.
107      */
setSummaryOn(CharSequence summary)108     public void setSummaryOn(CharSequence summary) {
109         mSummaryOn = summary;
110         if (isChecked()) {
111             notifyChanged();
112         }
113     }
114 
115     /**
116      * @see #setSummaryOn(CharSequence)
117      * @param summaryResId The summary as a resource.
118      */
setSummaryOn(int summaryResId)119     public void setSummaryOn(int summaryResId) {
120         setSummaryOn(getContext().getString(summaryResId));
121     }
122 
123     /**
124      * Returns the summary to be shown when checked.
125      * @return The summary.
126      */
getSummaryOn()127     public CharSequence getSummaryOn() {
128         return mSummaryOn;
129     }
130 
131     /**
132      * Sets the summary to be shown when unchecked.
133      *
134      * @param summary The summary to be shown when unchecked.
135      */
setSummaryOff(CharSequence summary)136     public void setSummaryOff(CharSequence summary) {
137         mSummaryOff = summary;
138         if (!isChecked()) {
139             notifyChanged();
140         }
141     }
142 
143     /**
144      * @see #setSummaryOff(CharSequence)
145      * @param summaryResId The summary as a resource.
146      */
setSummaryOff(int summaryResId)147     public void setSummaryOff(int summaryResId) {
148         setSummaryOff(getContext().getString(summaryResId));
149     }
150 
151     /**
152      * Returns the summary to be shown when unchecked.
153      * @return The summary.
154      */
getSummaryOff()155     public CharSequence getSummaryOff() {
156         return mSummaryOff;
157     }
158 
159     /**
160      * Returns whether dependents are disabled when this preference is on ({@code true})
161      * or when this preference is off ({@code false}).
162      *
163      * @return Whether dependents are disabled when this preference is on ({@code true})
164      *         or when this preference is off ({@code false}).
165      */
getDisableDependentsState()166     public boolean getDisableDependentsState() {
167         return mDisableDependentsState;
168     }
169 
170     /**
171      * Sets whether dependents are disabled when this preference is on ({@code true})
172      * or when this preference is off ({@code false}).
173      *
174      * @param disableDependentsState The preference state that should disable dependents.
175      */
setDisableDependentsState(boolean disableDependentsState)176     public void setDisableDependentsState(boolean disableDependentsState) {
177         mDisableDependentsState = disableDependentsState;
178     }
179 
180     @Override
onGetDefaultValue(TypedArray a, int index)181     protected Object onGetDefaultValue(TypedArray a, int index) {
182         return a.getBoolean(index, false);
183     }
184 
185     @Override
onSetInitialValue(boolean restoreValue, Object defaultValue)186     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
187         setChecked(restoreValue ? getPersistedBoolean(mChecked)
188                 : (Boolean) defaultValue);
189     }
190 
191     /**
192      * Sync a summary view contained within view's subhierarchy with the correct summary text.
193      * @param view View where a summary should be located
194      */
syncSummaryView(View view)195     void syncSummaryView(View view) {
196         // Sync the summary view
197         TextView summaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);
198         if (summaryView != null) {
199             boolean useDefaultSummary = true;
200             if (mChecked && !TextUtils.isEmpty(mSummaryOn)) {
201                 summaryView.setText(mSummaryOn);
202                 useDefaultSummary = false;
203             } else if (!mChecked && !TextUtils.isEmpty(mSummaryOff)) {
204                 summaryView.setText(mSummaryOff);
205                 useDefaultSummary = false;
206             }
207 
208             if (useDefaultSummary) {
209                 final CharSequence summary = getSummary();
210                 if (!TextUtils.isEmpty(summary)) {
211                     summaryView.setText(summary);
212                     useDefaultSummary = false;
213                 }
214             }
215 
216             int newVisibility = View.GONE;
217             if (!useDefaultSummary) {
218                 // Someone has written to it
219                 newVisibility = View.VISIBLE;
220             }
221             if (newVisibility != summaryView.getVisibility()) {
222                 summaryView.setVisibility(newVisibility);
223             }
224         }
225     }
226 
227     @Override
onSaveInstanceState()228     protected Parcelable onSaveInstanceState() {
229         final Parcelable superState = super.onSaveInstanceState();
230         if (isPersistent()) {
231             // No need to save instance state since it's persistent
232             return superState;
233         }
234 
235         final SavedState myState = new SavedState(superState);
236         myState.checked = isChecked();
237         return myState;
238     }
239 
240     @Override
onRestoreInstanceState(Parcelable state)241     protected void onRestoreInstanceState(Parcelable state) {
242         if (state == null || !state.getClass().equals(SavedState.class)) {
243             // Didn't save state for us in onSaveInstanceState
244             super.onRestoreInstanceState(state);
245             return;
246         }
247 
248         SavedState myState = (SavedState) state;
249         super.onRestoreInstanceState(myState.getSuperState());
250         setChecked(myState.checked);
251     }
252 
253     static class SavedState extends BaseSavedState {
254         boolean checked;
255 
SavedState(Parcel source)256         public SavedState(Parcel source) {
257             super(source);
258             checked = source.readInt() == 1;
259         }
260 
261         @Override
writeToParcel(Parcel dest, int flags)262         public void writeToParcel(Parcel dest, int flags) {
263             super.writeToParcel(dest, flags);
264             dest.writeInt(checked ? 1 : 0);
265         }
266 
SavedState(Parcelable superState)267         public SavedState(Parcelable superState) {
268             super(superState);
269         }
270 
271         public static final Parcelable.Creator<SavedState> CREATOR =
272                 new Parcelable.Creator<SavedState>() {
273             public SavedState createFromParcel(Parcel in) {
274                 return new SavedState(in);
275             }
276 
277             public SavedState[] newArray(int size) {
278                 return new SavedState[size];
279             }
280         };
281     }
282 }
283