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

Commit 7a9b93d4 authored by Charles Chen's avatar Charles Chen
Browse files

Verify enter bubble via overflow

Test: atest EnterBubbleViaOverflowMenuTest
Bug: 396020056
Flag: TEST_ONLY

Change-Id: I61735a69cb865a5257d949532096553e3628764c
parent 1dec94cb
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ class DismissExpandedBubbleViaBubbleViewTest(navBar: NavBar) : BubbleFlickerTest
    companion object : FlickerPropertyInitializer() {
        private val recordTraceWithTransitionRule = RecordTraceWithTransitionRule(
            setUpBeforeTransition = { launchBubbleViaBubbleMenu(testApp, tapl, wmHelper) },
            transition = { dismissBubbleAppViaBubbleView(uiDevice, wmHelper) },
            transition = { dismissBubbleAppViaBubbleView(wmHelper) },
            tearDownAfterTransition = { testApp.exit() }
        )
    }
+117 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.bubbles

import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresFlagsEnabled
import android.tools.NavBar
import android.tools.device.apphelpers.MessagingAppHelper
import android.tools.traces.component.ComponentNameMatcher.Companion.BUBBLE
import android.tools.traces.component.ComponentNameMatcher.Companion.LAUNCHER
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.Flags
import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.bubbles.testcase.BubbleAlwaysVisibleTestCases
import com.android.wm.shell.flicker.bubbles.testcase.BubbleAppBecomesExpandedTestCases
import com.android.wm.shell.flicker.bubbles.utils.ApplyPerParameterRule
import com.android.wm.shell.flicker.bubbles.utils.BubbleFlickerTestHelper.dismissBubbleAppViaBubbleView
import com.android.wm.shell.flicker.bubbles.utils.BubbleFlickerTestHelper.launchBubbleViaBubbleMenu
import com.android.wm.shell.flicker.bubbles.utils.BubbleFlickerTestHelper.launchBubbleViaOverflow
import com.android.wm.shell.flicker.bubbles.utils.FlickerPropertyInitializer
import com.android.wm.shell.flicker.bubbles.utils.RecordTraceWithTransitionRule
import org.junit.FixMethodOrder
import org.junit.Rule
import org.junit.runners.MethodSorters

/**
 * Test enter bubble via clicking the overflow view in the overflow page.
 *
 * To run this test: `atest WMShellExplicitFlickerTestsBubbles:EnterBubbleViaOverflowMenuTest`
 *
 * Pre-steps:
 * ```
 *     Launch [testApp] into bubble and dismiss. -> It's to make [testApp] shown in overflow page.
 *     Launch [messageApp] into bubble.
 * ```
 *
 * Actions:
 * ```
 *     Switch to the overflow page
 *     Launch [testApp] into bubble again by clicking the overflow view
 * ```
 *
 * Verified tests:
 * - [BubbleFlickerTestBase]
 * - [BubbleAlwaysVisibleTestCases]
 * - [BubbleAppBecomesExpandedTestCases]
 */
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE)
@RequiresDevice
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Presubmit
class EnterBubbleViaOverflowMenuTest(navBar: NavBar) : BubbleFlickerTestBase(),
    BubbleAlwaysVisibleTestCases, BubbleAppBecomesExpandedTestCases {

    companion object : FlickerPropertyInitializer() {
        private val messageApp = MessagingAppHelper()

        private val recordTraceWithTransitionRule = RecordTraceWithTransitionRule(
            setUpBeforeTransition = {
                // Launch and dismiss a bubble app to make it show in overflow.
                launchBubbleViaBubbleMenu(testApp, tapl, wmHelper)
                dismissBubbleAppViaBubbleView(wmHelper)
                // Launch message app to bubble to make overflow show.
                launchBubbleViaBubbleMenu(messageApp, tapl, wmHelper)
            },
            transition = { launchBubbleViaOverflow(testApp, wmHelper) },
            tearDownAfterTransition = {
                testApp.exit()
                messageApp.exit()
            }
        )
    }

    @get:Rule
    val setUpRule = ApplyPerParameterRule(
        Utils.testSetupRule(navBar).around(recordTraceWithTransitionRule),
        params = arrayOf(navBar)
    )

    override val traceDataReader
        get() = recordTraceWithTransitionRule.reader

    override fun focusChanges() {
        eventLogSubject.focusChanges(
            messageApp.toWindowName(),
            // Switch to the overflow page
            BUBBLE.toWindowName(),
            // Launch the test app to bubble
            testApp.toWindowName()
        )
    }

    override fun appWindowReplacesLauncherAsTopWindow() {
        wmTraceSubject
            // Before clicking the overflow, the focused app is messageApp.
            .isAppWindowOnTop(messageApp)
            .then()
            .isAppWindowOnTop(LAUNCHER)
            .then()
            .isAppWindowOnTop(testApp)
            .forAllEntries()
    }
}
 No newline at end of file
