OSDN Git Service

3d8a5a6a5cb1fc5496bd387e5fc8672493ff22a9
[android-x86/packages-apps-Settings.git] / src / com / android / settings / widget / SwitchBar.java
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.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;
36
37 import com.android.internal.logging.MetricsLogger;
38 import com.android.settings.R;
39 import com.android.settingslib.RestrictedLockUtils;
40
41 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
42
43 import java.util.ArrayList;
44
45 public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener,
46         View.OnClickListener {
47
48     public static interface OnSwitchChangeListener {
49         /**
50          * Called when the checked state of the Switch has changed.
51          *
52          * @param switchView The Switch view whose state has changed.
53          * @param isChecked  The new checked state of switchView.
54          */
55         void onSwitchChanged(Switch switchView, boolean isChecked);
56     }
57
58     private final TextAppearanceSpan mSummarySpan;
59
60     private ToggleSwitch mSwitch;
61     private View mRestrictedIcon;
62     private TextView mTextView;
63     private String mLabel;
64     private String mSummary;
65
66     private boolean mDisabledByAdmin = false;
67     private EnforcedAdmin mEnforcedAdmin = null;
68
69     private String mMetricsTag;
70
71     private ArrayList<OnSwitchChangeListener> mSwitchChangeListeners =
72             new ArrayList<OnSwitchChangeListener>();
73
74     private static int[] XML_ATTRIBUTES = {
75             R.attr.switchBarMarginStart, R.attr.switchBarMarginEnd,
76             R.attr.switchBarBackgroundColor};
77
78     public SwitchBar(Context context) {
79         this(context, null);
80     }
81
82     public SwitchBar(Context context, AttributeSet attrs) {
83         this(context, attrs, 0);
84     }
85
86     public SwitchBar(Context context, AttributeSet attrs, int defStyleAttr) {
87         this(context, attrs, defStyleAttr, 0);
88     }
89
90     public SwitchBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
91         super(context, attrs, defStyleAttr, defStyleRes);
92
93         LayoutInflater.from(context).inflate(R.layout.switch_bar, this);
94
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);
99         a.recycle();
100
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);
105         updateText();
106         ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) mTextView.getLayoutParams();
107         lp.setMarginStart(switchBarMarginStart);
108
109         mSwitch = (ToggleSwitch) findViewById(R.id.switch_widget);
110         // Prevent onSaveInstanceState() to be called as we are managing the state of the Switch
111         // on our own
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);
118
119         addOnSwitchChangeListener(new OnSwitchChangeListener() {
120             @Override
121             public void onSwitchChanged(Switch switchView, boolean isChecked) {
122                 setTextViewLabel(isChecked);
123             }
124         });
125
126         mRestrictedIcon = findViewById(R.id.restricted_icon);
127
128         setOnClickListener(this);
129
130         // Default is hide
131         setVisibility(View.GONE);
132     }
133
134     public void setMetricsTag(String tag) {
135         mMetricsTag = tag;
136     }
137
138     public void setTextViewLabel(boolean isChecked) {
139         mLabel = getResources()
140                 .getString(isChecked ? R.string.switch_on_text : R.string.switch_off_text);
141         updateText();
142     }
143
144     public void setSummary(String summary) {
145         mSummary = summary;
146         updateText();
147     }
148
149     private void updateText() {
150         if (TextUtils.isEmpty(mSummary)) {
151             mTextView.setText(mLabel);
152             return;
153         }
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);
159     }
160
161     public void setChecked(boolean checked) {
162         setTextViewLabel(checked);
163         mSwitch.setChecked(checked);
164     }
165
166     public void setCheckedInternal(boolean checked) {
167         setTextViewLabel(checked);
168         mSwitch.setCheckedInternal(checked);
169     }
170
171     public boolean isChecked() {
172         return mSwitch.isChecked();
173     }
174
175     public void setEnabled(boolean enabled) {
176         if (enabled && mDisabledByAdmin) {
177             setDisabledByAdmin(null);
178             return;
179         }
180         super.setEnabled(enabled);
181         mTextView.setEnabled(enabled);
182         mSwitch.setEnabled(enabled);
183     }
184
185     /**
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.
189      */
190     public void setDisabledByAdmin(EnforcedAdmin admin) {
191         mEnforcedAdmin = admin;
192         if (admin != null) {
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);
199         } else {
200             mDisabledByAdmin = false;
201             mSwitch.setVisibility(View.VISIBLE);
202             mRestrictedIcon.setVisibility(View.GONE);
203             setEnabled(true);
204         }
205     }
206
207     public final ToggleSwitch getSwitch() {
208         return mSwitch;
209     }
210
211     public void show() {
212         if (!isShowing()) {
213             setVisibility(View.VISIBLE);
214             mSwitch.setOnCheckedChangeListener(this);
215         }
216     }
217
218     public void hide() {
219         if (isShowing()) {
220             setVisibility(View.GONE);
221             mSwitch.setOnCheckedChangeListener(null);
222         }
223     }
224
225     public boolean isShowing() {
226         return (getVisibility() == View.VISIBLE);
227     }
228
229     @Override
230     public void onClick(View v) {
231         if (mDisabledByAdmin) {
232             MetricsLogger.histogram(mContext, mMetricsTag + "/switch_bar|restricted", 1);
233             RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
234         } else {
235             final boolean isChecked = !mSwitch.isChecked();
236             MetricsLogger.histogram(mContext, mMetricsTag + "/switch_bar|" + isChecked, 1);
237             setChecked(isChecked);
238         }
239     }
240
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);
245         }
246     }
247
248     @Override
249     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
250         propagateChecked(isChecked);
251     }
252
253     public void addOnSwitchChangeListener(OnSwitchChangeListener listener) {
254         if (mSwitchChangeListeners.contains(listener)) {
255             throw new IllegalStateException("Cannot add twice the same OnSwitchChangeListener");
256         }
257         mSwitchChangeListeners.add(listener);
258     }
259
260     public void removeOnSwitchChangeListener(OnSwitchChangeListener listener) {
261         if (!mSwitchChangeListeners.contains(listener)) {
262             throw new IllegalStateException("Cannot remove OnSwitchChangeListener");
263         }
264         mSwitchChangeListeners.remove(listener);
265     }
266
267     static class SavedState extends BaseSavedState {
268         boolean checked;
269         boolean visible;
270
271         SavedState(Parcelable superState) {
272             super(superState);
273         }
274
275         /**
276          * Constructor called from {@link #CREATOR}
277          */
278         private SavedState(Parcel in) {
279             super(in);
280             checked = (Boolean)in.readValue(null);
281             visible = (Boolean)in.readValue(null);
282         }
283
284         @Override
285         public void writeToParcel(Parcel out, int flags) {
286             super.writeToParcel(out, flags);
287             out.writeValue(checked);
288             out.writeValue(visible);
289         }
290
291         @Override
292         public String toString() {
293             return "SwitchBar.SavedState{"
294                     + Integer.toHexString(System.identityHashCode(this))
295                     + " checked=" + checked
296                     + " visible=" + visible + "}";
297         }
298
299         public static final Parcelable.Creator<SavedState> CREATOR
300                 = new Parcelable.Creator<SavedState>() {
301             public SavedState createFromParcel(Parcel in) {
302                 return new SavedState(in);
303             }
304
305             public SavedState[] newArray(int size) {
306                 return new SavedState[size];
307             }
308         };
309     }
310
311     @Override
312     public Parcelable onSaveInstanceState() {
313         Parcelable superState = super.onSaveInstanceState();
314
315         SavedState ss = new SavedState(superState);
316         ss.checked = mSwitch.isChecked();
317         ss.visible = isShowing();
318         return ss;
319     }
320
321     @Override
322     public void onRestoreInstanceState(Parcelable state) {
323         SavedState ss = (SavedState) state;
324
325         super.onRestoreInstanceState(ss.getSuperState());
326
327         mSwitch.setCheckedInternal(ss.checked);
328         setTextViewLabel(ss.checked);
329         setVisibility(ss.visible ? View.VISIBLE : View.GONE);
330         mSwitch.setOnCheckedChangeListener(ss.visible ? this : null);
331
332         requestLayout();
333     }
334
335     @Override
336     public CharSequence getAccessibilityClassName() {
337         return Switch.class.getName();
338     }
339
340     /** @hide */
341     @Override
342     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
343         super.onInitializeAccessibilityNodeInfoInternal(info);
344         info.setText(mTextView.getText());
345         info.setCheckable(true);
346         info.setChecked(mSwitch.isChecked());
347     }
348
349     /** @hide */
350     @Override
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());
358     }
359 }