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.settings.accessibility; 18 19 import static android.content.Context.MODE_PRIVATE; 20 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.SharedPreferences; 24 import android.content.res.Resources; 25 import android.provider.Settings; 26 import android.util.ArrayMap; 27 28 import androidx.annotation.VisibleForTesting; 29 import androidx.lifecycle.LifecycleObserver; 30 import androidx.preference.Preference; 31 import androidx.preference.PreferenceScreen; 32 33 import com.android.settings.R; 34 import com.android.settings.core.BasePreferenceController; 35 import com.android.settings.core.PreferenceControllerMixin; 36 import com.android.settingslib.core.lifecycle.Lifecycle; 37 import com.android.settingslib.widget.LayoutPreference; 38 import com.android.settingslib.widget.RadioButtonPreference; 39 40 import java.util.Map; 41 42 /** 43 * Controller class that controls accessibility autoclick settings. 44 */ 45 public class ToggleAutoclickPreferenceController extends BasePreferenceController implements 46 LifecycleObserver, RadioButtonPreference.OnClickListener, PreferenceControllerMixin { 47 48 @VisibleForTesting 49 static final String CONTROL_AUTOCLICK_DELAY_SECURE = 50 Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY; 51 52 @VisibleForTesting 53 static final String KEY_AUTOCLICK_CUSTOM_SEEKBAR = "autoclick_custom_seekbar"; 54 static final String KEY_DELAY_MODE = "delay_mode"; 55 56 @VisibleForTesting 57 static final int AUTOCLICK_OFF_MODE = 0; 58 59 @VisibleForTesting 60 static final int AUTOCLICK_CUSTOM_MODE = 2000; 61 62 // Pair the preference key and autoclick mode value. 63 @VisibleForTesting 64 Map<String, Integer> mAccessibilityAutoclickKeyToValueMap = new ArrayMap<>(); 65 66 private SharedPreferences mSharedPreferences; 67 private final ContentResolver mContentResolver; 68 private final Resources mResources; 69 private OnChangeListener mOnChangeListener; 70 private RadioButtonPreference mDelayModePref; 71 72 /** 73 * Seek bar preference for autoclick delay value. The seek bar has values between 0 and 74 * number of possible discrete autoclick delay values. These will have to be converted to actual 75 * delay values before saving them in settings. 76 */ 77 private LayoutPreference mSeekBerPreference; 78 private int mCurrentUiAutoClickMode; 79 ToggleAutoclickPreferenceController(Context context, String preferenceKey)80 public ToggleAutoclickPreferenceController(Context context, String preferenceKey) { 81 this(context, /* lifecycle= */ null, preferenceKey); 82 } 83 ToggleAutoclickPreferenceController(Context context, Lifecycle lifecycle, String preferenceKey)84 public ToggleAutoclickPreferenceController(Context context, Lifecycle lifecycle, 85 String preferenceKey) { 86 super(context, preferenceKey); 87 88 mSharedPreferences = context.getSharedPreferences(context.getPackageName(), MODE_PRIVATE); 89 mContentResolver = context.getContentResolver(); 90 mResources = context.getResources(); 91 92 setAutoclickModeToKeyMap(); 93 94 if (lifecycle != null) { 95 lifecycle.addObserver(this); 96 } 97 } 98 setOnChangeListener(OnChangeListener listener)99 public void setOnChangeListener(OnChangeListener listener) { 100 mOnChangeListener = listener; 101 } 102 103 @Override getAvailabilityStatus()104 public int getAvailabilityStatus() { 105 return AVAILABLE; 106 } 107 108 @Override displayPreference(PreferenceScreen screen)109 public void displayPreference(PreferenceScreen screen) { 110 super.displayPreference(screen); 111 112 mDelayModePref = (RadioButtonPreference) 113 screen.findPreference(getPreferenceKey()); 114 mDelayModePref.setOnClickListener(this); 115 mSeekBerPreference = (LayoutPreference) screen.findPreference(KEY_AUTOCLICK_CUSTOM_SEEKBAR); 116 updateState((Preference) mDelayModePref); 117 } 118 119 @Override onRadioButtonClicked(RadioButtonPreference preference)120 public void onRadioButtonClicked(RadioButtonPreference preference) { 121 final int value = mAccessibilityAutoclickKeyToValueMap.get(mPreferenceKey); 122 handleRadioButtonPreferenceChange(value); 123 if (mOnChangeListener != null) { 124 mOnChangeListener.onCheckedChanged(mDelayModePref); 125 } 126 } 127 updatePreferenceCheckedState(int mode)128 private void updatePreferenceCheckedState(int mode) { 129 if (mCurrentUiAutoClickMode == mode) { 130 mDelayModePref.setChecked(true); 131 } 132 } 133 updatePreferenceVisibleState(int mode)134 private void updatePreferenceVisibleState(int mode) { 135 mSeekBerPreference.setVisible(mCurrentUiAutoClickMode == mode); 136 } 137 138 @Override updateState(Preference preference)139 public void updateState(Preference preference) { 140 super.updateState(preference); 141 142 final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(), 143 Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0) == 1; 144 145 mCurrentUiAutoClickMode = 146 enabled ? getSharedPreferenceForAutoClickMode() : AUTOCLICK_OFF_MODE; 147 148 // Reset RadioButton. 149 mDelayModePref.setChecked(false); 150 final int mode = mAccessibilityAutoclickKeyToValueMap.get(mDelayModePref.getKey()); 151 updatePreferenceCheckedState(mode); 152 updatePreferenceVisibleState(mode); 153 } 154 155 /** Listener interface handles checked event. */ 156 public interface OnChangeListener { 157 /** 158 * A hook that is called when preference checked. 159 */ onCheckedChanged(Preference preference)160 void onCheckedChanged(Preference preference); 161 } 162 setAutoclickModeToKeyMap()163 private void setAutoclickModeToKeyMap() { 164 final String[] autoclickKeys = mResources.getStringArray( 165 R.array.accessibility_autoclick_control_selector_keys); 166 167 final int[] autoclickValues = mResources.getIntArray( 168 R.array.accessibility_autoclick_selector_values); 169 170 final int autoclickValueCount = autoclickValues.length; 171 for (int i = 0; i < autoclickValueCount; i++) { 172 mAccessibilityAutoclickKeyToValueMap.put(autoclickKeys[i], autoclickValues[i]); 173 } 174 } 175 handleRadioButtonPreferenceChange(int preference)176 private void handleRadioButtonPreferenceChange(int preference) { 177 putSecureInt(Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 178 (preference != AUTOCLICK_OFF_MODE) ? /* enabled */ 1 : /* disabled */ 0); 179 180 mSharedPreferences.edit().putInt(KEY_DELAY_MODE, preference).apply(); 181 182 if (preference != AUTOCLICK_CUSTOM_MODE) { 183 putSecureInt(CONTROL_AUTOCLICK_DELAY_SECURE, preference); 184 } 185 } 186 putSecureInt(String name, int value)187 private void putSecureInt(String name, int value) { 188 Settings.Secure.putInt(mContentResolver, name, value); 189 } 190 getSharedPreferenceForAutoClickMode()191 private int getSharedPreferenceForAutoClickMode() { 192 return mSharedPreferences.getInt(KEY_DELAY_MODE, AUTOCLICK_CUSTOM_MODE); 193 } 194 } 195