1 /* 2 * Copyright (C) 2021 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; 18 19 import android.content.Context; 20 import android.util.AttributeSet; 21 import android.view.MotionEvent; 22 import android.widget.Switch; 23 24 import androidx.annotation.Keep; 25 import androidx.annotation.Nullable; 26 import androidx.annotation.VisibleForTesting; 27 import androidx.preference.PreferenceViewHolder; 28 29 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 30 import com.android.settingslib.core.instrumentation.SettingsJankMonitor; 31 32 /** 33 * A custom preference that provides inline switch toggle. It has a mandatory field for title, and 34 * optional fields for icon and sub-text. And it can be restricted by admin state. 35 */ 36 public class PrimarySwitchPreference extends RestrictedPreference { 37 38 private Switch mSwitch; 39 private boolean mChecked; 40 private boolean mCheckedSet; 41 private boolean mEnableSwitch = true; 42 PrimarySwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)43 public PrimarySwitchPreference(Context context, AttributeSet attrs, 44 int defStyleAttr, int defStyleRes) { 45 super(context, attrs, defStyleAttr, defStyleRes); 46 } 47 PrimarySwitchPreference(Context context, AttributeSet attrs, int defStyleAttr)48 public PrimarySwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) { 49 super(context, attrs, defStyleAttr); 50 } 51 PrimarySwitchPreference(Context context, AttributeSet attrs)52 public PrimarySwitchPreference(Context context, AttributeSet attrs) { 53 super(context, attrs); 54 } 55 PrimarySwitchPreference(Context context)56 public PrimarySwitchPreference(Context context) { 57 super(context); 58 } 59 60 @Override getSecondTargetResId()61 protected int getSecondTargetResId() { 62 return R.layout.preference_widget_primary_switch; 63 } 64 65 @Override onBindViewHolder(PreferenceViewHolder holder)66 public void onBindViewHolder(PreferenceViewHolder holder) { 67 super.onBindViewHolder(holder); 68 mSwitch = (Switch) holder.findViewById(R.id.switchWidget); 69 if (mSwitch != null) { 70 mSwitch.setOnClickListener(v -> { 71 if (mSwitch != null && !mSwitch.isEnabled()) { 72 return; 73 } 74 final boolean newChecked = !mChecked; 75 if (callChangeListener(newChecked)) { 76 SettingsJankMonitor.detectToggleJank(getKey(), mSwitch); 77 setChecked(newChecked); 78 persistBoolean(newChecked); 79 } 80 }); 81 82 // Consumes move events to ignore drag actions. 83 mSwitch.setOnTouchListener((v, event) -> { 84 return event.getActionMasked() == MotionEvent.ACTION_MOVE; 85 }); 86 87 mSwitch.setContentDescription(getTitle()); 88 mSwitch.setChecked(mChecked); 89 mSwitch.setEnabled(mEnableSwitch); 90 } 91 } 92 isChecked()93 public boolean isChecked() { 94 return mSwitch != null && mChecked; 95 } 96 97 /** 98 * Used to validate the state of mChecked and mCheckedSet when testing, without requiring 99 * that a ViewHolder be bound to the object. 100 */ 101 @Keep 102 @Nullable getCheckedState()103 public Boolean getCheckedState() { 104 return mCheckedSet ? mChecked : null; 105 } 106 107 /** 108 * Set the checked status to be {@code checked}. 109 * 110 * @param checked The new checked status 111 */ setChecked(boolean checked)112 public void setChecked(boolean checked) { 113 // Always set checked the first time; don't assume the field's default of false. 114 final boolean changed = mChecked != checked; 115 if (changed || !mCheckedSet) { 116 mChecked = checked; 117 mCheckedSet = true; 118 if (mSwitch != null) { 119 mSwitch.setChecked(checked); 120 } 121 } 122 } 123 124 /** 125 * Set the Switch to be the status of {@code enabled}. 126 * 127 * @param enabled The new enabled status 128 */ setSwitchEnabled(boolean enabled)129 public void setSwitchEnabled(boolean enabled) { 130 mEnableSwitch = enabled; 131 if (mSwitch != null) { 132 mSwitch.setEnabled(enabled); 133 } 134 } 135 136 @VisibleForTesting(otherwise = VisibleForTesting.NONE) isSwitchEnabled()137 public boolean isSwitchEnabled() { 138 return mEnableSwitch; 139 } 140 141 /** 142 * If admin is not null, disables the switch. 143 * Otherwise, keep it enabled. 144 */ setDisabledByAdmin(EnforcedAdmin admin)145 public void setDisabledByAdmin(EnforcedAdmin admin) { 146 super.setDisabledByAdmin(admin); 147 setSwitchEnabled(admin == null); 148 } 149 getSwitch()150 public Switch getSwitch() { 151 return mSwitch; 152 } 153 154 @Override shouldHideSecondTarget()155 protected boolean shouldHideSecondTarget() { 156 return getSecondTargetResId() == 0; 157 } 158 } 159