1/*
2 * Copyright (C) 2015 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;
28import android.widget.Switch;
29
30import androidx.annotation.RestrictTo;
31import androidx.core.content.res.TypedArrayUtils;
32
33/**
34 * A {@link androidx.preference.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 SwitchPreference 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            SwitchPreference.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 SwitchPreference(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.SwitchPreference, defStyleAttr, defStyleRes);
84
85        setSummaryOn(TypedArrayUtils.getString(a, R.styleable.SwitchPreference_summaryOn,
86                R.styleable.SwitchPreference_android_summaryOn));
87
88        setSummaryOff(TypedArrayUtils.getString(a, R.styleable.SwitchPreference_summaryOff,
89                R.styleable.SwitchPreference_android_summaryOff));
90
91        setSwitchTextOn(TypedArrayUtils.getString(a,
92                R.styleable.SwitchPreference_switchTextOn,
93                R.styleable.SwitchPreference_android_switchTextOn));
94
95        setSwitchTextOff(TypedArrayUtils.getString(a,
96                R.styleable.SwitchPreference_switchTextOff,
97                R.styleable.SwitchPreference_android_switchTextOff));
98
99        setDisableDependentsState(TypedArrayUtils.getBoolean(a,
100                R.styleable.SwitchPreference_disableDependentsState,
101                R.styleable.SwitchPreference_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 SwitchPreference(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 SwitchPreference(Context context, AttributeSet attrs) {
126        this(context, attrs, TypedArrayUtils.getAttr(context,
127                androidx.preference.R.attr.switchPreferenceStyle,
128                android.R.attr.switchPreferenceStyle));
129    }
130
131    /**
132     * Construct a new SwitchPreference with default style options.
133     *
134     * @param context The Context that will style this preference
135     */
136    public SwitchPreference(Context context) {
137        this(context, null);
138    }
139
140    @Override
141    public void onBindViewHolder(PreferenceViewHolder holder) {
142        super.onBindViewHolder(holder);
143        View switchView = holder.findViewById(AndroidResources.ANDROID_R_SWITCH_WIDGET);
144        syncSwitchView(switchView);
145        syncSummaryView(holder);
146    }
147
148    /**
149     * Set the text displayed on the switch widget in the on state.
150     * This should be a very short string; one word if possible.
151     *
152     * @param onText Text to display in the on state
153     */
154    public void setSwitchTextOn(CharSequence onText) {
155        mSwitchOn = onText;
156        notifyChanged();
157    }
158
159    /**
160     * Set the text displayed on the switch widget in the off state.
161     * This should be a very short string; one word if possible.
162     *
163     * @param offText Text to display in the off state
164     */
165    public void setSwitchTextOff(CharSequence offText) {
166        mSwitchOff = offText;
167        notifyChanged();
168    }
169
170    /**
171     * Set the text displayed on the switch widget in the on state.
172     * This should be a very short string; one word if possible.
173     *
174     * @param resId The text as a string resource ID
175     */
176    public void setSwitchTextOn(int resId) {
177        setSwitchTextOn(getContext().getString(resId));
178    }
179
180    /**
181     * Set the text displayed on the switch widget in the off state.
182     * This should be a very short string; one word if possible.
183     *
184     * @param resId The text as a string resource ID
185     */
186    public void setSwitchTextOff(int resId) {
187        setSwitchTextOff(getContext().getString(resId));
188    }
189
190    /**
191     * @return The text that will be displayed on the switch widget in the on state
192     */
193    public CharSequence getSwitchTextOn() {
194        return mSwitchOn;
195    }
196
197    /**
198     * @return The text that will be displayed on the switch widget in the off state
199     */
200    public CharSequence getSwitchTextOff() {
201        return mSwitchOff;
202    }
203
204    /**
205     * @hide
206     */
207    @RestrictTo(LIBRARY_GROUP)
208    @Override
209    protected void performClick(View view) {
210        super.performClick(view);
211        syncViewIfAccessibilityEnabled(view);
212    }
213
214
215    private void syncViewIfAccessibilityEnabled(View view) {
216        AccessibilityManager accessibilityManager = (AccessibilityManager)
217                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
218        if (!accessibilityManager.isEnabled()) {
219            return;
220        }
221
222        View switchView = view.findViewById(AndroidResources.ANDROID_R_SWITCH_WIDGET);
223        syncSwitchView(switchView);
224
225        View summaryView = view.findViewById(android.R.id.summary);
226        syncSummaryView(summaryView);
227    }
228
229    private void syncSwitchView(View view) {
230        if (view instanceof Switch) {
231            final Switch switchView = (Switch) view;
232            switchView.setOnCheckedChangeListener(null);
233        }
234        if (view instanceof Checkable) {
235            ((Checkable) view).setChecked(mChecked);
236        }
237        if (view instanceof Switch) {
238            final Switch switchView = (Switch) view;
239            switchView.setTextOn(mSwitchOn);
240            switchView.setTextOff(mSwitchOff);
241            switchView.setOnCheckedChangeListener(mListener);
242        }
243    }
244}
245