• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm.flicker.helpers
18 
19 import android.app.Instrumentation
20 import android.content.Intent
21 import android.graphics.Region
22 import android.media.session.MediaController
23 import android.media.session.MediaSessionManager
24 import android.tools.device.apphelpers.BasePipAppHelper
25 import android.tools.helpers.FIND_TIMEOUT
26 import android.tools.helpers.SYSTEMUI_PACKAGE
27 import android.tools.traces.ConditionsFactory
28 import android.tools.traces.component.ComponentNameMatcher
29 import android.tools.traces.component.IComponentMatcher
30 import android.tools.traces.parsers.WindowManagerStateHelper
31 import android.tools.traces.parsers.toFlickerComponent
32 import androidx.test.uiautomator.By
33 import androidx.test.uiautomator.Until
34 import com.android.server.wm.flicker.testapp.ActivityOptions
35 
36 open class PipAppHelper(
37     instrumentation: Instrumentation,
38     appName: String = ActivityOptions.Pip.LABEL,
39     componentNameMatcher: ComponentNameMatcher = ActivityOptions.Pip.COMPONENT.toFlickerComponent(),
40 ) : BasePipAppHelper(instrumentation, appName, componentNameMatcher) {
41     private val mediaSessionManager: MediaSessionManager
42         get() =
43             context.getSystemService(MediaSessionManager::class.java)
44                 ?: error("Could not get MediaSessionManager")
45 
46     private val mediaController: MediaController?
47         get() =
48             mediaSessionManager.getActiveSessions(null).firstOrNull {
49                 it.packageName == packageName
50             }
51 
52     /**
53      * Launches the app through an intent instead of interacting with the launcher and waits until
54      * the app window is in PIP mode
55      */
56     @JvmOverloads
57     fun launchViaIntentAndWaitForPip(
58         wmHelper: WindowManagerStateHelper,
59         launchedAppComponentMatcherOverride: IComponentMatcher? = null,
60         action: String? = null,
61         stringExtras: Map<String, String>
62     ) {
63         launchViaIntent(
64             wmHelper,
65             launchedAppComponentMatcherOverride,
66             action,
67             stringExtras
68         )
69 
70         wmHelper
71             .StateSyncBuilder()
72             .withWindowSurfaceAppeared(this)
73             .add(ConditionsFactory.isWMStateComplete())
74             .withPipShown()
75             .waitForAndVerify()
76     }
77 
78     /** Expand the PIP window back to original task via intent and wait until the app is visible */
79     open fun exitPipToOriginalTaskViaIntent(wmHelper: WindowManagerStateHelper) =
80         launchViaIntent(wmHelper)
81 
82     fun changeAspectRatio(wmHelper: WindowManagerStateHelper) {
83         val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO")
84         context.sendBroadcast(intent)
85         // Wait on WMHelper on size change upon aspect ratio change
86         val windowRect = getWindowRect(wmHelper)
87         wmHelper
88             .StateSyncBuilder()
89             .add("pipAspectRatioChanged") {
90                 val pipAppWindow =
91                     it.wmState.visibleWindows.firstOrNull { window ->
92                         this.windowMatchesAnyOf(window)
93                     }
94                         ?: return@add false
95                 val pipRegion = pipAppWindow.frameRegion
96                 return@add pipRegion != Region(windowRect)
97             }
98             .waitForAndVerify()
99     }
100 
101     fun clickEnterPipButton(wmHelper: WindowManagerStateHelper) {
102         clickObject(ENTER_PIP_BUTTON_ID)
103 
104         // Wait on WMHelper or simply wait for 3 seconds
105         wmHelper.StateSyncBuilder().withPipShown().waitForAndVerify()
106         // when entering pip, the dismiss button is visible at the start. to ensure the pip
107         // animation is complete, wait until the pip dismiss button is no longer visible.
108         // b/176822698: dismiss-only state will be removed in the future
109         uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT)
110     }
111 
112     fun enableEnterPipOnUserLeaveHint() {
113         clickObject(ENTER_PIP_ON_USER_LEAVE_HINT)
114     }
115 
116     fun enableAutoEnterForPipActivity() {
117         clickObject(ENTER_PIP_AUTOENTER)
118     }
119 
120     fun clickStartMediaSessionButton() {
121         clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID)
122     }
123 
124     fun setSourceRectHint() {
125         clickObject(SOURCE_RECT_HINT)
126     }
127 
128     fun checkWithCustomActionsCheckbox() =
129         uiDevice
130             .findObject(By.res(packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID))
131             ?.takeIf { it.isCheckable }
132             ?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) }
133             ?: error("'With custom actions' checkbox not found")
134 
135     fun pauseMedia() =
136         mediaController?.transportControls?.pause() ?: error("No active media session found")
137 
138     fun stopMedia() =
139         mediaController?.transportControls?.stop() ?: error("No active media session found")
140 
141     @Deprecated(
142         "Use PipAppHelper.closePipWindow(wmHelper) instead",
143         ReplaceWith("closePipWindow(wmHelper)")
144     )
145     open fun closePipWindow() {
146         closePipWindow(WindowManagerStateHelper(instrumentation))
147     }
148 
149     companion object {
150         private const val TAG = "PipAppHelper"
151         private const val ENTER_PIP_BUTTON_ID = "enter_pip"
152         private const val WITH_CUSTOM_ACTIONS_BUTTON_ID = "with_custom_actions"
153         private const val MEDIA_SESSION_START_RADIO_BUTTON_ID = "media_session_start"
154         private const val ENTER_PIP_ON_USER_LEAVE_HINT = "enter_pip_on_leave_manual"
155         private const val ENTER_PIP_AUTOENTER = "enter_pip_on_leave_autoenter"
156         private const val SOURCE_RECT_HINT = "set_source_rect_hint"
157     }
158 }