Merge "Add handle view to bubble bar expanded view" into udc-qpr-dev

This commit is contained in:
Ivan Tkachenko
2023-05-19 17:02:04 +00:00
committed by Android (Google) Code Review
6 changed files with 233 additions and 74 deletions

View File

@@ -30,6 +30,8 @@
<color name="bubbles_light">#FFFFFF</color>
<color name="bubbles_dark">@color/GM2_grey_800</color>
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
<color name="bubble_bar_expanded_view_handle_light">#EBffffff</color>
<color name="bubble_bar_expanded_view_handle_dark">#99000000</color>
<!-- PiP -->
<color name="pip_custom_close_bg">#D93025</color>

View File

@@ -229,7 +229,11 @@
<!-- 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>
<dimen name="bubble_bar_expanded_view_handle_size">40dp</dimen>
<!-- The width of the drag handle shown along with a bubble bar expanded view. -->
<dimen name="bubble_bar_expanded_view_handle_width">128dp</dimen>
<!-- The height of the drag handle shown along with a bubble bar expanded view. -->
<dimen name="bubble_bar_expanded_view_handle_height">4dp</dimen>
<!-- Bottom and end margin for compat buttons. -->
<dimen name="compat_button_margin">24dp</dimen>

View File

@@ -16,6 +16,8 @@
package com.android.wm.shell.bubbles.bar;
import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.TypedArray;
@@ -46,10 +48,10 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
private BubbleController mController;
private BubbleTaskViewHelper mBubbleTaskViewHelper;
private HandleView mMenuView;
private TaskView mTaskView;
private BubbleBarHandleView mHandleView = new BubbleBarHandleView(getContext());
private @Nullable TaskView mTaskView;
private int mMenuHeight;
private int mHandleHeight;
private int mBackgroundColor;
private float mCornerRadius = 0f;
@@ -83,11 +85,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
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);
mHandleHeight = context.getResources().getDimensionPixelSize(
R.dimen.bubble_bar_expanded_view_handle_size);
addView(mHandleView);
applyThemeAttrs();
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@@ -114,7 +114,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
void applyThemeAttrs() {
boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
mContext.getResources());
final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
android.R.attr.dialogCornerRadius,
android.R.attr.colorBackgroundFloating});
mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
@@ -123,14 +123,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
ta.recycle();
mMenuView.setCornerRadius(mCornerRadius);
mMenuHeight = getResources().getDimensionPixelSize(
R.dimen.bubblebar_expanded_view_menu_size);
mHandleHeight = getResources().getDimensionPixelSize(
R.dimen.bubble_bar_expanded_view_handle_size);
if (mTaskView != null) {
mTaskView.setCornerRadius(mCornerRadius);
mTaskView.setElevation(150);
updateMenuColor();
updateHandleAndBackgroundColor(true /* animated */);
}
}
@@ -138,10 +136,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
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,
int menuViewHeight = Math.min(mHandleHeight, height);
measureChild(mHandleView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
MeasureSpec.getMode(heightMeasureSpec)));
if (mTaskView != null) {
@@ -153,12 +149,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// Drag handle above
final int dragHandleBottom = t + mMenuView.getMeasuredHeight();
mMenuView.layout(l, t, r, dragHandleBottom);
final int dragHandleBottom = t + mHandleView.getMeasuredHeight();
mHandleView.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,
mTaskView.layout(l, dragHandleBottom, r,
dragHandleBottom + mTaskView.getMeasuredHeight());
}
}
@@ -166,7 +162,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Override
public void onTaskCreated() {
setContentVisibility(true);
updateMenuColor();
updateHandleAndBackgroundColor(false /* animated */);
}
@Override
@@ -218,16 +214,33 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
}
}
/** Updates the menu bar to be the status bar color specified by the app. */
private void updateMenuColor() {
/**
* Updates the background color to match with task view status/bg color, and sets handle color
* to contrast with the background
*/
private void updateHandleAndBackgroundColor(boolean animated) {
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);
final int color = getTaskViewColor();
final boolean isRegionDark = Color.luminance(color) <= 0.5;
mHandleView.updateHandleColor(isRegionDark, animated);
setBackgroundColor(color);
}
/**
* Retrieves task view status/nav bar color or background if available
*
* TODO (b/283075226): Update with color sampling when
* RegionSamplingHelper or alternative is available
*/
private @ColorInt int getTaskViewColor() {
if (mTaskView == null || mTaskView.getTaskInfo() == null) return mBackgroundColor;
ActivityManager.TaskDescription taskDescription = mTaskView.getTaskInfo().taskDescription;
if (taskDescription.getStatusBarColor() != Color.TRANSPARENT) {
return taskDescription.getStatusBarColor();
} else if (taskDescription.getBackgroundColor() != Color.TRANSPARENT) {
return taskDescription.getBackgroundColor();
} else {
mMenuView.setBackgroundColor(mBackgroundColor);
return mBackgroundColor;
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
import com.android.wm.shell.R;
/**
* Handle view to show at the top of a bubble bar expanded view.
*/
public class BubbleBarHandleView extends View {
private static final long COLOR_CHANGE_DURATION = 120;
private final int mHandleWidth;
private final int mHandleHeight;
private final @ColorInt int mHandleLightColor;
private final @ColorInt int mHandleDarkColor;
private @Nullable ObjectAnimator mColorChangeAnim;
public BubbleBarHandleView(Context context) {
this(context, null);
}
public BubbleBarHandleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Resources resources = context.getResources();
mHandleWidth = resources.getDimensionPixelSize(
R.dimen.bubble_bar_expanded_view_handle_width);
mHandleHeight = resources.getDimensionPixelSize(
R.dimen.bubble_bar_expanded_view_handle_height);
mHandleLightColor = ContextCompat.getColor(context,
R.color.bubble_bar_expanded_view_handle_light);
mHandleDarkColor = ContextCompat.getColor(context,
R.color.bubble_bar_expanded_view_handle_dark);
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
final int handleCenterX = view.getWidth() / 2;
final int handleCenterY = view.getHeight() / 2;
final float handleRadius = mHandleHeight / 2f;
Rect handleBounds = new Rect(
handleCenterX - mHandleWidth / 2,
handleCenterY - mHandleHeight / 2,
handleCenterX + mHandleWidth / 2,
handleCenterY + mHandleHeight / 2);
outline.setRoundRect(handleBounds, handleRadius);
}
});
}
/**
* Updates the handle color.
*
* @param isRegionDark Whether the background behind the handle is dark, and thus the handle
* should be light (and vice versa).
* @param animated Whether to animate the change, or apply it immediately.
*/
public void updateHandleColor(boolean isRegionDark, boolean animated) {
int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor;
if (mColorChangeAnim != null) {
mColorChangeAnim.cancel();
}
if (animated) {
mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor);
mColorChangeAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mColorChangeAnim = null;
}
});
mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION);
mColorChangeAnim.start();
} else {
setBackgroundColor(newColor);
}
}
}

View File

@@ -1,42 +0,0 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.graphics.drawable.ColorDrawable;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.core.content.ContextCompat;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class BubbleBarHandleViewTest extends ShellTestCase {
private BubbleBarHandleView mHandleView;
@Before
public void setup() {
mHandleView = new BubbleBarHandleView(mContext);
}
@Test
public void testUpdateHandleColor_lightBg() {
mHandleView.updateHandleColor(false /* isRegionDark */, false /* animated */);
assertTrue(mHandleView.getClipToOutline());
assertTrue(mHandleView.getBackground() instanceof ColorDrawable);
ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground();
assertEquals(bgDrawable.getColor(),
ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_dark));
}
@Test
public void testUpdateHandleColor_darkBg() {
mHandleView.updateHandleColor(true /* isRegionDark */, false /* animated */);
assertTrue(mHandleView.getClipToOutline());
assertTrue(mHandleView.getBackground() instanceof ColorDrawable);
ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground();
assertEquals(bgDrawable.getColor(),
ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_light));
}
}