Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt +5 −9 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -78,7 +80,6 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(shadeRootview.display).thenReturn(display) whenever(display.displayId).thenReturn(0) whenever(resources.configuration).thenReturn(configuration) whenever(resources.configuration).thenReturn(configuration) whenever(defaultContext.displayId).thenReturn(0) Loading Loading @@ -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()) Loading @@ -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()) Loading @@ -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)) } Loading packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +2 −5 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 } Loading packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt +78 −25 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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) val (_, _, _, sourceWm) = getDisplayWindowProperties(fromId) val (_, _, destContext, destWm) = getDisplayWindowProperties(toId) withContext(mainThreadContext) { traceSection({ "MovingShadeWindow from $fromId to $toId" }) { withContext(mainContext) { traceSection("removeView") { sourceProperties.windowManager.removeView(shadeRootView) removeShade(sourceWm) addShade(destWm) overrideContextAndResources(newContext = destContext) registerConfigurationChange(destContext) } traceSection("ShadeDisplaysInteractor#onConfigurationChanged") { dispatchConfigurationChanged(destContext.resources.configuration) } } } private fun removeShade(wm: WindowManager): Unit = traceSection("removeView") { wm.removeView(shadeRootView) } private fun addShade(wm: WindowManager): Unit = traceSection("addView") { destinationProperties.windowManager.addView( shadeRootView, ShadeWindowLayoutParams.create(shadeContext), ) 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() } traceSection("SecondaryShadeInteractor#onConfigurationChanged") { configurationForwarder.onConfigurationChanged( destinationProperties.context.resources.configuration ) 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 } } Loading @@ -105,6 +158,6 @@ constructor( } private companion object { const val TAG = "SecondaryShadeInteractor" const val TAG = "ShadeDisplaysInteractor" } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt +5 −9 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -78,7 +80,6 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(shadeRootview.display).thenReturn(display) whenever(display.displayId).thenReturn(0) whenever(resources.configuration).thenReturn(configuration) whenever(resources.configuration).thenReturn(configuration) whenever(defaultContext.displayId).thenReturn(0) Loading Loading @@ -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()) Loading @@ -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()) Loading @@ -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)) } Loading
packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +2 −5 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 } Loading
packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt +78 −25 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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) val (_, _, _, sourceWm) = getDisplayWindowProperties(fromId) val (_, _, destContext, destWm) = getDisplayWindowProperties(toId) withContext(mainThreadContext) { traceSection({ "MovingShadeWindow from $fromId to $toId" }) { withContext(mainContext) { traceSection("removeView") { sourceProperties.windowManager.removeView(shadeRootView) removeShade(sourceWm) addShade(destWm) overrideContextAndResources(newContext = destContext) registerConfigurationChange(destContext) } traceSection("ShadeDisplaysInteractor#onConfigurationChanged") { dispatchConfigurationChanged(destContext.resources.configuration) } } } private fun removeShade(wm: WindowManager): Unit = traceSection("removeView") { wm.removeView(shadeRootView) } private fun addShade(wm: WindowManager): Unit = traceSection("addView") { destinationProperties.windowManager.addView( shadeRootView, ShadeWindowLayoutParams.create(shadeContext), ) 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() } traceSection("SecondaryShadeInteractor#onConfigurationChanged") { configurationForwarder.onConfigurationChanged( destinationProperties.context.resources.configuration ) 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 } } Loading @@ -105,6 +158,6 @@ constructor( } private companion object { const val TAG = "SecondaryShadeInteractor" const val TAG = "ShadeDisplaysInteractor" } }