diff options
3 files changed, 55 insertions, 21 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 1cc04b421132..5692975ee972 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -1502,7 +1502,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, // Do not create an indicator at all if we're not past transition height. DisplayLayout layout = mDisplayController .getDisplayLayout(relevantDecor.mTaskInfo.displayId); - if (ev.getRawY() < 2 * layout.stableInsets().top + // It's possible task is not at the top of the screen (e.g. bottom of vertical + // Splitscreen) + final int taskTop = relevantDecor.mTaskInfo.configuration.windowConfiguration + .getBounds().top; + if (ev.getRawY() < 2 * layout.stableInsets().top + taskTop && mMoveToDesktopAnimator == null) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt index 4a09614029dc..a5592f81a39e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt @@ -50,9 +50,8 @@ class ReusableWindowDecorViewHost( @VisibleForTesting val viewHostAdapter: SurfaceControlViewHostAdapter = SurfaceControlViewHostAdapter(context, display), + private val rootView: FrameLayout = FrameLayout(context) ) : WindowDecorViewHost, Warmable { - @VisibleForTesting val rootView = FrameLayout(context) - private var currentUpdateJob: Job? = null override val surfaceControl: SurfaceControl @@ -131,8 +130,10 @@ class ReusableWindowDecorViewHost( Trace.beginSection("ReusableWindowDecorViewHost#updateViewHost") viewHostAdapter.prepareViewHost(configuration, touchableRegion) onDrawTransaction?.let { viewHostAdapter.applyTransactionOnDraw(it) } - rootView.removeAllViews() - rootView.addView(view) + if (view.parent != rootView) { + rootView.removeAllViews() + rootView.addView(view) + } viewHostAdapter.updateView(rootView, attrs) Trace.endSection() } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt index d99a4825e580..c86730ed1dc7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt @@ -20,6 +20,7 @@ import android.testing.TestableLooper import android.view.SurfaceControl import android.view.View import android.view.WindowManager +import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase import com.google.common.truth.Truth.assertThat @@ -30,6 +31,9 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.kotlin.clearInvocations +import org.mockito.kotlin.never import org.mockito.kotlin.spy import org.mockito.kotlin.verify @@ -47,24 +51,46 @@ class ReusableWindowDecorViewHostTest : ShellTestCase() { fun update_differentView_replacesView() = runTest { val view = View(context) val lp = WindowManager.LayoutParams() - val reusableVH = createReusableViewHost() - reusableVH.updateView(view, lp, context.resources.configuration, null) + val rootView = FrameLayout(context) + val reusableVH = createReusableViewHost(rootView) + reusableVH.updateView(view, lp, context.resources.configuration) - assertThat(reusableVH.rootView.childCount).isEqualTo(1) - assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(view) + assertThat(rootView.childCount).isEqualTo(1) + assertThat(rootView.getChildAt(0)).isEqualTo(view) val newView = View(context) val newLp = WindowManager.LayoutParams() - reusableVH.updateView(newView, newLp, context.resources.configuration, null) + reusableVH.updateView(newView, newLp, context.resources.configuration) - assertThat(reusableVH.rootView.childCount).isEqualTo(1) - assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(newView) + assertThat(rootView.childCount).isEqualTo(1) + assertThat(rootView.getChildAt(0)).isEqualTo(newView) + } + + @Test + fun update_sameView_doesNotReplaceView() = runTest { + val view = View(context) + val lp = WindowManager.LayoutParams() + val spyRootView = spy(FrameLayout(context)) + val reusableVH = createReusableViewHost(spyRootView) + reusableVH.updateView(view, lp, context.resources.configuration) + + verify(spyRootView, times(1)).removeAllViews() + assertThat(spyRootView.childCount).isEqualTo(1) + assertThat(spyRootView.getChildAt(0)).isEqualTo(view) + + reusableVH.updateView(view, lp, context.resources.configuration) + + clearInvocations(spyRootView) + verify(spyRootView, never()).removeAllViews() + assertThat(spyRootView.childCount).isEqualTo(1) + assertThat(spyRootView.getChildAt(0)).isEqualTo(view) } @OptIn(ExperimentalCoroutinesApi::class) @Test fun updateView_clearsPendingAsyncJob() = runTest { - val reusableVH = createReusableViewHost() + val rootView = FrameLayout(context) + val reusableVH = createReusableViewHost(rootView) val asyncView = View(context) val syncView = View(context) val asyncAttrs = WindowManager.LayoutParams(100, 100) @@ -83,7 +109,6 @@ class ReusableWindowDecorViewHostTest : ShellTestCase() { view = syncView, attrs = syncAttrs, configuration = context.resources.configuration, - onDrawTransaction = null, ) // Would run coroutine if it hadn't been cancelled. @@ -91,7 +116,7 @@ class ReusableWindowDecorViewHostTest : ShellTestCase() { assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue() // View host view/attrs should match the ones from the sync call. - assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(syncView) + assertThat(rootView.getChildAt(0)).isEqualTo(syncView) assertThat(reusableVH.view()!!.layoutParams.width).isEqualTo(syncAttrs.width) } @@ -118,7 +143,8 @@ class ReusableWindowDecorViewHostTest : ShellTestCase() { @OptIn(ExperimentalCoroutinesApi::class) @Test fun updateViewAsync_clearsPendingAsyncJob() = runTest { - val reusableVH = createReusableViewHost() + val rootView = FrameLayout(context) + val reusableVH = createReusableViewHost(rootView) val view = View(context) reusableVH.updateViewAsync( @@ -136,7 +162,7 @@ class ReusableWindowDecorViewHostTest : ShellTestCase() { advanceUntilIdle() assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue() - assertThat(reusableVH.rootView.getChildAt(0)).isEqualTo(otherView) + assertThat(rootView.getChildAt(0)).isEqualTo(otherView) } @Test @@ -148,7 +174,6 @@ class ReusableWindowDecorViewHostTest : ShellTestCase() { view = view, attrs = WindowManager.LayoutParams(100, 100), configuration = context.resources.configuration, - onDrawTransaction = null, ) val t = mock(SurfaceControl.Transaction::class.java) @@ -159,19 +184,23 @@ class ReusableWindowDecorViewHostTest : ShellTestCase() { @Test fun warmUp_addsRootView() = runTest { - val reusableVH = createReusableViewHost().apply { warmUp() } + val rootView = FrameLayout(context) + val reusableVH = createReusableViewHost(rootView).apply { warmUp() } assertThat(reusableVH.viewHostAdapter.isInitialized()).isTrue() - assertThat(reusableVH.view()).isEqualTo(reusableVH.rootView) + assertThat(reusableVH.view()).isEqualTo(rootView) } - private fun CoroutineScope.createReusableViewHost() = + private fun CoroutineScope.createReusableViewHost( + rootView: FrameLayout = FrameLayout(context) + ) = ReusableWindowDecorViewHost( context = context, mainScope = this, display = context.display, id = 1, viewHostAdapter = spy(SurfaceControlViewHostAdapter(context, context.display)), + rootView ) private fun ReusableWindowDecorViewHost.view(): View? = viewHostAdapter.viewHost?.view |