| /* |
| * 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; |
| } |
| } |