Merge "Add new floating action menu for Accessibility targets. (2/n)"
This commit is contained in:
@@ -21,9 +21,9 @@
|
||||
<item android:id="@+id/menu_background_item">
|
||||
<shape android:shape="rectangle">
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/accessibility_floating_menu_single_radius"
|
||||
android:bottomLeftRadius="@dimen/accessibility_floating_menu_small_single_radius"
|
||||
android:bottomRightRadius="0dp"
|
||||
android:topLeftRadius="@dimen/accessibility_floating_menu_single_radius"
|
||||
android:topLeftRadius="@dimen/accessibility_floating_menu_small_single_radius"
|
||||
android:topRightRadius="0dp"/>
|
||||
<solid
|
||||
android:color="@color/accessibility_floating_menu_background"/>
|
||||
|
||||
@@ -19,14 +19,15 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/accessibility_floating_menu_padding"
|
||||
android:paddingStart="@dimen/accessibility_floating_menu_padding"
|
||||
android:paddingEnd="@dimen/accessibility_floating_menu_padding"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<View
|
||||
android:id="@+id/icon_view"
|
||||
android:layout_width="@dimen/accessibility_floating_menu_width_height"
|
||||
android:layout_height="@dimen/accessibility_floating_menu_width_height"/>
|
||||
android:layout_width="@dimen/accessibility_floating_menu_small_width_height"
|
||||
android:layout_height="@dimen/accessibility_floating_menu_small_width_height"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/transparent_divider"
|
||||
|
||||
@@ -1367,9 +1367,12 @@
|
||||
<dimen name="accessibility_floating_menu_stroke_inset">-2dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_margin">16dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_padding">6dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_width_height">36dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_single_radius">25dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_multiple_radius">20dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_small_width_height">36dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_small_single_radius">25dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_small_multiple_radius">20dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_large_width_height">56dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_large_single_radius">33dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen>
|
||||
|
||||
<dimen name="rounded_slider_height">48dp</dimen>
|
||||
<!-- rounded_slider_height / 2 -->
|
||||
|
||||
@@ -16,9 +16,13 @@
|
||||
|
||||
package com.android.systemui.accessibility.floatingmenu;
|
||||
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE;
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
|
||||
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
|
||||
|
||||
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
|
||||
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.ShapeType;
|
||||
import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.SizeType;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
@@ -44,6 +48,22 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
}
|
||||
};
|
||||
|
||||
private final ContentObserver mSizeContentObserver =
|
||||
new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
mMenuView.setSizeType(getSizeType(mContext));
|
||||
}
|
||||
};
|
||||
|
||||
private final ContentObserver mShapeContentObserver =
|
||||
new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
mMenuView.setShapeType(getShapeType(mContext));
|
||||
}
|
||||
};
|
||||
|
||||
public AccessibilityFloatingMenu(Context context) {
|
||||
mContext = context;
|
||||
mMenuView = new AccessibilityFloatingMenuView(context);
|
||||
@@ -66,13 +86,12 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
return;
|
||||
}
|
||||
|
||||
mMenuView.onTargetsChanged(getTargets(mContext, ACCESSIBILITY_BUTTON));
|
||||
mMenuView.show();
|
||||
mMenuView.onTargetsChanged(getTargets(mContext, ACCESSIBILITY_BUTTON));
|
||||
mMenuView.setSizeType(getSizeType(mContext));
|
||||
mMenuView.setShapeType(getShapeType(mContext));
|
||||
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
|
||||
/* notifyForDescendants */ false, mContentObserver,
|
||||
UserHandle.USER_CURRENT);
|
||||
registerContentObservers();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,6 +102,38 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
|
||||
mMenuView.hide();
|
||||
|
||||
unregisterContentObservers();
|
||||
}
|
||||
|
||||
private static int getSizeType(Context context) {
|
||||
return Settings.Secure.getInt(
|
||||
context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_SIZE, SizeType.SMALL);
|
||||
}
|
||||
|
||||
private static int getShapeType(Context context) {
|
||||
return Settings.Secure.getInt(
|
||||
context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
|
||||
ShapeType.CIRCLE);
|
||||
}
|
||||
|
||||
private void registerContentObservers() {
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
|
||||
/* notifyForDescendants */ false, mContentObserver,
|
||||
UserHandle.USER_CURRENT);
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
|
||||
/* notifyForDescendants */ false, mSizeContentObserver,
|
||||
UserHandle.USER_CURRENT);
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE),
|
||||
/* notifyForDescendants */ false, mShapeContentObserver,
|
||||
UserHandle.USER_CURRENT);
|
||||
}
|
||||
|
||||
private void unregisterContentObservers() {
|
||||
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
|
||||
mContext.getContentResolver().unregisterContentObserver(mSizeContentObserver);
|
||||
mContext.getContentResolver().unregisterContentObserver(mShapeContentObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,18 @@
|
||||
|
||||
package com.android.systemui.accessibility.floatingmenu;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
@@ -34,8 +37,11 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.internal.accessibility.dialog.AccessibilityTarget;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -50,21 +56,59 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
|
||||
private static final float DEFAULT_LOCATION_Y_PERCENTAGE = 0.8f;
|
||||
private static final int INDEX_MENU_ITEM = 0;
|
||||
private boolean mIsShowing;
|
||||
private RecyclerView mListView;
|
||||
@SizeType
|
||||
private int mSizeType = SizeType.SMALL;
|
||||
private int mMargin;
|
||||
private int mPadding;
|
||||
private int mScreenHeight;
|
||||
private int mScreenWidth;
|
||||
private int mIconWidth;
|
||||
private int mIconHeight;
|
||||
private final RecyclerView mListView;
|
||||
private final AccessibilityTargetAdapter mAdapter;
|
||||
private final WindowManager.LayoutParams mLayoutParams;
|
||||
private final WindowManager mWindowManager;
|
||||
private final AccessibilityTargetAdapter mAdapter;
|
||||
private final List<AccessibilityTarget> mTargets = new ArrayList<>();
|
||||
|
||||
@IntDef({
|
||||
SizeType.SMALL,
|
||||
SizeType.LARGE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface SizeType {
|
||||
int SMALL = 0;
|
||||
int LARGE = 1;
|
||||
}
|
||||
|
||||
@IntDef({
|
||||
ShapeType.CIRCLE,
|
||||
ShapeType.HALF_CIRCLE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface ShapeType {
|
||||
int CIRCLE = 0;
|
||||
int HALF_CIRCLE = 1;
|
||||
}
|
||||
|
||||
public AccessibilityFloatingMenuView(Context context) {
|
||||
this(context, new RecyclerView(context));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AccessibilityFloatingMenuView(Context context,
|
||||
RecyclerView listView) {
|
||||
super(context);
|
||||
|
||||
mListView = listView;
|
||||
mWindowManager = context.getSystemService(WindowManager.class);
|
||||
mLayoutParams = createDefaultLayoutParams();
|
||||
mAdapter = new AccessibilityTargetAdapter(mTargets);
|
||||
|
||||
initListView(context, mAdapter);
|
||||
updateStroke();
|
||||
updateDimensions();
|
||||
initListView();
|
||||
|
||||
final int uiMode = context.getResources().getConfiguration().uiMode;
|
||||
updateStrokeWith(uiMode);
|
||||
}
|
||||
|
||||
void show() {
|
||||
@@ -94,41 +138,100 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
|
||||
mTargets.addAll(newTargets);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
|
||||
final boolean hasMoreItems = mAdapter.getItemCount() > 1;
|
||||
final int resId = hasMoreItems
|
||||
? R.dimen.accessibility_floating_menu_multiple_radius
|
||||
: R.dimen.accessibility_floating_menu_single_radius;
|
||||
setRadius(resId);
|
||||
updateRadiusWith(mSizeType, mTargets.size());
|
||||
}
|
||||
|
||||
private void setRadius(@DimenRes int radiusResId) {
|
||||
final float radius = getResources().getDimension(radiusResId);
|
||||
void setSizeType(@SizeType int newSizeType) {
|
||||
mSizeType = newSizeType;
|
||||
|
||||
updateIconSizeWith(newSizeType);
|
||||
updateRadiusWith(newSizeType, mTargets.size());
|
||||
|
||||
// When the icon sized changed, the menu size and location will be impacted.
|
||||
updateLocation();
|
||||
}
|
||||
|
||||
void setShapeType(@ShapeType int newShapeType) {
|
||||
final boolean isCircleShape =
|
||||
newShapeType == ShapeType.CIRCLE;
|
||||
final float offset =
|
||||
isCircleShape
|
||||
? 0
|
||||
: getLayoutWidth() / 2.0f;
|
||||
mListView.animate().translationX(offset);
|
||||
|
||||
setOnTouchListener(
|
||||
isCircleShape
|
||||
? null
|
||||
: (view, event) -> onTouched(event));
|
||||
}
|
||||
|
||||
private boolean onTouched(MotionEvent event) {
|
||||
final int currentX = (int) event.getX();
|
||||
final int currentY = (int) event.getY();
|
||||
|
||||
final int menuHalfWidth = getLayoutWidth() / 2;
|
||||
final Rect touchDelegateBounds =
|
||||
new Rect(mMargin, mMargin, mMargin + menuHalfWidth, mMargin + getLayoutHeight());
|
||||
if (touchDelegateBounds.contains(currentX, currentY)) {
|
||||
// In order to correspond to the correct item of list view.
|
||||
event.setLocation(currentX - mMargin, currentY - mMargin);
|
||||
return mListView.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setRadius(float radius) {
|
||||
final float[] radii = new float[]{radius, radius, 0.0f, 0.0f, 0.0f, 0.0f, radius, radius};
|
||||
getMenuGradientDrawable().setCornerRadii(radii);
|
||||
}
|
||||
|
||||
private void initListView(Context context, AccessibilityTargetAdapter adapter) {
|
||||
final Resources res = context.getResources();
|
||||
final int margin =
|
||||
private void updateDimensions() {
|
||||
final Resources res = getResources();
|
||||
final DisplayMetrics dm = res.getDisplayMetrics();
|
||||
mScreenWidth = dm.widthPixels;
|
||||
mScreenHeight = dm.heightPixels;
|
||||
mMargin =
|
||||
res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_margin);
|
||||
final int elevation =
|
||||
res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
|
||||
final Drawable listViewBackground =
|
||||
context.getDrawable(R.drawable.accessibility_floating_menu_background);
|
||||
mPadding =
|
||||
res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_padding);
|
||||
}
|
||||
|
||||
mListView = new RecyclerView(context);
|
||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
|
||||
private void updateIconSizeWith(@SizeType int sizeType) {
|
||||
final Resources res = getResources();
|
||||
final int iconResId =
|
||||
sizeType == SizeType.SMALL
|
||||
? R.dimen.accessibility_floating_menu_small_width_height
|
||||
: R.dimen.accessibility_floating_menu_large_width_height;
|
||||
mIconWidth = res.getDimensionPixelSize(iconResId);
|
||||
mIconHeight = mIconWidth;
|
||||
|
||||
mAdapter.setIconWidthHeight(mIconWidth);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void initListView() {
|
||||
final Drawable listViewBackground =
|
||||
getContext().getDrawable(R.drawable.accessibility_floating_menu_background);
|
||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
|
||||
mListView.setBackground(listViewBackground);
|
||||
mListView.setAdapter(mAdapter);
|
||||
mListView.setLayoutManager(layoutManager);
|
||||
updateListView();
|
||||
|
||||
addView(mListView);
|
||||
}
|
||||
|
||||
private void updateListView() {
|
||||
final int elevation =
|
||||
getResources().getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
|
||||
final LayoutParams layoutParams =
|
||||
new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.setMarginsRelative(margin, margin, /* end= */ 0, margin);
|
||||
layoutParams.setMarginsRelative(mMargin, mMargin, /* end= */ 0, mMargin);
|
||||
mListView.setLayoutParams(layoutParams);
|
||||
mListView.setElevation(elevation);
|
||||
mListView.setBackground(listViewBackground);
|
||||
mListView.setAdapter(adapter);
|
||||
mListView.setLayoutManager(layoutManager);
|
||||
|
||||
addView(mListView);
|
||||
}
|
||||
|
||||
private WindowManager.LayoutParams createDefaultLayoutParams() {
|
||||
@@ -150,9 +253,12 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
updateLocation();
|
||||
updateDimensions();
|
||||
updateListView();
|
||||
updateIconSizeWith(mSizeType);
|
||||
updateColor();
|
||||
updateStroke();
|
||||
updateStrokeWith(newConfig.uiMode);
|
||||
updateLocation();
|
||||
}
|
||||
|
||||
private LayerDrawable getMenuLayerDrawable() {
|
||||
@@ -163,10 +269,12 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
|
||||
return (GradientDrawable) getMenuLayerDrawable().getDrawable(INDEX_MENU_ITEM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the floating menu to be fixed at the side of the screen.
|
||||
*/
|
||||
private void updateLocation() {
|
||||
final DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
mLayoutParams.x = dm.widthPixels;
|
||||
mLayoutParams.y = (int) (dm.heightPixels * DEFAULT_LOCATION_Y_PERCENTAGE);
|
||||
mLayoutParams.x = mScreenWidth - mMargin - getLayoutWidth();
|
||||
mLayoutParams.y = (int) (mScreenHeight * DEFAULT_LOCATION_Y_PERCENTAGE);
|
||||
mWindowManager.updateViewLayout(this, mLayoutParams);
|
||||
}
|
||||
|
||||
@@ -175,10 +283,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
|
||||
getMenuGradientDrawable().setColor(getResources().getColor(menuColorResId));
|
||||
}
|
||||
|
||||
private void updateStroke() {
|
||||
private void updateStrokeWith(int uiMode) {
|
||||
final Resources res = getResources();
|
||||
final boolean isNightMode =
|
||||
(res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
|
||||
(uiMode & Configuration.UI_MODE_NIGHT_MASK)
|
||||
== Configuration.UI_MODE_NIGHT_YES;
|
||||
|
||||
final int inset =
|
||||
@@ -192,4 +300,34 @@ public class AccessibilityFloatingMenuView extends FrameLayout {
|
||||
final int strokeColor = res.getColor(R.color.accessibility_floating_menu_stroke_dark);
|
||||
getMenuGradientDrawable().setStroke(strokeWidth, strokeColor);
|
||||
}
|
||||
|
||||
private void updateRadiusWith(@SizeType int sizeType, int itemCount) {
|
||||
setRadius(getResources().getDimensionPixelSize(getRadiusResId(sizeType, itemCount)));
|
||||
}
|
||||
|
||||
private @DimenRes int getRadiusResId(@SizeType int sizeType, int itemCount) {
|
||||
return sizeType == SizeType.SMALL
|
||||
? getSmallSizeResIdWith(itemCount)
|
||||
: getLargeSizeResIdWith(itemCount);
|
||||
}
|
||||
|
||||
private int getSmallSizeResIdWith(int itemCount) {
|
||||
return itemCount > 1
|
||||
? R.dimen.accessibility_floating_menu_small_multiple_radius
|
||||
: R.dimen.accessibility_floating_menu_small_single_radius;
|
||||
}
|
||||
|
||||
private int getLargeSizeResIdWith(int itemCount) {
|
||||
return itemCount > 1
|
||||
? R.dimen.accessibility_floating_menu_large_multiple_radius
|
||||
: R.dimen.accessibility_floating_menu_large_single_radius;
|
||||
}
|
||||
|
||||
private int getLayoutWidth() {
|
||||
return mPadding * 2 + mIconWidth;
|
||||
}
|
||||
|
||||
private int getLayoutHeight() {
|
||||
return (mPadding + mIconHeight) * mTargets.size() + mPadding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,17 @@
|
||||
|
||||
package com.android.systemui.accessibility.floatingmenu;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
|
||||
import static com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ItemType.FIRST_ITEM;
|
||||
import static com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ItemType.LAST_ITEM;
|
||||
import static com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ItemType.REGULAR_ITEM;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
||||
@@ -28,40 +35,70 @@ import com.android.internal.accessibility.dialog.AccessibilityTarget;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ViewHolder;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An adapter which shows the set of accessibility targets that can be performed.
|
||||
*/
|
||||
public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
|
||||
|
||||
private int mIconWidthHeight;
|
||||
private final List<AccessibilityTarget> mTargets;
|
||||
|
||||
@IntDef({
|
||||
FIRST_ITEM,
|
||||
REGULAR_ITEM,
|
||||
LAST_ITEM
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface ItemType {
|
||||
int FIRST_ITEM = 0;
|
||||
int REGULAR_ITEM = 1;
|
||||
int LAST_ITEM = 2;
|
||||
}
|
||||
|
||||
public AccessibilityTargetAdapter(List<AccessibilityTarget> targets) {
|
||||
mTargets = targets;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) {
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, @ItemType int itemType) {
|
||||
final View root = LayoutInflater.from(parent.getContext()).inflate(
|
||||
R.layout.accessibility_floating_menu_item, parent,
|
||||
/* attachToRoot= */ false);
|
||||
|
||||
if (itemType == FIRST_ITEM) {
|
||||
return new TopViewHolder(root);
|
||||
}
|
||||
|
||||
if (itemType == LAST_ITEM) {
|
||||
return new BottomViewHolder(root);
|
||||
}
|
||||
|
||||
return new ViewHolder(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
holder.mIconView.setBackground(mTargets.get(position).getIcon());
|
||||
|
||||
final boolean isFirstItem = (position == 0);
|
||||
final boolean isLastItem = (position == (getItemCount() - 1));
|
||||
final int padding = holder.itemView.getPaddingStart();
|
||||
final int paddingTop = isFirstItem ? padding : 0;
|
||||
final int paddingBottom = isLastItem ? padding : 0;
|
||||
holder.itemView.setPaddingRelative(padding, paddingTop, padding, paddingBottom);
|
||||
holder.updateIconWidthHeight(mIconWidthHeight);
|
||||
holder.itemView.setOnClickListener((v) -> mTargets.get(position).onSelected());
|
||||
holder.mDivider.setVisibility(isLastItem ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@ItemType
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == 0) {
|
||||
return FIRST_ITEM;
|
||||
}
|
||||
|
||||
if (position == (getItemCount() - 1)) {
|
||||
return LAST_ITEM;
|
||||
}
|
||||
|
||||
return REGULAR_ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,6 +106,10 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
|
||||
return mTargets.size();
|
||||
}
|
||||
|
||||
public void setIconWidthHeight(int iconWidthHeight) {
|
||||
mIconWidthHeight = iconWidthHeight;
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
final View mIconView;
|
||||
final View mDivider;
|
||||
@@ -78,5 +119,32 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
|
||||
mIconView = itemView.findViewById(R.id.icon_view);
|
||||
mDivider = itemView.findViewById(R.id.transparent_divider);
|
||||
}
|
||||
|
||||
void updateIconWidthHeight(int newValue) {
|
||||
final ViewGroup.LayoutParams layoutParams = mIconView.getLayoutParams();
|
||||
if (layoutParams.width == newValue) {
|
||||
return;
|
||||
}
|
||||
layoutParams.width = newValue;
|
||||
layoutParams.height = newValue;
|
||||
mIconView.setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
static class TopViewHolder extends ViewHolder {
|
||||
TopViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
final int padding = itemView.getPaddingStart();
|
||||
itemView.setPaddingRelative(padding, padding, padding, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static class BottomViewHolder extends ViewHolder {
|
||||
BottomViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mDivider.setVisibility(GONE);
|
||||
final int padding = itemView.getPaddingStart();
|
||||
itemView.setPaddingRelative(padding, 0, padding, padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
@@ -30,6 +31,7 @@ import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.accessibility.dialog.AccessibilityTarget;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -58,6 +60,9 @@ public class AccessibilityFloatingMenuTest extends SysuiTestCase {
|
||||
public void initMenu() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
final List<AccessibilityTarget> mTargets = new ArrayList<>();
|
||||
mTargets.add(mock(AccessibilityTarget.class));
|
||||
|
||||
final List<String> assignedTargets = new ArrayList<>();
|
||||
mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
|
||||
assignedTargets.add(MAGNIFICATION_CONTROLLER_NAME);
|
||||
|
||||
@@ -16,23 +16,29 @@
|
||||
|
||||
package com.android.systemui.accessibility.floatingmenu;
|
||||
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.View;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.accessibility.dialog.AccessibilityTarget;
|
||||
@@ -51,12 +57,18 @@ import java.util.List;
|
||||
/** Tests for {@link AccessibilityFloatingMenuView}. */
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
private AccessibilityFloatingMenuView mMenuView;
|
||||
|
||||
@Mock
|
||||
private WindowManager mWindowManager;
|
||||
|
||||
@Mock
|
||||
private ViewPropertyAnimator mAnimator;
|
||||
|
||||
private RecyclerView mListView;
|
||||
|
||||
private final List<AccessibilityTarget> mTargets = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
@@ -69,6 +81,7 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
|
||||
|
||||
mTargets.add(mock(AccessibilityTarget.class));
|
||||
mListView = spy(new RecyclerView(mContext));
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext);
|
||||
}
|
||||
|
||||
@@ -117,10 +130,10 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void updateListViewRadius_singleTarget_matchResult() {
|
||||
final float radius =
|
||||
getContext().getResources().getDimension(
|
||||
R.dimen.accessibility_floating_menu_single_radius);
|
||||
getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.accessibility_floating_menu_small_single_radius);
|
||||
final float[] expectedRadii =
|
||||
new float[] {radius, radius, 0.0f, 0.0f, 0.0f, 0.0f, radius, radius};
|
||||
new float[]{radius, radius, 0.0f, 0.0f, 0.0f, 0.0f, radius, radius};
|
||||
|
||||
mMenuView.onTargetsChanged(mTargets);
|
||||
final View view = mMenuView.getChildAt(0);
|
||||
@@ -131,4 +144,37 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
|
||||
|
||||
assertThat(actualRadii).isEqualTo(expectedRadii);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setSizeType_largeSize_matchResult() {
|
||||
final int shapeType = 2;
|
||||
final float radius = getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.accessibility_floating_menu_large_single_radius);
|
||||
final float[] expectedRadii =
|
||||
new float[]{radius, radius, 0.0f, 0.0f, 0.0f, 0.0f, radius, radius};
|
||||
final Drawable listViewBackground =
|
||||
mContext.getDrawable(R.drawable.accessibility_floating_menu_background);
|
||||
mListView = spy(new RecyclerView(mContext));
|
||||
mListView.setBackground(listViewBackground);
|
||||
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext, mListView);
|
||||
mMenuView.setSizeType(shapeType);
|
||||
final LayerDrawable layerDrawable =
|
||||
(LayerDrawable) mListView.getBackground();
|
||||
final GradientDrawable gradientDrawable =
|
||||
(GradientDrawable) layerDrawable.getDrawable(0);
|
||||
|
||||
assertThat(gradientDrawable.getCornerRadii()).isEqualTo(expectedRadii);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setShapeType_halfCircle_translationX() {
|
||||
final int shapeType = 2;
|
||||
doReturn(mAnimator).when(mListView).animate();
|
||||
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext, mListView);
|
||||
mMenuView.setShapeType(shapeType);
|
||||
|
||||
verify(mAnimator).translationX(anyFloat());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.systemui.accessibility.floatingmenu;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.accessibility.dialog.AccessibilityTarget;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ViewHolder;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Tests for {@link AccessibilityTargetAdapter}. */
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class AccessibilityTargetAdapterTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private AccessibilityTarget mAccessibilityTarget;
|
||||
|
||||
@Mock
|
||||
private Drawable mIcon;
|
||||
|
||||
@Mock
|
||||
private Drawable.ConstantState mConstantState;
|
||||
|
||||
private ViewHolder mViewHolder;
|
||||
private AccessibilityTargetAdapter mAdapter;
|
||||
private final List<AccessibilityTarget> mTargets = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mTargets.add(mAccessibilityTarget);
|
||||
mAdapter = new AccessibilityTargetAdapter(mTargets);
|
||||
|
||||
final View root = LayoutInflater.from(mContext).inflate(
|
||||
R.layout.accessibility_floating_menu_item, null);
|
||||
mViewHolder = new ViewHolder(root);
|
||||
when(mAccessibilityTarget.getIcon()).thenReturn(mIcon);
|
||||
when(mIcon.getConstantState()).thenReturn(mConstantState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindViewHolder_setIconWidthHeight_matchResult() {
|
||||
final int iconWidthHeight = 50;
|
||||
mAdapter.setIconWidthHeight(iconWidthHeight);
|
||||
|
||||
mAdapter.onBindViewHolder(mViewHolder, 0);
|
||||
final int actualIconWith = mViewHolder.mIconView.getLayoutParams().width;
|
||||
|
||||
assertThat(actualIconWith).isEqualTo(iconWidthHeight);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user