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

Commit bae6c332 authored by Nicolò Mazzucato's avatar Nicolò Mazzucato Committed by Android (Google) Code Review
Browse files

Merge changes I01abbef2,Ib1070f30 into main

* changes:
  Introduce ShadeDisplayPolicy
  Enable WindowContext to move to another display
parents 8caa04ec 3fa30d35
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -94,6 +94,23 @@ public class WindowContext extends ContextWrapper implements WindowProvider {
        mController.attachToDisplayArea(mType, getDisplayId(), mOptions);
    }

    /**
     * Updates this context to a new displayId.
     * <p>
     * Note that this doesn't re-parent previously attached windows (they should be removed and
     * re-added manually after this is called). Resources associated with this context will have
     * the correct value and configuration for the new display after this is called.
     */
    @Override
    public void updateDisplay(int displayId) {
        if (displayId == getDisplayId()) {
            return;
        }
        super.updateDisplay(displayId);
        mController.detachIfNeeded();
        mController.attachToDisplayArea(mType, displayId, mOptions);
    }

    @Override
    public Object getSystemService(String name) {
        if (WINDOW_SERVICE.equals(name)) {
+33 −0
Original line number Diff line number Diff line
@@ -29,6 +29,11 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import android.app.Activity;
import android.app.EmptyActivity;
@@ -60,6 +65,8 @@ import androidx.test.rule.ActivityTestRule;

import com.android.frameworks.coretests.R;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -88,9 +95,21 @@ public class WindowContextTest {
    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
    private final WindowContext mWindowContext = createWindowContext();
    private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService();
    private WindowTokenClientController mOriginalWindowTokenClientController;

    private static final int TIMEOUT_IN_SECONDS = 4;

    @Before
    public void setUp() {
        // Keeping the original to set it back after each test, in case they applied any override.
        mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
    }

    @After
    public void tearDown() {
        WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
    }

    @Test
    public void testCreateWindowContextWindowManagerAttachClientToken() {
        final WindowManager windowContextWm = WindowManagerImpl
@@ -320,6 +339,20 @@ public class WindowContextTest {
        }
    }

    @Test
    public void updateDisplay_wasAttached_detachThenAttachedPropagatedToTokenController() {
        final WindowTokenClientController mockWindowTokenClientController =
                mock(WindowTokenClientController.class);
        WindowTokenClientController.overrideForTesting(mockWindowTokenClientController);

        mWindowContext.updateDisplay(DEFAULT_DISPLAY + 1);

        verify(mockWindowTokenClientController).detachIfNeeded(any());
        verify(mockWindowTokenClientController).attachToDisplayArea(any(),
                anyInt(), /* displayId= */ eq(DEFAULT_DISPLAY + 1),
                any());
    }

    private WindowContext createWindowContext() {
        return createWindowContext(TYPE_APPLICATION_OVERLAY);
    }
+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.shade.data.repository

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.shade.display.SpecificDisplayIdPolicy
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class ShadeDisplaysRepositoryTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val defaultPolicy = SpecificDisplayIdPolicy(0)

    private val shadeDisplaysRepository =
        ShadeDisplaysRepositoryImpl(defaultPolicy, testScope.backgroundScope)

    @Test
    fun policy_changing_propagatedFromTheLatestPolicy() =
        testScope.runTest {
            val displayIds by collectValues(shadeDisplaysRepository.displayId)
            val policy1 = MutablePolicy()
            val policy2 = MutablePolicy()

            assertThat(displayIds).containsExactly(0)

            shadeDisplaysRepository.policy.value = policy1

            policy1.sendDisplayId(1)

            assertThat(displayIds).containsExactly(0, 1)

            policy1.sendDisplayId(2)

            assertThat(displayIds).containsExactly(0, 1, 2)

            shadeDisplaysRepository.policy.value = policy2

            assertThat(displayIds).containsExactly(0, 1, 2, 0)

            policy1.sendDisplayId(4)

            // Changes to the first policy don't affect the output now
            assertThat(displayIds).containsExactly(0, 1, 2, 0)

            policy2.sendDisplayId(5)

            assertThat(displayIds).containsExactly(0, 1, 2, 0, 5)
        }

    private class MutablePolicy : ShadeDisplayPolicy {
        fun sendDisplayId(id: Int) {
            _displayId.value = id
        }

        private val _displayId = MutableStateFlow(0)
        override val name: String
            get() = "mutable_policy"

        override val displayId: StateFlow<Int>
            get() = _displayId
    }
}
+56 −3
Original line number Diff line number Diff line
@@ -23,12 +23,17 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.shade.ShadePrimaryDisplayCommand
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.testKosmos
import com.google.common.truth.StringSubject
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -37,15 +42,26 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val commandRegistry = kosmos.commandRegistry
    private val displayRepository = kosmos.displayRepository
    private val shadeDisplaysRepository = ShadeDisplaysRepositoryImpl()
    private val defaultPolicy = kosmos.defaultShadeDisplayPolicy
    private val policy1 = makePolicy("policy_1")
    private val shadeDisplaysRepository = kosmos.shadeDisplaysRepository
    private val pw = PrintWriter(StringWriter())

    private val policies =
        setOf(defaultPolicy, policy1, makePolicy("policy_2"), makePolicy("policy_3"))

    private val underTest =
        ShadePrimaryDisplayCommand(commandRegistry, displayRepository, shadeDisplaysRepository)
        ShadePrimaryDisplayCommand(
            commandRegistry,
            displayRepository,
            shadeDisplaysRepository,
            policies,
            defaultPolicy,
        )

    @Before
    fun setUp() {
@@ -96,4 +112,41 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() {

            assertThat(displayId).isEqualTo(newDisplayId)
        }

    @Test
    fun policies_listsAllPolicies() =
        testScope.runTest {
            val stringWriter = StringWriter()
            commandRegistry.onShellCommand(
                PrintWriter(stringWriter),
                arrayOf("shade_display_override", "policies"),
            )
            val result = stringWriter.toString()

            assertThat(result).containsAllIn(policies.map { it.name })
        }

    @Test
    fun policies_setsSpecificPolicy() =
        testScope.runTest {
            val policy by collectLastValue(shadeDisplaysRepository.policy)

            commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", policy1.name))

            assertThat(policy!!.name).isEqualTo(policy1.name)
        }

    private fun makePolicy(policyName: String): ShadeDisplayPolicy {
        return object : ShadeDisplayPolicy {
            override val name: String
                get() = policyName

            override val displayId: StateFlow<Int>
                get() = MutableStateFlow(0)
        }
    }
}

private fun StringSubject.containsAllIn(strings: List<String>) {
    strings.forEach { contains(it) }
}
+19 −46
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
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
@@ -30,7 +29,6 @@ import com.android.systemui.display.data.repository.FakeDisplayWindowPropertiesR
import com.android.systemui.display.shared.model.DisplayWindowProperties
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.data.repository.FakeShadeDisplayRepository
import com.android.systemui.statusbar.phone.ConfigurationForwarder
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -39,6 +37,7 @@ 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.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
@@ -53,13 +52,10 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {

    private val shadeRootview = mock<WindowRootView>()
    private val positionRepository = FakeShadeDisplayRepository()
    private val defaultContext = mock<Context>()
    private val secondaryContext = mock<Context>()
    private val shadeContext = mock<Context>()
    private val contextStore = FakeDisplayWindowPropertiesRepository()
    private val testScope = TestScope(UnconfinedTestDispatcher())
    private val configurationForwarder = mock<ConfigurationForwarder>()
    private val defaultWm = mock<WindowManager>()
    private val secondaryWm = mock<WindowManager>()
    private val shadeWm = mock<WindowManager>()
    private val resources = mock<Resources>()
    private val configuration = mock<Configuration>()
    private val display = mock<Display>()
@@ -68,11 +64,9 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
        ShadeDisplaysInteractor(
            Optional.of(shadeRootview),
            positionRepository,
            MutableContextWrapper(defaultContext),
            resources,
            contextStore,
            shadeContext,
            shadeWm,
            testScope.backgroundScope,
            configurationForwarder,
            testScope.backgroundScope.coroutineContext,
        )

@@ -83,28 +77,15 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {

        whenever(resources.configuration).thenReturn(configuration)

        whenever(defaultContext.displayId).thenReturn(0)
        whenever(defaultContext.getSystemService(any())).thenReturn(defaultWm)
        whenever(defaultContext.resources).thenReturn(resources)
        whenever(shadeContext.displayId).thenReturn(0)
        whenever(shadeContext.getSystemService(any())).thenReturn(shadeWm)
        whenever(shadeContext.resources).thenReturn(resources)
        contextStore.insert(
            DisplayWindowProperties(
                displayId = 0,
                windowType = TYPE_NOTIFICATION_SHADE,
                context = defaultContext,
                windowManager = defaultWm,
                layoutInflater = mock(),
            )
        )

        whenever(secondaryContext.displayId).thenReturn(1)
        whenever(secondaryContext.getSystemService(any())).thenReturn(secondaryWm)
        whenever(secondaryContext.resources).thenReturn(resources)
        contextStore.insert(
            DisplayWindowProperties(
                displayId = 1,
                windowType = TYPE_NOTIFICATION_SHADE,
                context = secondaryContext,
                windowManager = secondaryWm,
                context = shadeContext,
                windowManager = shadeWm,
                layoutInflater = mock(),
            )
        )
@@ -117,8 +98,7 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
        interactor.start()
        testScope.advanceUntilIdle()

        verifyNoMoreInteractions(defaultWm)
        verifyNoMoreInteractions(secondaryWm)
        verifyNoMoreInteractions(shadeWm)
    }

    @Test
@@ -127,8 +107,10 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
        positionRepository.setDisplayId(1)
        interactor.start()

        verify(defaultWm).removeView(eq(shadeRootview))
        verify(secondaryWm).addView(eq(shadeRootview), any())
        inOrder(shadeWm).apply {
            verify(shadeWm).removeView(eq(shadeRootview))
            verify(shadeWm).addView(eq(shadeRootview), any())
        }
    }

    @Test
@@ -139,18 +121,9 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {

        positionRepository.setDisplayId(1)

        verify(defaultWm).removeView(eq(shadeRootview))
        verify(secondaryWm).addView(eq(shadeRootview), any())
        inOrder(shadeWm).apply {
            verify(shadeWm).removeView(eq(shadeRootview))
            verify(shadeWm).addView(eq(shadeRootview), any())
        }

    @Test
    fun start_shadePositionChanges_newConfigPropagated() {
        whenever(display.displayId).thenReturn(0)
        positionRepository.setDisplayId(0)
        interactor.start()

        positionRepository.setDisplayId(1)

        verify(configurationForwarder).onConfigurationChanged(eq(configuration))
    }
}
Loading