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

Commit 5986c572 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use reparentToDisplayId to move the shade between displays" into main

parents a17d435d 0b0c24c5
Loading
Loading
Loading
Loading
+6 −32
Original line number Diff line number Diff line
@@ -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))
    }
}
+9 −0
Original line number Diff line number Diff line
@@ -165,6 +165,15 @@ public class NotificationShadeWindowView extends WindowRootView {
        return handled;
    }

    @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);
+17 −1
Original line number Diff line number Diff line
@@ -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
@@ -78,6 +79,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
@@ -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 {
+10 −27
Original line number Diff line number Diff line
@@ -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 {
+6 −7
Original line number Diff line number Diff line
@@ -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,
        )