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
17package androidx.preference;
18
19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.util.AttributeSet;
24import android.view.View;
25import android.view.accessibility.AccessibilityManager;
26import android.widget.Checkable;
27import android.widget.CompoundButton;
28
29import androidx.annotation.RestrictTo;
30import androidx.appcompat.widget.SwitchCompat;
31import 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*/
44public 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
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     */
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     */
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     */
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     */
134    public SwitchPreferenceCompat(Context context) {
135        this(context, null);
136    }
137
138    @Override
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     */
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     */
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     */
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     */
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     */
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     */
198    public CharSequence getSwitchTextOff() {
199        return mSwitchOff;
200    }
201
202    /**
203     * @hide
204     */
205    @RestrictTo(LIBRARY_GROUP)
206    @Override
207    protected void performClick(View view) {
208        super.performClick(view);
209        syncViewIfAccessibilityEnabled(view);
210    }
211
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
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