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