Introduce BubbleBarExpandedView

* Adds the new BubbleBarExpandedView & basic HandleView classes for
  rendering a bubble's expanded view when bubbles are in the bubble
  bar.

* The HandleView class will eventually render the manage menu
  but this is not implemented yet.

* Adds BubbleBarExpandedView to BubbleViewProvider & Bubble, this will
  be a nullable view that is only inflated when the bubble bar is
  active. The inflation / creation of this view is done in a separate
  CL.

Test: treehugger / manual with other CLs
Bug: 253318833
Change-Id: Iee05e39fc7c06f5210c0adccbe69ba6991e722df
This commit is contained in:
Mady Mellor
2023-02-15 16:26:50 -08:00
parent 6ffe1437e6
commit fa7f85b531
7 changed files with 392 additions and 4 deletions

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<com.android.wm.shell.bubbles.bar.BubbleBarExpandedView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="vertical"
android:id="@+id/bubble_bar_expanded_view">
</com.android.wm.shell.bubbles.bar.BubbleBarExpandedView>

View File

@@ -228,6 +228,8 @@
<dimen name="bubble_user_education_stack_padding">16dp</dimen>
<!-- Size of the bubble bar (height), should match transient_taskbar_size in Launcher. -->
<dimen name="bubblebar_size">72dp</dimen>
<!-- The size of the drag handle / menu shown along with a bubble bar expanded view. -->
<dimen name="bubblebar_expanded_view_menu_size">16dp</dimen>
<!-- Bottom and end margin for compat buttons. -->
<dimen name="compat_button_margin">24dp</dimen>

View File

