1 /* 2 * Copyright (C) 2020 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.settingslib.widget; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.graphics.drawable.Drawable; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.util.AttributeSet; 25 import android.view.LayoutInflater; 26 import android.view.View; 27 import android.widget.CompoundButton; 28 import android.widget.LinearLayout; 29 import android.widget.Switch; 30 import android.widget.TextView; 31 32 import androidx.annotation.ColorInt; 33 34 import com.android.settingslib.utils.BuildCompatUtils; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 39 /** 40 * MainSwitchBar is a View with a customized Switch. 41 * This component is used as the main switch of the page 42 * to enable or disable the prefereces on the page. 43 */ 44 public class MainSwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener { 45 46 private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>(); 47 48 @ColorInt 49 private int mBackgroundColor; 50 @ColorInt 51 private int mBackgroundActivatedColor; 52 53 protected TextView mTextView; 54 protected Switch mSwitch; 55 private Drawable mBackgroundOn; 56 private Drawable mBackgroundOff; 57 private Drawable mBackgroundDisabled; 58 private View mFrameView; 59 MainSwitchBar(Context context)60 public MainSwitchBar(Context context) { 61 this(context, null); 62 } 63 MainSwitchBar(Context context, AttributeSet attrs)64 public MainSwitchBar(Context context, AttributeSet attrs) { 65 this(context, attrs, 0); 66 } 67 MainSwitchBar(Context context, AttributeSet attrs, int defStyleAttr)68 public MainSwitchBar(Context context, AttributeSet attrs, int defStyleAttr) { 69 this(context, attrs, defStyleAttr, 0); 70 } 71 MainSwitchBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)72 public MainSwitchBar(Context context, AttributeSet attrs, int defStyleAttr, 73 int defStyleRes) { 74 super(context, attrs, defStyleAttr, defStyleRes); 75 76 LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this); 77 78 if (!BuildCompatUtils.isAtLeastS()) { 79 final TypedArray a = context.obtainStyledAttributes( 80 new int[]{android.R.attr.colorAccent}); 81 mBackgroundActivatedColor = a.getColor(0, 0); 82 mBackgroundColor = context.getColor(R.color.material_grey_600); 83 a.recycle(); 84 } 85 86 setFocusable(true); 87 setClickable(true); 88 89 mFrameView = findViewById(R.id.frame); 90 mTextView = (TextView) findViewById(R.id.switch_text); 91 mSwitch = (Switch) findViewById(android.R.id.switch_widget); 92 if (BuildCompatUtils.isAtLeastS()) { 93 mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on); 94 mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off); 95 mBackgroundDisabled = getContext().getDrawable( 96 R.drawable.settingslib_switch_bar_bg_disabled); 97 } 98 addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked)); 99 100 if (mSwitch.getVisibility() == VISIBLE) { 101 mSwitch.setOnCheckedChangeListener(this); 102 } 103 104 setChecked(mSwitch.isChecked()); 105 106 if (attrs != null) { 107 final TypedArray a = context.obtainStyledAttributes(attrs, 108 androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/, 109 0 /*defStyleRes*/); 110 final CharSequence title = a.getText( 111 androidx.preference.R.styleable.Preference_android_title); 112 setTitle(title); 113 a.recycle(); 114 } 115 116 setBackground(mSwitch.isChecked()); 117 } 118 119 @Override onCheckedChanged(CompoundButton buttonView, boolean isChecked)120 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 121 propagateChecked(isChecked); 122 } 123 124 @Override performClick()125 public boolean performClick() { 126 mSwitch.performClick(); 127 return super.performClick(); 128 } 129 130 /** 131 * Update the switch status 132 */ setChecked(boolean checked)133 public void setChecked(boolean checked) { 134 if (mSwitch != null) { 135 mSwitch.setChecked(checked); 136 } 137 setBackground(checked); 138 } 139 140 /** 141 * Return the status of the Switch 142 */ isChecked()143 public boolean isChecked() { 144 return mSwitch.isChecked(); 145 } 146 147 /** 148 * Return the Switch 149 */ getSwitch()150 public final Switch getSwitch() { 151 return mSwitch; 152 } 153 154 /** 155 * Set the title text 156 */ setTitle(CharSequence text)157 public void setTitle(CharSequence text) { 158 if (mTextView != null) { 159 mTextView.setText(text); 160 } 161 } 162 163 /** 164 * Set icon space reserved for title 165 */ setIconSpaceReserved(boolean iconSpaceReserved)166 public void setIconSpaceReserved(boolean iconSpaceReserved) { 167 if (mTextView != null && !BuildCompatUtils.isAtLeastS()) { 168 LayoutParams params = (LayoutParams) mTextView.getLayoutParams(); 169 int iconSpace = getContext().getResources().getDimensionPixelSize( 170 R.dimen.settingslib_switchbar_subsettings_margin_start); 171 params.setMarginStart(iconSpaceReserved ? iconSpace : 0); 172 mTextView.setLayoutParams(params); 173 } 174 } 175 176 /** 177 * Show the MainSwitchBar 178 */ show()179 public void show() { 180 setVisibility(View.VISIBLE); 181 mSwitch.setOnCheckedChangeListener(this); 182 } 183 184 /** 185 * Hide the MainSwitchBar 186 */ hide()187 public void hide() { 188 if (isShowing()) { 189 setVisibility(View.GONE); 190 mSwitch.setOnCheckedChangeListener(null); 191 } 192 } 193 194 /** 195 * Return the displaying status of MainSwitchBar 196 */ isShowing()197 public boolean isShowing() { 198 return (getVisibility() == View.VISIBLE); 199 } 200 201 /** 202 * Adds a listener for switch changes 203 */ addOnSwitchChangeListener(OnMainSwitchChangeListener listener)204 public void addOnSwitchChangeListener(OnMainSwitchChangeListener listener) { 205 if (!mSwitchChangeListeners.contains(listener)) { 206 mSwitchChangeListeners.add(listener); 207 } 208 } 209 210 /** 211 * Remove a listener for switch changes 212 */ removeOnSwitchChangeListener(OnMainSwitchChangeListener listener)213 public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) { 214 mSwitchChangeListeners.remove(listener); 215 } 216 217 /** 218 * Enable or disable the text and switch. 219 */ setEnabled(boolean enabled)220 public void setEnabled(boolean enabled) { 221 super.setEnabled(enabled); 222 mTextView.setEnabled(enabled); 223 mSwitch.setEnabled(enabled); 224 225 if (BuildCompatUtils.isAtLeastS()) { 226 if (enabled) { 227 mFrameView.setBackground(isChecked() ? mBackgroundOn : mBackgroundOff); 228 } else { 229 mFrameView.setBackground(mBackgroundDisabled); 230 } 231 } 232 } 233 propagateChecked(boolean isChecked)234 private void propagateChecked(boolean isChecked) { 235 setBackground(isChecked); 236 237 final int count = mSwitchChangeListeners.size(); 238 for (int n = 0; n < count; n++) { 239 mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked); 240 } 241 } 242 setBackground(boolean isChecked)243 private void setBackground(boolean isChecked) { 244 if (!BuildCompatUtils.isAtLeastS()) { 245 setBackgroundColor(isChecked ? mBackgroundActivatedColor : mBackgroundColor); 246 } else { 247 mFrameView.setBackground(isChecked ? mBackgroundOn : mBackgroundOff); 248 } 249 } 250 251 static class SavedState extends BaseSavedState { 252 boolean mChecked; 253 boolean mVisible; 254 SavedState(Parcelable superState)255 SavedState(Parcelable superState) { 256 super(superState); 257 } 258 259 /** 260 * Constructor called from {@link #CREATOR} 261 */ SavedState(Parcel in)262 private SavedState(Parcel in) { 263 super(in); 264 mChecked = (Boolean) in.readValue(null); 265 mVisible = (Boolean) in.readValue(null); 266 } 267 268 @Override writeToParcel(Parcel out, int flags)269 public void writeToParcel(Parcel out, int flags) { 270 super.writeToParcel(out, flags); 271 out.writeValue(mChecked); 272 out.writeValue(mVisible); 273 } 274 275 @Override toString()276 public String toString() { 277 return "MainSwitchBar.SavedState{" 278 + Integer.toHexString(System.identityHashCode(this)) 279 + " checked=" + mChecked 280 + " visible=" + mVisible + "}"; 281 } 282 283 public static final Parcelable.Creator<SavedState> CREATOR = 284 new Parcelable.Creator<SavedState>() { 285 @Override 286 public SavedState createFromParcel(Parcel in) { 287 return new SavedState(in); 288 } 289 290 @Override 291 public SavedState[] newArray(int size) { 292 return new SavedState[size]; 293 } 294 }; 295 } 296 297 @Override onSaveInstanceState()298 public Parcelable onSaveInstanceState() { 299 Parcelable superState = super.onSaveInstanceState(); 300 301 SavedState ss = new SavedState(superState); 302 ss.mChecked = mSwitch.isChecked(); 303 ss.mVisible = isShowing(); 304 return ss; 305 } 306 307 @Override onRestoreInstanceState(Parcelable state)308 public void onRestoreInstanceState(Parcelable state) { 309 SavedState ss = (SavedState) state; 310 311 super.onRestoreInstanceState(ss.getSuperState()); 312 313 mSwitch.setChecked(ss.mChecked); 314 setChecked(ss.mChecked); 315 setBackground(ss.mChecked); 316 setVisibility(ss.mVisible ? View.VISIBLE : View.GONE); 317 mSwitch.setOnCheckedChangeListener(ss.mVisible ? this : null); 318 319 requestLayout(); 320 } 321 } 322