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

Commit 4ed00d31 authored by Orhan Uysal's avatar Orhan Uysal
Browse files

Add enterDesktopWithDrag scenario.

Implement
- EnterDesktopWithDrag scenario
- EnterDesktopWithDragLandscape FaaS test
- DesktopModeUtils

Test: atest EnterDesktopWithDragLandscape
Bug: 316318390
Change-Id: I76d73764ca68147c53efafce1b331e18481f3325
parent 0471058f
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > WM Shell > Freeform
# Bug component: 929241

uysalorhan@google.com
pragyabajoria@google.com
 No newline at end of file
+74 −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.wm.shell.flicker.service.desktopmode.flicker

import android.tools.Rotation
import android.tools.flicker.AssertionInvocationGroup
import android.tools.flicker.FlickerConfig
import android.tools.flicker.annotation.ExpectedScenarios
import android.tools.flicker.annotation.FlickerConfigProvider
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd
import android.tools.flicker.config.AssertionTemplates
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerConfigEntry
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.config.ScenarioId
import android.tools.flicker.config.desktopmode.Components
import android.tools.flicker.extractors.ITransitionMatcher
import android.tools.flicker.extractors.ShellTransitionScenarioExtractor
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
import android.tools.traces.wm.Transition
import android.tools.traces.wm.TransitionType
import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(FlickerServiceJUnit4ClassRunner::class)
class EnterDesktopWithDragLandscape : EnterDesktopWithDrag(Rotation.ROTATION_90) {
    @ExpectedScenarios(["END_DRAG_TO_DESKTOP"]) @Test override fun enterDesktopWithDrag() =
        super.enterDesktopWithDrag()

    companion object {
        private val END_DRAG_TO_DESKTOP = FlickerConfigEntry(
            scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"),
            extractor = ShellTransitionScenarioExtractor(
                transitionMatcher = object : ITransitionMatcher {
                    override fun findAll(
                        transitions: Collection<Transition>
                    ): Collection<Transition> {
                        return transitions.filter {
                            it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP}
                    }
                }),
            assertions = AssertionTemplates.COMMON_ASSERTIONS +
                    listOf(
                        AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP),
                        AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP),
                        AppWindowHasDesktopModeInitialBoundsAtTheEnd(Components.DESKTOP_MODE_APP)
                    ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
        )

        @JvmStatic
        @FlickerConfigProvider
        fun flickerConfigProvider(): FlickerConfig =
            FlickerConfig()
                    .use(FlickerServiceConfig.DEFAULT)
                    .use(END_DRAG_TO_DESKTOP)
    }
}
+74 −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.wm.shell.flicker.service.desktopmode.flicker

import android.tools.Rotation
import android.tools.flicker.AssertionInvocationGroup
import android.tools.flicker.FlickerConfig
import android.tools.flicker.annotation.ExpectedScenarios
import android.tools.flicker.annotation.FlickerConfigProvider
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd
import android.tools.flicker.config.AssertionTemplates
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerConfigEntry
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.config.ScenarioId
import android.tools.flicker.config.desktopmode.Components
import android.tools.flicker.extractors.ITransitionMatcher
import android.tools.flicker.extractors.ShellTransitionScenarioExtractor
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
import android.tools.traces.wm.Transition
import android.tools.traces.wm.TransitionType
import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(FlickerServiceJUnit4ClassRunner::class)
class EnterDesktopWithDragPortrait : EnterDesktopWithDrag(Rotation.ROTATION_0) {
    @ExpectedScenarios(["END_DRAG_TO_DESKTOP"]) @Test override fun enterDesktopWithDrag() =
        super.enterDesktopWithDrag()

    companion object {
        private val END_DRAG_TO_DESKTOP = FlickerConfigEntry(
            scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"),
            extractor = ShellTransitionScenarioExtractor(
                transitionMatcher = object : ITransitionMatcher {
                    override fun findAll(
                        transitions: Collection<Transition>
                    ): Collection<Transition> {
                        return transitions.filter {
                            it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP}
                    }
                }),
            assertions = AssertionTemplates.COMMON_ASSERTIONS +
                    listOf(
                        AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP),
                        AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP),
                        AppWindowHasDesktopModeInitialBoundsAtTheEnd(Components.DESKTOP_MODE_APP)
                    ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
        )

