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

Commit f6613ab4 authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "Verify enter bubble via overflow" into main

parents 3037832e 7a9b93d4
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"