Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit be61c4e1 authored by Liran Binyamin's avatar Liran Binyamin
Browse files

Clip BubbleBarExpandedView for IME

Respond to IME positioning changes and clip the BubbleBarExpandedView
to make sure there's no overlap with the IME.

Next I'll add logic to translate the expanded view if there's enough
space above it to reduce clipping.

Flag: com.android.wm.shell.enable_bubble_bar
Bug: 377329425
Test: atest BubbleBarExpandedViewTest
Change-Id: I5136b37f1d4e2eaab077fe29c57cbf96777c3d5a
parent 6d787e44
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
+5 −0
Original line number Diff line number Diff line
<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>
+100 −10
Original line number Diff line number Diff line
@@ -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)
        }
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -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);
            }
        }
    }

    /**
+14 −1
Original line number Diff line number Diff line
@@ -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) {
Loading