diff options
7 files changed, 135 insertions, 22 deletions
diff --git a/packages/SystemUI/res/layout/bubble_permission_view.xml b/packages/SystemUI/res/layout/bubble_permission_view.xml index 7fbb78a6feee..c9d8a9128d7c 100644 --- a/packages/SystemUI/res/layout/bubble_permission_view.xml +++ b/packages/SystemUI/res/layout/bubble_permission_view.xml @@ -17,7 +17,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="@dimen/bubble_permission_height" android:animateLayoutChanges="true" android:orientation="vertical" android:paddingStart="@dimen/bubble_expanded_header_horizontal_padding" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1f6e3c2ff876..536bc4edf341 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1045,4 +1045,8 @@ <dimen name="bubble_header_icon_size">48dp</dimen> <!-- Size of the app icon shown in the bubble permission view --> <dimen name="bubble_permission_icon_size">24dp</dimen> + <!-- Space between the pointer triangle and the bubble expanded view --> + <dimen name="bubble_pointer_margin">8dp</dimen> + <!-- Height of the permission prompt shown with bubbles --> + <dimen name="bubble_permission_height">120dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 4eea9f883f81..471619e897c5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -80,16 +80,21 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe // Enables some subset of notifs to automatically become bubbles private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false; - // Secure settings flags - // Feature level flag + /** Flag to enable or disable the entire feature */ private static final String ENABLE_BUBBLES = "experiment_enable_bubbles"; - // Auto bubble flags set whether different notification types should be presented as a bubble + /** Auto bubble flags set whether different notif types should be presented as a bubble */ private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging"; private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing"; private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all"; - // Use an activity view for an auto-bubbled notification if it has an appropriate content intent + + /** Use an activityView for an auto-bubbled notifs if it has an appropriate content intent */ private static final String ENABLE_BUBBLE_CONTENT_INTENT = "experiment_bubble_content_intent"; + /** Whether the row of bubble circles are anchored to the top or bottom of the screen. */ + private static final String ENABLE_BUBBLES_AT_TOP = "experiment_enable_top_bubbles"; + /** Flag to position the header below the activity view */ + private static final String ENABLE_BUBBLE_FOOTER = "experiment_enable_bubble_footer"; + private final Context mContext; private final NotificationEntryManager mNotificationEntryManager; private final IActivityTaskManager mActivityTaskManager; @@ -548,6 +553,22 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe ENABLE_BUBBLES, 1) != 0; } + /** + * Whether bubbles should be positioned at the top of the screen or not. + */ + public static boolean showBubblesAtTop(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_BUBBLES_AT_TOP, 0) != 0; + } + + /** + * Whether the bubble chrome should display as a footer or not (in which case it's a header). + */ + public static boolean useFooter(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_BUBBLE_FOOTER, 0) != 0; + } + /** PinnedStackListener that dispatches IME visibility updates to the stack. */ private class BubblesImeListener extends IPinnedStackListener.Stub { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 7884800611aa..b635033ea771 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -70,8 +70,13 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState; public class BubbleExpandedView extends LinearLayout implements View.OnClickListener { private static final String TAG = "BubbleExpandedView"; + // Configurable via bubble settings; just for testing + private boolean mUseFooter; + private boolean mShowOnTop; + // The triangle pointing to the expanded view private View mPointerView; + private int mPointerMargin; // Header private View mHeaderView; @@ -90,6 +95,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private int mMinHeight; private int mHeaderHeight; + private int mBubbleHeight; + private int mPermissionHeight; private NotificationEntry mEntry; private PackageManager mPm; @@ -150,6 +157,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mPm = context.getPackageManager(); mMinHeight = getResources().getDimensionPixelSize( R.dimen.bubble_expanded_default_height); + mPointerMargin = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_margin); try { mNotificationManagerService = INotificationManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE)); @@ -172,8 +180,11 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList int bgColor = ta.getColor(0, Color.WHITE); ta.recycle(); + mShowOnTop = BubbleController.showBubblesAtTop(getContext()); + mUseFooter = BubbleController.useFooter(getContext()); + ShapeDrawable triangleDrawable = new ShapeDrawable( - TriangleShape.create(width, height, true /* pointUp */)); + TriangleShape.create(width, height, mShowOnTop /* pointUp */)); triangleDrawable.setTint(bgColor); mPointerView.setBackground(triangleDrawable); @@ -195,6 +206,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mHeaderHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_expanded_header_height); + mPermissionHeight = getContext().getResources().getDimensionPixelSize( + R.dimen.bubble_permission_height); mHeaderView = findViewById(R.id.header_layout); mDeepLinkIcon = findViewById(R.id.deep_link_button); mSettingsIcon = findViewById(R.id.settings_button); @@ -226,6 +239,15 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom)); return view.onApplyWindowInsets(insets); }); + + if (!mShowOnTop) { + removeView(mPointerView); + if (mUseFooter) { + removeView(viewWrapper); + addView(viewWrapper); + } + addView(mPointerView); + } } /** @@ -332,7 +354,11 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList // Use notification view mNotifRow = mEntry.getRow(); - addView(mNotifRow); + if (mShowOnTop) { + addView(mNotifRow); + } else { + addView(mNotifRow, mUseFooter ? 0 : 1); + } } updateView(); } @@ -345,6 +371,17 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList return true; } + /** + * @return total height that the expanded view occupies. + */ + int getExpandedSize() { + int chromeHeight = mPermissionView.getVisibility() != View.VISIBLE + ? mHeaderHeight + : mPermissionHeight; + return mBubbleHeight + mPointerView.getHeight() + mPointerMargin + + chromeHeight; + } + void updateHeight() { if (usingActivityView()) { Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); @@ -358,12 +395,19 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList ? data.getDesiredHeight() : mMinHeight; } - int max = mStackView.getMaxExpandedHeight() - mHeaderHeight; + int chromeHeight = mPermissionView.getVisibility() != View.VISIBLE + ? mHeaderHeight + : mPermissionHeight; + int max = mStackView.getMaxExpandedHeight() - chromeHeight - mPointerView.getHeight() + - mPointerMargin; int height = Math.min(desiredHeight, max); height = Math.max(height, mMinHeight); LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams(); lp.height = height; + mBubbleHeight = height; mActivityView.setLayoutParams(lp); + } else { + mBubbleHeight = mNotifRow != null ? mNotifRow.getIntrinsicHeight() : mMinHeight; } } @@ -412,6 +456,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } else if (mOnBubbleBlockedListener != null) { mOnBubbleBlockedListener.onBubbleBlocked(mEntry); } + mStackView.onExpandedHeightChanged(); logBubbleClickEvent(mEntry.notification, allowed ? StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_IN : StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_OUT); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index e20be8e552df..167bf47664e0 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -173,7 +173,7 @@ public class BubbleStackView extends FrameLayout { int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mStackAnimationController = new StackAnimationController(); - mExpandedAnimationController = new ExpandedAnimationController(); + mExpandedAnimationController = new ExpandedAnimationController(mDisplaySize); mBubbleContainer = new PhysicsAnimationLayout(context); mBubbleContainer.setMaxRenderedChildren( @@ -513,8 +513,7 @@ public class BubbleStackView extends FrameLayout { final float yStart = Math.min( mStackAnimationController.getStackPosition().y, mExpandedAnimateYDistance); - final float yDest = getStatusBarHeight() - + mExpandedBubble.iconView.getHeight() + mBubblePadding; + final float yDest = getYPositionForExpandedView(); if (shouldExpand) { mExpandedViewContainer.setTranslationX(xStart); @@ -668,13 +667,39 @@ public class BubbleStackView extends FrameLayout { * y position when the bubbles are expanded as well as the bounds of the dismiss target. */ int getMaxExpandedHeight() { + boolean showOnTop = BubbleController.showBubblesAtTop(getContext()); int expandedY = (int) mExpandedAnimationController.getExpandedY(); - int bubbleContainerHeight = mBubbleContainer.getChildAt(0) != null - ? mBubbleContainer.getChildAt(0).getHeight() - : 0; - // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset - int pipDismissHeight = mPipDismissHeight - getBottomInset(); - return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight; + if (showOnTop) { + // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset + int pipDismissHeight = mPipDismissHeight - getBottomInset(); + return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight; + } else { + return expandedY - getStatusBarHeight(); + } + } + + /** + * Calculates the y position of the expanded view when it is expanded. + */ + float getYPositionForExpandedView() { + boolean showOnTop = BubbleController.showBubblesAtTop(getContext()); + if (showOnTop) { + return getStatusBarHeight() + mBubbleSize + mBubblePadding; + } else { + return mExpandedAnimationController.getExpandedY() + - mExpandedBubble.expandedView.getExpandedSize() - mBubblePadding; + } + } + + /** + * Called when the height of the currently expanded view has changed (not via an + * update to the bubble's desired height but for some other reason, e.g. permission view + * goes away). + */ + void onExpandedHeightChanged() { + if (mIsExpanded) { + requestUpdate(); + } } /** @@ -751,6 +776,8 @@ public class BubbleStackView extends FrameLayout { mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); if (mIsExpanded) { + final float y = getYPositionForExpandedView(); + mExpandedViewContainer.setTranslationY(y); mExpandedBubble.expandedView.updateView(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index f0d9be1e484a..f7896b0b1201 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -17,6 +17,7 @@ package com.android.systemui.bubbles.animation; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.PointF; import android.view.View; import android.view.WindowInsets; @@ -25,6 +26,7 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.R; +import com.android.systemui.bubbles.BubbleController; import com.google.android.collect.Sets; @@ -61,6 +63,14 @@ public class ExpandedAnimationController private float mBubbleSizePx; /** Height of the status bar. */ private float mStatusBarHeight; + /** Size of display. */ + private Point mDisplaySize; + /** Size of dismiss target at bottom of screen. */ + private float mPipDismissHeight; + + public ExpandedAnimationController(Point displaySize) { + mDisplaySize = displaySize; + } /** * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause @@ -88,6 +98,7 @@ public class ExpandedAnimationController mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height); } /** @@ -204,16 +215,19 @@ public class ExpandedAnimationController /** The Y value of the row of expanded bubbles. */ public float getExpandedY() { + boolean showOnTop = mLayout != null + && BubbleController.showBubblesAtTop(mLayout.getContext()); final WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null; - if (insets != null) { + if (showOnTop && insets != null) { return mBubblePaddingPx + Math.max( mStatusBarHeight, insets.getDisplayCutout() != null ? insets.getDisplayCutout().getSafeInsetTop() : 0); + } else { + int bottomInset = insets != null ? insets.getSystemWindowInsetBottom() : 0; + return mDisplaySize.y - mBubbleSizePx - (mPipDismissHeight - bottomInset); } - - return mBubblePaddingPx; } /** Runs the given Runnable after all translation-related animations have ended. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java index 3bd582f955af..b4059c5db3b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java @@ -19,6 +19,7 @@ package com.android.systemui.bubbles.animation; import static org.junit.Assert.assertEquals; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.PointF; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; @@ -40,7 +41,8 @@ import org.mockito.Spy; public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestCase { @Spy - private ExpandedAnimationController mExpandedController = new ExpandedAnimationController(); + private ExpandedAnimationController mExpandedController = + new ExpandedAnimationController(new Point(500, 1000) /* displaySize */); private int mStackOffset; private float mBubblePadding; @@ -167,7 +169,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)), mLayout.getChildAt(i).getTranslationX(), 2f); - assertEquals(mBubblePadding + mCutoutInsetSize, + assertEquals(mExpandedController.getExpandedY(), mLayout.getChildAt(i).getTranslationY(), 2f); if (i < mMaxRenderedBubbles) { |