diff options
5 files changed, 48 insertions, 67 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt index a8d5c31873de..e93d0effe742 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt @@ -16,32 +16,26 @@ package com.android.systemui.shade.domain.interactor -import android.content.mockedContext import android.content.res.Configuration import android.content.res.mockResources import android.view.Display -import android.view.mockWindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.scene.ui.view.mockShadeRootView import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository import com.android.systemui.testKosmos -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.advanceUntilIdle import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.inOrder +import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.mock -import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever -@OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) @SmallTest class ShadeDisplaysInteractorTest : SysuiTestCase() { @@ -49,9 +43,7 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { private val shadeRootview = kosmos.mockShadeRootView private val positionRepository = kosmos.fakeShadeDisplaysRepository - private val shadeContext = kosmos.mockedContext - private val testScope = kosmos.testScope - private val shadeWm = kosmos.mockWindowManager + private val shadeContext = kosmos.mockedWindowContext private val resources = kosmos.mockResources private val configuration = mock<Configuration>() private val display = mock<Display>() @@ -66,8 +58,8 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(resources.configuration).thenReturn(configuration) whenever(shadeContext.displayId).thenReturn(0) - whenever(shadeContext.getSystemService(any())).thenReturn(shadeWm) whenever(shadeContext.resources).thenReturn(resources) + whenever(shadeContext.display).thenReturn(display) } @Test @@ -77,7 +69,7 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { underTest.start() - verifyNoMoreInteractions(shadeWm) + verify(shadeContext, never()).reparentToDisplay(any()) } @Test @@ -87,24 +79,6 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { underTest.start() - inOrder(shadeWm).apply { - verify(shadeWm).removeView(eq(shadeRootview)) - verify(shadeWm).addView(eq(shadeRootview), any()) - } - } - - @Test - fun start_shadePositionChanges_removedThenAdded() { - whenever(display.displayId).thenReturn(0) - positionRepository.setDisplayId(0) - underTest.start() - - positionRepository.setDisplayId(1) - testScope.advanceUntilIdle() - - inOrder(shadeWm).apply { - verify(shadeWm).removeView(eq(shadeRootview)) - verify(shadeWm).addView(eq(shadeRootview), any()) - } + verify(shadeContext).reparentToDisplay(eq(1)) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java index f5fc1f414f82..bf672be3c8d0 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java @@ -166,6 +166,15 @@ public class NotificationShadeWindowView extends WindowRootView { } @Override + public void onMovedToDisplay(int displayId, Configuration config) { + super.onMovedToDisplay(displayId, config); + ShadeWindowGoesAround.isUnexpectedlyInLegacyMode(); + // When the window is moved we're only receiving a call to this method instead of the + // onConfigurationChange itself. Let's just trigegr a normal config change. + onConfigurationChanged(config); + } + + @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (mConfigurationForwarder != null) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt index ff39a3ddc17c..a002aa53736a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt @@ -22,6 +22,7 @@ import android.view.LayoutInflater import android.view.WindowManager import android.view.WindowManager.LayoutParams import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE +import android.window.WindowContext import com.android.systemui.CoreStartable import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.ConfigurationStateImpl @@ -81,6 +82,19 @@ object ShadeDisplayAwareModule { @Provides @ShadeDisplayAware @SysUISingleton + fun provideShadeDisplayAwareWindowContext(@ShadeDisplayAware context: Context): WindowContext { + ShadeWindowGoesAround.isUnexpectedlyInLegacyMode() + // We rely on the fact context is a WindowContext as the API to reparent windows is only + // available there. + return (context as? WindowContext) + ?: error( + "ShadeDisplayAware context must be a window context to allow window reparenting." + ) + } + + @Provides + @ShadeDisplayAware + @SysUISingleton fun provideShadeWindowLayoutParams(@ShadeDisplayAware context: Context): LayoutParams { return ShadeWindowLayoutParams.create(context) } @@ -203,7 +217,9 @@ object ShadeDisplayAwareModule { @Provides @IntoMap @ClassKey(ShadePrimaryDisplayCommand::class) - fun provideShadePrimaryDisplayCommand(impl: Provider<ShadePrimaryDisplayCommand>): CoreStartable { + fun provideShadePrimaryDisplayCommand( + impl: Provider<ShadePrimaryDisplayCommand> + ): CoreStartable { return if (ShadeWindowGoesAround.isEnabled) { impl.get() } else { diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt index 34148671cf2a..08c03e28d596 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt @@ -16,10 +16,8 @@ package com.android.systemui.shade.domain.interactor -import android.content.Context import android.util.Log -import android.view.WindowManager -import android.view.WindowManager.LayoutParams +import android.window.WindowContext import androidx.annotation.UiThread import com.android.app.tracing.coroutines.launchTraced import com.android.app.tracing.traceSection @@ -45,9 +43,7 @@ class ShadeDisplaysInteractor constructor( optionalShadeRootView: Optional<WindowRootView>, private val shadePositionRepository: ShadeDisplaysRepository, - @ShadeDisplayAware private val shadeContext: Context, - @ShadeDisplayAware private val shadeLayoutParams: LayoutParams, - @ShadeDisplayAware private val wm: WindowManager, + @ShadeDisplayAware private val shadeContext: WindowContext, @Background private val bgScope: CoroutineScope, @Main private val mainThreadContext: CoroutineContext, ) : CoreStartable { @@ -72,7 +68,11 @@ constructor( /** Tries to move the shade. If anything wrong happens, fails gracefully without crashing. */ private suspend fun moveShadeWindowTo(destinationId: Int) { Log.d(TAG, "Trying to move shade window to display with id $destinationId") - val currentDisplay = shadeRootView.display + // Why using the shade context here instead of the view's Display? + // The context's display is updated before the view one, so it is a better indicator of + // which display the shade is supposed to be at. The View display is updated after the first + // rendering with the new config. + val currentDisplay = shadeContext.display if (currentDisplay == null) { Log.w(TAG, "Current shade display is null") return @@ -83,7 +83,7 @@ constructor( return } try { - withContext(mainThreadContext) { moveShadeWindow(toId = destinationId) } + withContext(mainThreadContext) { reparentToDisplayId(id = destinationId) } } catch (e: IllegalStateException) { Log.e( TAG, @@ -94,25 +94,8 @@ constructor( } @UiThread - private fun moveShadeWindow(toId: Int) { - traceSection({ "moveShadeWindow to $toId" }) { - removeShadeWindow() - updateContextDisplay(toId) - addShadeWindow() - } - } - - @UiThread - private fun removeShadeWindow(): Unit = - traceSection("removeShadeWindow") { wm.removeView(shadeRootView) } - - @UiThread - private fun addShadeWindow(): Unit = - traceSection("addShadeWindow") { wm.addView(shadeRootView, shadeLayoutParams) } - - @UiThread - private fun updateContextDisplay(newDisplayId: Int) { - traceSection("updateContextDisplay") { shadeContext.updateDisplay(newDisplayId) } + private fun reparentToDisplayId(id: Int) { + traceSection({ "reparentToDisplayId(id=$id)" }) { shadeContext.reparentToDisplay(id) } } private companion object { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt index db4df38e038a..f2af619a4ad7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt @@ -17,25 +17,24 @@ package com.android.systemui.shade.domain.interactor import android.content.mockedContext -import android.view.mockWindowManager +import android.window.WindowContext import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.scene.ui.view.mockShadeRootView import com.android.systemui.shade.ShadeWindowLayoutParams import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository import java.util.Optional +import org.mockito.kotlin.mock -val Kosmos.shadeLayoutParams by Kosmos.Fixture { - ShadeWindowLayoutParams.create(mockedContext) -} +val Kosmos.shadeLayoutParams by Kosmos.Fixture { ShadeWindowLayoutParams.create(mockedContext) } + +val Kosmos.mockedWindowContext by Kosmos.Fixture { mock<WindowContext>() } val Kosmos.shadeDisplaysInteractor by Kosmos.Fixture { ShadeDisplaysInteractor( Optional.of(mockShadeRootView), fakeShadeDisplaysRepository, - mockedContext, - shadeLayoutParams, - mockWindowManager, + mockedWindowContext, testScope.backgroundScope, testScope.backgroundScope.coroutineContext, ) |