2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.widget;
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.view.accessibility.AccessibilityEvent;
31 import android.view.accessibility.AccessibilityNodeInfo;
32 import android.widget.CompoundButton;
33 import android.widget.LinearLayout;
34 import android.widget.Switch;
35 import android.widget.TextView;
37 import com.android.internal.logging.MetricsLogger;
38 import com.android.settings.R;
39 import com.android.settingslib.RestrictedLockUtils;
41 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
43 import java.util.ArrayList;
45 public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener,
46 View.OnClickListener {
48 public static interface OnSwitchChangeListener {
50 * Called when the checked state of the Switch has changed.
52 * @param switchView The Switch view whose state has changed.
53 * @param isChecked The new checked state of switchView.
55 void onSwitchChanged(Switch switchView, boolean isChecked);
58 private final TextAppearanceSpan mSummarySpan;
60 private ToggleSwitch mSwitch;
61 private View mRestrictedIcon;
62 private TextView mTextView;
63 private String mLabel;
64 private String mSummary;
66 private boolean mDisabledByAdmin = false;
67 private EnforcedAdmin mEnforcedAdmin = null;
69 private String mMetricsTag;
71 private ArrayList<OnSwitchChangeListener> mSwitchChangeListeners =
72 new ArrayList<OnSwitchChangeListener>();
74 private static int[] XML_ATTRIBUTES = {
75 R.attr.switchBarMarginStart, R.attr.switchBarMarginEnd,
76 R.attr.switchBarBackgroundColor};
78 public SwitchBar(Context context) {
82 public SwitchBar(Context context, AttributeSet attrs) {
83 this(context, attrs, 0);
86 public SwitchBar(Context context, AttributeSet attrs, int defStyleAttr) {
87 this(context, attrs, defStyleAttr, 0);
90 public SwitchBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
91 super(context, attrs, defStyleAttr, defStyleRes);
93 LayoutInflater.from(context).inflate(R.layout.switch_bar, this);
95 final TypedArray a = context.obtainStyledAttributes(attrs, XML_ATTRIBUTES);
96 int switchBarMarginStart = (int) a.getDimension(0, 0);
97 int switchBarMarginEnd = (int) a.getDimension(1, 0);
98 int switchBarBackgroundColor = (int) a.getColor(2, 0);
101 mTextView = (TextView) findViewById(R.id.switch_text);
102 mTextView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
103 mLabel = getResources().getString(R.string.switch_off_text);
104 mSummarySpan = new TextAppearanceSpan(mContext, R.style.TextAppearance_Small_SwitchBar);
106 ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) mTextView.getLayoutParams();
107 lp.setMarginStart(switchBarMarginStart);
109 mSwitch = (ToggleSwitch) findViewById(R.id.switch_widget);
110 // Prevent onSaveInstanceState() to be called as we are managing the state of the Switch
112 mSwitch.setSaveEnabled(false);
113 mSwitch.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
114 lp = (MarginLayoutParams) mSwitch.getLayoutParams();
115 lp.setMarginEnd(switchBarMarginEnd);
116 setBackgroundColor(switchBarBackgroundColor);
117 mSwitch.setBackgroundColor(switchBarBackgroundColor);
119 addOnSwitchChangeListener(new OnSwitchChangeListener() {
121 public void onSwitchChanged(Switch switchView, boolean isChecked) {
122 setTextViewLabel(isChecked);
126 mRestrictedIcon = findViewById(R.id.restricted_icon);
128 setOnClickListener(this);
131 setVisibility(View.GONE);
134 public void setMetricsTag(String tag) {
138 public void setTextViewLabel(boolean isChecked) {
139 mLabel = getResources()
140 .getString(isChecked ? R.string.switch_on_text : R.string.switch_off_text);
144 public void setSummary(String summary) {
149 private void updateText() {
150 if (TextUtils.isEmpty(mSummary)) {
151 mTextView.setText(mLabel);
154 final SpannableStringBuilder ssb = new SpannableStringBuilder(mLabel).append('\n');
155 final int start = ssb.length();
156 ssb.append(mSummary);
157 ssb.setSpan(mSummarySpan, start, ssb.length(), 0);
158 mTextView.setText(ssb);
161 public void setChecked(boolean checked) {
162 setTextViewLabel(checked);
163 mSwitch.setChecked(checked);
166 public void setCheckedInternal(boolean checked) {
167 setTextViewLabel(checked);
168 mSwitch.setCheckedInternal(checked);
171 public boolean isChecked() {
172 return mSwitch.isChecked();
175 public void setEnabled(boolean enabled) {
176 if (enabled && mDisabledByAdmin) {
177 setDisabledByAdmin(null);
180 super.setEnabled(enabled);
181 mTextView.setEnabled(enabled);
182 mSwitch.setEnabled(enabled);
186 * If admin is not null, disables the text and switch but keeps the view clickable.
187 * Otherwise, calls setEnabled which will enables the entire view including
188 * the text and switch.
190 public void setDisabledByAdmin(EnforcedAdmin admin) {
191 mEnforcedAdmin = admin;
193 super.setEnabled(true);
194 mDisabledByAdmin = true;
195 mTextView.setEnabled(false);
196 mSwitch.setEnabled(false);
197 mSwitch.setVisibility(View.GONE);
198 mRestrictedIcon.setVisibility(View.VISIBLE);
200 mDisabledByAdmin = false;
201 mSwitch.setVisibility(View.VISIBLE);
202 mRestrictedIcon.setVisibility(View.GONE);
207 public final ToggleSwitch getSwitch() {
213 setVisibility(View.VISIBLE);
214 mSwitch.setOnCheckedChangeListener(this);
220 setVisibility(View.GONE);
221 mSwitch.setOnCheckedChangeListener(null);
225 public boolean isShowing() {
226 return (getVisibility() == View.VISIBLE);
230 public void onClick(View v) {
231 if (mDisabledByAdmin) {
232 MetricsLogger.histogram(mContext, mMetricsTag + "/switch_bar|restricted", 1);
233 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
235 final boolean isChecked = !mSwitch.isChecked();
236 MetricsLogger.histogram(mContext, mMetricsTag + "/switch_bar|" + isChecked, 1);
237 setChecked(isChecked);
241 public void propagateChecked(boolean isChecked) {
242 final int count = mSwitchChangeListeners.size();
243 for (int n = 0; n < count; n++) {
244 mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked);
249 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
250 propagateChecked(isChecked);
253 public void addOnSwitchChangeListener(OnSwitchChangeListener listener) {
254 if (mSwitchChangeListeners.contains(listener)) {
255 throw new IllegalStateException("Cannot add twice the same OnSwitchChangeListener");
257 mSwitchChangeListeners.add(listener);
260 public void removeOnSwitchChangeListener(OnSwitchChangeListener listener) {
261 if (!mSwitchChangeListeners.contains(listener)) {
262 throw new IllegalStateException("Cannot remove OnSwitchChangeListener");
264 mSwitchChangeListeners.remove(listener);
267 static class SavedState extends BaseSavedState {
271 SavedState(Parcelable superState) {
276 * Constructor called from {@link #CREATOR}
278 private SavedState(Parcel in) {
280 checked = (Boolean)in.readValue(null);
281 visible = (Boolean)in.readValue(null);
285 public void writeToParcel(Parcel out, int flags) {
286 super.writeToParcel(out, flags);
287 out.writeValue(checked);
288 out.writeValue(visible);
292 public String toString() {
293 return "SwitchBar.SavedState{"
294 + Integer.toHexString(System.identityHashCode(this))
295 + " checked=" + checked
296 + " visible=" + visible + "}";
299 public static final Parcelable.Creator<SavedState> CREATOR
300 = new Parcelable.Creator<SavedState>() {
301 public SavedState createFromParcel(Parcel in) {
302 return new SavedState(in);
305 public SavedState[] newArray(int size) {
306 return new SavedState[size];
312 public Parcelable onSaveInstanceState() {
313 Parcelable superState = super.onSaveInstanceState();
315 SavedState ss = new SavedState(superState);
316 ss.checked = mSwitch.isChecked();
317 ss.visible = isShowing();
322 public void onRestoreInstanceState(Parcelable state) {
323 SavedState ss = (SavedState) state;
325 super.onRestoreInstanceState(ss.getSuperState());
327 mSwitch.setCheckedInternal(ss.checked);
328 setTextViewLabel(ss.checked);
329 setVisibility(ss.visible ? View.VISIBLE : View.GONE);
330 mSwitch.setOnCheckedChangeListener(ss.visible ? this : null);
336 public CharSequence getAccessibilityClassName() {
337 return Switch.class.getName();
342 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
343 super.onInitializeAccessibilityNodeInfoInternal(info);
344 info.setText(mTextView.getText());
345 info.setCheckable(true);
346 info.setChecked(mSwitch.isChecked());
351 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
352 super.onInitializeAccessibilityEventInternal(event);
353 // Don't say "on on" or "off off" - rather, speak the state only once. We need to specify
354 // this explicitly as each of our children (the textview and the checkbox) contribute to
355 // the state once, giving us duplicate text by default.
356 event.setContentDescription(mTextView.getText());
357 event.setChecked(mSwitch.isChecked());