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.util.AttributeSet;
24 import android.view.View;
25 import android.view.accessibility.AccessibilityManager;
26 import android.widget.Checkable;
27 import android.widget.CompoundButton;
28 
29 import androidx.annotation.RestrictTo;
30 import androidx.appcompat.widget.SwitchCompat;
31 import androidx.core.content.res.TypedArrayUtils;
32 
33 /**
34 * A {@link Preference} that provides a two-state toggleable option.
35 * <p>
36 * This preference will store a boolean into the SharedPreferences.
37 *
38 * @attr name android:summaryOff
39 * @attr name android:summaryOn
40 * @attr name android:switchTextOff
41 * @attr name android:switchTextOn
42 * @attr name android:disableDependentsState
43 */
44 public class SwitchPreferenceCompat extends TwoStatePreference {
45     private final Listener mListener = new Listener();
46 
47     // Switch text for on and off states
48     private CharSequence mSwitchOn;
49     private CharSequence mSwitchOff;
50 
51     private class Listener implements CompoundButton.OnCheckedChangeListener {
52         @Override
onCheckedChanged(CompoundButton buttonView, boolean isChecked)53         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
54             if (!callChangeListener(isChecked)) {
55                 // Listener didn't like it, change it back.
56                 // CompoundButton will make sure we don't recurse.
57                 buttonView.setChecked(!isChecked);
58                 return;
59             }
60 
61             SwitchPreferenceCompat.this.setChecked(isChecked);
62         }
63     }
64 
65     /**
66      * Construct a new SwitchPreference with the given style options.
67      *
68      * @param context The Context that will style this preference
69      * @param attrs Style attributes that differ from the default
70      * @param defStyleAttr An attribute in the current theme that contains a
71      *        reference to a style resource that supplies default values for
72      *        the view. Can be 0 to not look for defaults.
73      * @param defStyleRes A resource identifier of a style resource that
74      *        supplies default values for the view, used only if
75      *        defStyleAttr is 0 or can not be found in the theme. Can be 0
76      *        to not look for defaults.
77      */
SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)78     public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr,
79             int defStyleRes) {
80         super(context, attrs, defStyleAttr, defStyleRes);
81 
82         TypedArray a = context.obtainStyledAttributes(attrs,
83                 R.styleable.SwitchPreferenceCompat, defStyleAttr, defStyleRes);
84 
85         setSummaryOn(TypedArrayUtils.getString(a, R.styleable.SwitchPreferenceCompat_summaryOn,
86                 R.styleable.SwitchPreferenceCompat_android_summaryOn));
87 
88         setSummaryOff(TypedArrayUtils.getString(a, R.styleable.SwitchPreferenceCompat_summaryOff,
89                 R.styleable.SwitchPreferenceCompat_android_summaryOff));
90 
91         setSwitchTextOn(TypedArrayUtils.getString(a,
92                 R.styleable.SwitchPreferenceCompat_switchTextOn,
93                 R.styleable.SwitchPreferenceCompat_android_switchTextOn));
94 
95         setSwitchTextOff(TypedArrayUtils.getString(a,
96                 R.styleable.SwitchPreferenceCompat_switchTextOff,
97                 R.styleable.SwitchPreferenceCompat_android_switchTextOff));
98 
99         setDisableDependentsState(TypedArrayUtils.getBoolean(a,
100                 R.styleable.SwitchPreferenceCompat_disableDependentsState,
101                 R.styleable.SwitchPreferenceCompat_android_disableDependentsState, false));
102 
103         a.recycle();
104     }
105 
106     /**
107      * Construct a new SwitchPreference with the given style options.
108      *
109      * @param context The Context that will style this preference
110      * @param attrs Style attributes that differ from the default
111      * @param defStyleAttr An attribute in the current theme that contains a
112      *        reference to a style resource that supplies default values for
113      *        the view. Can be 0 to not look for defaults.
114      */
SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr)115     public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
116         this(context, attrs, defStyleAttr, 0);
117     }
118 
119     /**
120      * Construct a new SwitchPreference with the given style options.
121      *
122      * @param context The Context that will style this preference
123      * @param attrs Style attributes that differ from the default
124      */
SwitchPreferenceCompat(Context context, AttributeSet attrs)125     public SwitchPreferenceCompat(Context context, AttributeSet attrs) {
126         this(context, attrs, R.attr.switchPreferenceCompatStyle);
127     }
128 
129     /**
130      * Construct a new SwitchPreference with default style options.
131      *
132      * @param context The Context that will style this preference
133      */
SwitchPreferenceCompat(Context context)134     public SwitchPreferenceCompat(Context context) {
135         this(context, null);
136     }
137 
138     @Override
onBindViewHolder(PreferenceViewHolder holder)139     public void onBindViewHolder(PreferenceViewHolder holder) {
140         super.onBindViewHolder(holder);
141         View switchView = holder.findViewById(R.id.switchWidget);
142         syncSwitchView(switchView);
143         syncSummaryView(holder);
144     }
145 
146     /**
147      * Set the text displayed on the switch widget in the on state.
148      * This should be a very short string; one word if possible.
149      *
150      * @param onText Text to display in the on state
151      */
setSwitchTextOn(CharSequence onText)152     public void setSwitchTextOn(CharSequence onText) {
153         mSwitchOn = onText;
154         notifyChanged();
155     }
156 
157     /**
158      * Set the text displayed on the switch widget in the off state.
159      * This should be a very short string; one word if possible.
160      *
161      * @param offText Text to display in the off state
162      */
setSwitchTextOff(CharSequence offText)163     public void setSwitchTextOff(CharSequence offText) {
164         mSwitchOff = offText;
165         notifyChanged();
166     }
167 
168     /**
169      * Set the text displayed on the switch widget in the on state.
170      * This should be a very short string; one word if possible.
171      *
172      * @param resId The text as a string resource ID
173      */
setSwitchTextOn(int resId)174     public void setSwitchTextOn(int resId) {
175         setSwitchTextOn(getContext().getString(resId));
176     }
177 
178     /**
179      * Set the text displayed on the switch widget in the off state.
180      * This should be a very short string; one word if possible.
181      *
182      * @param resId The text as a string resource ID
183      */
setSwitchTextOff(int resId)184     public void setSwitchTextOff(int resId) {
185         setSwitchTextOff(getContext().getString(resId));
186     }
187 
188     /**
189      * @return The text that will be displayed on the switch widget in the on state
190      */
getSwitchTextOn()191     public CharSequence getSwitchTextOn() {
192         return mSwitchOn;
193     }
194 
195     /**
196      * @return The text that will be displayed on the switch widget in the off state
197      */
getSwitchTextOff()198     public CharSequence getSwitchTextOff() {
199         return mSwitchOff;
200     }
201 
202     /**
203      * @hide
204      */
205     @RestrictTo(LIBRARY_GROUP)
206     @Override
performClick(View view)207     protected void performClick(View view) {
208         super.performClick(view);
209         syncViewIfAccessibilityEnabled(view);
210     }
211 
syncViewIfAccessibilityEnabled(View view)212     private void syncViewIfAccessibilityEnabled(View view) {
213         AccessibilityManager accessibilityManager = (AccessibilityManager)
214                 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
215         if (!accessibilityManager.isEnabled()) {
216             return;
217         }
218 
219         View switchView = view.findViewById(R.id.switchWidget);
220         syncSwitchView(switchView);
221 
222         View summaryView = view.findViewById(android.R.id.summary);
223         syncSummaryView(summaryView);
224     }
225 
syncSwitchView(View view)226     private void syncSwitchView(View view) {
227         if (view instanceof SwitchCompat) {
228             final SwitchCompat switchView = (SwitchCompat) view;
229             switchView.setOnCheckedChangeListener(null);
230         }
231         if (view instanceof Checkable) {
232             ((Checkable) view).setChecked(mChecked);
233         }
234         if (view instanceof SwitchCompat) {
235             final SwitchCompat switchView = (SwitchCompat) view;
236             switchView.setTextOn(mSwitchOn);
237             switchView.setTextOff(mSwitchOff);
238             switchView.setOnCheckedChangeListener(mListener);
239         }
240     }
241 }
242