diff --git a/res/layout/pointer_icon_stroke_style_layout.xml b/res/layout/pointer_icon_stroke_style_layout.xml
new file mode 100644
index 00000000000..acf919ada99
--- /dev/null
+++ b/res/layout/pointer_icon_stroke_style_layout.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index ce48720cf78..8a96727d0a2 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -179,6 +179,7 @@
8dp
1dp
3dp
+ 8dp
8dp
- 1.0
- 2.5
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 303a5c2dfc4..3063f777449 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4501,6 +4501,14 @@
Change pointer fill style to pink
Change pointer fill style to blue
+
+ Pointer stroke style
+
+ White
+
+ Black
+
+ None
Learn touchpad gestures
diff --git a/res/xml/trackpad_settings.xml b/res/xml/trackpad_settings.xml
index 04422dd5df9..a479e17ce1c 100644
--- a/res/xml/trackpad_settings.xml
+++ b/res/xml/trackpad_settings.xml
@@ -68,6 +68,12 @@
android:order="50"
settings:controller="com.android.settings.inputmethod.PointerFillStylePreferenceController"/>
+
+
true);
+
+ int currentStroke = getPreferenceDataStore().getInt(Settings.System.POINTER_STROKE_STYLE,
+ POINTER_ICON_VECTOR_STYLE_STROKE_WHITE);
+ initRadioButton(holder, R.id.stroke_style_white, POINTER_ICON_VECTOR_STYLE_STROKE_WHITE,
+ currentStroke);
+ initRadioButton(holder, R.id.stroke_style_black, POINTER_ICON_VECTOR_STYLE_STROKE_BLACK,
+ currentStroke);
+ initRadioButton(holder, R.id.stroke_style_none, POINTER_ICON_VECTOR_STYLE_STROKE_NONE,
+ currentStroke);
+ }
+
+ private void initRadioButton(@NonNull PreferenceViewHolder holder, int id, int strokeStyle,
+ int currentStroke) {
+ RadioButton radioButton = (RadioButton) holder.findViewById(id);
+ if (radioButton == null) {
+ return;
+ }
+ radioButton.setOnCheckedChangeListener((v, isChecked) -> {
+ if (isChecked) {
+ getPreferenceDataStore().putInt(Settings.System.POINTER_STROKE_STYLE, strokeStyle);
+ }
+ });
+ radioButton.setChecked(currentStroke == strokeStyle);
+ radioButton.setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_ARROW));
+ }
+}
diff --git a/src/com/android/settings/inputmethod/PointerStrokeStylePreferenceController.java b/src/com/android/settings/inputmethod/PointerStrokeStylePreferenceController.java
new file mode 100644
index 00000000000..e4ea996d8d6
--- /dev/null
+++ b/src/com/android/settings/inputmethod/PointerStrokeStylePreferenceController.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2024 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.inputmethod;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceDataStore;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+
+public class PointerStrokeStylePreferenceController extends BasePreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_POINTER_STROKE_STYLE = "pointer_stroke_style";
+
+ public PointerStrokeStylePreferenceController(@NonNull Context context) {
+ super(context, KEY_POINTER_STROKE_STYLE);
+ }
+
+ @AvailabilityStatus
+ public int getAvailabilityStatus() {
+ return android.view.flags.Flags.enableVectorCursorA11ySettings() ? AVAILABLE
+ : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(@NonNull PreferenceScreen screen) {
+ super.displayPreference(screen);
+ Preference pointerStrokeStylePreference = screen.findPreference(KEY_POINTER_STROKE_STYLE);
+ if (pointerStrokeStylePreference == null) {
+ return;
+ }
+ pointerStrokeStylePreference.setPreferenceDataStore(new PreferenceDataStore() {
+ @Override
+ public void putInt(@NonNull String key, int value) {
+ Settings.System.putIntForUser(mContext.getContentResolver(), key, value,
+ UserHandle.USER_CURRENT);
+ }
+
+ @Override
+ public int getInt(@NonNull String key, int defValue) {
+ return Settings.System.getIntForUser(mContext.getContentResolver(), key, defValue,
+ UserHandle.USER_CURRENT);
+ }
+ });
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/inputmethod/PointerStrokeStylePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/PointerStrokeStylePreferenceControllerTest.java
new file mode 100644
index 00000000000..9b4d5ef0b7c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/inputmethod/PointerStrokeStylePreferenceControllerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2024 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.inputmethod;
+
+import static android.view.flags.Flags.enableVectorCursorA11ySettings;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.inputmethod.PointerStrokeStylePreferenceController.KEY_POINTER_STROKE_STYLE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link PointerStrokeStylePreferenceController} */
+@RunWith(RobolectricTestRunner.class)
+public class PointerStrokeStylePreferenceControllerTest {
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ PreferenceScreen mPreferenceScreen;
+
+ private Context mContext;
+ private PointerStrokeStylePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mController = new PointerStrokeStylePreferenceController(mContext);
+ }
+
+ @Test
+ public void displayPreference_initializeDataStore() {
+ Preference strokePreference = new Preference(mContext);
+ strokePreference.setKey(KEY_POINTER_STROKE_STYLE);
+ when(mPreferenceScreen.findPreference(eq(KEY_POINTER_STROKE_STYLE))).thenReturn(
+ strokePreference);
+
+ mController.displayPreference(mPreferenceScreen);
+
+ assertNotNull(strokePreference.getPreferenceDataStore());
+ }
+
+ @Test
+ public void getAvailabilityStatus_flagEnabled() {
+ assumeTrue(enableVectorCursorA11ySettings());
+
+ assertEquals(mController.getAvailabilityStatus(), AVAILABLE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/inputmethod/PointerStrokeStylePreferenceTest.java b/tests/robotests/src/com/android/settings/inputmethod/PointerStrokeStylePreferenceTest.java
new file mode 100644
index 00000000000..33b85922c46
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/inputmethod/PointerStrokeStylePreferenceTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2024 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.inputmethod;
+
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_BLACK;
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.view.View;
+import android.widget.RadioButton;
+
+import androidx.preference.PreferenceDataStore;
+import androidx.preference.PreferenceViewHolder;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link PointerStrokeStylePreference} */
+@RunWith(RobolectricTestRunner.class)
+public class PointerStrokeStylePreferenceTest {
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ PreferenceDataStore mPreferenceDataStore;
+
+ private Context mContext;
+ private PreferenceViewHolder mViewHolder;
+ private PointerStrokeStylePreference mPreference;
+
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mPreference = new PointerStrokeStylePreference(mContext, null);
+ }
+
+ @Test
+ public void onBindViewHolder_getCurrentStrokeStyleFromDataStore() {
+ final View view = spy(View.inflate(mContext, mPreference.getLayoutResource(), null));
+ mViewHolder = PreferenceViewHolder.createInstanceForTests(view);
+ mPreference.setPreferenceDataStore(mPreferenceDataStore);
+
+ mPreference.onBindViewHolder(mViewHolder);
+
+ verify(mPreferenceDataStore).getInt(Settings.System.POINTER_STROKE_STYLE,
+ POINTER_ICON_VECTOR_STYLE_STROKE_WHITE);
+ }
+
+ @Test
+ public void setChecked_radioButtonUpdatesDataStore() {
+ final View view = spy(View.inflate(mContext, mPreference.getLayoutResource(), null));
+ mViewHolder = PreferenceViewHolder.createInstanceForTests(view);
+ mPreference.setPreferenceDataStore(mPreferenceDataStore);
+ RadioButton radioButton = (RadioButton) view.findViewById(R.id.stroke_style_black);
+ mPreference.onBindViewHolder(mViewHolder);
+
+ radioButton.setChecked(true);
+
+ verify(mPreferenceDataStore).getInt(Settings.System.POINTER_STROKE_STYLE,
+ POINTER_ICON_VECTOR_STYLE_STROKE_WHITE);
+ verify(mPreferenceDataStore).putInt(Settings.System.POINTER_STROKE_STYLE,
+ POINTER_ICON_VECTOR_STYLE_STROKE_BLACK);
+ }
+}