| /* |
| * Copyright (C) 2023 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.wm.shell.bubbles.bar; |
| |
| import static com.android.wm.shell.animation.Interpolators.ALPHA_IN; |
| import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT; |
| |
| import android.annotation.Nullable; |
| import android.content.Context; |
| import android.graphics.Rect; |
| import android.graphics.Region; |
| import android.graphics.drawable.ColorDrawable; |
| import android.view.View; |
| import android.view.ViewTreeObserver; |
| import android.widget.FrameLayout; |
| |
| import com.android.wm.shell.bubbles.BubbleController; |
| import com.android.wm.shell.bubbles.BubblePositioner; |
| import com.android.wm.shell.bubbles.BubbleViewProvider; |
| |
| /** |
| * Similar to {@link com.android.wm.shell.bubbles.BubbleStackView}, this view is added to window |
| * manager to display bubbles. However, it is only used when bubbles are being displayed in |
| * launcher in the bubble bar. This view does not show a stack of bubbles that can be moved around |
| * on screen and instead shows & animates the expanded bubble for the bubble bar. |
| */ |
| public class BubbleBarLayerView extends FrameLayout |
| implements ViewTreeObserver.OnComputeInternalInsetsListener { |
| |
| private static final String TAG = BubbleBarLayerView.class.getSimpleName(); |
| |
| private static final float SCRIM_ALPHA = 0.2f; |
| |
| private final BubbleController mBubbleController; |
| private final BubblePositioner mPositioner; |
| private final BubbleBarAnimationHelper mAnimationHelper; |
| private final View mScrimView; |
| |
| @Nullable |
| private BubbleViewProvider mExpandedBubble; |
| private BubbleBarExpandedView mExpandedView; |
| |
| // TODO(b/273310265) - currently the view is always on the right, need to update for RTL. |
| /** Whether the expanded view is displaying on the left of the screen or not. */ |
| private boolean mOnLeft = false; |
| |
| /** Whether a bubble is expanded. */ |
| private boolean mIsExpanded = false; |
| |
| private final Region mTouchableRegion = new Region(); |
| private final Rect mTempRect = new Rect(); |
| |
| public BubbleBarLayerView(Context context, BubbleController controller) { |
| super(context); |
| mBubbleController = controller; |
| mPositioner = mBubbleController.getPositioner(); |
| |
| mAnimationHelper = new BubbleBarAnimationHelper(context, |
| this, mPositioner); |
| |
| mScrimView = new View(getContext()); |
| mScrimView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); |
| mScrimView.setBackgroundDrawable(new ColorDrawable( |
| getResources().getColor(android.R.color.system_neutral1_1000))); |
| addView(mScrimView); |
| mScrimView.setAlpha(0f); |
| mScrimView.setBackgroundDrawable(new ColorDrawable( |
| getResources().getColor(android.R.color.system_neutral1_1000))); |
| |
| setOnClickListener(view -> { |
| mBubbleController.collapseStack(); |
| }); |
| } |
| |
| @Override |
| protected void onAttachedToWindow() { |
| super.onAttachedToWindow(); |
| mPositioner.update(); |
| getViewTreeObserver().addOnComputeInternalInsetsListener(this); |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| super.onDetachedFromWindow(); |
| getViewTreeObserver().removeOnComputeInternalInsetsListener(this); |
| |
| if (mExpandedView != null) { |
| removeView(mExpandedView); |
| mExpandedView = null; |
| } |
| } |
| |
| @Override |
| public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { |
| inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); |
| mTouchableRegion.setEmpty(); |
| getTouchableRegion(mTouchableRegion); |
| inoutInfo.touchableRegion.set(mTouchableRegion); |
| } |
| |
| /** Updates the sizes of any displaying expanded view. */ |
| public void onDisplaySizeChanged() { |
| if (mIsExpanded && mExpandedView != null) { |
| updateExpandedView(); |
| } |
| } |
| |
| /** Whether the stack of bubbles is expanded or not. */ |
| public boolean isExpanded() { |
| return mIsExpanded; |
| } |
| |
| // (TODO: b/273310265): BubblePositioner should be source of truth when this work is done. |
| /** Whether the expanded view is positioned on the left or right side of the screen. */ |
| public boolean isOnLeft() { |
| return mOnLeft; |
| } |
| |
| /** Shows the expanded view of the provided bubble. */ |
| public void showExpandedView(BubbleViewProvider b) { |
| BubbleBarExpandedView expandedView = b.getBubbleBarExpandedView(); |
| if (expandedView == null) { |
| return; |
| } |
| if (mExpandedBubble != null && !b.getKey().equals(mExpandedBubble.getKey())) { |
| removeView(mExpandedView); |
| mExpandedView = null; |
| } |
| if (mExpandedView == null) { |
| mExpandedBubble = b; |
| mExpandedView = expandedView; |
| final int width = mPositioner.getExpandedViewWidthForBubbleBar(); |
| final int height = mPositioner.getExpandedViewHeightForBubbleBar(); |
| mExpandedView.setVisibility(GONE); |
| addView(mExpandedView, new FrameLayout.LayoutParams(width, height)); |
| } |
| |
| mIsExpanded = true; |
| mBubbleController.getSysuiProxy().onStackExpandChanged(true); |
| mAnimationHelper.animateExpansion(mExpandedBubble); |
| showScrim(true); |
| } |
| |
| /** Collapses any showing expanded view */ |
| public void collapse() { |
| mIsExpanded = false; |
| final BubbleBarExpandedView viewToRemove = mExpandedView; |
| mAnimationHelper.animateCollapse(() -> removeView(viewToRemove)); |
| mBubbleController.getSysuiProxy().onStackExpandChanged(false); |
| mExpandedView = null; |
| showScrim(false); |
| } |
| |
| /** Updates the expanded view size and position. */ |
| private void updateExpandedView() { |
| if (mExpandedView == null) return; |
| final int padding = mPositioner.getBubbleBarExpandedViewPadding(); |
| final int width = mPositioner.getExpandedViewWidthForBubbleBar(); |
| final int height = mPositioner.getExpandedViewHeightForBubbleBar(); |
| FrameLayout.LayoutParams lp = (LayoutParams) mExpandedView.getLayoutParams(); |
| lp.width = width; |
| lp.height = height; |
| mExpandedView.setLayoutParams(lp); |
| if (mOnLeft) { |
| mExpandedView.setX(mPositioner.getInsets().left + padding); |
| } else { |
| mExpandedView.setX(mPositioner.getAvailableRect().width() - width - padding); |
| } |
| mExpandedView.setY(mPositioner.getInsets().top + padding); |
| mExpandedView.updateLocation(); |
| } |
| |
| private void showScrim(boolean show) { |
| if (show) { |
| mScrimView.animate() |
| .setInterpolator(ALPHA_IN) |
| .alpha(SCRIM_ALPHA) |
| .start(); |
| } else { |
| mScrimView.animate() |
| .alpha(0f) |
| .setInterpolator(ALPHA_OUT) |
| .start(); |
| } |
| } |
| |
| /** |
| * Fills in the touchable region for expanded view. This is used by window manager to |
| * decide which touch events go to the expanded view. |
| */ |
| private void getTouchableRegion(Region outRegion) { |
| mTempRect.setEmpty(); |
| if (mIsExpanded) { |
| getBoundsOnScreen(mTempRect); |
| outRegion.op(mTempRect, Region.Op.UNION); |
| } |
| } |
| } |