Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f7284d3 authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Change strategy to move the shade

The cl works around the issue of context and resources not being updated after a window is moved to another display by swapping the context underlying implementation using "MutableContextWrapper".

It seems for now the mechanism to update resources and context attributes after moving a window is fully implemented only for activity windows.

While we find the best way to make this work in the framework, this cl updates context and resources manually from sysui, and give the chance to spot which UI elements are not reacting correctly to the configuration change.

Also, here to get the configuration change the "ComponentCallbacks" approach is used. Ideally, in the final solution, after the shade window is moved it will receive the configuration change directly from VRI (so, we wouldn't need to manualy propagate it as happening here.

+ The configuration change needs to be propagated from the main thread.
+ Before the shadeRootView is added to the display its "View.display" value is null.

Bug: 362719719
Bug: 374267505
Test: ShadeDisplaysInteractorTest
Flag: com.android.systemui.shade_window_goes_around
Change-Id: Id09ca817c6a39f9e967848955773f26d05e62581
parent b7245a56
Loading
Loading
Loading
Loading
+5 −9
Original line number Diff line number Diff line
@@ -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
@@ -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)
@@ -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))
    }
+2 −5
Original line number Diff line number Diff line
@@ -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
        }
+78 −25
Original line number Diff line number Diff line
@@ -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)
        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
        }
    }

@@ -105,6 +158,6 @@ constructor(
    }

    private companion object {
        const val TAG = "SecondaryShadeInteractor"
        const val TAG = "ShadeDisplaysInteractor"
    }
}