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