Add the new migration tooltip for accessibility floating menu.(1/n)
Goal: If users have been using the accessibility button in android R, after the device is upgraded from android R to S, the system will pop up the tooltip at the first boot to show the information about the Accessibility button was replaced with the floating menu. Patch Action: 1. Create the tooltip view 2. Create new secure settings key for migration tooltip Bug: 175365399 Test: atest AccessibilityFloatingMenuViewTest AccessibilityFloatingMenuTest AccessibilityFloatingMenuTooltipViewTest Change-Id: I50b792efcbb631778a668e967fd727806a3f3df4
This commit is contained in:
@@ -4919,6 +4919,15 @@ public class SettingsProvider extends ContentProvider {
|
||||
String.valueOf(defAccessibilityButtonMode), /* tag= */
|
||||
null, /* makeDefault= */ true,
|
||||
SettingsState.SYSTEM_PACKAGE_NAME);
|
||||
|
||||
if (hasValueInA11yButtonTargets(secureSettings)) {
|
||||
secureSettings.insertSettingLocked(
|
||||
Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
|
||||
/* enabled */ "1",
|
||||
/* tag= */ null,
|
||||
/* makeDefault= */ false,
|
||||
SettingsState.SYSTEM_PACKAGE_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5145,13 +5154,21 @@ public class SettingsProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
private boolean isAccessibilityButtonInNavigationBarOn(SettingsState secureSettings) {
|
||||
final boolean hasValueInA11yBtnTargets = !TextUtils.isEmpty(
|
||||
secureSettings.getSettingLocked(
|
||||
Secure.ACCESSIBILITY_BUTTON_TARGETS).getValue());
|
||||
return hasValueInA11yButtonTargets(secureSettings) && !isGestureNavigateEnabled();
|
||||
}
|
||||
|
||||
private boolean isGestureNavigateEnabled() {
|
||||
final int navigationMode = getContext().getResources().getInteger(
|
||||
com.android.internal.R.integer.config_navBarInteractionMode);
|
||||
return navigationMode == NAV_BAR_MODE_GESTURAL;
|
||||
}
|
||||
|
||||
return hasValueInA11yBtnTargets && (navigationMode != NAV_BAR_MODE_GESTURAL);
|
||||
private boolean hasValueInA11yButtonTargets(SettingsState secureSettings) {
|
||||
final Setting a11yButtonTargetsSettings =
|
||||
secureSettings.getSettingLocked(Secure.ACCESSIBILITY_BUTTON_TARGETS);
|
||||
|
||||
return !a11yButtonTargetsSettings.isNull()
|
||||
&& !TextUtils.isEmpty(a11yButtonTargetsSettings.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?androidprv:attr/colorAccentPrimary" />
|
||||
<corners android:radius="@dimen/accessibility_floating_tooltip_text_corner_radius" />
|
||||
</shape>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tooltip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="@dimen/accessibility_floating_tooltip_padding"
|
||||
android:background="@drawable/accessibility_floating_tooltip_background"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColorLink="@android:color/black"
|
||||
android:text="@string/accessibility_floating_button_migration_tooltip"
|
||||
android:textSize="@dimen/accessibility_floating_tooltip_font_size"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/arrow"
|
||||
android:layout_width="@dimen/accessibility_floating_tooltip_arrow_width"
|
||||
android:layout_height="@dimen/accessibility_floating_tooltip_arrow_height"
|
||||
android:layout_marginLeft="@dimen/accessibility_floating_tooltip_arrow_margin"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
</LinearLayout>
|
||||
@@ -1442,6 +1442,15 @@
|
||||
<dimen name="accessibility_floating_menu_large_single_radius">33dp</dimen>
|
||||
<dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen>
|
||||
|
||||
<dimen name="accessibility_floating_tooltip_arrow_width">8dp</dimen>
|
||||
<dimen name="accessibility_floating_tooltip_arrow_height">16dp</dimen>
|
||||
<dimen name="accessibility_floating_tooltip_arrow_margin">-2dp</dimen>
|
||||
<dimen name="accessibility_floating_tooltip_arrow_corner_radius">2dp</dimen>
|
||||
<dimen name="accessibility_floating_tooltip_text_corner_radius">8dp</dimen>
|
||||
<dimen name="accessibility_floating_tooltip_margin">16dp</dimen>
|
||||
<dimen name="accessibility_floating_tooltip_padding">16dp</dimen>
|
||||
<dimen name="accessibility_floating_tooltip_font_size">14sp</dimen>
|
||||
|
||||
<dimen name="rounded_slider_height">48dp</dimen>
|
||||
<!-- rounded_slider_height / 2 -->
|
||||
<dimen name="rounded_slider_corner_radius">24dp</dimen>
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.accessibility.floatingmenu;
|
||||
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED;
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE;
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT;
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY;
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
|
||||
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
|
||||
@@ -39,10 +40,12 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
* Contains logic for an accessibility floating menu view.
|
||||
*/
|
||||
public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
private static final int DEFAULT_FADE_EFFECT_ENABLED = 1;
|
||||
private static final int DEFAULT_FADE_EFFECT_IS_ENABLED = 1;
|
||||
private static final int DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED = 0;
|
||||
private static final float DEFAULT_OPACITY_VALUE = 0.55f;
|
||||
private final Context mContext;
|
||||
private final AccessibilityFloatingMenuView mMenuView;
|
||||
private final MigrationTooltipView mMigrationTooltipView;
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private final ContentObserver mContentObserver =
|
||||
@@ -86,6 +89,7 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
AccessibilityFloatingMenu(Context context, AccessibilityFloatingMenuView menuView) {
|
||||
mContext = context;
|
||||
mMenuView = menuView;
|
||||
mMigrationTooltipView = new MigrationTooltipView(mContext, mMenuView);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,6 +110,8 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
mMenuView.setSizeType(getSizeType(mContext));
|
||||
mMenuView.setShapeType(getShapeType(mContext));
|
||||
|
||||
showMigrationTooltipIfNecessary();
|
||||
|
||||
registerContentObservers();
|
||||
}
|
||||
|
||||
@@ -116,14 +122,32 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
|
||||
}
|
||||
|
||||
mMenuView.hide();
|
||||
mMigrationTooltipView.hide();
|
||||
|
||||
unregisterContentObservers();
|
||||
}
|
||||
|
||||
// Migration tooltip was the android S feature. It's just used on the Android version from R
|
||||
// to S. In addition, it only shows once.
|
||||
private void showMigrationTooltipIfNecessary() {
|
||||
if (isMigrationTooltipPromptEnabled(mContext)) {
|
||||
mMigrationTooltipView.show();
|
||||
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT, /* disabled */ 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isMigrationTooltipPromptEnabled(Context context) {
|
||||
return Settings.Secure.getInt(
|
||||
context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
|
||||
DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED) == /* enabled */ 1;
|
||||
}
|
||||
|
||||
private static boolean isFadeEffectEnabled(Context context) {
|
||||
return Settings.Secure.getInt(
|
||||
context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
|
||||
DEFAULT_FADE_EFFECT_ENABLED) == /* enable */ 1;
|
||||
DEFAULT_FADE_EFFECT_IS_ENABLED) == /* enabled */ 1;
|
||||
}
|
||||
|
||||
private static float getOpacityValue(Context context) {
|
||||
|
||||
@@ -299,10 +299,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
if (super.performAccessibilityAction(action, arguments)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
fadeIn();
|
||||
|
||||
final Rect bounds = getAvailableBounds();
|
||||
@@ -340,7 +336,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return super.performAccessibilityAction(action, arguments);
|
||||
}
|
||||
|
||||
void show() {
|
||||
@@ -411,6 +407,12 @@ public class AccessibilityFloatingMenuView extends FrameLayout
|
||||
fadeOut();
|
||||
}
|
||||
|
||||
Rect getWindowLocationOnScreen() {
|
||||
final int left = mCurrentLayoutParams.x;
|
||||
final int top = mCurrentLayoutParams.y;
|
||||
return new Rect(left, top, left + getWindowWidth(), top + getWindowHeight());
|
||||
}
|
||||
|
||||
void updateOpacityWith(boolean isFadeEffectEnabled, float newOpacityValue) {
|
||||
mIsFadeEffectEnabled = isFadeEffectEnabled;
|
||||
mFadeOutValue = newOpacityValue;
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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 android.util.TypedValue.COMPLEX_UNIT_PX;
|
||||
import static android.view.View.MeasureSpec.AT_MOST;
|
||||
import static android.view.View.MeasureSpec.UNSPECIFIED;
|
||||
|
||||
import android.annotation.UiContext;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.CornerPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.recents.TriangleShape;
|
||||
|
||||
/**
|
||||
* Base tooltip view that shows the information about the operation of the
|
||||
* Accessibility floating menu. In addition, the anchor view is only for {@link
|
||||
* AccessibilityFloatingMenuView}, it should be more suited for displaying one-off menus to avoid
|
||||
* the performance hit for the extra window.
|
||||
*/
|
||||
class BaseTooltipView extends FrameLayout {
|
||||
private int mFontSize;
|
||||
private int mTextViewMargin;
|
||||
private int mTextViewPadding;
|
||||
private int mTextViewCornerRadius;
|
||||
private int mArrowMargin;
|
||||
private int mArrowWidth;
|
||||
private int mArrowHeight;
|
||||
private int mArrowCornerRadius;
|
||||
private int mScreenWidth;
|
||||
private boolean mIsShowing;
|
||||
private View mArrowView;
|
||||
private TextView mTextView;
|
||||
private final WindowManager.LayoutParams mCurrentLayoutParams;
|
||||
private final WindowManager mWindowManager;
|
||||
private final AccessibilityFloatingMenuView mAnchorView;
|
||||
|
||||
BaseTooltipView(@UiContext Context context, AccessibilityFloatingMenuView anchorView) {
|
||||
super(context);
|
||||
mWindowManager = context.getSystemService(WindowManager.class);
|
||||
mAnchorView = anchorView;
|
||||
mCurrentLayoutParams = createDefaultLayoutParams();
|
||||
|
||||
updateDimensions();
|
||||
initViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
updateDimensions();
|
||||
|
||||
updateTextView();
|
||||
updateArrow();
|
||||
|
||||
mAnchorView.onConfigurationChanged(newConfig);
|
||||
final Rect anchorViewLocation = mAnchorView.getWindowLocationOnScreen();
|
||||
updateWidthWith(anchorViewLocation);
|
||||
updateLocationWith(anchorViewLocation);
|
||||
|
||||
mWindowManager.updateViewLayout(this, mCurrentLayoutParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
|
||||
hide();
|
||||
}
|
||||
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
|
||||
info.addAction(AccessibilityAction.ACTION_DISMISS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
if (action == AccessibilityAction.ACTION_DISMISS.getId()) {
|
||||
hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.performAccessibilityAction(action, arguments);
|
||||
}
|
||||
|
||||
void show() {
|
||||
if (isShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIsShowing = true;
|
||||
final Rect anchorViewLocation = mAnchorView.getWindowLocationOnScreen();
|
||||
updateWidthWith(anchorViewLocation);
|
||||
updateLocationWith(anchorViewLocation);
|
||||
|
||||
mWindowManager.addView(this, mCurrentLayoutParams);
|
||||
}
|
||||
|
||||
void hide() {
|
||||
if (!isShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIsShowing = false;
|
||||
mWindowManager.removeView(this);
|
||||
}
|
||||
|
||||
void setDescription(CharSequence text) {
|
||||
mTextView.setText(text);
|
||||
}
|
||||
|
||||
private boolean isShowing() {
|
||||
return mIsShowing;
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
final View contentView =
|
||||
LayoutInflater.from(getContext()).inflate(
|
||||
R.layout.accessibility_floating_menu_tooltip, this, false);
|
||||
|
||||
mArrowView = contentView.findViewById(R.id.arrow);
|
||||
drawArrow(mArrowView);
|
||||
|
||||
mTextView = contentView.findViewById(R.id.text);
|
||||
|
||||
addView(contentView);
|
||||
}
|
||||
|
||||
private static WindowManager.LayoutParams createDefaultLayoutParams() {
|
||||
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
params.windowAnimations = android.R.style.Animation_Translucent;
|
||||
params.gravity = Gravity.START | Gravity.TOP;
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
private void updateDimensions() {
|
||||
final Resources res = getResources();
|
||||
final DisplayMetrics dm = res.getDisplayMetrics();
|
||||
mScreenWidth = dm.widthPixels;
|
||||
mArrowWidth =
|
||||
res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_width);
|
||||
mArrowHeight =
|
||||
res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_height);
|
||||
mArrowMargin =
|
||||
res.getDimensionPixelSize(
|
||||
R.dimen.accessibility_floating_tooltip_arrow_margin);
|
||||
mArrowCornerRadius =
|
||||
res.getDimensionPixelSize(
|
||||
R.dimen.accessibility_floating_tooltip_arrow_corner_radius);
|
||||
mFontSize =
|
||||
res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_font_size);
|
||||
mTextViewMargin =
|
||||
res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_margin);
|
||||
mTextViewPadding =
|
||||
res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_padding);
|
||||
mTextViewCornerRadius =
|
||||
res.getDimensionPixelSize(
|
||||
R.dimen.accessibility_floating_tooltip_text_corner_radius);
|
||||
}
|
||||
|
||||
private void updateTextView() {
|
||||
mTextView.setTextSize(COMPLEX_UNIT_PX, mFontSize);
|
||||
mTextView.setPadding(mTextViewPadding, mTextViewPadding, mTextViewPadding,
|
||||
mTextViewPadding);
|
||||
|
||||
final GradientDrawable gradientDrawable = (GradientDrawable) mTextView.getBackground();
|
||||
gradientDrawable.setCornerRadius(mTextViewCornerRadius);
|
||||
}
|
||||
|
||||
private void updateArrow() {
|
||||
final ShapeDrawable shapeDrawable = (ShapeDrawable) mArrowView.getBackground();
|
||||
final Paint paint = shapeDrawable.getPaint();
|
||||
paint.setPathEffect(new CornerPathEffect(mArrowCornerRadius));
|
||||
|
||||
final LinearLayout.LayoutParams layoutParams =
|
||||
(LinearLayout.LayoutParams) mArrowView.getLayoutParams();
|
||||
layoutParams.width = mArrowWidth;
|
||||
layoutParams.height = mArrowHeight;
|
||||
layoutParams.setMargins(mArrowMargin, 0, 0, 0);
|
||||
mArrowView.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
private void updateWidthWith(Rect anchorViewLocation) {
|
||||
final ViewGroup.LayoutParams layoutParams = mTextView.getLayoutParams();
|
||||
layoutParams.width = getTextWidthWith(anchorViewLocation);
|
||||
mTextView.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
private void updateLocationWith(Rect anchorViewLocation) {
|
||||
mCurrentLayoutParams.x =
|
||||
mScreenWidth - getWindowWidthWith(anchorViewLocation) - anchorViewLocation.width();
|
||||
mCurrentLayoutParams.y =
|
||||
anchorViewLocation.centerY() - (getTextHeightWith(anchorViewLocation) / 2);
|
||||
}
|
||||
|
||||
private void drawArrow(View view) {
|
||||
final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||
final TriangleShape triangleShape =
|
||||
TriangleShape.createHorizontal(layoutParams.width, layoutParams.height,
|
||||
false);
|
||||
final ShapeDrawable arrowDrawable = new ShapeDrawable(triangleShape);
|
||||
final Paint arrowPaint = arrowDrawable.getPaint();
|
||||
arrowPaint.setColor(Utils.getColorAttrDefaultColor(getContext(),
|
||||
com.android.internal.R.attr.colorAccentPrimary));
|
||||
final CornerPathEffect effect = new CornerPathEffect(mArrowCornerRadius);
|
||||
arrowPaint.setPathEffect(effect);
|
||||
view.setBackground(arrowDrawable);
|
||||
}
|
||||
|
||||
private int getTextWidthWith(Rect anchorViewLocation) {
|
||||
final int widthSpec =
|
||||
MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
|
||||
final int heightSpec =
|
||||
MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
|
||||
mTextView.measure(widthSpec, heightSpec);
|
||||
return mTextView.getMeasuredWidth();
|
||||
}
|
||||
|
||||
private int getTextHeightWith(Rect anchorViewLocation) {
|
||||
final int widthSpec =
|
||||
MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
|
||||
final int heightSpec =
|
||||
MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
|
||||
mTextView.measure(widthSpec, heightSpec);
|
||||
return mTextView.getMeasuredHeight();
|
||||
}
|
||||
|
||||
private int getAvailableTextWidthWith(Rect anchorViewLocation) {
|
||||
return mScreenWidth - anchorViewLocation.width() - mArrowWidth - mArrowMargin
|
||||
- mTextViewMargin;
|
||||
}
|
||||
|
||||
private int getWindowWidthWith(Rect anchorViewLocation) {
|
||||
return getTextWidthWith(anchorViewLocation) + mArrowWidth + mArrowMargin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Migration tooltip view that shows the information about the Accessibility button was replaced
|
||||
* with the floating menu.
|
||||
*/
|
||||
class MigrationTooltipView extends BaseTooltipView {
|
||||
MigrationTooltipView(Context context, AccessibilityFloatingMenuView anchorView) {
|
||||
super(context, anchorView);
|
||||
|
||||
setDescription(
|
||||
getResources().getString(R.string.accessibility_floating_button_migration_tooltip));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.accessibility.MotionEventHelper;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/** Tests for {@link BaseTooltipView}. */
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class BaseTooltipViewTest extends SysuiTestCase {
|
||||
|
||||
@Mock
|
||||
private WindowManager mWindowManager;
|
||||
|
||||
private AccessibilityFloatingMenuView mMenuView;
|
||||
private BaseTooltipView mToolTipView;
|
||||
|
||||
private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
final WindowManager wm = mContext.getSystemService(WindowManager.class);
|
||||
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
|
||||
mWindowManager).getMaximumWindowMetrics();
|
||||
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
|
||||
|
||||
mMenuView = new AccessibilityFloatingMenuView(mContext);
|
||||
mToolTipView = new BaseTooltipView(mContext, mMenuView);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void showToolTipView_success() {
|
||||
mToolTipView.show();
|
||||
|
||||
verify(mWindowManager).addView(eq(mToolTipView), any(WindowManager.LayoutParams.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void touchOutsideWhenToolTipViewShown_dismiss() {
|
||||
final MotionEvent outsideEvent =
|
||||
mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0,
|
||||
/* eventTime= */1,
|
||||
MotionEvent.ACTION_OUTSIDE,
|
||||
/* x= */ 0,
|
||||
/* y= */ 0);
|
||||
|
||||
mToolTipView.show();
|
||||
mToolTipView.dispatchTouchEvent(outsideEvent);
|
||||
|
||||
verify(mWindowManager).removeView(mToolTipView);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAccessibilityActionList_matchResult() {
|
||||
final AccessibilityNodeInfo infos = new AccessibilityNodeInfo();
|
||||
mToolTipView.onInitializeAccessibilityNodeInfo(infos);
|
||||
|
||||
assertThat(infos.getActionList().size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessibilityAction_dismiss_success() {
|
||||
final BaseTooltipView tooltipView =
|
||||
spy(new BaseTooltipView(mContext, mMenuView));
|
||||
|
||||
final boolean isActionPerformed =
|
||||
tooltipView.performAccessibilityAction(
|
||||
AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS.getId(),
|
||||
/* arguments= */ null);
|
||||
|
||||
assertThat(isActionPerformed).isTrue();
|
||||
verify(tooltipView).hide();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mToolTipView.hide();
|
||||
mMotionEventHelper.recycleEvents();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user