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