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

Commit 39bf0644 authored by Orhan Uysal's avatar Orhan Uysal Committed by Android (Google) Code Review
Browse files

Merge "Add enterDesktopWithDrag scenario." into main

parents 81b273a7 4ed00d31
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)
    }
}