Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +5 −1 Original line number Diff line number Diff line Loading @@ -1531,7 +1531,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; } Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt +5 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -131,8 +130,10 @@ class ReusableWindowDecorViewHost( Trace.beginSection("ReusableWindowDecorViewHost#updateViewHost") viewHostAdapter.prepareViewHost(configuration, touchableRegion) onDrawTransaction?.let { viewHostAdapter.applyTransactionOnDraw(it) } if (view.parent != rootView) { rootView.removeAllViews() rootView.addView(view) } viewHostAdapter.updateView(rootView, attrs) Trace.endSection() } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt +45 −16 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) Loading @@ -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. Loading @@ -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) } Loading @@ -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( Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +5 −1 Original line number Diff line number Diff line Loading @@ -1531,7 +1531,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; } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt +5 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -131,8 +130,10 @@ class ReusableWindowDecorViewHost( Trace.beginSection("ReusableWindowDecorViewHost#updateViewHost") viewHostAdapter.prepareViewHost(configuration, touchableRegion) onDrawTransaction?.let { viewHostAdapter.applyTransactionOnDraw(it) } if (view.parent != rootView) { rootView.removeAllViews() rootView.addView(view) } viewHostAdapter.updateView(rootView, attrs) Trace.endSection() } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHostTest.kt +45 −16 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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) Loading @@ -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. Loading @@ -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) } Loading @@ -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( Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading