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