Skip to content

Commit

Permalink
Add vector-specific scale A11Y settings to PointerIcon.
Browse files Browse the repository at this point in the history
Bug: 305193969
Test: PointerIconLoadingTest
Flag: com.android.systemui.enable_vector_cursor_a11y_settings
Change-Id: I0baaa3840e6986bed8875124fb5cab251a2e23ab
  • Loading branch information
Pat Manning committed Jun 11, 2024
1 parent 6052526 commit 9817df0
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 20 deletions.
10 changes: 10 additions & 0 deletions core/java/android/provider/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -6100,6 +6100,15 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean
@TestApi
public static final String POINTER_SPEED = "pointer_speed";

/**
* Pointer scale setting.
*
* <p>This float value represents the scale by which the size of the pointer increases.
* @hide
*/
@Readable
public static final String POINTER_SCALE = "pointer_scale";

/**
* Touchpad pointer speed setting.
* This is an integer value in a range between -7 and +7, so there are 15 possible values.
Expand Down Expand Up @@ -6358,6 +6367,7 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean
PRIVATE_SETTINGS.add(SIP_ASK_ME_EACH_TIME);
PRIVATE_SETTINGS.add(POINTER_SPEED);
PRIVATE_SETTINGS.add(POINTER_FILL_STYLE);
PRIVATE_SETTINGS.add(POINTER_SCALE);
PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED);
PRIVATE_SETTINGS.add(EGG_MODE);
PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT);
Expand Down
46 changes: 29 additions & 17 deletions core/java/android/view/PointerIcon.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ public final class PointerIcon implements Parcelable {
/** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_END =
POINTER_ICON_VECTOR_STYLE_FILL_BLUE;

/** @hide */ public static final float DEFAULT_POINTER_SCALE = 1f;
/** @hide */ public static final float LARGE_POINTER_SCALE = 2.5f;

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mType;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Expand Down Expand Up @@ -253,7 +256,7 @@ private PointerIcon(int type) {
* @hide
*/
public static @NonNull PointerIcon getLoadedSystemIcon(@NonNull Context context, int type,
boolean useLargeIcons) {
boolean useLargeIcons, float pointerScale) {
if (type == TYPE_NOT_SPECIFIED) {
throw new IllegalStateException("Cannot load icon for type TYPE_NOT_SPECIFIED");
}
Expand All @@ -268,13 +271,18 @@ private PointerIcon(int type) {
}

final int defStyle;
// TODO(b/305193969): Use scaled vectors when large icons are requested.
if (useLargeIcons) {
defStyle = com.android.internal.R.style.LargePointer;
} else if (android.view.flags.Flags.enableVectorCursors()) {
if (android.view.flags.Flags.enableVectorCursorA11ySettings()) {
defStyle = com.android.internal.R.style.VectorPointer;
} else {
defStyle = com.android.internal.R.style.Pointer;
// TODO(b/346358375): Remove useLargeIcons and the legacy pointer styles when
// enableVectorCursorA11ySetting is rolled out.
if (useLargeIcons) {
defStyle = com.android.internal.R.style.LargePointer;
} else if (android.view.flags.Flags.enableVectorCursors()) {
defStyle = com.android.internal.R.style.VectorPointer;
} else {
defStyle = com.android.internal.R.style.Pointer;
}
}
TypedArray a = context.obtainStyledAttributes(null,
com.android.internal.R.styleable.Pointer,
Expand All @@ -286,11 +294,11 @@ private PointerIcon(int type) {
Log.w(TAG, "Missing theme resources for pointer icon type " + type);
return type == TYPE_DEFAULT
? getSystemIcon(TYPE_NULL)
: getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons);
: getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons, pointerScale);
}

final PointerIcon icon = new PointerIcon(type);
icon.loadResource(context.getResources(), resourceId, context.getTheme());
icon.loadResource(context.getResources(), resourceId, context.getTheme(), pointerScale);
return icon;
}

Expand Down Expand Up @@ -353,7 +361,7 @@ private boolean isLoaded() {
}

PointerIcon icon = new PointerIcon(TYPE_CUSTOM);
icon.loadResource(resources, resourceId, null);
icon.loadResource(resources, resourceId, null, DEFAULT_POINTER_SCALE);
return icon;
}

