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 static com.android.settings.accessibility.ToggleAutoclickPreferenceController.KEY_DELAY_MODE; 22 import static com.android.settings.accessibility.ToggleAutoclickPreferenceFragment.Quantity; 23 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.content.SharedPreferences; 27 import android.provider.Settings; 28 import android.view.accessibility.AccessibilityManager; 29 import android.widget.ImageView; 30 import android.widget.SeekBar; 31 import android.widget.TextView; 32 33 import androidx.preference.PreferenceScreen; 34 35 import com.android.settings.R; 36 import com.android.settings.core.BasePreferenceController; 37 import com.android.settingslib.core.lifecycle.Lifecycle; 38 import com.android.settingslib.core.lifecycle.LifecycleObserver; 39 import com.android.settingslib.core.lifecycle.events.OnPause; 40 import com.android.settingslib.core.lifecycle.events.OnResume; 41 import com.android.settingslib.widget.LayoutPreference; 42 43 /** 44 * Controller class that controls accessibility autoclick seekbar settings. 45 */ 46 public class ToggleAutoclickCustomSeekbarController extends BasePreferenceController 47 implements LifecycleObserver, OnResume, OnPause, 48 SharedPreferences.OnSharedPreferenceChangeListener { 49 50 private static final String CONTROL_AUTOCLICK_DELAY_SECURE = 51 Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY; 52 private static final String KEY_CUSTOM_DELAY_VALUE = "custom_delay_value"; 53 54 // Min allowed autoclick delay value. 55 static final int MIN_AUTOCLICK_DELAY_MS = 200; 56 57 // Max allowed autoclick delay value. 58 static final int MAX_AUTOCLICK_DELAY_MS = 1000; 59 60 // Allowed autoclick delay values are discrete. 61 // This is the difference between two allowed values. 62 private static final int AUTOCLICK_DELAY_STEP = 100; 63 64 private final SharedPreferences mSharedPreferences; 65 private final ContentResolver mContentResolver; 66 private ImageView mShorter; 67 private ImageView mLonger; 68 private SeekBar mSeekBar; 69 private TextView mDelayLabel; 70 71 private final SeekBar.OnSeekBarChangeListener mSeekBarChangeListener = 72 new SeekBar.OnSeekBarChangeListener() { 73 74 @Override 75 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 76 updateCustomDelayValue(seekBarProgressToDelay(progress)); 77 } 78 79 @Override 80 public void onStartTrackingTouch(SeekBar seekBar) { 81 // Nothing to do. 82 } 83 84 @Override 85 public void onStopTrackingTouch(SeekBar seekBar) { 86 // Nothing to do. 87 } 88 }; 89 ToggleAutoclickCustomSeekbarController(Context context, String preferenceKey)90 public ToggleAutoclickCustomSeekbarController(Context context, String preferenceKey) { 91 super(context, preferenceKey); 92 mSharedPreferences = context.getSharedPreferences(context.getPackageName(), MODE_PRIVATE); 93 mContentResolver = context.getContentResolver(); 94 } 95 ToggleAutoclickCustomSeekbarController(Context context, Lifecycle lifecycle, String preferenceKey)96 public ToggleAutoclickCustomSeekbarController(Context context, Lifecycle lifecycle, 97 String preferenceKey) { 98 this(context, preferenceKey); 99 100 if (lifecycle != null) { 101 lifecycle.addObserver(this); 102 } 103 } 104 105 @Override getAvailabilityStatus()106 public int getAvailabilityStatus() { 107 return AVAILABLE; 108 } 109 110 @Override onResume()111 public void onResume() { 112 if (mSharedPreferences != null) { 113 mSharedPreferences.registerOnSharedPreferenceChangeListener(this); 114 } 115 } 116 117 @Override onPause()118 public void onPause() { 119 if (mSharedPreferences != null) { 120 mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this); 121 } 122 } 123 124 @Override displayPreference(PreferenceScreen screen)125 public void displayPreference(PreferenceScreen screen) { 126 super.displayPreference(screen); 127 final LayoutPreference preference = screen.findPreference(getPreferenceKey()); 128 129 if (isAvailable()) { 130 int delayMillis = getSharedPreferenceForDelayValue(); 131 // Initialize seek bar preference. Sets seek bar size to the number of possible delay 132 // values. 133 mSeekBar = preference.findViewById(R.id.autoclick_delay); 134 mSeekBar.setMax(delayToSeekBarProgress(MAX_AUTOCLICK_DELAY_MS)); 135 mSeekBar.setProgress(delayToSeekBarProgress(delayMillis)); 136 mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener); 137 138 mDelayLabel = preference.findViewById(R.id.current_label); 139 mDelayLabel.setText(delayTimeToString(delayMillis)); 140 141 mShorter = preference.findViewById(R.id.shorter); 142 mShorter.setOnClickListener(v -> { 143 minusDelayByImageView(); 144 }); 145 146 mLonger = preference.findViewById(R.id.longer); 147 mLonger.setOnClickListener(v -> { 148 plusDelayByImageView(); 149 }); 150 } 151 } 152 153 @Override onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)154 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 155 if (KEY_DELAY_MODE.equals(key)) { 156 final int delayMillis = getSharedPreferenceForDelayValue(); 157 updateCustomDelayValue(delayMillis); 158 } 159 } 160 161 /** Converts seek bar preference progress value to autoclick delay associated with it. */ seekBarProgressToDelay(int progress)162 private int seekBarProgressToDelay(int progress) { 163 return progress * AUTOCLICK_DELAY_STEP + MIN_AUTOCLICK_DELAY_MS; 164 } 165 166 /** 167 * Converts autoclick delay value to seek bar preference progress values that represents said 168 * delay. 169 */ delayToSeekBarProgress(int delayMillis)170 private int delayToSeekBarProgress(int delayMillis) { 171 return (delayMillis - MIN_AUTOCLICK_DELAY_MS) / AUTOCLICK_DELAY_STEP; 172 } 173 getSharedPreferenceForDelayValue()174 private int getSharedPreferenceForDelayValue() { 175 final int delayMillis = Settings.Secure.getInt(mContentResolver, 176 Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, 177 AccessibilityManager.AUTOCLICK_DELAY_DEFAULT); 178 179 return mSharedPreferences.getInt(KEY_CUSTOM_DELAY_VALUE, delayMillis); 180 } 181 putSecureInt(String name, int value)182 private void putSecureInt(String name, int value) { 183 Settings.Secure.putInt(mContentResolver, name, value); 184 } 185 updateCustomDelayValue(int delayMillis)186 private void updateCustomDelayValue(int delayMillis) { 187 putSecureInt(CONTROL_AUTOCLICK_DELAY_SECURE, delayMillis); 188 mSharedPreferences.edit().putInt(KEY_CUSTOM_DELAY_VALUE, delayMillis).apply(); 189 mSeekBar.setProgress(delayToSeekBarProgress(delayMillis)); 190 mDelayLabel.setText(delayTimeToString(delayMillis)); 191 } 192 minusDelayByImageView()193 private void minusDelayByImageView() { 194 final int delayMillis = getSharedPreferenceForDelayValue(); 195 if (delayMillis > MIN_AUTOCLICK_DELAY_MS) { 196 updateCustomDelayValue(delayMillis - AUTOCLICK_DELAY_STEP); 197 } 198 } 199 plusDelayByImageView()200 private void plusDelayByImageView() { 201 final int delayMillis = getSharedPreferenceForDelayValue(); 202 if (delayMillis < MAX_AUTOCLICK_DELAY_MS) { 203 updateCustomDelayValue(delayMillis + AUTOCLICK_DELAY_STEP); 204 } 205 } 206 delayTimeToString(int delayMillis)207 private CharSequence delayTimeToString(int delayMillis) { 208 final int quantity = (delayMillis == 1000) ? Quantity.ONE : Quantity.FEW; 209 final float delaySecond = (float) delayMillis / 1000; 210 // Only show integer when delay time is 1. 211 final String decimalFormat = (delaySecond == 1) ? "%.0f" : "%.1f"; 212 213 return mContext.getResources().getQuantityString( 214 R.plurals.accessibilty_autoclick_delay_unit_second, 215 quantity, String.format(decimalFormat, delaySecond)); 216 } 217 } 218