Merge "Add Settings for vector-specific PointerIcon fill colors." into main

This commit is contained in:
Treehugger Robot
2024-06-07 16:31:59 +00:00
committed by Android (Google) Code Review
7 changed files with 376 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="16dp" />
<solid android:color="?androidprv:attr/materialColorSurface"/>
</shape>

View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:orientation="vertical"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingStart="?android:attr/listPreferredItemPaddingStart">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/pointer_fill_style_circle_padding"
android:text="@string/pointer_fill_style"
android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceListItem" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/button_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/pointer_fill_style_circle_padding"
android:layout_marginTop="@dimen/pointer_fill_style_circle_padding"
android:background="@drawable/pointer_icon_fill_style_background"
android:gravity="center_horizontal"
android:padding="@dimen/pointer_fill_style_circle_padding">
<ImageView
android:id="@+id/button_black"
android:layout_width="@dimen/pointer_fill_style_circle_diameter"
android:layout_height="@dimen/pointer_fill_style_circle_diameter"
android:layout_margin="@dimen/pointer_fill_style_circle_padding"
android:adjustViewBounds="true"
android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
android:contentDescription="@string/pointer_fill_style_black_button"
android:scaleType="fitCenter" />
<ImageView
android:id="@+id/button_green"
android:layout_width="@dimen/pointer_fill_style_circle_diameter"
android:layout_height="@dimen/pointer_fill_style_circle_diameter"
android:layout_margin="@dimen/pointer_fill_style_circle_padding"
android:adjustViewBounds="true"
android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
android:contentDescription="@string/pointer_fill_style_green_button"
android:scaleType="fitCenter" />
<ImageView
android:id="@+id/button_yellow"
android:layout_width="@dimen/pointer_fill_style_circle_diameter"
android:layout_height="@dimen/pointer_fill_style_circle_diameter"
android:layout_margin="@dimen/pointer_fill_style_circle_padding"
android:adjustViewBounds="true"
android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
android:contentDescription="@string/pointer_fill_style_yellow_button"
android:scaleType="fitCenter" />
<ImageView
android:id="@+id/button_pink"
android:layout_width="@dimen/pointer_fill_style_circle_diameter"
android:layout_height="@dimen/pointer_fill_style_circle_diameter"
android:layout_margin="@dimen/pointer_fill_style_circle_padding"
android:adjustViewBounds="true"
android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
android:contentDescription="@string/pointer_fill_style_pink_button"
android:scaleType="fitCenter" />
<ImageView
android:id="@+id/button_blue"
android:layout_width="@dimen/pointer_fill_style_circle_diameter"
android:layout_height="@dimen/pointer_fill_style_circle_diameter"
android:layout_margin="@dimen/pointer_fill_style_circle_padding"
android:adjustViewBounds="true"
android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
android:contentDescription="@string/pointer_fill_style_blue_button"
android:scaleType="fitCenter" />
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>

View File

@@ -174,6 +174,12 @@
<dimen name="keyboard_picker_radius">28dp</dimen>
<dimen name="keyboard_picker_text_size">16sp</dimen>
<!-- Pointer -->
<dimen name="pointer_fill_style_circle_diameter">52dp</dimen>
<dimen name="pointer_fill_style_circle_padding">8dp</dimen>
<dimen name="pointer_fill_style_shape_default_stroke">1dp</dimen>
<dimen name="pointer_fill_style_shape_hovered_stroke">3dp</dimen>
<!-- RemoteAuth-->
<dimen name="remoteauth_fragment_padding_horizontal">40dp</dimen>
<dimen name="remoteauth_fragment_subtitle_text_size">14sp</dimen>

View File

@@ -4456,6 +4456,18 @@
<string name="trackpad_bottom_right_tap_summary">Tap the bottom right corner of the touchpad for more options</string>
<!-- Title text for 'Pointer speed'. [CHAR LIMIT=35] -->
<string name="trackpad_pointer_speed">Pointer speed</string>
<!-- Title text for mouse pointer fill style. [CHAR LIMIT=35] -->
<string name="pointer_fill_style">Pointer fill style</string>
<!-- Content description for black pointer fill style. [CHAR LIMIT=35] -->
<string name="pointer_fill_style_black_button">Change pointer fill style to black</string>
<!-- Content description for green pointer fill style. [CHAR LIMIT=35] -->
<string name="pointer_fill_style_green_button">Change pointer fill style to green</string>
<!-- Content description for yellow pointer fill style. [CHAR LIMIT=35] -->
<string name="pointer_fill_style_yellow_button">Change pointer fill style to yellow</string>
<!-- Content description for pink pointer fill style. [CHAR LIMIT=35] -->
<string name="pointer_fill_style_pink_button">Change pointer fill style to pink</string>
<!-- Content description for blue pointer fill style. [CHAR LIMIT=35] -->
<string name="pointer_fill_style_blue_button">Change pointer fill style to blue</string>
<!-- Title for the button to trigger the 'touch gesture' education. [CHAR LIMIT=35] -->
<string name="trackpad_touch_gesture">Learn touchpad gestures</string>
<!-- Search keywords for "touchpad" -->

View File

@@ -62,6 +62,13 @@
android:selectable="false"
settings:controller="com.android.settings.inputmethod.TrackpadPointerSpeedPreferenceController"/>
<com.android.settings.inputmethod.PointerFillStylePreference
android:key="pointer_fill_style"
android:title="@string/pointer_fill_style"
android:order="50"
android:dialogTitle="@string/pointer_fill_style"
settings:controller="com.android.settings.inputmethod.PointerFillStylePreferenceController"/>
<com.android.settingslib.widget.ButtonPreference
android:key="trackpad_touch_gesture"
android:title="@string/trackpad_touch_gesture"

View File

