Merge "Add handle view to bubble bar expanded view" into udc-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7adbc2c635
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user