        @JvmStatic
        @FlickerConfigProvider
        fun flickerConfigProvider(): FlickerConfig =
            FlickerConfig()
                    .use(FlickerServiceConfig.DEFAULT)
                    .use(END_DRAG_TO_DESKTOP)
    }
}
+63 −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.wm.shell.flicker.service.desktopmode.scenarios

import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.DesktopModeUtils
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test

@Ignore("Base Test Class")
abstract class EnterDesktopWithDrag
@JvmOverloads
constructor(val rotation: Rotation = Rotation.ROTATION_0) {

    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
    private val tapl = LauncherInstrumentation()
    private val wmHelper = WindowManagerStateHelper(instrumentation)
    private val device = UiDevice.getInstance(instrumentation)
    private val testApp = SimpleAppHelper(instrumentation)

    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)

    @Before
    fun setup() {
        tapl.setEnableRotation(true)
        tapl.setExpectedRotation(rotation.value)
    }

    @Test
    open fun enterDesktopWithDrag() {
        DesktopModeUtils.enterDesktopWithDrag(wmHelper, device, testApp)
    }

    @After
    fun teardown() {
        testApp.exit(wmHelper)
    }
}
+112 −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.wm.shell.flicker.utils

import android.tools.device.apphelpers.StandardAppHelper
import android.tools.helpers.SYSTEMUI_PACKAGE
import android.tools.traces.component.IComponentMatcher
import android.tools.traces.parsers.WindowManagerStateHelper
import android.tools.traces.wm.WindowingMode
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until

/**
 * Provides a collection of utility functions for desktop mode testing.
 */
object DesktopModeUtils {
    private const val TIMEOUT_MS = 3_000L
    private const val CAPTION = "desktop_mode_caption"
    private const val CAPTION_HANDLE = "caption_handle"
    private const val MAXIMIZE_BUTTON = "maximize_button_view"

    private val captionFullscreen: BySelector
        get() = By.res(SYSTEMUI_PACKAGE, CAPTION)
    private val captionHandle: BySelector
        get() = By.res(SYSTEMUI_PACKAGE, CAPTION_HANDLE)
    private val maximizeButton: BySelector
        get() = By.res(SYSTEMUI_PACKAGE, MAXIMIZE_BUTTON)

    /**
     * Wait for an app moved to desktop to finish its transition.
     */
    private fun waitForAppToMoveToDesktop(
        wmHelper: WindowManagerStateHelper,
        currentApp: IComponentMatcher,
    ) {
        wmHelper
            .StateSyncBuilder()
            .withWindowSurfaceAppeared(currentApp)
            .withFreeformApp(currentApp)
            .withAppTransitionIdle()
            .waitForAndVerify()
    }

    /**
     * Click maximise button on the app header for the given app.
     */
    fun maximiseDesktopApp(
        wmHelper: WindowManagerStateHelper,
        device: UiDevice,
        currentApp: StandardAppHelper
    ) {
        if (wmHelper.getWindow(currentApp)?.windowingMode
            != WindowingMode.WINDOWING_MODE_FREEFORM.value)
            error("expected a freeform window to maximise but window is not in freefrom mode")

        val maximizeButton =
            device.wait(Until.findObject(maximizeButton), TIMEOUT_MS)
                ?: error("Unable to find view $maximizeButton\n")
        maximizeButton.click()
    }

    /**
     * Move an app to Desktop by dragging the app handle at the top.
     */
    fun enterDesktopWithDrag(
        wmHelper: WindowManagerStateHelper,
        device: UiDevice,
        currentApp: StandardAppHelper,
    ) {
        currentApp.launchViaIntent(wmHelper)
        dragToDesktop(wmHelper, currentApp, device)
        waitForAppToMoveToDesktop(wmHelper, currentApp)
    }

    private fun dragToDesktop(
        wmHelper: WindowManagerStateHelper,
        currentApp: StandardAppHelper,
        device: UiDevice
    ) {
        val windowRect = wmHelper.getWindowRegion(currentApp).bounds
        val startX = windowRect.centerX()

        // Start dragging a little under the top to prevent dragging the notification shade.
        val startY = 10

        val displayRect =
            wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
                ?: throw IllegalStateException("Default display is null")

        // The position we want to drag to
        val endY = displayRect.centerY() / 2

        // drag the window to move to desktop
        device.drag(startX, startY, startX, endY, 100)
    }
}