@@ -0,0 +1,160 @@
/*
* 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_FILL_BLACK;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLUE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_GREEN;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_PINK;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_YELLOW;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.StateSet;
import android.view.Gravity;
import android.view.PointerIcon;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settingslib.Utils;
public class PointerFillStylePreference extends Preference {
@Nullable private LinearLayout mButtonHolder;
public PointerFillStylePreference(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.pointer_icon_fill_style_layout);
}
@Override
public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mButtonHolder = (LinearLayout) holder.findViewById(R.id.button_holder);
// Intercept hover events so setting row does not highlight when hovering buttons.
if (mButtonHolder != null) {
mButtonHolder.setOnHoverListener((v, e) -> true);
}
int currentStyle = getPreferenceDataStore().getInt(Settings.System.POINTER_FILL_STYLE,
POINTER_ICON_VECTOR_STYLE_FILL_BLACK);
initStyleButton(holder, R.id.button_black, POINTER_ICON_VECTOR_STYLE_FILL_BLACK,
currentStyle);
initStyleButton(holder, R.id.button_green, POINTER_ICON_VECTOR_STYLE_FILL_GREEN,
currentStyle);
initStyleButton(holder, R.id.button_yellow, POINTER_ICON_VECTOR_STYLE_FILL_YELLOW,
currentStyle);
initStyleButton(holder, R.id.button_pink, POINTER_ICON_VECTOR_STYLE_FILL_PINK,
currentStyle);
initStyleButton(holder, R.id.button_blue, POINTER_ICON_VECTOR_STYLE_FILL_BLUE,
currentStyle);
}
private void initStyleButton(@NonNull PreferenceViewHolder holder, int id, int style,
int currentStyle) {
ImageView button = (ImageView) holder.findViewById(id);
if (button == null) {
return;
}
int[] attrs = {com.android.internal.R.attr.pointerIconVectorFill};
try (TypedArray ta = getContext().obtainStyledAttributes(
PointerIcon.vectorFillStyleToResource(style), attrs)) {
button.setBackground(getBackgroundSelector(ta.getColor(0, Color.BLACK)));
}
button.setForeground(getForegroundDrawable(style, currentStyle));
button.setForegroundGravity(Gravity.CENTER);
button.setOnClickListener(
(v) -> {
getPreferenceDataStore().putInt(Settings.System.POINTER_FILL_STYLE, style);
setButtonChecked(id);
});
button.setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_ARROW));
}
// Generate background instead of defining in XML so we can use res color from the platform.
private StateListDrawable getBackgroundSelector(int color) {
StateListDrawable background = new StateListDrawable();
Resources res = getContext().getResources();
int ovalSize = res.getDimensionPixelSize(R.dimen.pointer_fill_style_circle_diameter);
background.setBounds(0, 0, ovalSize, ovalSize);
// Add hovered state first! The order states are added matters for a StateListDrawable.
GradientDrawable hoveredOval = new GradientDrawable();
hoveredOval.setColor(color);
int textColor = Utils.getColorAttr(getContext(),
com.android.internal.R.attr.materialColorOutline).getDefaultColor();
hoveredOval.setStroke(
res.getDimensionPixelSize(R.dimen.pointer_fill_style_shape_hovered_stroke),
textColor);
hoveredOval.setSize(ovalSize, ovalSize);
hoveredOval.setBounds(0, 0, ovalSize, ovalSize);
hoveredOval.setCornerRadius(ovalSize / 2f);
background.addState(new int[]{android.R.attr.state_hovered}, hoveredOval);
GradientDrawable defaultOval = new GradientDrawable();
defaultOval.setColor(color);
defaultOval.setStroke(
res.getDimensionPixelSize(R.dimen.pointer_fill_style_shape_default_stroke),
textColor);
defaultOval.setSize(ovalSize, ovalSize);
defaultOval.setBounds(0, 0, ovalSize, ovalSize);
defaultOval.setCornerRadius(ovalSize / 2f);
background.addState(StateSet.WILD_CARD, defaultOval);
return background;
}
private Drawable getForegroundDrawable(int style, int currentStyle) {
Resources res = getContext().getResources();
int ovalSize = res.getDimensionPixelSize(R.dimen.pointer_fill_style_circle_diameter);
Drawable checkMark = getContext().getDrawable(R.drawable.ic_check_24dp);
int padding = res.getDimensionPixelSize(R.dimen.pointer_fill_style_circle_padding) / 2;
checkMark.setBounds(padding, padding, ovalSize - padding, ovalSize - padding);
checkMark.setColorFilter(new BlendModeColorFilter(Color.WHITE, BlendMode.SRC_IN));
checkMark.setAlpha(style == currentStyle ? 255 : 0);
return checkMark;
}
private void setButtonChecked(int id) {
if (mButtonHolder == null) {
return;
}
for (int i = 0; i < mButtonHolder.getChildCount(); i++) {
View child = mButtonHolder.getChildAt(i);
if (child != null) {
child.getForeground().setAlpha(child.getId() == id ? 255 : 0);
}
}
}
}

View File

@@ -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 PointerFillStylePreferenceController extends BasePreferenceController {
@VisibleForTesting
static final String KEY_POINTER_FILL_STYLE = "pointer_fill_style";
public PointerFillStylePreferenceController(@NonNull Context context) {
super(context, KEY_POINTER_FILL_STYLE);
}
@AvailabilityStatus
public int getAvailabilityStatus() {
return android.view.flags.Flags.enableVectorCursorA11ySettings() ? AVAILABLE
: CONDITIONALLY_UNAVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
Preference pointerFillStylePreference = screen.findPreference(KEY_POINTER_FILL_STYLE);
if (pointerFillStylePreference == null) {
return;
}
pointerFillStylePreference.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);
}
});
}
}