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:
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user