Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a57f5b24 authored by Yuhan Yang's avatar Yuhan Yang Committed by Android (Google) Code Review
Browse files

Merge "Update mouse keys acceleration slider UI" into main

parents e204ddd4 72232e0e
Loading
Loading
Loading
Loading
+0 −79
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright (C) 2025 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.
-->
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="8dp"
    android:paddingBottom="16dp">

    <TextView
        android:id="@+id/mouse_keys_acceleration"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="8dp"
        android:padding="8dp"
        android:text="@string/mouse_keys_acceleration_seekbar_title"
        android:textAppearance="?android:attr/textAppearanceListItem"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/mouse_keys_acceleration_seekbar" />

    <ImageView
        android:id="@+id/slow_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:background="?android:attr/selectableItemBackgroundBorderless"
        android:src="@drawable/ic_remove_24dp"
        android:layout_marginStart="16dp"
        android:contentDescription="@string/mouse_keys_acceleration_slow_icon_desc"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/mouse_keys_acceleration_seekbar"
        app:layout_constraintBottom_toBottomOf="@id/mouse_keys_acceleration_seekbar" />

    <ImageView
        android:id="@+id/fast_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:background="?android:attr/selectableItemBackgroundBorderless"
        android:src="@drawable/ic_add_24dp"
        android:contentDescription="@string/mouse_keys_acceleration_fast_icon_desc"
        android:layout_marginEnd="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@id/mouse_keys_acceleration_seekbar"
        app:layout_constraintBottom_toBottomOf="@id/mouse_keys_acceleration_seekbar" />

    <SeekBar
        android:id="@+id/mouse_keys_acceleration_seekbar"
        style="@android:style/Widget.Material.SeekBar"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:max="10"
        android:min="0"
        app:layout_constraintTop_toBottomOf="@id/mouse_keys_acceleration"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@id/slow_icon"
        app:layout_constraintEnd_toStartOf="@id/fast_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
+10 −3
Original line number Diff line number Diff line
@@ -32,9 +32,16 @@
        android:persistent="false"
        settings:controller="com.android.settings.inputmethod.KeyboardAccessibilityMouseKeysShortcutController"/>

    <com.android.settingslib.widget.LayoutPreference
        android:key="mouse_keys_acceleration_seekbar"
        android:layout="@layout/mouse_keys_acceleration_seekbar"
    <com.android.settingslib.widget.SliderPreference
        android:key="mouse_keys_acceleration_slider"
        android:title="@string/mouse_keys_acceleration_seekbar_title"
        android:min="0"
        android:max="10"
        settings:seekBarIncrement="1"
        settings:iconEnd="@drawable/ic_add_24dp"
        settings:iconEndContentDescription="@string/screen_zoom_make_larger_desc"
        settings:iconStart="@drawable/ic_remove_24dp"
        settings:iconStartContentDescription="@string/screen_zoom_make_smaller_desc"
        settings:controller="com.android.settings.inputmethod.MouseKeysAccelerationController"/>

    <com.android.settingslib.widget.LayoutPreference
+47 −71
Original line number Diff line number Diff line
@@ -15,30 +15,31 @@
 */

package com.android.settings.inputmethod;

import android.content.ContentResolver;
import android.content.Context;
import android.hardware.input.InputSettings;
import android.provider.Settings;
import android.widget.ImageView;
import android.widget.SeekBar;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;

import com.android.server.accessibility.Flags;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.widget.LayoutPreference;
import com.android.settings.core.SliderPreferenceController;
import com.android.settingslib.widget.SliderPreference;