@@ -47,6 +47,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.common.bubbles.BubbleInfo;
import java.io.PrintWriter;
@@ -87,8 +88,18 @@ public class Bubble implements BubbleViewProvider {
private String mAppName;
private ShortcutInfo mShortcutInfo;
private String mMetadataShortcutId;
/**
* If {@link BubbleController#isShowingAsBubbleBar()} is true, the only view that will be
* populated will be {@link #mBubbleBarExpandedView}. If it is false, {@link #mIconView}
* and {@link #mExpandedView} will be populated.
*/
@Nullable
private BadgedImageView mIconView;
@Nullable
private BubbleExpandedView mExpandedView;
@Nullable
private BubbleBarExpandedView mBubbleBarExpandedView;
private BubbleViewInfoTask mInflationTask;
private boolean mInflateSynchronously;
@@ -327,12 +338,18 @@ public class Bubble implements BubbleViewProvider {
return mIconView;
}
@Override
@Nullable
@Override
public BubbleExpandedView getExpandedView() {
return mExpandedView;
}
@Nullable
@Override
public BubbleBarExpandedView getBubbleBarExpandedView() {
return mBubbleBarExpandedView;
}
@Nullable
public String getTitle() {
return mTitle;
@@ -364,6 +381,9 @@ public class Bubble implements BubbleViewProvider {
mExpandedView.cleanUpExpandedState();
mExpandedView = null;
}
if (mBubbleBarExpandedView != null) {
mBubbleBarExpandedView.cleanUpExpandedState();
}
if (mIntent != null) {
mIntent.unregisterCancelListener(mIntentCancelListener);
}
@@ -445,7 +465,7 @@ public class Bubble implements BubbleViewProvider {
}
boolean isInflated() {
return mIconView != null && mExpandedView != null;
return (mIconView != null && mExpandedView != null) || mBubbleBarExpandedView != null;
}
void stopInflation() {
@@ -478,6 +498,9 @@ public class Bubble implements BubbleViewProvider {
if (mExpandedView != null) {
mExpandedView.update(this /* bubble */);
}
if (mBubbleBarExpandedView != null) {
mBubbleBarExpandedView.update(this /* bubble */);
}
if (mIconView != null) {
mIconView.setRenderedBubble(this /* bubble */);
}
@@ -607,6 +630,9 @@ public class Bubble implements BubbleViewProvider {
*/
@Override
public int getTaskId() {
if (mBubbleBarExpandedView != null) {
return mBubbleBarExpandedView.getTaskId();
}
return mExpandedView != null ? mExpandedView.getTaskId() : mTaskId;
}

View File

@@ -29,6 +29,7 @@ import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.FrameLayout
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView
class BubbleOverflow(
private val context: Context,
@@ -136,6 +137,10 @@ class BubbleOverflow(
return expandedView
}
override fun getBubbleBarExpandedView(): BubbleBarExpandedView? {
return null
}
override fun getDotColor(): Int {
return dotColor
}

View File

@@ -22,18 +22,40 @@ import android.view.View;
import androidx.annotation.Nullable;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
/**
* Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow.
*/
public interface BubbleViewProvider {
@Nullable BubbleExpandedView getExpandedView();
/**
* Returns the icon view used for a bubble (the click target when collapsed). This is populated
* when bubbles are floating, i.e. when {@link BubbleController#isShowingAsBubbleBar()} is
* false.
*/
@Nullable
View getIconView();
/**
* Returns the expanded view used for a bubble. This is populated when bubbles are floating,
* i.e. when {@link BubbleController#isShowingAsBubbleBar()} is false.
*/
@Nullable
BubbleExpandedView getExpandedView();
/**
* Returns the expanded view used for a bubble being show in the bubble bar. This is populated
* when {@link BubbleController#isShowingAsBubbleBar()} is true.
*/
@Nullable
BubbleBarExpandedView getBubbleBarExpandedView();
/**
* Sets whether the contents of the bubble's TaskView should be visible.
*/
void setTaskViewVisibility(boolean visible);
@Nullable View getIconView();
String getKey();

View File

@@ -0,0 +1,267 @@
/*
* 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 android.app.ActivityManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Outline;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
import com.android.wm.shell.taskview.TaskView;
/**
* Expanded view of a bubble when it's part of the bubble bar.
*
* {@link BubbleController#isShowingAsBubbleBar()}
*/
public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskViewHelper.Listener {
private static final String TAG = BubbleBarExpandedView.class.getSimpleName();
private static final int INVALID_TASK_ID = -1;
private BubbleController mController;
private BubbleTaskViewHelper mBubbleTaskViewHelper;
private HandleView mMenuView;
private TaskView mTaskView;
private int mMenuHeight;
private int mBackgroundColor;
private float mCornerRadius = 0f;
/**
* Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If
* {@link #mIsAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha
* value until the animation ends.
*/
private boolean mIsContentVisible = false;
private boolean mIsAnimating;
public BubbleBarExpandedView(Context context) {
this(context, null);
}
public BubbleBarExpandedView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BubbleBarExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public BubbleBarExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
Context context = getContext();
setElevation(getResources().getDimensionPixelSize(R.dimen.bubble_elevation));
mMenuHeight = context.getResources().getDimensionPixelSize(
R.dimen.bubblebar_expanded_view_menu_size);
mMenuView = new HandleView(context);
addView(mMenuView);
applyThemeAttrs();
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
}
});
}
/** Set the BubbleController on the view, must be called before doing anything else. */
public void initialize(BubbleController controller) {
mController = controller;
mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController,
/* listener= */ this,
/* viewParent= */ this);
mTaskView = mBubbleTaskViewHelper.getTaskView();
addView(mTaskView);
mTaskView.setEnableSurfaceClipping(true);
mTaskView.setCornerRadius(mCornerRadius);
}
// TODO (b/275087636): call this when theme/config changes
void applyThemeAttrs() {
boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
mContext.getResources());
final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
android.R.attr.dialogCornerRadius,
android.R.attr.colorBackgroundFloating});
mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
mCornerRadius = mCornerRadius / 2f;
mBackgroundColor = ta.getColor(1, Color.WHITE);
ta.recycle();
mMenuView.setCornerRadius(mCornerRadius);
mMenuHeight = getResources().getDimensionPixelSize(
R.dimen.bubblebar_expanded_view_menu_size);
if (mTaskView != null) {
mTaskView.setCornerRadius(mCornerRadius);
mTaskView.setElevation(150);
updateMenuColor();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
// Add corner radius here so that the menu extends behind the rounded corners of TaskView.
int menuViewHeight = Math.min((int) (mMenuHeight + mCornerRadius), height);
measureChild(mMenuView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
MeasureSpec.getMode(heightMeasureSpec)));
if (mTaskView != null) {
int taskViewHeight = height - menuViewHeight;
measureChild(mTaskView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(taskViewHeight,
MeasureSpec.getMode(heightMeasureSpec)));
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Drag handle above
final int dragHandleBottom = t + mMenuView.getMeasuredHeight();
mMenuView.layout(l, t, r, dragHandleBottom);
if (mTaskView != null) {
// Subtract radius so that the menu extends behind the rounded corners of TaskView.
mTaskView.layout(l, (int) (dragHandleBottom - mCornerRadius), r,
dragHandleBottom + mTaskView.getMeasuredHeight());
}
}
@Override
public void onTaskCreated() {
setContentVisibility(true);
updateMenuColor();
}
@Override
public void onContentVisibilityChanged(boolean visible) {
setContentVisibility(visible);
}
@Override
public void onBackPressed() {
mController.collapseStack();
}
/** Cleans up task view, should be called when the bubble is no longer active. */
public void cleanUpExpandedState() {
if (mBubbleTaskViewHelper != null) {
if (mTaskView != null) {
removeView(mTaskView);
}
mBubbleTaskViewHelper.cleanUpTaskView();
}
}
/** Updates the bubble shown in this task view. */
public void update(Bubble bubble) {
mBubbleTaskViewHelper.update(bubble);
}
/** The task id of the activity shown in the task view, if it exists. */
public int getTaskId() {
return mBubbleTaskViewHelper != null ? mBubbleTaskViewHelper.getTaskId() : INVALID_TASK_ID;
}
/**
* Call when the location or size of the view has changed to update TaskView.
*/
public void updateLocation() {
if (mTaskView == null) return;
mTaskView.onLocationChanged();
}
/** Sets the alpha of the task view. */
public void setContentVisibility(boolean visible) {
mIsContentVisible = visible;
if (mTaskView == null) return;
if (!mIsAnimating) {
mTaskView.setAlpha(visible ? 1f : 0f);
}
}
/** Updates the menu bar to be the status bar color specified by the app. */
private void updateMenuColor() {
if (mTaskView == null) return;
ActivityManager.RunningTaskInfo info = mTaskView.getTaskInfo();
final int taskBgColor = info.taskDescription.getStatusBarColor();
final int color = Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
if (color != -1) {
mMenuView.setBackgroundColor(color);
} else {
mMenuView.setBackgroundColor(mBackgroundColor);
}
}
/**
* Sets the alpha of both this view and the task view.
*/
public void setTaskViewAlpha(float alpha) {
if (mTaskView != null) {
mTaskView.setAlpha(alpha);
}
setAlpha(alpha);
}
/**
* Sets whether the surface displaying app content should sit on top. This is useful for
* ordering surfaces during animations. When content is drawn on top of the app (e.g. bubble
* being dragged out, the manage menu) this is set to false, otherwise it should be true.
*/
public void setSurfaceZOrderedOnTop(boolean onTop) {
if (mTaskView == null) {
return;
}
mTaskView.setZOrderedOnTop(onTop, true /* allowDynamicChange */);
}
/**
* Sets whether the view is animating, in this case we won't change the content visibility
* until the animation is done.
*/
public void setAnimating(boolean animating) {
mIsAnimating = animating;
// If we're done animating, apply the correct visibility.
if (!animating) {
setContentVisibility(mIsContentVisible);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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 android.content.Context;
import android.view.Gravity;
import android.widget.LinearLayout;
/**
* Handle / menu view to show at the top of a bubble bar expanded view.
*/
public class HandleView extends LinearLayout {
// TODO(b/273307221): implement the manage menu in this view.
public HandleView(Context context) {
super(context);
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER);
}
/**
* The menu extends past the top of the TaskView because of the rounded corners. This means
* to center content in the menu we must subtract the radius (i.e. the amount of space covered
* by TaskView).
*/
public void setCornerRadius(float radius) {
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (int) radius);
}
}