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