diff options
7 files changed, 190 insertions, 12 deletions
diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml index f8f8338e5f04..fd578a959e3b 100644 --- a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml @@ -3,6 +3,8 @@ <application android:debuggable="true" android:supportsRtl="true" > <uses-library android:name="android.test.runner" /> + <activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarAnimationHelperTest$TestActivity" + android:exported="true"/> </application> <instrumentation diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml index ffcd7d46fbae..bb111dbeffff 100644 --- a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml @@ -1,3 +1,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.wm.shell.multivalenttests"> + <application> + <activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarAnimationHelperTest$TestActivity" + android:exported="true"/> + </application> </manifest> + diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt index 3e01256fd67c..957f0ca502a1 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt @@ -17,14 +17,19 @@ package com.android.wm.shell.bubbles.bar import android.animation.AnimatorTestRule +import android.app.Activity import android.app.ActivityManager import android.content.Context import android.graphics.Insets +import android.graphics.Outline import android.graphics.Rect +import android.os.Bundle import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.widget.FrameLayout +import android.widget.FrameLayout.LayoutParams +import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -63,6 +68,7 @@ import org.mockito.kotlin.whenever class BubbleBarAnimationHelperTest { @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this) + private lateinit var activityScenario: ActivityScenario<TestActivity> companion object { const val SCREEN_WIDTH = 2000 @@ -83,6 +89,8 @@ class BubbleBarAnimationHelperTest { fun setUp() { ProtoLog.REQUIRE_PROTOLOGTOOL = false ProtoLog.init() + activityScenario = ActivityScenario.launch(TestActivity::class.java) + activityScenario.onActivity { activity -> container = activity.container } val windowManager = context.getSystemService(WindowManager::class.java) bubblePositioner = BubblePositioner(context, windowManager) bubblePositioner.setShowingInBubbleBar(true) @@ -102,8 +110,6 @@ class BubbleBarAnimationHelperTest { mainExecutor = TestShellExecutor() bgExecutor = TestShellExecutor() - container = FrameLayout(context) - animationHelper = BubbleBarAnimationHelper(context, bubblePositioner) } @@ -121,7 +127,7 @@ class BubbleBarAnimationHelperTest { val semaphore = Semaphore(0) val after = Runnable { semaphore.release() } - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, toBubble, after) animatorTestRule.advanceTimeBy(1000) } @@ -145,7 +151,7 @@ class BubbleBarAnimationHelperTest { .updateHandleColor(/* isRegionDark= */ true, /* animated= */ false) val toBubble = createBubble(key = "to").initialize(container) - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, toBubble, /* afterAnimation= */ null) animatorTestRule.advanceTimeBy(1000) } @@ -161,7 +167,7 @@ class BubbleBarAnimationHelperTest { val toBubbleTaskController = mock<TaskViewTaskController>() val toBubble = createBubble("to", toBubbleTaskController).initialize(container) - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, toBubble) {} // Start the animation, but don't finish animatorTestRule.advanceTimeBy(100) @@ -183,7 +189,7 @@ class BubbleBarAnimationHelperTest { val semaphore = Semaphore(0) val after = Runnable { semaphore.release() } - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, overflow, after) animatorTestRule.advanceTimeBy(1000) } @@ -206,7 +212,7 @@ class BubbleBarAnimationHelperTest { val semaphore = Semaphore(0) val after = Runnable { semaphore.release() } - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(overflow, toBubble, after) animatorTestRule.advanceTimeBy(1000) } @@ -226,7 +232,7 @@ class BubbleBarAnimationHelperTest { val taskController = mock<TaskViewTaskController>() val bubble = createBubble("key", taskController).initialize(container) - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateExpansion(bubble) {} animatorTestRule.advanceTimeBy(1000) } @@ -243,6 +249,80 @@ class BubbleBarAnimationHelperTest { verify(taskController).setWindowBounds(any()) } + @Test + fun animateExpansion() { + val bubble = createBubble(key = "b1").initialize(container) + val bbev = bubble.bubbleBarExpandedView!! + + val semaphore = Semaphore(0) + val after = Runnable { semaphore.release() } + + activityScenario.onActivity { + bbev.onTaskCreated() + animationHelper.animateExpansion(bubble, after) + animatorTestRule.advanceTimeBy(1000) + } + getInstrumentation().waitForIdleSync() + + assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() + assertThat(bbev.alpha).isEqualTo(1) + } + + @Test + fun onImeTopChanged_noOverlap() { + val bubble = createBubble(key = "b1").initialize(container) + val bbev = bubble.bubbleBarExpandedView!! + + val semaphore = Semaphore(0) + val after = Runnable { semaphore.release() } + + activityScenario.onActivity { + bbev.onTaskCreated() + animationHelper.animateExpansion(bubble, after) + animatorTestRule.advanceTimeBy(1000) + } + getInstrumentation().waitForIdleSync() + + assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() + + val bbevBottom = bbev.contentBottomOnScreen + bubblePositioner.insets.top + activityScenario.onActivity { + // notify that the IME top coordinate is greater than the bottom of the expanded view. + // there's no overlap so it should not be clipped. + animationHelper.onImeTopChanged(bbevBottom * 2) + } + val outline = Outline() + bbev.outlineProvider.getOutline(bbev, outline) + assertThat(outline.mRect.bottom).isEqualTo(bbev.height) + } + + @Test + fun onImeTopChanged_overlapsWithExpandedView() { + val bubble = createBubble(key = "b1").initialize(container) + val bbev = bubble.bubbleBarExpandedView!! + + val semaphore = Semaphore(0) + val after = Runnable { semaphore.release() } + + activityScenario.onActivity { + bbev.onTaskCreated() + animationHelper.animateExpansion(bubble, after) + animatorTestRule.advanceTimeBy(1000) + } + getInstrumentation().waitForIdleSync() + + assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() + + activityScenario.onActivity { + // notify that the IME top coordinate is less than the bottom of the expanded view, + // meaning it overlaps with it so we should be clipping the expanded view. + animationHelper.onImeTopChanged(bbev.contentBottomOnScreen - 10) + } + val outline = Outline() + bbev.outlineProvider.getOutline(bbev, outline) + assertThat(outline.mRect.bottom).isEqualTo(bbev.height - 10) + } + private fun createBubble( key: String, taskViewTaskController: TaskViewTaskController = mock<TaskViewTaskController>(), @@ -273,14 +353,24 @@ class BubbleBarAnimationHelperTest { } private fun Bubble.initialize(container: ViewGroup): Bubble { - getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) } + activityScenario.onActivity { container.addView(bubbleBarExpandedView) } // Mark taskView's visible bubbleBarExpandedView!!.onContentVisibilityChanged(true) return this } private fun BubbleOverflow.initialize(container: ViewGroup): BubbleOverflow { - getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) } + activityScenario.onActivity { container.addView(bubbleBarExpandedView) } return this } + + class TestActivity : Activity() { + lateinit var container: FrameLayout + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + container = FrameLayout(applicationContext) + container.layoutParams = LayoutParams(50, 50) + setContentView(container) + } + } } 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 4fc8af126247..348f13a493b1 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 @@ -2564,6 +2564,16 @@ public class BubbleController implements ConfigurationChangeListener, return IME_ANIMATION_DEFAULT; } + + @Override + public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { + if (mContext.getDisplayId() != displayId) { + return; + } + if (mLayerView != null) { + mLayerView.onImeTopChanged(imeTop); + } + } } /** 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 3188e5b9c6d2..de6d1f6c8852 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 @@ -30,6 +30,8 @@ import static com.android.wm.shell.bubbles.bar.BubbleBarExpandedView.TASK_VIEW_A import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED; import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE; +import static java.lang.Math.max; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -375,7 +377,6 @@ public class BubbleBarAnimationHelper { return animator; } - /** * Animate the expanded bubble when it is being dragged */ @@ -586,6 +587,18 @@ public class BubbleBarAnimationHelper { } } + /** Handles IME position changes. */ + public void onImeTopChanged(int imeTop) { + BubbleBarExpandedView bbev = getExpandedView(); + if (bbev == null) { + Log.w(TAG, "Bubble bar expanded view was null when IME top changed"); + return; + } + int bbevBottom = bbev.getContentBottomOnScreen(); + int clip = max(bbevBottom - imeTop, 0); + bbev.updateBottomClip(clip); + } + private @Nullable BubbleBarExpandedView getExpandedView() { BubbleViewProvider bubble = mExpandedBubble; if (bubble != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 65c929ab6fb4..e073b02dc630 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -137,6 +137,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private Executor mBackgroundExecutor; private final Rect mSampleRect = new Rect(); private final int[] mLoc = new int[2]; + private final Rect mTempBounds = new Rect(); /** Height of the caption inset at the top of the TaskView */ private int mCaptionHeight; @@ -161,6 +162,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private boolean mIsAnimating; private boolean mIsDragging; + private boolean mIsClipping = false; + private int mBottomClip = 0; + /** An enum value that tracks the visibility state of the task view */ private enum TaskViewVisibilityState { /** The task view is going away, and we're waiting for the surface to be destroyed. */ @@ -203,7 +207,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCurrentCornerRadius); + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight() - mBottomClip, + mCurrentCornerRadius); } }); // Set a touch sink to ensure that clicks on the caption area do not propagate to the parent @@ -661,6 +666,52 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } } + /** The y coordinate of the bottom of the expanded view. */ + public int getContentBottomOnScreen() { + if (mOverflowView != null) { + mOverflowView.getBoundsOnScreen(mTempBounds); + } + if (mTaskView != null) { + mTaskView.getBoundsOnScreen(mTempBounds); + } + // return the bottom of the content rect, adjusted for insets so the result is in screen + // coordinate + return mTempBounds.bottom + mPositioner.getInsets().top; + } + + /** Update the amount by which to clip the expanded view at the bottom. */ + public void updateBottomClip(int bottomClip) { + mBottomClip = bottomClip; + onClipUpdate(); + } + + private void onClipUpdate() { + if (mBottomClip == 0) { + if (mIsClipping) { + mIsClipping = false; + if (mTaskView != null) { + mTaskView.setClipBounds(null); + mTaskView.setEnableSurfaceClipping(false); + } + invalidateOutline(); + } + } else { + if (!mIsClipping) { + mIsClipping = true; + if (mTaskView != null) { + mTaskView.setEnableSurfaceClipping(true); + } + } + invalidateOutline(); + if (mTaskView != null) { + Rect clipBounds = new Rect(0, 0, + mTaskView.getWidth(), + mTaskView.getHeight() - mBottomClip); + mTaskView.setClipBounds(clipBounds); + } + } + } + private void recreateRegionSamplingHelper() { if (mRegionSamplingHelper != null) { mRegionSamplingHelper.stopAndDestroy(); 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 425afbed0742..6bca3d4ee5c4 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 @@ -424,6 +424,13 @@ public class BubbleBarLayerView extends FrameLayout } } + /** Handles IME position changes. */ + public void onImeTopChanged(int imeTop) { + if (mIsExpanded) { + mAnimationHelper.onImeTopChanged(imeTop); + } + } + /** * Log the event only if {@link #mExpandedBubble} is a {@link Bubble}. * <p> |