+67 −25
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.wm.shell.flicker.bubbles.utils

import android.graphics.Point
import android.platform.systemui_tapl.ui.Bubble
import android.platform.systemui_tapl.ui.Root
import android.tools.device.apphelpers.BrowserAppHelper
@@ -27,14 +26,17 @@ import android.tools.device.apphelpers.MessagingAppHelper
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.traces.ConditionsFactory
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.annotation.IntRange
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.android.launcher3.tapl.AppIcon
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import com.google.common.truth.Truth.assertWithMessage

/**
@@ -53,10 +55,39 @@ internal object BubbleFlickerTestHelper {
        testApp: StandardAppHelper,
        tapl: LauncherInstrumentation,
        wmHelper: WindowManagerStateHelper,
        @IntRange(from = FIRST_APP_ICON_SOURCE.toLong(), to = LAST_APP_ICON_SOURCE.toLong())
        fromSource: Int = FROM_ALL_APPS,
    ) {
        val appName = testApp.appName
        // Go to all apps to launch app into a bubble.
        tapl.goHome().switchToAllApps()
        launchAndWaitForBubbleAppExpanded(testApp, tapl, wmHelper)
        val appIcon = when (fromSource) {
            FROM_ALL_APPS -> tapl.goHome().switchToAllApps().getAppIcon(appName)
            FROM_TASK_BAR -> {
                SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, appName)
                val overview = tapl.goHome().switchToOverview()
                val taskBar = overview.taskbar ?: error("Can't find TaskBar")
                taskBar.getAppIcon(testApp.appName)
            }
            FROM_HOME_SCREEN -> {
                val workspace = tapl.workspace
                val homeScreenIcon = workspace.tryGetWorkspaceAppIcon(testApp.appName)
                if (homeScreenIcon != null) {
                    // If there's an icon on the homeScreen, just use it.
                    homeScreenIcon
                } else {
                    // Here we do a trick:
                    // We move the app icon from all apps to hotseat, and then drag it to a new
                    // created empty page of home screen.
                    SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, appName)
                    val hotseatIcon = workspace.getHotseatAppIcon(appName)
                    val pageDelta = workspace.pageCount - workspace.currentPage
                    workspace.dragIcon(hotseatIcon, pageDelta)
                    workspace.getWorkspaceAppIcon(appName)
                }
            }
            else -> error("Unknown fromSource: $fromSource")
        }
        launchAndWaitForBubbleAppExpanded(testApp, appIcon, wmHelper)
    }

    /**
@@ -83,6 +114,20 @@ internal object BubbleFlickerTestHelper {
            .that(Root.get().bubble.isEducationVisible).isFalse()
    }

    /**
     * Launch bubble via clicking the overflow view.
     *
     * @param testApp the test app to launch into bubble
     * @param wmHelper the [WindowManagerStateHelper]
     */
    fun launchBubbleViaOverflow(testApp: StandardAppHelper, wmHelper: WindowManagerStateHelper) {
        val overflow = Root.get().expandedBubbleStack.openOverflow()
        overflow.verifyHasBubbles()
        overflow.openBubble()

        waitAndAssertBubbleAppInExpandedState(testApp, wmHelper)
    }

    /**
     * Collapses the bubble app [testApp] via back key.
     *
@@ -166,10 +211,9 @@ internal object BubbleFlickerTestHelper {
    /**
     * Dismisses the bubble app via dragging the bubble to dismiss view.
     *
     * @param uiDevice the UI automator to get the bubble view [UiObject2]
     * @param wmHelper the [WindowManagerStateHelper]
     */
    fun dismissBubbleAppViaBubbleView(uiDevice: UiDevice, wmHelper: WindowManagerStateHelper) {
    fun dismissBubbleAppViaBubbleView(wmHelper: WindowManagerStateHelper) {
        // Checks bubble is showing.
        wmHelper
            .StateSyncBuilder()
@@ -179,9 +223,7 @@ internal object BubbleFlickerTestHelper {
            .waitForAndVerify()

        // Drag the bubble icon to the position of dismiss view to dismiss bubble app.
        uiDevice.bubbleIcon?.run {
            drag(Point(uiDevice.displayWidth / 2, uiDevice.displayHeight), 1000)
        }
        Root.get().expandedBubbleStack.bubbles[0].dismiss()

        waitAndAssertBubbleAppDismissed(wmHelper)
    }
@@ -215,22 +257,17 @@ internal object BubbleFlickerTestHelper {
            .waitForAndVerify()
    }

    /**
     * Launches as many bubble apps as a bubble stack or a bubble bar can contain and collapse.
     *
     * @param tapl the [LauncherInstrumentation]
     * @param wmHelper the [WindowManagerStateHelper]
     * @return the [Bubble] icon objects of the launched bubble apps
     */
    fun launchMultipleBubbleAppsViaBubbleMenuAndCollapse(
        tapl: LauncherInstrumentation,
        wmHelper: WindowManagerStateHelper,
    ): List<Bubble> {
        // Go to all apps to launch app into a bubble.
        tapl.goHome().switchToAllApps()
        val allApps = tapl.allApps

        bubbleApps.forEach { testApp ->
            launchAndWaitForBubbleAppExpanded(testApp, tapl, wmHelper)
            val appIcon = allApps.getAppIcon(testApp.appName)
            launchAndWaitForBubbleAppExpanded(testApp, appIcon, wmHelper)
            if (testApp != bubbleApps.last()) {
                Root.get().expandedBubbleStack.closeByClickingOutside()
            }
@@ -283,22 +320,17 @@ internal object BubbleFlickerTestHelper {

    private fun launchAndWaitForBubbleAppExpanded(
        testApp: StandardAppHelper,
        tapl: LauncherInstrumentation,
        appIcon: AppIcon,
        wmHelper: WindowManagerStateHelper,
    ) {
        val allApps = tapl.allApps
        val simpleAppIcon = allApps.getAppIcon(testApp.appName)
        // Open the bubble menu and click.
        simpleAppIcon.openMenu().bubbleMenuItem.click()
        appIcon.openMenu().bubbleMenuItem.click()

        waitAndAssertBubbleAppInExpandedState(testApp, wmHelper)

        // Don't check bubble icons if the testApp is IME because IME occludes the overflow.
        if (testApp !is ImeAppHelper) {
        assertWithMessage("The education must not show for Application bubble")
            .that(Root.get().bubble.isEducationVisible).isFalse()
    }
    }

    private fun waitAndAssertBubbleAppInExpandedState(
        testApp: StandardAppHelper,
@@ -352,6 +384,16 @@ internal object BubbleFlickerTestHelper {
    private fun UiDevice.launcherSelector(resourcesId: String): BySelector =
        By.pkg(launcherPackageName).res(launcherPackageName, resourcesId)

    /** Launches the bubble from all apps page. */
    const val FROM_ALL_APPS = 0
    /** Launches the bubble from home screen page. */
    const val FROM_HOME_SCREEN = 1
    /** Launches the bubble from the task bar. */
    const val FROM_TASK_BAR = 2

    private const val FIRST_APP_ICON_SOURCE = FROM_ALL_APPS
    private const val LAST_APP_ICON_SOURCE = FROM_TASK_BAR

    private const val FIND_OBJECT_TIMEOUT = 4000L
    private const val SYSUI_PACKAGE = "com.android.systemui"
    private const val RES_ID_BUBBLE_VIEW = "bubble_view"