1 /* 2 * Copyright (C) 2014 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 com.android.settings.widget; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 import android.text.SpannableStringBuilder; 24 import android.text.TextUtils; 25 import android.text.style.TextAppearanceSpan; 26 import android.util.AttributeSet; 27 import android.view.LayoutInflater; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.widget.CompoundButton; 31 import android.widget.LinearLayout; 32 import android.widget.Switch; 33 import android.widget.TextView; 34 35 import com.android.settings.R; 36 37 import java.util.ArrayList; 38 39 public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener, 40 View.OnClickListener { 41 42 public static interface OnSwitchChangeListener { 43 /** 44 * Called when the checked state of the Switch has changed. 45 * 46 * @param switchView The Switch view whose state has changed. 47 * @param isChecked The new checked state of switchView. 48 */ onSwitchChanged(Switch switchView, boolean isChecked)49 void onSwitchChanged(Switch switchView, boolean isChecked); 50 } 51 52 private final TextAppearanceSpan mSummarySpan; 53 54 private ToggleSwitch mSwitch; 55 private TextView mTextView; 56 private String mLabel; 57 private String mSummary; 58 59 private ArrayList<OnSwitchChangeListener> mSwitchChangeListeners = 60 new ArrayList<OnSwitchChangeListener>(); 61 62 private static int[] MARGIN_ATTRIBUTES = { 63 R.attr.switchBarMarginStart, R.attr.switchBarMarginEnd}; 64 SwitchBar(Context context)65 public SwitchBar(Context context) { 66 this(context, null); 67 } 68 SwitchBar(Context context, AttributeSet attrs)69 public SwitchBar(Context context, AttributeSet attrs) { 70 this(context, attrs, 0); 71 } 72 SwitchBar(Context context, AttributeSet attrs, int defStyleAttr)73 public SwitchBar(Context context, AttributeSet attrs, int defStyleAttr) { 74 this(context, attrs, defStyleAttr, 0); 75 } 76 SwitchBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)77 public SwitchBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 78 super(context, attrs, defStyleAttr, defStyleRes); 79 80 LayoutInflater.from(context).inflate(R.layout.switch_bar, this); 81 82 final TypedArray a = context.obtainStyledAttributes(attrs, MARGIN_ATTRIBUTES); 83 int switchBarMarginStart = (int) a.getDimension(0, 0); 84 int switchBarMarginEnd = (int) a.getDimension(1, 0); 85 a.recycle(); 86 87 mTextView = (TextView) findViewById(R.id.switch_text); 88 mLabel = getResources().getString(R.string.switch_off_text); 89 mSummarySpan = new TextAppearanceSpan(mContext, R.style.TextAppearance_Small_SwitchBar); 90 updateText(); 91 ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) mTextView.getLayoutParams(); 92 lp.setMarginStart(switchBarMarginStart); 93 94 mSwitch = (ToggleSwitch) findViewById(R.id.switch_widget); 95 // Prevent onSaveInstanceState() to be called as we are managing the state of the Switch 96 // on our own 97 mSwitch.setSaveEnabled(false); 98 lp = (MarginLayoutParams) mSwitch.getLayoutParams(); 99 lp.setMarginEnd(switchBarMarginEnd); 100 101 addOnSwitchChangeListener(new OnSwitchChangeListener() { 102 @Override 103 public void onSwitchChanged(Switch switchView, boolean isChecked) { 104 setTextViewLabel(isChecked); 105 } 106 }); 107 108 setOnClickListener(this); 109 110 // Default is hide 111 setVisibility(View.GONE); 112 } 113 setTextViewLabel(boolean isChecked)114 public void setTextViewLabel(boolean isChecked) { 115 mLabel = getResources() 116 .getString(isChecked ? R.string.switch_on_text : R.string.switch_off_text); 117 updateText(); 118 } 119 setSummary(String summary)120 public void setSummary(String summary) { 121 mSummary = summary; 122 updateText(); 123 } 124 updateText()125 private void updateText() { 126 if (TextUtils.isEmpty(mSummary)) { 127 mTextView.setText(mLabel); 128 return; 129 } 130 final SpannableStringBuilder ssb = new SpannableStringBuilder(mLabel).append('\n'); 131 final int start = ssb.length(); 132 ssb.append(mSummary); 133 ssb.setSpan(mSummarySpan, start, ssb.length(), 0); 134 mTextView.setText(ssb); 135 } 136 setChecked(boolean checked)137 public void setChecked(boolean checked) { 138 setTextViewLabel(checked); 139 mSwitch.setChecked(checked); 140 } 141 setCheckedInternal(boolean checked)142 public void setCheckedInternal(boolean checked) { 143 setTextViewLabel(checked); 144 mSwitch.setCheckedInternal(checked); 145 } 146 isChecked()147 public boolean isChecked() { 148 return mSwitch.isChecked(); 149 } 150 setEnabled(boolean enabled)151 public void setEnabled(boolean enabled) { 152 super.setEnabled(enabled); 153 mTextView.setEnabled(enabled); 154 mSwitch.setEnabled(enabled); 155 } 156 getSwitch()157 public final ToggleSwitch getSwitch() { 158 return mSwitch; 159 } 160 show()161 public void show() { 162 if (!isShowing()) { 163 setVisibility(View.VISIBLE); 164 mSwitch.setOnCheckedChangeListener(this); 165 } 166 } 167 hide()168 public void hide() { 169 if (isShowing()) { 170 setVisibility(View.GONE); 171 mSwitch.setOnCheckedChangeListener(null); 172 } 173 } 174 isShowing()175 public boolean isShowing() { 176 return (getVisibility() == View.VISIBLE); 177 } 178 179 @Override onClick(View v)180 public void onClick(View v) { 181 final boolean isChecked = !mSwitch.isChecked(); 182 setChecked(isChecked); 183 } 184 propagateChecked(boolean isChecked)185 public void propagateChecked(boolean isChecked) { 186 final int count = mSwitchChangeListeners.size(); 187 for (int n = 0; n < count; n++) { 188 mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked); 189 } 190 } 191 192 @Override onCheckedChanged(CompoundButton buttonView, boolean isChecked)193 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 194 propagateChecked(isChecked); 195 } 196 addOnSwitchChangeListener(OnSwitchChangeListener listener)197 public void addOnSwitchChangeListener(OnSwitchChangeListener listener) { 198 if (mSwitchChangeListeners.contains(listener)) { 199 throw new IllegalStateException("Cannot add twice the same OnSwitchChangeListener"); 200 } 201 mSwitchChangeListeners.add(listener); 202 } 203 removeOnSwitchChangeListener(OnSwitchChangeListener listener)204 public void removeOnSwitchChangeListener(OnSwitchChangeListener listener) { 205 if (!mSwitchChangeListeners.contains(listener)) { 206 throw new IllegalStateException("Cannot remove OnSwitchChangeListener"); 207 } 208 mSwitchChangeListeners.remove(listener); 209 } 210 211 static class SavedState extends BaseSavedState { 212 boolean checked; 213 boolean visible; 214 SavedState(Parcelable superState)215 SavedState(Parcelable superState) { 216 super(superState); 217 } 218 219 /** 220 * Constructor called from {@link #CREATOR} 221 */ SavedState(Parcel in)222 private SavedState(Parcel in) { 223 super(in); 224 checked = (Boolean)in.readValue(null); 225 visible = (Boolean)in.readValue(null); 226 } 227 228 @Override writeToParcel(Parcel out, int flags)229 public void writeToParcel(Parcel out, int flags) { 230 super.writeToParcel(out, flags); 231 out.writeValue(checked); 232 out.writeValue(visible); 233 } 234 235 @Override toString()236 public String toString() { 237 return "SwitchBar.SavedState{" 238 + Integer.toHexString(System.identityHashCode(this)) 239 + " checked=" + checked 240 + " visible=" + visible + "}"; 241 } 242 243 public static final Parcelable.Creator<SavedState> CREATOR 244 = new Parcelable.Creator<SavedState>() { 245 public SavedState createFromParcel(Parcel in) { 246 return new SavedState(in); 247 } 248 249 public SavedState[] newArray(int size) { 250 return new SavedState[size]; 251 } 252 }; 253 } 254 255 @Override onSaveInstanceState()256 public Parcelable onSaveInstanceState() { 257 Parcelable superState = super.onSaveInstanceState(); 258 259 SavedState ss = new SavedState(superState); 260 ss.checked = mSwitch.isChecked(); 261 ss.visible = isShowing(); 262 return ss; 263 } 264 265 @Override onRestoreInstanceState(Parcelable state)266 public void onRestoreInstanceState(Parcelable state) { 267 SavedState ss = (SavedState) state; 268 269 super.onRestoreInstanceState(ss.getSuperState()); 270 271 mSwitch.setCheckedInternal(ss.checked); 272 setTextViewLabel(ss.checked); 273 setVisibility(ss.visible ? View.VISIBLE : View.GONE); 274 mSwitch.setOnCheckedChangeListener(ss.visible ? this : null); 275 276 requestLayout(); 277 } 278 } 279