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