Expand Down Expand Up @@ -460,20 +468,21 @@ private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) {
}

private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources,
VectorDrawable vectorDrawable) {
VectorDrawable vectorDrawable, float pointerScale) {
// Ensure we pass the display metrics into the Bitmap constructor so that it is initialized
// with the correct density.
Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(),
vectorDrawable.getIntrinsicWidth(),
vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */);
(int) (vectorDrawable.getIntrinsicWidth() * pointerScale),
(int) (vectorDrawable.getIntrinsicHeight() * pointerScale),
Bitmap.Config.ARGB_8888, true /* hasAlpha */);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
return new BitmapDrawable(resources, bitmap);
}

private void loadResource(@NonNull Resources resources, @XmlRes int resourceId,
@Nullable Resources.Theme theme) {
@Nullable Resources.Theme theme, float pointerScale) {
final XmlResourceParser parser = resources.getXml(resourceId);
final int bitmapRes;
final float hotSpotX;
Expand All @@ -484,8 +493,10 @@ private void loadResource(@NonNull Resources resources, @XmlRes int resourceId,
final TypedArray a = resources.obtainAttributes(
parser, com.android.internal.R.styleable.PointerIcon);
bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0)
* pointerScale;
hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0)
* pointerScale;
a.recycle();
} catch (Exception ex) {
throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex);
Expand Down Expand Up @@ -534,15 +545,16 @@ private void loadResource(@NonNull Resources resources, @XmlRes int resourceId,
}
if (isVectorAnimation) {
drawableFrame = getBitmapDrawableFromVectorDrawable(resources,
(VectorDrawable) drawableFrame);
(VectorDrawable) drawableFrame, pointerScale);
}
mBitmapFrames[i - 1] = getBitmapFromDrawable((BitmapDrawable) drawableFrame);
}
}
}
if (drawable instanceof VectorDrawable) {
mDrawNativeDropShadow = true;
drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable);
drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable,
pointerScale);
}
if (!(drawable instanceof BitmapDrawable)) {
throw new IllegalArgumentException("<pointer-icon> bitmap attribute must "
Expand Down
1 change: 1 addition & 0 deletions core/proto/android/providers/settings/system.proto
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ message SystemSettingsProto {
option (android.msg_privacy).dest = DEST_EXPLICIT;

optional SettingProto pointer_fill_style = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto pointer_scale = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Pointer pointer = 37;
optional SettingProto pointer_speed = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ private static String[] getSettingsToBackUp() {
Settings.System.SIP_RECEIVE_CALLS,
Settings.System.POINTER_SPEED,
Settings.System.POINTER_FILL_STYLE,
Settings.System.POINTER_SCALE,
Settings.System.VIBRATE_ON,
Settings.System.VIBRATE_WHEN_RINGING,
Settings.System.RINGTONE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR;
import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
import static android.view.PointerIcon.LARGE_POINTER_SCALE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BEGIN;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_END;

Expand Down Expand Up @@ -211,6 +213,8 @@ public boolean validate(String value) {
VALIDATORS.put(System.POINTER_FILL_STYLE,
new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_FILL_BEGIN,
POINTER_ICON_VECTOR_STYLE_FILL_END));
VALIDATORS.put(System.POINTER_SCALE,
new InclusiveFloatRangeValidator(DEFAULT_POINTER_SCALE, LARGE_POINTER_SCALE));
VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7));
VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2915,6 +2915,9 @@ private static void dumpProtoSystemSettingsLocked(
dumpSetting(s, p,
Settings.System.POINTER_FILL_STYLE,
SystemSettingsProto.Pointer.POINTER_FILL_STYLE);
dumpSetting(s, p,
Settings.System.POINTER_SCALE,
SystemSettingsProto.Pointer.POINTER_SCALE);
p.end(pointerToken);
dumpSetting(s, p,
Settings.System.POINTER_SPEED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3352,6 +3352,10 @@ void setPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill int fillStyle)
mPointerIconCache.setPointerFillStyle(fillStyle);
}

void setPointerScale(float scale) {
mPointerIconCache.setPointerScale(scale);
}

interface KeyboardBacklightControllerInterface {
default void incrementKeyboardBacklight(int deviceId) {}
default void decrementKeyboardBacklight(int deviceId) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.android.server.input;

import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
import static android.view.flags.Flags.enableVectorCursorA11ySettings;

Expand Down Expand Up @@ -101,7 +102,9 @@ class InputSettingsObserver extends ContentObserver {
Map.entry(Settings.Secure.getUriFor(Settings.Secure.STYLUS_POINTER_ICON_ENABLED),
(reason) -> updateStylusPointerIconEnabled()),
Map.entry(Settings.System.getUriFor(Settings.System.POINTER_FILL_STYLE),
(reason) -> updatePointerFillStyleFromSettings()));
(reason) -> updatePointerFillStyleFromSettings()),
Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SCALE),
(reason) -> updatePointerScaleFromSettings()));
}

