diff options
| author | 2024-02-01 13:39:45 -0800 | |
|---|---|---|
| committer | 2024-03-06 15:18:27 -0800 | |
| commit | 89f6fa684fcfa9ce83b3c8571a38395781e94e59 (patch) | |
| tree | 0f8efe185aad6c4ae0691f7738547244671d37b8 | |
| parent | a4f4dfc966775d837f1ed4d8f3fd9883138ad28e (diff) | |
Introduce BubbleBarLocation to define bubble bar location
BubbleBarLocation allows bubble bar to be positioned at the default location.
This is on the right side for LTR languages and on the left for RTL.
In addition it includes values for left and right. This allows overriding
of the default position. And pin the bar to either side.
The bar does not animate between the two states. And will immediately show
on the other side when an update is received.
Bug: 313661121
Flag: ACONFIG com.android.wm.shell.enable_bubble_bar DEVELOPMENT
Test: atest WMShellUnitTests
Change-Id: I498b20d5e7fac1d5aacdcff6a903864d6bb57ffa
11 files changed, 255 insertions, 19 deletions
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt index 9cd14fca6a9d..e422198c40c5 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt @@ -28,6 +28,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.wm.shell.R import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT +import com.android.wm.shell.common.bubbles.BubbleBarLocation import com.google.common.truth.Truth.assertThat import com.google.common.util.concurrent.MoreExecutors.directExecutor import org.junit.Before @@ -486,6 +487,32 @@ class BubblePositionerTest { positioner.screenRect.width() - paddings[0] - paddings[2]) } + @Test + fun testIsBubbleBarOnLeft_defaultsToRight() { + positioner.bubbleBarLocation = BubbleBarLocation.DEFAULT + assertThat(positioner.isBubbleBarOnLeft).isFalse() + + // Check that left and right return expected position + positioner.bubbleBarLocation = BubbleBarLocation.LEFT + assertThat(positioner.isBubbleBarOnLeft).isTrue() + positioner.bubbleBarLocation = BubbleBarLocation.RIGHT + assertThat(positioner.isBubbleBarOnLeft).isFalse() + } + + @Test + fun testIsBubbleBarOnLeft_rtlEnabled_defaultsToLeft() { + positioner.update(defaultDeviceConfig.copy(isRtl = true)) + + positioner.bubbleBarLocation = BubbleBarLocation.DEFAULT + assertThat(positioner.isBubbleBarOnLeft).isTrue() + + // Check that left and right return expected position + positioner.bubbleBarLocation = BubbleBarLocation.LEFT + assertThat(positioner.isBubbleBarOnLeft).isTrue() + positioner.bubbleBarLocation = BubbleBarLocation.RIGHT + assertThat(positioner.isBubbleBarOnLeft).isFalse() + } + private val defaultYPosition: Float /** * Calculates the Y position bubbles should be placed based on the config. Based on the diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 96aaf02cb5e3..9585842a2014 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -103,6 +103,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.common.bubbles.BubbleBarLocation; import com.android.wm.shell.common.bubbles.BubbleBarUpdate; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; @@ -708,6 +709,30 @@ public class BubbleController implements ConfigurationChangeListener, return mBubbleProperties.isBubbleBarEnabled() && mBubblePositioner.isLargeScreen(); } + /** + * Returns current {@link BubbleBarLocation} if bubble bar is being used. + * Otherwise returns <code>null</code> + */ + @Nullable + public BubbleBarLocation getBubbleBarLocation() { + if (canShowAsBubbleBar()) { + return mBubblePositioner.getBubbleBarLocation(); + } + return null; + } + + /** + * Update bubble bar location and trigger and update to listeners + */ + public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { + if (canShowAsBubbleBar()) { + mBubblePositioner.setBubbleBarLocation(bubbleBarLocation); + BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate(); + bubbleBarUpdate.bubbleBarLocation = bubbleBarLocation; + mBubbleStateListener.onBubbleStateChange(bubbleBarUpdate); + } + } + /** Whether this userId belongs to the current user. */ private boolean isCurrentProfile(int userId) { return userId == UserHandle.USER_ALL @@ -1179,7 +1204,7 @@ public class BubbleController implements ConfigurationChangeListener, */ @VisibleForTesting public void expandStackAndSelectBubbleFromLauncher(String key, Rect bubbleBarBounds) { - mBubblePositioner.setBubbleBarPosition(bubbleBarBounds); + mBubblePositioner.setBubbleBarBounds(bubbleBarBounds); if (BubbleOverflow.KEY.equals(key)) { mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 6c2f925119f3..f504f06b526b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -255,7 +255,9 @@ public class BubbleData { * Returns a bubble bar update populated with the current list of active bubbles. */ public BubbleBarUpdate getInitialStateForBubbleBar() { - return mStateChange.getInitialState(); + BubbleBarUpdate initialState = mStateChange.getInitialState(); + initialState.bubbleBarLocation = mPositioner.getBubbleBarLocation(); + return initialState; } public void setSuppressionChangedListener(Bubbles.BubbleMetadataFlagListener listener) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index a5853d621cb5..b215b616dcce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -32,6 +32,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconNormalizer; import com.android.wm.shell.R; +import com.android.wm.shell.common.bubbles.BubbleBarLocation; /** * Keeps track of display size, configuration, and specific bubble sizes. One place for all @@ -95,6 +96,7 @@ public class BubblePositioner { private PointF mRestingStackPosition; private boolean mShowingInBubbleBar; + private BubbleBarLocation mBubbleBarLocation = BubbleBarLocation.DEFAULT; private final Rect mBubbleBarBounds = new Rect(); public BubblePositioner(Context context, WindowManager windowManager) { @@ -797,14 +799,36 @@ public class BubblePositioner { mShowingInBubbleBar = showingInBubbleBar; } + public void setBubbleBarLocation(BubbleBarLocation location) { + mBubbleBarLocation = location; + } + + public BubbleBarLocation getBubbleBarLocation() { + return mBubbleBarLocation; + } + + /** + * @return <code>true</code> when bubble bar is on the left and <code>false</code> when on right + */ + public boolean isBubbleBarOnLeft() { + return mBubbleBarLocation.isOnLeft(mDeviceConfig.isRtl()); + } + /** * Sets the position of the bubble bar in display coordinates. */ - public void setBubbleBarPosition(Rect bubbleBarBounds) { + public void setBubbleBarBounds(Rect bubbleBarBounds) { mBubbleBarBounds.set(bubbleBarBounds); } /** + * Returns the display coordinates of the bubble bar. + */ + public Rect getBubbleBarBounds() { + return mBubbleBarBounds; + } + + /** * How wide the expanded view should be when showing from the bubble bar. */ public int getExpandedViewWidthForBubbleBar(boolean isOverflow) { @@ -831,11 +855,4 @@ public class BubblePositioner { public int getBubbleBarExpandedViewPadding() { return mExpandedViewPadding; } - - /** - * Returns the display coordinates of the bubble bar. - */ - public Rect getBubbleBarBounds() { - return mBubbleBarBounds; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index 8946f41e96a7..9eb963237115 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -477,7 +477,7 @@ public class BubbleBarAnimationHelper { private Point getExpandedViewRestPosition(Size size) { final int padding = mPositioner.getBubbleBarExpandedViewPadding(); Point point = new Point(); - if (mLayerView.isOnLeft()) { + if (mPositioner.isBubbleBarOnLeft()) { point.x = mPositioner.getInsets().left + padding; } else { point.x = mPositioner.getAvailableRect().width() - size.getWidth() - padding; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index 42799d975e1b..d20e66d067f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -155,12 +155,6 @@ public class BubbleBarLayerView extends FrameLayout return mIsExpanded; } - // TODO(b/313661121) - when dragging is implemented, check user setting first - /** Whether the expanded view is positioned on the left or right side of the screen. */ - public boolean isOnLeft() { - return getLayoutDirection() == LAYOUT_DIRECTION_RTL; - } - /** Shows the expanded view of the provided bubble. */ public void showExpandedView(BubbleViewProvider b) { BubbleBarExpandedView expandedView = b.getBubbleBarExpandedView(); @@ -352,7 +346,7 @@ public class BubbleBarLayerView extends FrameLayout lp.width = width; lp.height = height; mExpandedView.setLayoutParams(lp); - if (isOnLeft()) { + if (mPositioner.isBubbleBarOnLeft()) { mExpandedView.setX(mPositioner.getInsets().left + padding); } else { mExpandedView.setX(mPositioner.getAvailableRect().width() - width - padding); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt new file mode 100644 index 000000000000..f0bdfdef1073 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 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.common.bubbles + +import android.os.Parcel +import android.os.Parcelable + +/** + * The location of the bubble bar. + */ +enum class BubbleBarLocation : Parcelable { + /** + * Place bubble bar at the default location for the chosen system language. + * If an RTL language is used, it is on the left. Otherwise on the right. + */ + DEFAULT, + /** Default bubble bar location is overridden. Place bubble bar on the left. */ + LEFT, + /** Default bubble bar location is overridden. Place bubble bar on the right. */ + RIGHT; + + /** + * Returns whether bubble bar is pinned to the left edge or right edge. + */ + fun isOnLeft(isRtl: Boolean): Boolean { + if (this == DEFAULT) { + return isRtl + } + return this == LEFT + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeString(name) + } + + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator<BubbleBarLocation> { + override fun createFromParcel(parcel: Parcel): BubbleBarLocation { + return parcel.readString()?.let { valueOf(it) } ?: DEFAULT + } + + override fun newArray(size: Int) = arrayOfNulls<BubbleBarLocation>(size) + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java index fc627a8dcb36..c7d52cbe1895 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java @@ -46,6 +46,8 @@ public class BubbleBarUpdate implements Parcelable { public String suppressedBubbleKey; @Nullable public String unsupressedBubbleKey; + @Nullable + public BubbleBarLocation bubbleBarLocation; // This is only populated if bubbles have been removed. public List<RemovedBubble> removedBubbles = new ArrayList<>(); @@ -75,6 +77,8 @@ public class BubbleBarUpdate implements Parcelable { parcel.readStringList(bubbleKeysInOrder); currentBubbleList = parcel.readParcelableList(new ArrayList<>(), BubbleInfo.class.getClassLoader()); + bubbleBarLocation = parcel.readParcelable(BubbleBarLocation.class.getClassLoader(), + BubbleBarLocation.class); } /** @@ -89,7 +93,8 @@ public class BubbleBarUpdate implements Parcelable { || !bubbleKeysInOrder.isEmpty() || suppressedBubbleKey != null || unsupressedBubbleKey != null - || !currentBubbleList.isEmpty(); + || !currentBubbleList.isEmpty() + || bubbleBarLocation != null; } @Override @@ -105,6 +110,7 @@ public class BubbleBarUpdate implements Parcelable { + " removedBubbles=" + removedBubbles + " bubbles=" + bubbleKeysInOrder + " currentBubbleList=" + currentBubbleList + + " bubbleBarLocation=" + bubbleBarLocation + " }"; } @@ -126,6 +132,7 @@ public class BubbleBarUpdate implements Parcelable { parcel.writeParcelableList(removedBubbles, flags); parcel.writeStringList(bubbleKeysInOrder); parcel.writeParcelableList(currentBubbleList, flags); + parcel.writeParcelable(bubbleBarLocation, flags); } @NonNull diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index fa0aba5a6ee9..48e396a4817f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -49,6 +49,8 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.bubbles.BubbleData.TimeSource; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.bubbles.BubbleBarLocation; +import com.android.wm.shell.common.bubbles.BubbleBarUpdate; import com.google.common.collect.ImmutableList; @@ -1207,6 +1209,19 @@ public class BubbleDataTest extends ShellTestCase { assertOverflowChangedTo(ImmutableList.of()); } + @Test + public void test_getInitialStateForBubbleBar_includesInitialBubblesAndPosition() { + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryA2, 2000); + mPositioner.setBubbleBarLocation(BubbleBarLocation.LEFT); + + BubbleBarUpdate update = mBubbleData.getInitialStateForBubbleBar(); + assertThat(update.currentBubbleList).hasSize(2); + assertThat(update.currentBubbleList.get(0).getKey()).isEqualTo(mEntryA2.getKey()); + assertThat(update.currentBubbleList.get(1).getKey()).isEqualTo(mEntryA1.getKey()); + assertThat(update.bubbleBarLocation).isEqualTo(BubbleBarLocation.LEFT); + } + private void verifyUpdateReceived() { verify(mListener).applyUpdate(mUpdateCaptor.capture()); reset(mListener); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt new file mode 100644 index 000000000000..27e0b196f0be --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 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.common.bubbles + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.common.bubbles.BubbleBarLocation.DEFAULT +import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT +import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class BubbleBarLocationTest : ShellTestCase() { + + @Test + fun isOnLeft_rtlEnabled_defaultsToLeft() { + assertThat(DEFAULT.isOnLeft(isRtl = true)).isTrue() + } + + @Test + fun isOnLeft_rtlDisabled_defaultsToRight() { + assertThat(DEFAULT.isOnLeft(isRtl = false)).isFalse() + } + + @Test + fun isOnLeft_left_trueForAllLanguageDirections() { + assertThat(LEFT.isOnLeft(isRtl = false)).isTrue() + assertThat(LEFT.isOnLeft(isRtl = true)).isTrue() + } + + @Test + fun isOnLeft_right_falseForAllLanguageDirections() { + assertThat(RIGHT.isOnLeft(isRtl = false)).isFalse() + assertThat(RIGHT.isOnLeft(isRtl = true)).isFalse() + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index fbefb0eedfa8..c0d3d27e94a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -90,6 +90,7 @@ import android.view.View; import android.view.ViewTreeObserver; import android.view.WindowManager; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; @@ -196,6 +197,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.common.bubbles.BubbleBarLocation; import com.android.wm.shell.common.bubbles.BubbleBarUpdate; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; @@ -2251,6 +2253,30 @@ public class BubblesTest extends SysuiTestCase { verify(mBubbleController).onSensitiveNotificationProtectionStateChanged(false); } + @Test + public void setBubbleBarLocation_listenerNotified() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT); + assertThat(bubbleStateListener.mLastUpdate).isNotNull(); + assertThat(bubbleStateListener.mLastUpdate.bubbleBarLocation).isEqualTo( + BubbleBarLocation.LEFT); + } + + @Test + public void setBubbleBarLocation_barDisabled_shouldBeIgnored() { + mBubbleProperties.mIsBubbleBarEnabled = false; + mPositioner.setIsLargeScreen(true); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT); + assertThat(bubbleStateListener.mStateChangeCalls).isEqualTo(0); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); @@ -2436,8 +2462,15 @@ public class BubblesTest extends SysuiTestCase { } private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener { + + int mStateChangeCalls = 0; + @Nullable + BubbleBarUpdate mLastUpdate; + @Override public void onBubbleStateChange(BubbleBarUpdate update) { + mStateChangeCalls++; + mLastUpdate = update; } } |