Loading res/xml/accessibility_autoclick_settings.xml +13 −11 Original line number Diff line number Diff line Loading @@ -21,38 +21,41 @@ <com.android.settingslib.widget.TopIntroPreference android:key="accessibility_autoclick_intro" android:persistent="false" android:title="@string/accessibility_autoclick_intro_text"/> <com.android.settingslib.widget.IllustrationPreference android:key="accessibility_autoclick_banner" android:persistent="false" android:selectable="false" settings:searchable="false" settings:lottie_rawRes="@drawable/accessibility_dwell"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_default" android:title="@string/accessibility_autoclick_default_title" /> android:title="@string/accessibility_autoclick_default_title" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_200ms" android:title="@string/accessibility_autoclick_short_title" android:summary="@string/accessibility_autoclick_short_summary" /> android:summary="@string/accessibility_autoclick_short_summary" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_600ms" android:title="@string/accessibility_autoclick_medium_title" android:summary="@string/accessibility_autoclick_medium_summary" /> android:summary="@string/accessibility_autoclick_medium_summary" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_1sec" android:title="@string/accessibility_autoclick_long_title" android:summary="@string/accessibility_autoclick_long_summary" /> android:summary="@string/accessibility_autoclick_long_summary" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_custom" android:title="@string/accessibility_autoclick_custom_title" /> android:title="@string/accessibility_autoclick_custom_title" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.LayoutPreference android:key="autoclick_custom_seekbar" Loading @@ -64,7 +67,6 @@ <com.android.settings.accessibility.AccessibilityFooterPreference android:key="accessibility_autoclick_footer" android:title="@string/accessibility_autoclick_description" android:persistent="false" android:selectable="false" settings:searchable="false" settings:controller="com.android.settings.accessibility.ToggleAutoclickFooterPreferenceController"/> Loading src/com/android/settings/accessibility/AutoclickPreferenceController.java +34 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.settings.accessibility; import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import android.content.Context; import android.provider.Settings; import android.view.accessibility.AccessibilityManager; Loading @@ -23,8 +26,19 @@ import android.view.accessibility.AccessibilityManager; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; /** Preference controller for autoclick (dwell timing). */ public class AutoclickPreferenceController extends BasePreferenceController { /** * Resource ids from which autoclick preference summaries should be derived. The strings have * placeholder for integer delay value. */ private static final int[] AUTOCLICK_PREFERENCE_SUMMARIES = { R.plurals.accessibilty_autoclick_preference_subtitle_short_delay, R.plurals.accessibilty_autoclick_preference_subtitle_medium_delay, R.plurals.accessibilty_autoclick_preference_subtitle_long_delay }; public AutoclickPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); } Loading @@ -37,14 +51,29 @@ public class AutoclickPreferenceController extends BasePreferenceController { @Override public CharSequence getSummary() { final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0) == 1; Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, OFF) == ON; if (!enabled) { return mContext.getResources().getText(R.string.off); } final int delay = Settings.Secure.getInt(mContext.getContentResolver(), final int delayMillis = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT); return ToggleAutoclickPreferenceFragment.getAutoclickPreferenceSummary( mContext.getResources(), delay); final int summaryIndex = getAutoclickPreferenceSummaryIndex(delayMillis); return AutoclickUtils.getAutoclickDelaySummary(mContext.getResources(), AUTOCLICK_PREFERENCE_SUMMARIES[summaryIndex], delayMillis); } /** Finds index of the summary that should be used for the provided autoclick delay. */ private int getAutoclickPreferenceSummaryIndex(int delay) { if (delay <= AutoclickUtils.MIN_AUTOCLICK_DELAY_MS) { return 0; } if (delay >= AutoclickUtils.MAX_AUTOCLICK_DELAY_MS) { return AUTOCLICK_PREFERENCE_SUMMARIES.length - 1; } int delayRange = AutoclickUtils.MAX_AUTOCLICK_DELAY_MS - AutoclickUtils.MIN_AUTOCLICK_DELAY_MS; int rangeSize = (delayRange) / (AUTOCLICK_PREFERENCE_SUMMARIES.length - 1); return (delay - AutoclickUtils.MIN_AUTOCLICK_DELAY_MS) / rangeSize; } } src/com/android/settings/accessibility/AutoclickUtils.java 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.accessibility; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.PluralsRes; import android.content.res.Resources; import java.lang.annotation.Retention; /** Provides utility methods related auto click. */ public final class AutoclickUtils { /** Used for autoclick mode in the preferences editor. */ static final String KEY_DELAY_MODE = "delay_mode"; /** Used for autoclick custom delay in the preferences editor. */ static final String KEY_CUSTOM_DELAY_VALUE = "custom_delay_value"; /** Min allowed autoclick delay value. */ static final int MIN_AUTOCLICK_DELAY_MS = 200; /** Max allowed autoclick delay value. */ static final int MAX_AUTOCLICK_DELAY_MS = 1000; /** * Allowed autoclick delay values are discrete. This is the difference between two allowed * values. */ static final int AUTOCLICK_DELAY_STEP = 100; @Retention(SOURCE) @IntDef({ Quantity.ONE, Quantity.FEW }) private @interface Quantity { int ONE = 1; int FEW = 3; } /** * Gets string that should be used for provided autoclick delay. * * @param resources Resources from which string should be retrieved. * @param id The desired resource identifier, as generated by the aapt * tool. This integer encodes the package, type, and resource * entry. The value 0 is an invalid identifier. * @param delayMillis Delay for whose value summary should be retrieved. */ public static CharSequence getAutoclickDelaySummary(Resources resources, @PluralsRes int id, int delayMillis) { final int quantity = (delayMillis == 1000) ? Quantity.ONE : Quantity.FEW; final float delaySecond = (float) delayMillis / 1000; // Only show integer when delay time is 1. final String decimalFormat = (delaySecond == 1) ? "%.0f" : "%.1f"; return resources.getQuantityString(id, quantity, String.format(decimalFormat, delaySecond)); } private AutoclickUtils(){} } src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java +18 −58 Original line number Diff line number Diff line Loading @@ -18,8 +18,11 @@ package com.android.settings.accessibility; import static android.content.Context.MODE_PRIVATE; import static com.android.settings.accessibility.ToggleAutoclickPreferenceController.KEY_DELAY_MODE; import static com.android.settings.accessibility.ToggleAutoclickPreferenceFragment.Quantity; import static com.android.settings.accessibility.AutoclickUtils.AUTOCLICK_DELAY_STEP; import static com.android.settings.accessibility.AutoclickUtils.KEY_CUSTOM_DELAY_VALUE; import static com.android.settings.accessibility.AutoclickUtils.KEY_DELAY_MODE; import static com.android.settings.accessibility.AutoclickUtils.MAX_AUTOCLICK_DELAY_MS; import static com.android.settings.accessibility.AutoclickUtils.MIN_AUTOCLICK_DELAY_MS; import android.content.ContentResolver; import android.content.Context; Loading @@ -35,36 +38,16 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; import com.android.settingslib.widget.LayoutPreference; /** * Controller class that controls accessibility autoclick seekbar settings. */ /** Controller class that controls accessibility autoclick seekbar settings. */ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceController implements LifecycleObserver, OnResume, OnPause, implements LifecycleObserver, OnStart, OnStop, SharedPreferences.OnSharedPreferenceChangeListener { private static final String CONTROL_AUTOCLICK_DELAY_SECURE = Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY; @VisibleForTesting static final String KEY_CUSTOM_DELAY_VALUE = "custom_delay_value"; // Min allowed autoclick delay value. static final int MIN_AUTOCLICK_DELAY_MS = 200; // Max allowed autoclick delay value. static final int MAX_AUTOCLICK_DELAY_MS = 1000; // Allowed autoclick delay values are discrete. // This is the difference between two allowed values. @VisibleForTesting static final int AUTOCLICK_DELAY_STEP = 100; private final SharedPreferences mSharedPreferences; private final ContentResolver mContentResolver; private ImageView mShorter; Loading Loading @@ -98,29 +81,20 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro mContentResolver = context.getContentResolver(); } public ToggleAutoclickCustomSeekbarController(Context context, Lifecycle lifecycle, String preferenceKey) { this(context, preferenceKey); if (lifecycle != null) { lifecycle.addObserver(this); } } @Override public int getAvailabilityStatus() { return AVAILABLE; } @Override public void onResume() { public void onStart() { if (mSharedPreferences != null) { mSharedPreferences.registerOnSharedPreferenceChangeListener(this); } } @Override public void onPause() { public void onStop() { if (mSharedPreferences != null) { mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this); } Loading @@ -132,7 +106,7 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro final LayoutPreference preference = screen.findPreference(getPreferenceKey()); if (isAvailable()) { int delayMillis = getSharedPreferenceForDelayValue(); final int delayMillis = getSharedPreferenceForDelayValue(); // Initialize seek bar preference. Sets seek bar size to the number of possible delay // values. mSeekBar = preference.findViewById(R.id.autoclick_delay); Loading @@ -144,14 +118,10 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro mDelayLabel.setText(delayTimeToString(delayMillis)); mShorter = preference.findViewById(R.id.shorter); mShorter.setOnClickListener(v -> { minusDelayByImageView(); }); mShorter.setOnClickListener(v -> minusDelayByImageView()); mLonger = preference.findViewById(R.id.longer); mLonger.setOnClickListener(v -> { plusDelayByImageView(); }); mLonger.setOnClickListener(v -> plusDelayByImageView()); } } Loading Loading @@ -184,12 +154,9 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro return mSharedPreferences.getInt(KEY_CUSTOM_DELAY_VALUE, delayMillis); } private void putSecureInt(String name, int value) { Settings.Secure.putInt(mContentResolver, name, value); } private void updateCustomDelayValue(int delayMillis) { putSecureInt(CONTROL_AUTOCLICK_DELAY_SECURE, delayMillis); Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, delayMillis); mSharedPreferences.edit().putInt(KEY_CUSTOM_DELAY_VALUE, delayMillis).apply(); mSeekBar.setProgress(delayToSeekBarProgress(delayMillis)); mDelayLabel.setText(delayTimeToString(delayMillis)); Loading @@ -208,15 +175,8 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro updateCustomDelayValue(delayMillis + AUTOCLICK_DELAY_STEP); } } private CharSequence delayTimeToString(int delayMillis) { final int quantity = (delayMillis == 1000) ? Quantity.ONE : Quantity.FEW; final float delaySecond = (float) delayMillis / 1000; // Only show integer when delay time is 1. final String decimalFormat = (delaySecond == 1) ? "%.0f" : "%.1f"; return mContext.getResources().getQuantityString( R.plurals.accessibilty_autoclick_delay_unit_second, quantity, String.format(decimalFormat, delaySecond)); return AutoclickUtils.getAutoclickDelaySummary(mContext.getResources(), R.plurals.accessibilty_autoclick_delay_unit_second, delayMillis); } } src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java +61 −109 Original line number Diff line number Diff line Loading @@ -18,6 +18,10 @@ package com.android.settings.accessibility; import static android.content.Context.MODE_PRIVATE; import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import static com.android.settings.accessibility.AutoclickUtils.KEY_DELAY_MODE; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; Loading @@ -25,49 +29,28 @@ import android.content.res.Resources; import android.provider.Settings; import android.util.ArrayMap; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.LifecycleObserver; import androidx.annotation.Nullable; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; import com.android.settingslib.widget.LayoutPreference; import com.android.settingslib.widget.SelectorWithWidgetPreference; import java.util.Map; /** * Controller class that controls accessibility autoclick settings. */ /** Controller class that controls accessibility autoclick settings. */ public class ToggleAutoclickPreferenceController extends BasePreferenceController implements LifecycleObserver, SelectorWithWidgetPreference.OnClickListener, PreferenceControllerMixin { @VisibleForTesting static final String CONTROL_AUTOCLICK_DELAY_SECURE = Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY; @VisibleForTesting static final String KEY_AUTOCLICK_CUSTOM_SEEKBAR = "autoclick_custom_seekbar"; static final String KEY_DELAY_MODE = "delay_mode"; @VisibleForTesting static final int AUTOCLICK_OFF_MODE = 0; LifecycleObserver, OnStart, OnStop, SelectorWithWidgetPreference.OnClickListener, SharedPreferences.OnSharedPreferenceChangeListener { @VisibleForTesting static final int AUTOCLICK_CUSTOM_MODE = 2000; // Pair the preference key and autoclick mode value. @VisibleForTesting Map<String, Integer> mAccessibilityAutoclickKeyToValueMap = new ArrayMap<>(); private SharedPreferences mSharedPreferences; private final ContentResolver mContentResolver; private final Resources mResources; private OnChangeListener mOnChangeListener; private SelectorWithWidgetPreference mDelayModePref; private static final String KEY_AUTOCLICK_CUSTOM_SEEKBAR = "autoclick_custom_seekbar"; private static final int AUTOCLICK_OFF_MODE = 0; private static final int AUTOCLICK_CUSTOM_MODE = 2000; /** * Seek bar preference for autoclick delay value. The seek bar has values between 0 and Loading @@ -75,29 +58,33 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle * delay values before saving them in settings. */ private LayoutPreference mSeekBerPreference; private int mCurrentUiAutoClickMode; private SelectorWithWidgetPreference mDelayModePref; private Map<String, Integer> mAccessibilityAutoclickKeyToValueMap = new ArrayMap<>(); private final SharedPreferences mSharedPreferences; private final ContentResolver mContentResolver; private final Resources mResources; public ToggleAutoclickPreferenceController(Context context, String preferenceKey) { this(context, /* lifecycle= */ null, preferenceKey); } public ToggleAutoclickPreferenceController(Context context, Lifecycle lifecycle, String preferenceKey) { super(context, preferenceKey); mSharedPreferences = context.getSharedPreferences(context.getPackageName(), MODE_PRIVATE); mContentResolver = context.getContentResolver(); mResources = context.getResources(); } setAutoclickModeToKeyMap(); if (lifecycle != null) { lifecycle.addObserver(this); @Override public void onStart() { mSharedPreferences.registerOnSharedPreferenceChangeListener(this); } @Override public void onStop() { mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this); } public void setOnChangeListener(OnChangeListener listener) { mOnChangeListener = listener; @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) { updateState(mDelayModePref); } @Override Loading @@ -109,86 +96,51 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mDelayModePref = (SelectorWithWidgetPreference) screen.findPreference(getPreferenceKey()); mDelayModePref = screen.findPreference(getPreferenceKey()); mDelayModePref.setOnClickListener(this); mSeekBerPreference = (LayoutPreference) screen.findPreference(KEY_AUTOCLICK_CUSTOM_SEEKBAR); updateState((Preference) mDelayModePref); mSeekBerPreference = screen.findPreference(KEY_AUTOCLICK_CUSTOM_SEEKBAR); updateState(mDelayModePref); } @Override public void onRadioButtonClicked(SelectorWithWidgetPreference preference) { final int value = mAccessibilityAutoclickKeyToValueMap.get(mPreferenceKey); handleRadioButtonPreferenceChange(value); if (mOnChangeListener != null) { mOnChangeListener.onCheckedChanged(mDelayModePref); } } private void updatePreferenceCheckedState(int mode) { if (mCurrentUiAutoClickMode == mode) { mDelayModePref.setChecked(true); } final int mode = getAutoclickModeToKeyMap().get(mPreferenceKey); Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, (mode != AUTOCLICK_OFF_MODE) ? ON : OFF); mSharedPreferences.edit().putInt(KEY_DELAY_MODE, mode).apply(); if (mode != AUTOCLICK_CUSTOM_MODE) { Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, mode); } private void updatePreferenceVisibleState(int mode) { mSeekBerPreference.setVisible(mCurrentUiAutoClickMode == mode); } @Override public void updateState(Preference preference) { super.updateState(preference); final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0) == 1; mCurrentUiAutoClickMode = enabled ? getSharedPreferenceForAutoClickMode() : AUTOCLICK_OFF_MODE; // Reset RadioButton. mDelayModePref.setChecked(false); final int mode = mAccessibilityAutoclickKeyToValueMap.get(mDelayModePref.getKey()); updatePreferenceCheckedState(mode); updatePreferenceVisibleState(mode); Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, OFF) == ON; final int currentUiAutoClickMode = enabled ? mSharedPreferences.getInt(KEY_DELAY_MODE, AUTOCLICK_CUSTOM_MODE) : AUTOCLICK_OFF_MODE; final int mode = getAutoclickModeToKeyMap().get(mDelayModePref.getKey()); mDelayModePref.setChecked(currentUiAutoClickMode == mode); if (mode == AUTOCLICK_CUSTOM_MODE) { mSeekBerPreference.setVisible(mDelayModePref.isChecked()); } /** Listener interface handles checked event. */ public interface OnChangeListener { /** * A hook that is called when preference checked. */ void onCheckedChanged(Preference preference); } private void setAutoclickModeToKeyMap() { /** Returns the paring preference key and autoclick mode value listing. */ private Map<String, Integer> getAutoclickModeToKeyMap() { if (mAccessibilityAutoclickKeyToValueMap.size() == 0) { final String[] autoclickKeys = mResources.getStringArray( R.array.accessibility_autoclick_control_selector_keys); final int[] autoclickValues = mResources.getIntArray( R.array.accessibility_autoclick_selector_values); final int autoclickValueCount = autoclickValues.length; for (int i = 0; i < autoclickValueCount; i++) { mAccessibilityAutoclickKeyToValueMap.put(autoclickKeys[i], autoclickValues[i]); } } private void handleRadioButtonPreferenceChange(int preference) { putSecureInt(Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, (preference != AUTOCLICK_OFF_MODE) ? /* enabled */ 1 : /* disabled */ 0); mSharedPreferences.edit().putInt(KEY_DELAY_MODE, preference).apply(); if (preference != AUTOCLICK_CUSTOM_MODE) { putSecureInt(CONTROL_AUTOCLICK_DELAY_SECURE, preference); } } private void putSecureInt(String name, int value) { Settings.Secure.putInt(mContentResolver, name, value); } private int getSharedPreferenceForAutoClickMode() { return mSharedPreferences.getInt(KEY_DELAY_MODE, AUTOCLICK_CUSTOM_MODE); return mAccessibilityAutoclickKeyToValueMap; } } Loading
res/xml/accessibility_autoclick_settings.xml +13 −11 Original line number Diff line number Diff line Loading @@ -21,38 +21,41 @@ <com.android.settingslib.widget.TopIntroPreference android:key="accessibility_autoclick_intro" android:persistent="false" android:title="@string/accessibility_autoclick_intro_text"/> <com.android.settingslib.widget.IllustrationPreference android:key="accessibility_autoclick_banner" android:persistent="false" android:selectable="false" settings:searchable="false" settings:lottie_rawRes="@drawable/accessibility_dwell"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_default" android:title="@string/accessibility_autoclick_default_title" /> android:title="@string/accessibility_autoclick_default_title" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_200ms" android:title="@string/accessibility_autoclick_short_title" android:summary="@string/accessibility_autoclick_short_summary" /> android:summary="@string/accessibility_autoclick_short_summary" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_600ms" android:title="@string/accessibility_autoclick_medium_title" android:summary="@string/accessibility_autoclick_medium_summary" /> android:summary="@string/accessibility_autoclick_medium_summary" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_1sec" android:title="@string/accessibility_autoclick_long_title" android:summary="@string/accessibility_autoclick_long_summary" /> android:summary="@string/accessibility_autoclick_long_summary" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.SelectorWithWidgetPreference android:key="accessibility_control_autoclick_custom" android:title="@string/accessibility_autoclick_custom_title" /> android:title="@string/accessibility_autoclick_custom_title" settings:controller="com.android.settings.accessibility.ToggleAutoclickPreferenceController"/> <com.android.settingslib.widget.LayoutPreference android:key="autoclick_custom_seekbar" Loading @@ -64,7 +67,6 @@ <com.android.settings.accessibility.AccessibilityFooterPreference android:key="accessibility_autoclick_footer" android:title="@string/accessibility_autoclick_description" android:persistent="false" android:selectable="false" settings:searchable="false" settings:controller="com.android.settings.accessibility.ToggleAutoclickFooterPreferenceController"/> Loading
src/com/android/settings/accessibility/AutoclickPreferenceController.java +34 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.settings.accessibility; import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import android.content.Context; import android.provider.Settings; import android.view.accessibility.AccessibilityManager; Loading @@ -23,8 +26,19 @@ import android.view.accessibility.AccessibilityManager; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; /** Preference controller for autoclick (dwell timing). */ public class AutoclickPreferenceController extends BasePreferenceController { /** * Resource ids from which autoclick preference summaries should be derived. The strings have * placeholder for integer delay value. */ private static final int[] AUTOCLICK_PREFERENCE_SUMMARIES = { R.plurals.accessibilty_autoclick_preference_subtitle_short_delay, R.plurals.accessibilty_autoclick_preference_subtitle_medium_delay, R.plurals.accessibilty_autoclick_preference_subtitle_long_delay }; public AutoclickPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); } Loading @@ -37,14 +51,29 @@ public class AutoclickPreferenceController extends BasePreferenceController { @Override public CharSequence getSummary() { final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0) == 1; Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, OFF) == ON; if (!enabled) { return mContext.getResources().getText(R.string.off); } final int delay = Settings.Secure.getInt(mContext.getContentResolver(), final int delayMillis = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT); return ToggleAutoclickPreferenceFragment.getAutoclickPreferenceSummary( mContext.getResources(), delay); final int summaryIndex = getAutoclickPreferenceSummaryIndex(delayMillis); return AutoclickUtils.getAutoclickDelaySummary(mContext.getResources(), AUTOCLICK_PREFERENCE_SUMMARIES[summaryIndex], delayMillis); } /** Finds index of the summary that should be used for the provided autoclick delay. */ private int getAutoclickPreferenceSummaryIndex(int delay) { if (delay <= AutoclickUtils.MIN_AUTOCLICK_DELAY_MS) { return 0; } if (delay >= AutoclickUtils.MAX_AUTOCLICK_DELAY_MS) { return AUTOCLICK_PREFERENCE_SUMMARIES.length - 1; } int delayRange = AutoclickUtils.MAX_AUTOCLICK_DELAY_MS - AutoclickUtils.MIN_AUTOCLICK_DELAY_MS; int rangeSize = (delayRange) / (AUTOCLICK_PREFERENCE_SUMMARIES.length - 1); return (delay - AutoclickUtils.MIN_AUTOCLICK_DELAY_MS) / rangeSize; } }
src/com/android/settings/accessibility/AutoclickUtils.java 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.accessibility; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.PluralsRes; import android.content.res.Resources; import java.lang.annotation.Retention; /** Provides utility methods related auto click. */ public final class AutoclickUtils { /** Used for autoclick mode in the preferences editor. */ static final String KEY_DELAY_MODE = "delay_mode"; /** Used for autoclick custom delay in the preferences editor. */ static final String KEY_CUSTOM_DELAY_VALUE = "custom_delay_value"; /** Min allowed autoclick delay value. */ static final int MIN_AUTOCLICK_DELAY_MS = 200; /** Max allowed autoclick delay value. */ static final int MAX_AUTOCLICK_DELAY_MS = 1000; /** * Allowed autoclick delay values are discrete. This is the difference between two allowed * values. */ static final int AUTOCLICK_DELAY_STEP = 100; @Retention(SOURCE) @IntDef({ Quantity.ONE, Quantity.FEW }) private @interface Quantity { int ONE = 1; int FEW = 3; } /** * Gets string that should be used for provided autoclick delay. * * @param resources Resources from which string should be retrieved. * @param id The desired resource identifier, as generated by the aapt * tool. This integer encodes the package, type, and resource * entry. The value 0 is an invalid identifier. * @param delayMillis Delay for whose value summary should be retrieved. */ public static CharSequence getAutoclickDelaySummary(Resources resources, @PluralsRes int id, int delayMillis) { final int quantity = (delayMillis == 1000) ? Quantity.ONE : Quantity.FEW; final float delaySecond = (float) delayMillis / 1000; // Only show integer when delay time is 1. final String decimalFormat = (delaySecond == 1) ? "%.0f" : "%.1f"; return resources.getQuantityString(id, quantity, String.format(decimalFormat, delaySecond)); } private AutoclickUtils(){} }
src/com/android/settings/accessibility/ToggleAutoclickCustomSeekbarController.java +18 −58 Original line number Diff line number Diff line Loading @@ -18,8 +18,11 @@ package com.android.settings.accessibility; import static android.content.Context.MODE_PRIVATE; import static com.android.settings.accessibility.ToggleAutoclickPreferenceController.KEY_DELAY_MODE; import static com.android.settings.accessibility.ToggleAutoclickPreferenceFragment.Quantity; import static com.android.settings.accessibility.AutoclickUtils.AUTOCLICK_DELAY_STEP; import static com.android.settings.accessibility.AutoclickUtils.KEY_CUSTOM_DELAY_VALUE; import static com.android.settings.accessibility.AutoclickUtils.KEY_DELAY_MODE; import static com.android.settings.accessibility.AutoclickUtils.MAX_AUTOCLICK_DELAY_MS; import static com.android.settings.accessibility.AutoclickUtils.MIN_AUTOCLICK_DELAY_MS; import android.content.ContentResolver; import android.content.Context; Loading @@ -35,36 +38,16 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; import com.android.settingslib.widget.LayoutPreference; /** * Controller class that controls accessibility autoclick seekbar settings. */ /** Controller class that controls accessibility autoclick seekbar settings. */ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceController implements LifecycleObserver, OnResume, OnPause, implements LifecycleObserver, OnStart, OnStop, SharedPreferences.OnSharedPreferenceChangeListener { private static final String CONTROL_AUTOCLICK_DELAY_SECURE = Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY; @VisibleForTesting static final String KEY_CUSTOM_DELAY_VALUE = "custom_delay_value"; // Min allowed autoclick delay value. static final int MIN_AUTOCLICK_DELAY_MS = 200; // Max allowed autoclick delay value. static final int MAX_AUTOCLICK_DELAY_MS = 1000; // Allowed autoclick delay values are discrete. // This is the difference between two allowed values. @VisibleForTesting static final int AUTOCLICK_DELAY_STEP = 100; private final SharedPreferences mSharedPreferences; private final ContentResolver mContentResolver; private ImageView mShorter; Loading Loading @@ -98,29 +81,20 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro mContentResolver = context.getContentResolver(); } public ToggleAutoclickCustomSeekbarController(Context context, Lifecycle lifecycle, String preferenceKey) { this(context, preferenceKey); if (lifecycle != null) { lifecycle.addObserver(this); } } @Override public int getAvailabilityStatus() { return AVAILABLE; } @Override public void onResume() { public void onStart() { if (mSharedPreferences != null) { mSharedPreferences.registerOnSharedPreferenceChangeListener(this); } } @Override public void onPause() { public void onStop() { if (mSharedPreferences != null) { mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this); } Loading @@ -132,7 +106,7 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro final LayoutPreference preference = screen.findPreference(getPreferenceKey()); if (isAvailable()) { int delayMillis = getSharedPreferenceForDelayValue(); final int delayMillis = getSharedPreferenceForDelayValue(); // Initialize seek bar preference. Sets seek bar size to the number of possible delay // values. mSeekBar = preference.findViewById(R.id.autoclick_delay); Loading @@ -144,14 +118,10 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro mDelayLabel.setText(delayTimeToString(delayMillis)); mShorter = preference.findViewById(R.id.shorter); mShorter.setOnClickListener(v -> { minusDelayByImageView(); }); mShorter.setOnClickListener(v -> minusDelayByImageView()); mLonger = preference.findViewById(R.id.longer); mLonger.setOnClickListener(v -> { plusDelayByImageView(); }); mLonger.setOnClickListener(v -> plusDelayByImageView()); } } Loading Loading @@ -184,12 +154,9 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro return mSharedPreferences.getInt(KEY_CUSTOM_DELAY_VALUE, delayMillis); } private void putSecureInt(String name, int value) { Settings.Secure.putInt(mContentResolver, name, value); } private void updateCustomDelayValue(int delayMillis) { putSecureInt(CONTROL_AUTOCLICK_DELAY_SECURE, delayMillis); Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, delayMillis); mSharedPreferences.edit().putInt(KEY_CUSTOM_DELAY_VALUE, delayMillis).apply(); mSeekBar.setProgress(delayToSeekBarProgress(delayMillis)); mDelayLabel.setText(delayTimeToString(delayMillis)); Loading @@ -208,15 +175,8 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro updateCustomDelayValue(delayMillis + AUTOCLICK_DELAY_STEP); } } private CharSequence delayTimeToString(int delayMillis) { final int quantity = (delayMillis == 1000) ? Quantity.ONE : Quantity.FEW; final float delaySecond = (float) delayMillis / 1000; // Only show integer when delay time is 1. final String decimalFormat = (delaySecond == 1) ? "%.0f" : "%.1f"; return mContext.getResources().getQuantityString( R.plurals.accessibilty_autoclick_delay_unit_second, quantity, String.format(decimalFormat, delaySecond)); return AutoclickUtils.getAutoclickDelaySummary(mContext.getResources(), R.plurals.accessibilty_autoclick_delay_unit_second, delayMillis); } }
src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java +61 −109 Original line number Diff line number Diff line Loading @@ -18,6 +18,10 @@ package com.android.settings.accessibility; import static android.content.Context.MODE_PRIVATE; import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import static com.android.settings.accessibility.AutoclickUtils.KEY_DELAY_MODE; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; Loading @@ -25,49 +29,28 @@ import android.content.res.Resources; import android.provider.Settings; import android.util.ArrayMap; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.LifecycleObserver; import androidx.annotation.Nullable; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; import com.android.settingslib.widget.LayoutPreference; import com.android.settingslib.widget.SelectorWithWidgetPreference; import java.util.Map; /** * Controller class that controls accessibility autoclick settings. */ /** Controller class that controls accessibility autoclick settings. */ public class ToggleAutoclickPreferenceController extends BasePreferenceController implements LifecycleObserver, SelectorWithWidgetPreference.OnClickListener, PreferenceControllerMixin { @VisibleForTesting static final String CONTROL_AUTOCLICK_DELAY_SECURE = Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY; @VisibleForTesting static final String KEY_AUTOCLICK_CUSTOM_SEEKBAR = "autoclick_custom_seekbar"; static final String KEY_DELAY_MODE = "delay_mode"; @VisibleForTesting static final int AUTOCLICK_OFF_MODE = 0; LifecycleObserver, OnStart, OnStop, SelectorWithWidgetPreference.OnClickListener, SharedPreferences.OnSharedPreferenceChangeListener { @VisibleForTesting static final int AUTOCLICK_CUSTOM_MODE = 2000; // Pair the preference key and autoclick mode value. @VisibleForTesting Map<String, Integer> mAccessibilityAutoclickKeyToValueMap = new ArrayMap<>(); private SharedPreferences mSharedPreferences; private final ContentResolver mContentResolver; private final Resources mResources; private OnChangeListener mOnChangeListener; private SelectorWithWidgetPreference mDelayModePref; private static final String KEY_AUTOCLICK_CUSTOM_SEEKBAR = "autoclick_custom_seekbar"; private static final int AUTOCLICK_OFF_MODE = 0; private static final int AUTOCLICK_CUSTOM_MODE = 2000; /** * Seek bar preference for autoclick delay value. The seek bar has values between 0 and Loading @@ -75,29 +58,33 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle * delay values before saving them in settings. */ private LayoutPreference mSeekBerPreference; private int mCurrentUiAutoClickMode; private SelectorWithWidgetPreference mDelayModePref; private Map<String, Integer> mAccessibilityAutoclickKeyToValueMap = new ArrayMap<>(); private final SharedPreferences mSharedPreferences; private final ContentResolver mContentResolver; private final Resources mResources; public ToggleAutoclickPreferenceController(Context context, String preferenceKey) { this(context, /* lifecycle= */ null, preferenceKey); } public ToggleAutoclickPreferenceController(Context context, Lifecycle lifecycle, String preferenceKey) { super(context, preferenceKey); mSharedPreferences = context.getSharedPreferences(context.getPackageName(), MODE_PRIVATE); mContentResolver = context.getContentResolver(); mResources = context.getResources(); } setAutoclickModeToKeyMap(); if (lifecycle != null) { lifecycle.addObserver(this); @Override public void onStart() { mSharedPreferences.registerOnSharedPreferenceChangeListener(this); } @Override public void onStop() { mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this); } public void setOnChangeListener(OnChangeListener listener) { mOnChangeListener = listener; @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) { updateState(mDelayModePref); } @Override Loading @@ -109,86 +96,51 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mDelayModePref = (SelectorWithWidgetPreference) screen.findPreference(getPreferenceKey()); mDelayModePref = screen.findPreference(getPreferenceKey()); mDelayModePref.setOnClickListener(this); mSeekBerPreference = (LayoutPreference) screen.findPreference(KEY_AUTOCLICK_CUSTOM_SEEKBAR); updateState((Preference) mDelayModePref); mSeekBerPreference = screen.findPreference(KEY_AUTOCLICK_CUSTOM_SEEKBAR); updateState(mDelayModePref); } @Override public void onRadioButtonClicked(SelectorWithWidgetPreference preference) { final int value = mAccessibilityAutoclickKeyToValueMap.get(mPreferenceKey); handleRadioButtonPreferenceChange(value); if (mOnChangeListener != null) { mOnChangeListener.onCheckedChanged(mDelayModePref); } } private void updatePreferenceCheckedState(int mode) { if (mCurrentUiAutoClickMode == mode) { mDelayModePref.setChecked(true); } final int mode = getAutoclickModeToKeyMap().get(mPreferenceKey); Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, (mode != AUTOCLICK_OFF_MODE) ? ON : OFF); mSharedPreferences.edit().putInt(KEY_DELAY_MODE, mode).apply(); if (mode != AUTOCLICK_CUSTOM_MODE) { Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, mode); } private void updatePreferenceVisibleState(int mode) { mSeekBerPreference.setVisible(mCurrentUiAutoClickMode == mode); } @Override public void updateState(Preference preference) { super.updateState(preference); final boolean enabled = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0) == 1; mCurrentUiAutoClickMode = enabled ? getSharedPreferenceForAutoClickMode() : AUTOCLICK_OFF_MODE; // Reset RadioButton. mDelayModePref.setChecked(false); final int mode = mAccessibilityAutoclickKeyToValueMap.get(mDelayModePref.getKey()); updatePreferenceCheckedState(mode); updatePreferenceVisibleState(mode); Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, OFF) == ON; final int currentUiAutoClickMode = enabled ? mSharedPreferences.getInt(KEY_DELAY_MODE, AUTOCLICK_CUSTOM_MODE) : AUTOCLICK_OFF_MODE; final int mode = getAutoclickModeToKeyMap().get(mDelayModePref.getKey()); mDelayModePref.setChecked(currentUiAutoClickMode == mode); if (mode == AUTOCLICK_CUSTOM_MODE) { mSeekBerPreference.setVisible(mDelayModePref.isChecked()); } /** Listener interface handles checked event. */ public interface OnChangeListener { /** * A hook that is called when preference checked. */ void onCheckedChanged(Preference preference); } private void setAutoclickModeToKeyMap() { /** Returns the paring preference key and autoclick mode value listing. */ private Map<String, Integer> getAutoclickModeToKeyMap() { if (mAccessibilityAutoclickKeyToValueMap.size() == 0) { final String[] autoclickKeys = mResources.getStringArray( R.array.accessibility_autoclick_control_selector_keys); final int[] autoclickValues = mResources.getIntArray( R.array.accessibility_autoclick_selector_values); final int autoclickValueCount = autoclickValues.length; for (int i = 0; i < autoclickValueCount; i++) { mAccessibilityAutoclickKeyToValueMap.put(autoclickKeys[i], autoclickValues[i]); } } private void handleRadioButtonPreferenceChange(int preference) { putSecureInt(Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, (preference != AUTOCLICK_OFF_MODE) ? /* enabled */ 1 : /* disabled */ 0); mSharedPreferences.edit().putInt(KEY_DELAY_MODE, preference).apply(); if (preference != AUTOCLICK_CUSTOM_MODE) { putSecureInt(CONTROL_AUTOCLICK_DELAY_SECURE, preference); } } private void putSecureInt(String name, int value) { Settings.Secure.putInt(mContentResolver, name, value); } private int getSharedPreferenceForAutoClickMode() { return mSharedPreferences.getInt(KEY_DELAY_MODE, AUTOCLICK_CUSTOM_MODE); return mAccessibilityAutoclickKeyToValueMap; } }