/**
Expand Down Expand Up @@ -277,4 +280,14 @@ private void updatePointerFillStyleFromSettings() {
UserHandle.USER_CURRENT);
mService.setPointerFillStyle(pointerFillStyle);
}

private void updatePointerScaleFromSettings() {
if (!enableVectorCursorA11ySettings()) {
return;
}
final float pointerScale = Settings.System.getFloatForUser(mContext.getContentResolver(),
Settings.System.POINTER_SCALE, DEFAULT_POINTER_SCALE,
UserHandle.USER_CURRENT);
mService.setPointerScale(pointerScale);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.android.server.input;

import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK;

import android.annotation.NonNull;
Expand Down Expand Up @@ -63,6 +64,8 @@ final class PointerIconCache {
@GuardedBy("mLoadedPointerIconsByDisplayAndType")
private @PointerIcon.PointerIconVectorStyleFill int mPointerIconFillStyle =
POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
@GuardedBy("mLoadedPointerIconsByDisplayAndType")
private float mPointerIconScale = DEFAULT_POINTER_SCALE;

private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
Expand Down Expand Up @@ -117,6 +120,11 @@ public void setPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill int fill
mUiThreadHandler.post(() -> handleSetPointerFillStyle(fillStyle));
}

/** Set the scale for vector pointer icons. */
public void setPointerScale(float scale) {
mUiThreadHandler.post(() -> handleSetPointerScale(scale));
}

/**
* Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if
* it isn't already cached.
Expand All @@ -137,7 +145,7 @@ public void setPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill int fill
theme.applyStyle(PointerIcon.vectorFillStyleToResource(mPointerIconFillStyle),
/* force= */ true);
icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme),
type, mUseLargePointerIcons);
type, mUseLargePointerIcons, mPointerIconScale);
iconsByType.put(type, icon);
}
return Objects.requireNonNull(icon);
Expand Down Expand Up @@ -215,6 +223,19 @@ private void handleSetPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill i
mNative.reloadPointerIcons();
}

@android.annotation.UiThread
private void handleSetPointerScale(float scale) {
synchronized (mLoadedPointerIconsByDisplayAndType) {
if (mPointerIconScale == scale) {
return;
}
mPointerIconScale = scale;
// Clear all cached icons on all displays.
mLoadedPointerIconsByDisplayAndType.clear();
}
mNative.reloadPointerIcons();
}

// Updates the cached display density for the given displayId, and returns true if
// the cached density changed.
@GuardedBy("mLoadedPointerIconsByDisplayAndType")
Expand Down
Binary file added tests/Input/assets/testPointerScale.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 23 additions & 1 deletion tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,29 @@ class PointerIconLoadingTest {
PointerIcon.getLoadedSystemIcon(
ContextThemeWrapper(context, theme),
PointerIcon.TYPE_ARROW,
/* useLargeIcons= */ false)
/* useLargeIcons= */ false,
/* pointerScale= */ 1f)

pointerIcon.getBitmap().assertAgainstGolden(
screenshotRule,
testName.methodName,
exactScreenshotMatcher
)
}

@Test
fun testPointerScale() {
assumeTrue(enableVectorCursors())
assumeTrue(enableVectorCursorA11ySettings())

val pointerScale = 2f

val pointerIcon =
PointerIcon.getLoadedSystemIcon(
context,
PointerIcon.TYPE_ARROW,
/* useLargeIcons= */ false,
pointerScale)

pointerIcon.getBitmap().assertAgainstGolden(
screenshotRule,
Expand Down

0 comments on commit 9817df0

Please sign in to comment.