/** Controller class that controls mouse keys acceleration slider settings. */
public class MouseKeysAccelerationController extends SliderPreferenceController implements
        Preference.OnPreferenceChangeListener {

import java.util.Objects;
    private static final int MAX_ACCELERATION_POSITION = 10;
    private static final int MIN_ACCELERATION_POSITION = 0;

/** Controller class that controls mouse keys acceleration seekbar settings. */
public class MouseKeysAccelerationController extends BasePreferenceController  {
    private static final float ACCELERATION_STEP = 0.1f;
    static final float ACCELERATION_STEP = 0.1f;

    private final ContentResolver mContentResolver;
    @SuppressWarnings("NullAway")
    private SeekBar mSeekBar;

    public MouseKeysAccelerationController(@NonNull Context context,
            @NonNull String preferenceKey) {
@@ -49,46 +50,30 @@ public class MouseKeysAccelerationController extends BasePreferenceController {
    @Override
    public void displayPreference(@NonNull PreferenceScreen screen) {
        super.displayPreference(screen);
        final LayoutPreference preference = screen.findPreference(getPreferenceKey());
        final SliderPreference preference = screen.findPreference(getPreferenceKey());
        if (preference == null) {
            return;
        }

        final float accelerationFromSettings = getAccelerationFromSettings();
        mSeekBar = Objects.requireNonNull(
                preference.findViewById(R.id.mouse_keys_acceleration_seekbar));

        // Scale the float acceleration value to the SeekBar's integer range.
        mSeekBar.setProgress(convertAccelerationToProgress(accelerationFromSettings));

        mSeekBar.setOnSeekBarChangeListener(
                new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(@NonNull SeekBar seekBar, int progress,
                            boolean fromUser) {
                        // Convert the SeekBar's integer progress back to a float value
                        float acceleration = convertProgressToAcceleration(progress);
                        updateAccelerationValue(acceleration);
        preference.setTickVisible(true);
        updateState(preference);
    }

    @Override
                    public void onStartTrackingTouch(@NonNull SeekBar seekBar) {
                        // Nothing to do.
    public boolean setSliderPosition(int position) {
        if (position < getMin() || position > getMax()) {
            return false;
        }

                    @Override
                    public void onStopTrackingTouch(@NonNull SeekBar seekBar) {
                        // Nothing to do.
        float acceleration = convertProgressToAcceleration(position);
        updateAccelerationValue(acceleration);
        return true;
    }
                });

        ImageView slow = preference.findViewById(R.id.fast_icon);
        ImageView fast = preference.findViewById(R.id.slow_icon);
        if (slow == null || fast == null) {
            return;
        }
        slow.setOnClickListener(v -> increaseAccelerationByImageView());
        fast.setOnClickListener(v -> decreaseAccelerationByImageView());
    @Override
    public int getSliderPosition() {
        final float accelerationFromSettings = getAccelerationFromSettings();
        return convertAccelerationToProgress(accelerationFromSettings);
    }

    private void updateAccelerationValue(float acceleration) {
@@ -96,26 +81,6 @@ public class MouseKeysAccelerationController extends BasePreferenceController {
                Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION, acceleration);
    }

    private void decreaseAccelerationByImageView() {
        float currentAcceleration = convertProgressToAcceleration(mSeekBar.getProgress());
        if (currentAcceleration > mSeekBar.getMin()) {
            float newAcceleration = Math.max(mSeekBar.getMin(),
                    currentAcceleration - ACCELERATION_STEP);
            mSeekBar.setProgress(convertAccelerationToProgress(newAcceleration));
            updateAccelerationValue(newAcceleration);
        }
    }

    private void increaseAccelerationByImageView() {
        float currentAcceleration = convertProgressToAcceleration(mSeekBar.getProgress());
        if (currentAcceleration < mSeekBar.getMax()) {
            float newAcceleration = Math.min(mSeekBar.getMax(),
                    currentAcceleration + ACCELERATION_STEP);
            mSeekBar.setProgress(convertAccelerationToProgress(newAcceleration));
            updateAccelerationValue(newAcceleration);
        }
    }

    private float getAccelerationFromSettings() {
        return Settings.Secure.getFloat(mContentResolver,
                Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
@@ -127,13 +92,24 @@ public class MouseKeysAccelerationController extends BasePreferenceController {
        return Flags.enableMouseKeyEnhancement() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
    }

    /** Helper function to convert float acceleration to SeekBar integer progress */
    private int convertAccelerationToProgress(float acceleration) {
        return Math.round((acceleration - mSeekBar.getMin()) / ACCELERATION_STEP);
    @Override
    public int getMin() {
        return MIN_ACCELERATION_POSITION;
    }

    @Override
    public int getMax() {
        return MAX_ACCELERATION_POSITION;
    }

    /** Helper function to convert float acceleration to integer progress */
    @VisibleForTesting
    int convertAccelerationToProgress(float acceleration) {
        return Math.round((acceleration - getMin()) / ACCELERATION_STEP);
    }

    /** Helper function to convert SeekBar integer progress back to float acceleration */
    /** Helper function to convert integer progress back to float acceleration */
    private float convertProgressToAcceleration(int progress) {
        return mSeekBar.getMin() + (progress * ACCELERATION_STEP);
        return getMin() + (progress * ACCELERATION_STEP);
    }
}
+29 −101
Original line number Diff line number Diff line
@@ -21,28 +21,18 @@ import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_U

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.doReturn;

import android.content.Context;
import android.hardware.input.InputSettings;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.widget.ImageView;
import android.widget.SeekBar;

import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;

import com.android.settings.R;
import com.android.settingslib.widget.LayoutPreference;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
@@ -51,30 +41,17 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class MouseKeysAccelerationControllerTest {

    private static final String KEY_CUSTOM_SEEKBAR = "mouse_keys_acceleration_seekbar";
    private static final String KEY_CUSTOM_SLIDER = "mouse_keys_acceleration_slider";

    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
    @Mock private PreferenceScreen mScreen;
    @Mock private LayoutPreference mLayoutPreference;
    @Spy private Context mContext = ApplicationProvider.getApplicationContext();
    private ImageView mSlow;
    private ImageView mFast;
    private SeekBar mSeekBar;

    private MouseKeysAccelerationController mController;
    Context mContext = ApplicationProvider.getApplicationContext();

    @Before
    public void setUp() {
        mSlow = new ImageView(mContext);
        mFast = new ImageView(mContext);
        mSeekBar = new SeekBar(mContext);
        mController = new MouseKeysAccelerationController(mContext, KEY_CUSTOM_SEEKBAR);
        doReturn(mLayoutPreference).when(mScreen).findPreference(KEY_CUSTOM_SEEKBAR);
        doReturn(mSeekBar)
                .when(mLayoutPreference)
                .findViewById(R.id.mouse_keys_acceleration_seekbar);
        doReturn(mSlow).when(mLayoutPreference).findViewById(R.id.slow_icon);
        doReturn(mFast).when(mLayoutPreference).findViewById(R.id.fast_icon);
        mController = new MouseKeysAccelerationController(mContext, KEY_CUSTOM_SLIDER);
    }

    @Test
@@ -90,86 +67,37 @@ public class MouseKeysAccelerationControllerTest {
    }

    @Test
    public void displayPreference_initSeekBar() {
        Settings.Secure.putFloat(
                mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
                .5f);
        mController.displayPreference(mScreen);

        assertThat(mSeekBar.getProgress()).isEqualTo(5);
    }
    public void setSliderPosition_accelerationValue_shouldReturnTrue() {
        int position = 3;

    @Test
    public void onSettingsChanged_updateAccelerationValue() {
        Settings.Secure.putFloat(
                mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
                .5f);

        mController.displayPreference(mScreen);
        final float actualAccelerationValue =
                Settings.Secure.getFloat(
                        mContext.getContentResolver(),
                        Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
                        /* def= */ 0.0f);

        assertThat(mSeekBar.getProgress()).isEqualTo(5);
        assertThat(actualAccelerationValue).isEqualTo(.5f);
    }
        boolean result = mController.setSliderPosition(position);

    @Test
    public void onSeekBarProgressChanged_updateAccelerationValue() {
        Settings.Secure.putFloat(
                mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
                .5f);
        mController.displayPreference(mScreen);
        mSeekBar.setProgress(8);
        final float actualAccelerationValue =
                Settings.Secure.getFloat(
                        mContext.getContentResolver(),
                        Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
                        /* def= */ 0.0f);

        assertThat(mSeekBar.getProgress()).isEqualTo(8);
        assertThat(actualAccelerationValue).isEqualTo(.8f);
        assertThat(result).isTrue();
        assertThat(mController.getSliderPosition())
                .isEqualTo(position);
    }

    @Test
    public void onDecreaseClicked_updateAccelerationValue() {
        Settings.Secure.putFloat(
                mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
                .5f);
        mController.displayPreference(mScreen);
        mSlow.callOnClick();
        final float actualAccelerationValue =
                Settings.Secure.getFloat(
                        mContext.getContentResolver(),
                        Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
                        /* def= */ 0.0f);

        assertThat(mSeekBar.getProgress()).isEqualTo(4);
        assertThat(actualAccelerationValue).isEqualTo(.4f);
    public void setSliderPosition_accelerationValueOverMaxValue_shouldReturnFalse() {
        int position = mController.getMax() + 1;

        boolean result = mController.setSliderPosition(position);

        assertThat(result).isFalse();
        assertThat(mController.getSliderPosition())
                .isEqualTo(mController.convertAccelerationToProgress(
                        InputSettings.DEFAULT_MOUSE_KEYS_ACCELERATION));
    }

    @Test
    public void onIncreaseClicked_updateAccelerationValue() {
        Settings.Secure.putFloat(
                mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
                .5f);

        mController.displayPreference(mScreen);
        mFast.callOnClick();
        final float actualAccelerationValue =
                Settings.Secure.getFloat(
                        mContext.getContentResolver(),
                        Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ACCELERATION,
                        /* def= */ 0.0f);

        assertThat(mSeekBar.getProgress()).isEqualTo(6);
        assertThat(actualAccelerationValue).isEqualTo(.6f);
    public void setSliderPosition_accelerationValueBelowMinValue_shouldReturnFalse() {
        int position = mController.getMin() - 1;

        boolean result = mController.setSliderPosition(position);

        assertThat(result).isFalse();
        assertThat(mController.getSliderPosition())
                .isEqualTo(mController.convertAccelerationToProgress(
                        InputSettings.DEFAULT_MOUSE_KEYS_ACCELERATION));
    }
}