diff options
3 files changed, 85 insertions, 39 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 8ef1e568cd58..08a22a87c2ba 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 @@ -17,6 +17,7 @@ package com.android.systemui.shade.domain.interactor import android.content.Context +import android.content.MutableContextWrapper import android.content.res.Configuration import android.content.res.Resources import android.view.Display @@ -66,11 +67,12 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { ShadeDisplaysInteractor( shadeRootview, positionRepository, - defaultContext, + MutableContextWrapper(defaultContext), + resources, contextStore, - testScope, + testScope.backgroundScope, configurationForwarder, - testScope.coroutineContext, + testScope.backgroundScope.coroutineContext, ) @Before @@ -79,7 +81,6 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(display.displayId).thenReturn(0) whenever(resources.configuration).thenReturn(configuration) - whenever(resources.configuration).thenReturn(configuration) whenever(defaultContext.displayId).thenReturn(0) whenever(defaultContext.getSystemService(any())).thenReturn(defaultWm) @@ -124,7 +125,6 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(display.displayId).thenReturn(0) positionRepository.setDisplayId(1) interactor.start() - testScope.advanceUntilIdle() verify(defaultWm).removeView(eq(shadeRootview)) verify(secondaryWm).addView(eq(shadeRootview), any()) @@ -135,10 +135,8 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(display.displayId).thenReturn(0) positionRepository.setDisplayId(0) interactor.start() - testScope.advanceUntilIdle() positionRepository.setDisplayId(1) - testScope.advanceUntilIdle() verify(defaultWm).removeView(eq(shadeRootview)) verify(secondaryWm).addView(eq(shadeRootview), any()) @@ -149,10 +147,8 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(display.displayId).thenReturn(0) positionRepository.setDisplayId(0) interactor.start() - testScope.advanceUntilIdle() positionRepository.setDisplayId(1) - testScope.advanceUntilIdle() verify(configurationForwarder).onConfigurationChanged(eq(configuration)) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt index 4f73a3456cad..bf2ea56b0c67 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt @@ -17,9 +17,9 @@ package com.android.systemui.shade import android.content.Context +import android.content.MutableContextWrapper import android.content.res.Resources import android.view.LayoutInflater -import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY import com.android.systemui.CoreStartable import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.ConfigurationStateImpl @@ -29,7 +29,6 @@ import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImp import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.res.R import com.android.systemui.shade.data.repository.ShadeDisplaysRepository import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround @@ -62,9 +61,7 @@ object ShadeDisplayAwareModule { @SysUISingleton fun provideShadeDisplayAwareContext(context: Context): Context { return if (ShadeWindowGoesAround.isEnabled) { - context - .createWindowContext(context.display, TYPE_APPLICATION_OVERLAY, /* options= */ null) - .apply { setTheme(R.style.Theme_SystemUI) } + MutableContextWrapper(context) } else { context } 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 1055dcb55d5f..18dbba1d0d25 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,8 +16,13 @@ package com.android.systemui.shade.domain.interactor +import android.content.ComponentCallbacks import android.content.Context +import android.content.MutableContextWrapper +import android.content.res.Configuration +import android.content.res.Resources import android.util.Log +import android.view.WindowManager import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE import com.android.app.tracing.coroutines.launchTraced import com.android.app.tracing.traceSection @@ -46,12 +51,17 @@ constructor( private val shadeRootView: WindowRootView, private val shadePositionRepository: ShadeDisplaysRepository, @ShadeDisplayAware private val shadeContext: Context, + @ShadeDisplayAware private val shadeResources: Resources, private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, @Background private val bgScope: CoroutineScope, - @ShadeDisplayAware private val configurationForwarder: ConfigurationForwarder, - @Main private val mainContext: CoroutineContext, + @ShadeDisplayAware private val shadeConfigurationForwarder: ConfigurationForwarder, + @Main private val mainThreadContext: CoroutineContext, ) : CoreStartable { + // TODO: b/362719719 - Get rid of this callback as the root view should automatically get the + // correct configuration once it's moved to another window. + private var unregisterConfigChangedCallbacks: (() -> Unit)? = null + override fun start() { ShadeWindowGoesAround.isUnexpectedlyInLegacyMode() bgScope.launchTraced(TAG) { @@ -60,43 +70,86 @@ constructor( } /** Tries to move the shade. If anything wrong happens, fails gracefully without crashing. */ - private suspend fun moveShadeWindowTo(destinationDisplayId: Int) { - val currentId = shadeRootView.display.displayId - if (currentId == destinationDisplayId) { + private suspend fun moveShadeWindowTo(destinationId: Int) { + Log.d(TAG, "Trying to move shade window to display with id $destinationId") + val currentDisplay = shadeRootView.display + if (currentDisplay == null) { + Log.w(TAG, "Current shade display is null") + return + } + val currentId = currentDisplay.displayId + if (currentId == destinationId) { Log.w(TAG, "Trying to move the shade to a display it was already in") return } try { - moveShadeWindow(fromId = currentId, toId = destinationDisplayId) + moveShadeWindow(fromId = currentId, toId = destinationId) } catch (e: IllegalStateException) { Log.e( TAG, - "Unable to move the shade window from display $currentId to $destinationDisplayId", + "Unable to move the shade window from display $currentId to $destinationId", e, ) } } private suspend fun moveShadeWindow(fromId: Int, toId: Int) { - val sourceProperties = getDisplayWindowProperties(fromId) - val destinationProperties = getDisplayWindowProperties(toId) - traceSection({ "MovingShadeWindow from $fromId to $toId" }) { - withContext(mainContext) { - traceSection("removeView") { - sourceProperties.windowManager.removeView(shadeRootView) - } - traceSection("addView") { - destinationProperties.windowManager.addView( - shadeRootView, - ShadeWindowLayoutParams.create(shadeContext), - ) - } + val (_, _, _, sourceWm) = getDisplayWindowProperties(fromId) + val (_, _, destContext, destWm) = getDisplayWindowProperties(toId) + withContext(mainThreadContext) { + traceSection({ "MovingShadeWindow from $fromId to $toId" }) { + removeShade(sourceWm) + addShade(destWm) + overrideContextAndResources(newContext = destContext) + registerConfigurationChange(destContext) + } + traceSection("ShadeDisplaysInteractor#onConfigurationChanged") { + dispatchConfigurationChanged(destContext.resources.configuration) } } - traceSection("SecondaryShadeInteractor#onConfigurationChanged") { - configurationForwarder.onConfigurationChanged( - destinationProperties.context.resources.configuration - ) + } + + private fun removeShade(wm: WindowManager): Unit = + traceSection("removeView") { wm.removeView(shadeRootView) } + + private fun addShade(wm: WindowManager): Unit = + traceSection("addView") { + wm.addView(shadeRootView, ShadeWindowLayoutParams.create(shadeContext)) + } + + private fun overrideContextAndResources(newContext: Context) { + val contextWrapper = + shadeContext as? MutableContextWrapper + ?: error("Shade context is not a MutableContextWrapper!") + contextWrapper.baseContext = newContext + // Override needed in case someone is keeping a reference to the resources from the old + // context. + // TODO: b/362719719 - This shouldn't be needed, as resources should be updated when the + // window is moved to the new display automatically. + shadeResources.impl = shadeContext.resources.impl + } + + private fun dispatchConfigurationChanged(newConfig: Configuration) { + shadeConfigurationForwarder.onConfigurationChanged(newConfig) + shadeRootView.dispatchConfigurationChanged(newConfig) + shadeRootView.requestLayout() + } + + private fun registerConfigurationChange(context: Context) { + // we should keep only one at the time. + unregisterConfigChangedCallbacks?.invoke() + val callback = + object : ComponentCallbacks { + override fun onConfigurationChanged(newConfig: Configuration) { + dispatchConfigurationChanged(newConfig) + } + + override fun onLowMemory() {} + } + context.registerComponentCallbacks(callback) + unregisterConfigChangedCallbacks = { + context.unregisterComponentCallbacks(callback) + unregisterConfigChangedCallbacks = null } } @@ -105,6 +158,6 @@ constructor( } private companion object { - const val TAG = "SecondaryShadeInteractor" + const val TAG = "ShadeDisplaysInteractor" } } |