Home | History | Annotate | Download | only in preference
      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 
     17 package android.preference;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.content.res.TypedArray;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 import android.util.AttributeSet;
     25 import android.view.View;
     26 import android.view.accessibility.AccessibilityEvent;
     27 import android.view.accessibility.AccessibilityManager;
     28 import 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  */
     35 public abstract class TwoStatePreference extends Preference {
     36 
     37     private CharSequence mSummaryOn;
     38     private CharSequence mSummaryOff;
     39     boolean mChecked;
     40     private boolean mCheckedSet;
     41     private boolean mSendClickAccessibilityEvent;
     42     private boolean mDisableDependentsState;
     43 
     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         mSendClickAccessibilityEvent = true;
     64 
     65         if (!callChangeListener(newValue)) {
     66             return;
     67         }
     68 
     69         setChecked(newValue);
     70     }
     71 
     72     /**
     73      * Sets the checked state and saves it to the {@link SharedPreferences}.
     74      *
     75      * @param checked The checked state.
     76      */
     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      */
     96     public boolean isChecked() {
     97         return mChecked;
     98     }
     99 
    100     @Override
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    179     public void setDisableDependentsState(boolean disableDependentsState) {
    180         mDisableDependentsState = disableDependentsState;
    181     }
    182 
    183     @Override
    184     protected Object onGetDefaultValue(TypedArray a, int index) {
    185         return a.getBoolean(index, false);
    186     }
    187 
    188     @Override
    189     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
    190         setChecked(restoreValue ? getPersistedBoolean(mChecked)
    191                 : (Boolean) defaultValue);
    192     }
    193 
    194     void sendAccessibilityEvent(View view) {
    195         // Since the view is still not attached we create, populate,
    196         // and send the event directly since we do not know when it
    197         // will be attached and posting commands is not as clean.
    198         AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getContext());
    199         if (mSendClickAccessibilityEvent && accessibilityManager.isEnabled()) {
    200             AccessibilityEvent event = AccessibilityEvent.obtain();
    201             event.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED);
    202             view.onInitializeAccessibilityEvent(event);
    203             view.dispatchPopulateAccessibilityEvent(event);
    204             accessibilityManager.sendAccessibilityEvent(event);
    205         }
    206         mSendClickAccessibilityEvent = false;
    207     }
    208 
    209     /**
    210      * Sync a summary view contained within view's subhierarchy with the correct summary text.
    211      * @param view View where a summary should be located
    212      */
    213     void syncSummaryView(View view) {
    214         // Sync the summary view
    215         TextView summaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);
    216         if (summaryView != null) {
    217             boolean useDefaultSummary = true;
    218             if (mChecked && mSummaryOn != null) {
    219                 summaryView.setText(mSummaryOn);
    220                 useDefaultSummary = false;
    221             } else if (!mChecked && mSummaryOff != null) {
    222                 summaryView.setText(mSummaryOff);
    223                 useDefaultSummary = false;
    224             }
    225 
    226             if (useDefaultSummary) {
    227                 final CharSequence summary = getSummary();
    228                 if (summary != null) {
    229                     summaryView.setText(summary);
    230                     useDefaultSummary = false;
    231                 }
    232             }
    233 
    234             int newVisibility = View.GONE;
    235             if (!useDefaultSummary) {
    236                 // Someone has written to it
    237                 newVisibility = View.VISIBLE;
    238             }
    239             if (newVisibility != summaryView.getVisibility()) {
    240                 summaryView.setVisibility(newVisibility);
    241             }
    242         }
    243     }
    244 
    245     @Override
    246     protected Parcelable onSaveInstanceState() {
    247         final Parcelable superState = super.onSaveInstanceState();
    248         if (isPersistent()) {
    249             // No need to save instance state since it's persistent
    250             return superState;
    251         }
    252 
    253         final SavedState myState = new SavedState(superState);
    254         myState.checked = isChecked();
    255         return myState;
    256     }
    257 
    258     @Override
    259     protected void onRestoreInstanceState(Parcelable state) {
    260         if (state == null || !state.getClass().equals(SavedState.class)) {
    261             // Didn't save state for us in onSaveInstanceState
    262             super.onRestoreInstanceState(state);
    263             return;
    264         }
    265 
    266         SavedState myState = (SavedState) state;
    267         super.onRestoreInstanceState(myState.getSuperState());
    268         setChecked(myState.checked);
    269     }
    270 
    271     static class SavedState extends BaseSavedState {
    272         boolean checked;
    273 
    274         public SavedState(Parcel source) {
    275             super(source);
    276             checked = source.readInt() == 1;
    277         }
    278 
    279         @Override
    280         public void writeToParcel(Parcel dest, int flags) {
    281             super.writeToParcel(dest, flags);
    282             dest.writeInt(checked ? 1 : 0);
    283         }
    284 
    285         public SavedState(Parcelable superState) {
    286             super(superState);
    287         }
    288 
    289         public static final Parcelable.Creator<SavedState> CREATOR =
    290                 new Parcelable.Creator<SavedState>() {
    291             public SavedState createFromParcel(Parcel in) {
    292                 return new SavedState(in);
    293             }
    294 
    295             public SavedState[] newArray(int size) {
    296                 return new SavedState[size];
    297             }
    298         };
    299     }
    300 }
    301