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