blob: 5ec024ebc917d30acdcabf2cf8aa425bcffbfb85 [file] [log] [blame]
/*
* Copyright (C) 2022 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.SuppressLint;
import android.content.ComponentCallbacks;
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.PointF;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
/**
* The tooltip view shows the information about the operation of the anchor view {@link MenuView}
* . It's just shown on the left or right of the anchor view.
*/
@SuppressLint("ViewConstructor")
class MenuEduTooltipView extends FrameLayout implements ComponentCallbacks {
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 mColorAccentPrimary;
private View mArrowLeftView;
private View mArrowRightView;
private TextView mMessageView;
private final MenuViewAppearance mMenuViewAppearance;
MenuEduTooltipView(@NonNull Context context, MenuViewAppearance menuViewAppearance) {
super(context);
mMenuViewAppearance = menuViewAppearance;
updateResources();
initViews();
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
updateResources();
updateMessageView();
updateArrowView();
updateLocationAndVisibility();
}
@Override
public void onLowMemory() {
// Do nothing.
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getContext().registerComponentCallbacks(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getContext().unregisterComponentCallbacks(this);
}
void show(CharSequence message) {
mMessageView.setText(message);
updateLocationAndVisibility();
}
void updateLocationAndVisibility() {
final boolean isTooltipOnRightOfAnchor = mMenuViewAppearance.isMenuOnLeftSide();
updateArrowVisibilityWith(isTooltipOnRightOfAnchor);
updateLocationWith(getMenuBoundsInParent(), isTooltipOnRightOfAnchor);
}
/**
* Gets the bounds of the {@link MenuView}. Besides, its parent view {@link MenuViewLayer} is
* also the root view of the tooltip view.
*
* @return The menu bounds based on its parent view.
*/
private Rect getMenuBoundsInParent() {
final Rect bounds = new Rect();
final PointF position = mMenuViewAppearance.getMenuPosition();
bounds.set((int) position.x, (int) position.y,
(int) position.x + mMenuViewAppearance.getMenuWidth(),
(int) position.y + mMenuViewAppearance.getMenuHeight());
return bounds;
}
private void updateResources() {
final Resources res = getResources();
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);
mColorAccentPrimary = Utils.getColorAttrDefaultColor(getContext(),
com.android.internal.R.attr.colorAccentPrimary);
}
private void updateLocationWith(Rect anchorBoundsInParent, boolean isTooltipOnRightOfAnchor) {
final int widthSpec = MeasureSpec.makeMeasureSpec(
getAvailableTextViewWidth(isTooltipOnRightOfAnchor), AT_MOST);
final int heightSpec = MeasureSpec.makeMeasureSpec(/* size= */ 0, UNSPECIFIED);
mMessageView.measure(widthSpec, heightSpec);
final LinearLayout.LayoutParams textViewParams =
(LinearLayout.LayoutParams) mMessageView.getLayoutParams();
textViewParams.width = mMessageView.getMeasuredWidth();
mMessageView.setLayoutParams(textViewParams);
final int layoutWidth = mMessageView.getMeasuredWidth() + mArrowWidth + mArrowMargin;
setTranslationX(isTooltipOnRightOfAnchor
? anchorBoundsInParent.right
: anchorBoundsInParent.left - layoutWidth);
setTranslationY(anchorBoundsInParent.centerY() - (mMessageView.getMeasuredHeight() / 2.0f));
}
private void updateMessageView() {
mMessageView.setTextSize(COMPLEX_UNIT_PX, mFontSize);
mMessageView.setPadding(mTextViewPadding, mTextViewPadding, mTextViewPadding,
mTextViewPadding);
final GradientDrawable gradientDrawable = (GradientDrawable) mMessageView.getBackground();
gradientDrawable.setCornerRadius(mTextViewCornerRadius);
gradientDrawable.setColor(mColorAccentPrimary);
}
private void updateArrowView() {
drawArrow(mArrowLeftView, /* isPointingLeft= */ true);
drawArrow(mArrowRightView, /* isPointingLeft= */ false);
}
private void updateArrowVisibilityWith(boolean isTooltipOnRightOfAnchor) {
if (isTooltipOnRightOfAnchor) {
mArrowLeftView.setVisibility(VISIBLE);
mArrowRightView.setVisibility(GONE);
} else {
mArrowLeftView.setVisibility(GONE);
mArrowRightView.setVisibility(VISIBLE);
}
}
private void drawArrow(View arrowView, boolean isPointingLeft) {
final TriangleShape triangleShape =
TriangleShape.createHorizontal(mArrowWidth, mArrowHeight, isPointingLeft);
final ShapeDrawable arrowDrawable = new ShapeDrawable(triangleShape);
final Paint arrowPaint = arrowDrawable.getPaint();
arrowPaint.setColor(mColorAccentPrimary);
final CornerPathEffect effect = new CornerPathEffect(mArrowCornerRadius);
arrowPaint.setPathEffect(effect);
arrowView.setBackground(arrowDrawable);
}
private void initViews() {
final View contentView = LayoutInflater.from(getContext()).inflate(
R.layout.accessibility_floating_menu_tooltip, /* root= */ this, /* attachToRoot= */
false);
mMessageView = contentView.findViewById(R.id.text);
mMessageView.setMovementMethod(LinkMovementMethod.getInstance());
mArrowLeftView = contentView.findViewById(R.id.arrow_left);
mArrowRightView = contentView.findViewById(R.id.arrow_right);
updateMessageView();
updateArrowView();
addView(contentView);
}
private int getAvailableTextViewWidth(boolean isOnRightOfAnchor) {
final PointF position = mMenuViewAppearance.getMenuPosition();
final int availableWidth = isOnRightOfAnchor
? mMenuViewAppearance.getMenuDraggableBounds().width() - (int) position.x
: (int) position.x;
return availableWidth - mArrowWidth - mArrowMargin - mTextViewMargin;
}
}