1/ Add test apps from WM shell package into flicker

Add both apps to the same package to unify all test apps and helpers into a single reusable place

Test: atest FlickerLibTest && atest FlickerTests
Change-Id: I9fa18eb31b50c4e6869352250be0c89a9acb0be8
This commit is contained in:
Nataniel Borges
2022-09-19 16:05:15 +00:00
parent 083a6d9aab
commit 9c3d628683
23 changed files with 1505 additions and 169 deletions

View File

@@ -15,41 +15,41 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.wm.flicker.testapp">
package="com.android.server.wm.flicker.testapp">
<uses-sdk android:minSdkVersion="29"
android:targetSdkVersion="29"/>
android:targetSdkVersion="29"/>
<application android:allowBackup="false"
android:supportsRtl="true">
android:supportsRtl="true">
<uses-library android:name="androidx.window.extensions" android:required="false"/>
<activity android:name=".SimpleActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
android:theme="@style/CutoutShortEdges"
android:label="SimpleApp"
android:exported="true">
android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
android:theme="@style/CutoutShortEdges"
android:label="SimpleActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
android:theme="@style/CutoutShortEdges"
android:label="ImeApp"
android:exported="true">
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
android:theme="@style/CutoutShortEdges"
android:label="ImeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ImeActivityAutoFocus"
android:theme="@style/CutoutShortEdges"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
android:windowSoftInputMode="stateVisible"
android:configChanges="orientation|screenSize"
android:label="ImeAppAutoFocus"
android:exported="true">
android:theme="@style/CutoutShortEdges"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
android:windowSoftInputMode="stateVisible"
android:configChanges="orientation|screenSize"
android:label="ImeAppAutoFocus"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -66,34 +66,34 @@
</intent-filter>
</activity>
<activity android:name=".SeamlessRotationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="SeamlessApp"
android:exported="true">
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="SeamlessActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".NonResizeableActivity"
android:theme="@style/CutoutShortEdges"
android:resizeableActivity="false"
android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
android:label="NonResizeableApp"
android:exported="true"
android:showOnLockScreen="true">
android:theme="@style/CutoutShortEdges"
android:resizeableActivity="false"
android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
android:label="NonResizeableActivity"
android:exported="true"
android:showOnLockScreen="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ButtonActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ButtonActivity"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="ButtonActivity"
android:exported="true">
<activity android:name=".LaunchNewActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="LaunchNewActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -111,55 +111,56 @@
</intent-filter>
</activity>
<activity android:name=".DialogThemedActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
android:configChanges="orientation|screenSize"
android:theme="@style/DialogTheme"
android:label="DialogThemedActivity"
android:exported="true">
android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
android:configChanges="orientation|screenSize"
android:theme="@style/DialogTheme"
android:label="DialogThemedActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".PortraitOnlyActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitOnlyActivity"
android:theme="@style/CutoutShortEdges"
android:screenOrientation="portrait"
android:configChanges="orientation|screenSize"
android:exported="true">
android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitOnlyActivity"
android:theme="@style/CutoutShortEdges"
android:screenOrientation="portrait"
android:configChanges="orientation|screenSize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ImeEditorPopupDialogActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity"
android:configChanges="orientation|screenSize"
android:theme="@style/CutoutShortEdges"
android:label="ImeEditorPopupDialogActivity"
android:exported="true">
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity"
android:configChanges="orientation|screenSize"
android:theme="@style/CutoutShortEdges"
android:label="ImeEditorPopupDialogActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ShowWhenLockedActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ShowWhenLockedActivity"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="ShowWhenLockedActivity"
android:showWhenLocked="true"
android:exported="true">
android:taskAffinity="com.android.server.wm.flicker.testapp.ShowWhenLockedActivity"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="ShowWhenLockedActivity"
android:showWhenLocked="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".NotificationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.NotificationActivity"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:exported="true">
android:taskAffinity="com.android.server.wm.flicker.testapp.NotificationActivity"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="NotificationActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -173,8 +174,8 @@
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
@@ -193,42 +194,110 @@
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false"/>
<activity android:name=".MailActivity"
android:exported="true"
android:label="MailApp"
android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity"
android:theme="@style/Theme.AppCompat.Light">
android:exported="true"
android:label="MailActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity"
android:theme="@style/Theme.AppCompat.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".GameActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
android:immersive="true"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="screenSize"
android:label="GameApp"
android:exported="true">
android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
android:immersive="true"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="screenSize"
android:label="GameActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".PipActivity"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:taskAffinity="com.android.server.wm.flicker.testapp.PipActivity"
android:theme="@style/CutoutShortEdges"
android:launchMode="singleTop"
android:label="PipActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".SplitScreenActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity"
android:theme="@style/CutoutShortEdges"
android:label="SplitScreenPrimaryActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".SplitScreenSecondaryActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity"
android:theme="@style/CutoutShortEdges"
android:label="SplitScreenSecondaryActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".SendNotificationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SendNotificationActivity"
android:theme="@style/CutoutShortEdges"
android:label="SendNotificationActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".LaunchBubbleActivity"
android:label="LaunchBubbleActivity"
android:exported="true"
android:theme="@style/CutoutShortEdges"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".BubbleActivity"
android:label="BubbleActivity"
android:exported="false"
android:theme="@style/CutoutShortEdges"
android:resizeableActivity="true"/>
<service
android:name=".AssistantInteractionSessionService"
android:exported="true"
android:permission="android.permission.BIND_VOICE_INTERACTION" />
android:permission="android.permission.BIND_VOICE_INTERACTION"/>
<service
android:name=".AssistantRecognitionService"
android:exported="true"
android:label="Test Voice Interaction Service">
<intent-filter>
<action android:name="android.speech.RecognitionService" />
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.speech.RecognitionService"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data
android:name="android.speech"
android:resource="@xml/recognition_service" />
android:resource="@xml/recognition_service"/>
</service>
<service
android:name=".AssistantInteractionService"
@@ -236,12 +305,12 @@
android:label="Test Voice Interaction Service"
android:permission="android.permission.BIND_VOICE_INTERACTION">
<intent-filter>
<action android:name="android.service.voice.VoiceInteractionService" />
<action android:name="android.service.voice.VoiceInteractionService"/>
</intent-filter>
<meta-data
android:name="android.voice_interaction"
android:resource="@xml/interaction_service" />
android:resource="@xml/interaction_service"/>
</service>
</application>
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7.2,14.4m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
<path
android:fillColor="#FF000000"
android:pathData="M14.8,18m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
<path
android:fillColor="#FF000000"
android:pathData="M15.2,8.8m-4.8,0a4.8,4.8 0,1 1,9.6 0a4.8,4.8 0,1 1,-9.6 0"/>
</vector>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,4c-4.97,0 -9,3.58 -9,8c0,1.53 0.49,2.97 1.33,4.18c0.12,0.18 0.2,0.46 0.1,0.66c-0.33,0.68 -0.79,1.52 -1.38,2.39c-0.12,0.17 0.01,0.41 0.21,0.39c0.63,-0.05 1.86,-0.26 3.38,-0.91c0.17,-0.07 0.36,-0.06 0.52,0.03C8.55,19.54 10.21,20 12,20c4.97,0 9,-3.58 9,-8S16.97,4 12,4zM16.94,11.63l-3.29,3.29c-0.13,0.13 -0.34,0.04 -0.34,-0.14v-1.57c0,-0.11 -0.1,-0.21 -0.21,-0.2c-2.19,0.06 -3.65,0.65 -5.14,1.95c-0.15,0.13 -0.38,0 -0.33,-0.19c0.7,-2.57 2.9,-4.57 5.5,-4.75c0.1,-0.01 0.18,-0.09 0.18,-0.19V8.2c0,-0.18 0.22,-0.27 0.34,-0.14l3.29,3.29C17.02,11.43 17.02,11.55 16.94,11.63z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button_finish"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:text="Finish" />
<Button
android:id="@+id/button_new_task"
android:layout_width="wrap_content"
android:layout_height="46dp"
android:layout_marginStart="8dp"
android:text="New Task" />
<Button
android:id="@+id/button_new_bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="New Bubble" />
<Button
android:id="@+id/button_activity_for_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="Activity For Result" />
</LinearLayout>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/black">
<Button
android:id="@+id/button_create"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Add Bubble" />
<Button
android:id="@+id/button_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/button_create"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="Cancel Bubble" />
<Button
android:id="@+id/button_cancel_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/button_cancel"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="Cancel All Bubble" />
</RelativeLayout>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/black">
<Button
android:id="@+id/button_send_notification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Send Notification" />
</RelativeLayout>

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/holo_blue_bright">
<!-- All the buttons (and other clickable elements) should be arranged in a way so that it is
possible to "cycle" over all them by clicking on the D-Pad DOWN button. The way we do it
here is by arranging them this vertical LL and by relying on the nextFocusDown attribute
where things are arranged differently and to circle back up to the top once we reach the
bottom. -->
<Button
android:id="@+id/enter_pip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter PIP"
android:onClick="enterPip"/>
<CheckBox
android:id="@+id/with_custom_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="With custom actions"/>
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:checkedButton="@id/enter_pip_on_leave_disabled">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter PiP on home press"/>
<RadioButton
android:id="@+id/enter_pip_on_leave_disabled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Disabled"
android:onClick="onAutoPipSelected"/>
<RadioButton
android:id="@+id/enter_pip_on_leave_manual"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Via code behind"
android:onClick="onAutoPipSelected"/>
<RadioButton
android:id="@+id/enter_pip_on_leave_autoenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Auto-enter PiP"
android:onClick="onAutoPipSelected"/>
</RadioGroup>
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:checkedButton="@id/ratio_default">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ratio"/>
<RadioButton
android:id="@+id/ratio_default"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Default"
android:onClick="onRatioSelected"/>
<RadioButton
android:id="@+id/ratio_square"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Square [1:1]"
android:onClick="onRatioSelected"/>
<RadioButton
android:id="@+id/ratio_wide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Wide [2:1]"
android:onClick="onRatioSelected"/>
<RadioButton
android:id="@+id/ratio_tall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tall [1:2]"
android:onClick="onRatioSelected"/>
</RadioGroup>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Media Session"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/media_session_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:nextFocusDown="@id/media_session_stop"
android:text="Start"/>
<Button
android:id="@+id/media_session_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:nextFocusDown="@id/enter_pip"
android:text="Stop"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/holo_green_light">
<TextView
android:id="@+id/SplitScreenTest"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical|center_horizontal"
android:textIsSelectable="true"
android:text="PrimaryActivity"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/holo_blue_light">
<TextView
android:id="@+id/SplitScreenTest"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical|center_horizontal"
android:text="SecondaryActivity"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>

View File

@@ -16,9 +16,6 @@
package com.android.server.wm.flicker.testapp;
import static com.android.server.wm.flicker.testapp.ActivityOptions.ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME;
import static com.android.server.wm.flicker.testapp.ActivityOptions.ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
@@ -39,8 +36,6 @@ public class ActivityEmbeddingMainActivity extends Activity {
private static final String TAG = "ActivityEmbeddingMainActivity";
private static final float DEFAULT_SPLIT_RATIO = 0.5f;
private ActivityEmbeddingComponent mEmbeddingComponent;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -51,20 +46,24 @@ public class ActivityEmbeddingMainActivity extends Activity {
/** R.id.launch_placeholder_split_button onClick */
public void launchPlaceholderSplit(View view) {
startActivity(new Intent().setComponent(
ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME));
startActivity(
new Intent().setComponent(
ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT
)
);
}
private void initializeSplitRules() {
mEmbeddingComponent = ActivityEmbeddingAppHelper.getActivityEmbeddingComponent();
if (mEmbeddingComponent == null) {
ActivityEmbeddingComponent embeddingComponent =
ActivityEmbeddingAppHelper.getActivityEmbeddingComponent();
if (embeddingComponent == null) {
// Embedding not supported
Log.d(TAG, "ActivityEmbedding is not supported on this device");
finish();
return;
}
mEmbeddingComponent.setEmbeddingRules(getSplitRules());
embeddingComponent.setEmbeddingRules(getSplitRules());
}
private Set<EmbeddingRule> getSplitRules() {
@@ -72,10 +71,10 @@ public class ActivityEmbeddingMainActivity extends Activity {
final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule.Builder(
new Intent().setComponent(
ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME),
ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT),
activity -> activity instanceof ActivityEmbeddingPlaceholderPrimaryActivity,
intent -> intent.getComponent().equals(
ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME),
ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT),
windowMetrics -> true)
.setSplitRatio(DEFAULT_SPLIT_RATIO)
.build();

View File

@@ -19,94 +19,181 @@ package com.android.server.wm.flicker.testapp;
import android.content.ComponentName;
public class ActivityOptions {
public static final String EXTRA_STARVE_UI_THREAD = "StarveUiThread";
public static final String FLICKER_APP_PACKAGE = "com.android.server.wm.flicker.testapp";
public static final String SEAMLESS_ACTIVITY_LAUNCHER_NAME = "SeamlessApp";
public static final ComponentName SEAMLESS_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SeamlessRotationActivity");
public static class SimpleActivity {
public static final String LABEL = "SimpleActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SimpleActivity");
}
public static final String IME_ACTIVITY_AUTO_FOCUS_LAUNCHER_NAME = "ImeAppAutoFocus";
public static final ComponentName IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeActivityAutoFocus");
public static class SeamlessRotation {
public static final String LABEL = "SeamlessRotationActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SeamlessRotationActivity");
public static final String IME_ACTIVITY_LAUNCHER_NAME = "ImeActivity";
public static final ComponentName IME_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
public static final String EXTRA_STARVE_UI_THREAD = "StarveUiThread";
}
public static class Ime {
public static class Default {
public static final String LABEL = "ImeActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeActivity");
}
public static final String IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME = "ImeStateInitializeActivity";
public static final ComponentName IME_ACTIVITY_INITIALIZE_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
public static class AutoFocusActivity {
public static final String LABEL = "ImeAppAutoFocus";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeActivityAutoFocus");
}
public static class StateInitializeActivity {
public static final String LABEL = "ImeStateInitializeActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeStateInitializeActivity");
}
public static final String SIMPLE_ACTIVITY_LAUNCHER_NAME = "SimpleApp";
public static final ComponentName SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SimpleActivity");
public static final String NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME = "NonResizeableApp";
public static final ComponentName NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".NonResizeableActivity");
public static final String BUTTON_ACTIVITY_LAUNCHER_NAME = "ButtonApp";
public static final ComponentName BUTTON_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ButtonActivity");
public static final String LAUNCH_NEW_TASK_ACTIVITY_LAUNCHER_NAME = "LaunchNewTaskApp";
public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity";
public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".DialogThemedActivity");
public static final String PORTRAIT_ONLY_ACTIVITY_LAUNCHER_NAME = "PortraitOnlyActivity";
public static final ComponentName PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".PortraitOnlyActivity");
public static final String EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME =
"ImeEditorPopupDialogActivity";
public static final ComponentName EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
public static class EditorPopupDialogActivity {
public static final String LABEL = "ImeEditorPopupDialogActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeEditorPopupDialogActivity");
}
}
public static final String SHOW_WHEN_LOCKED_ACTIVITY_LAUNCHER_NAME = "ShowWhenLockedApp";
public static final ComponentName SHOW_WHEN_LOCKED_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ShowWhenLockedActivity");
public static class NonResizeableActivity {
public static final String LABEL = "NonResizeableActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".NonResizeableActivity");
}
public static final String NOTIFICATION_ACTIVITY_LAUNCHER_NAME = "NotificationApp";
public static final ComponentName NOTIFICATION_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".NotificationActivity");
public static class DialogThemedActivity {
public static final String LABEL = "DialogThemedActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".DialogThemedActivity");
}
public static final String ACTIVITY_EMBEDDING_LAUNCHER_NAME = "ActivityEmbeddingMainActivity";
public static final ComponentName ACTIVITY_EMBEDDING_MAIN_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
public static class PortraitOnlyActivity {
public static final String LABEL = "PortraitOnlyActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".PortraitOnlyActivity");
public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
}
public static class ActivityEmbedding {
public static class MainActivity {
public static final String LABEL = "ActivityEmbeddingMainActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingMainActivity");
public static final ComponentName
ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME = new ComponentName(
FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderPrimaryActivity");
public static final ComponentName
ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME = new ComponentName(
FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity");
}
public static final String MAIL_ACTIVITY_LAUNCHER_NAME = "MailActivity";
public static final ComponentName MAIL_ACTIVITY_COMPONENT_NAME = new ComponentName(
FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".MailActivity");
public static class PlaceholderPrimaryActivity {
public static final String LABEL = "ActivityEmbeddingPlaceholderPrimaryActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderPrimaryActivity");
}
public static final String GAME_ACTIVITY_LAUNCHER_NAME = "GameApp";
public static final ComponentName GAME_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".GameActivity");
public static class PlaceholderSecondaryActivity {
public static final String LABEL = "ActivityEmbeddingPlaceholderSecondaryActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity");
}
}
public static class Notification {
public static final String LABEL = "NotificationActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".NotificationActivity");
}
public static class Mail {
public static final String LABEL = "MailActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".MailActivity");
}
public static class ShowWhenLockedActivity {
public static final String LABEL = "ShowWhenLockedActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ShowWhenLockedActivity");
}
public static class LaunchNewTask {
public static final String LABEL = "LaunchNewTaskActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
}
public static class Game {
public static final String LABEL = "GameActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".GameActivity");
}
public static class LaunchNewActivity {
public static final String LABEL = "LaunchNewActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".LaunchNewActivity");
}
public static class PipActivity {
// Test App > Pip Activity
public static final String LABEL = "PipActivity";
public static final String MENU_ACTION_NO_OP = "No-Op";
public static final String MENU_ACTION_ON = "On";
public static final String MENU_ACTION_OFF = "Off";
public static final String MENU_ACTION_CLEAR = "Clear";
// Intent action that this activity dynamically registers to enter picture-in-picture
public static final String ACTION_ENTER_PIP =
FLICKER_APP_PACKAGE + ".PipActivity.ENTER_PIP";
// Intent action that this activity dynamically registers to set requested orientation.
// Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
public static final String ACTION_SET_REQUESTED_ORIENTATION =
FLICKER_APP_PACKAGE + ".PipActivity.SET_REQUESTED_ORIENTATION";
// Calls enterPictureInPicture() on creation
public static final String EXTRA_ENTER_PIP = "enter_pip";
// Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
public static final String EXTRA_PIP_ORIENTATION = "fixed_orientation";
// Adds a click listener to finish this activity when it is clicked
public static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".PipActivity");
}
public static class SplitScreenActivity {
public static class Primary {
public static final String LABEL = "SplitScreenPrimaryActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SplitScreenActivity");
}
public static class Secondary {
public static final String LABEL = "SplitScreenSecondaryActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SplitScreenSecondaryActivity");
}
}
public static class SendNotificationActivity {
public static final String LABEL = "SendNotificationActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SendNotificationActivity");
}
public static class Bubbles {
public static class LaunchBubble {
public static final String LABEL = "LaunchBubbleActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".LaunchBubbleActivity");
}
public static class BubbleActivity {
public static final String LABEL = "BubbleActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".BubbleActivity");
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2022 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.server.wm.flicker.testapp;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
public class BubbleActivity extends Activity {
private int mNotifId = 0;
public BubbleActivity() {
super();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null) {
mNotifId = intent.getIntExtra(BubbleHelper.EXTRA_BUBBLE_NOTIF_ID, -1);
} else {
mNotifId = -1;
}
setContentView(R.layout.activity_bubble);
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
String result = resultCode == Activity.RESULT_OK ? "OK" : "CANCELLED";
Toast.makeText(this, "Activity result: " + result, Toast.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2022 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.server.wm.flicker.testapp;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
import android.graphics.drawable.Icon;
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
import android.view.WindowManager;
import java.util.HashMap;
public class BubbleHelper {
static final String EXTRA_BUBBLE_NOTIF_ID = "EXTRA_BUBBLE_NOTIF_ID";
static final String CHANNEL_ID = "bubbles";
static final String CHANNEL_NAME = "Bubbles";
static final int DEFAULT_HEIGHT_DP = 300;
private static BubbleHelper sInstance;
private final Context mContext;
private NotificationManager mNotificationManager;
private float mDisplayHeight;
private HashMap<Integer, BubbleInfo> mBubbleMap = new HashMap<>();
private int mNextNotifyId = 0;
private int mColourIndex = 0;
public static class BubbleInfo {
public int id;
public int height;
public Icon icon;
public BubbleInfo(int id, int height, Icon icon) {
this.id = id;
this.height = height;
this.icon = icon;
}
}
public static BubbleHelper getInstance(Context context) {
if (sInstance == null) {
sInstance = new BubbleHelper(context);
}
return sInstance;
}
private BubbleHelper(Context context) {
mContext = context;
mNotificationManager = context.getSystemService(NotificationManager.class);
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("Channel that posts bubbles");
channel.setAllowBubbles(true);
mNotificationManager.createNotificationChannel(channel);
Point p = new Point();
WindowManager wm = context.getSystemService(WindowManager.class);
wm.getDefaultDisplay().getRealSize(p);
mDisplayHeight = p.y;
}
private int getNextNotifyId() {
int id = mNextNotifyId;
mNextNotifyId++;
return id;
}
private Icon getIcon() {
return Icon.createWithResource(mContext, R.drawable.bg);
}
public int addNewBubble(boolean autoExpand, boolean suppressNotif) {
int id = getNextNotifyId();
BubbleInfo info = new BubbleInfo(id, DEFAULT_HEIGHT_DP, getIcon());
mBubbleMap.put(info.id, info);
Notification.BubbleMetadata data = getBubbleBuilder(info)
.setSuppressNotification(suppressNotif)
.setAutoExpandBubble(false)
.build();
Notification notification = getNotificationBuilder(info.id)
.setBubbleMetadata(data).build();
mNotificationManager.notify(info.id, notification);
return info.id;
}
private Notification.Builder getNotificationBuilder(int id) {
Person chatBot = new Person.Builder()
.setBot(true)
.setName("BubbleChat")
.setImportant(true)
.build();
String shortcutId = "BubbleChat";
return new Notification.Builder(mContext, CHANNEL_ID)
.setChannelId(CHANNEL_ID)
.setShortcutId(shortcutId)
.setContentTitle("BubbleChat")
.setContentIntent(PendingIntent.getActivity(mContext, 0,
new Intent(mContext, LaunchBubbleActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT))
.setStyle(new Notification.MessagingStyle(chatBot)
.setConversationTitle("BubbleChat")
.addMessage("BubbleChat",
SystemClock.currentThreadTimeMillis() - 300000, chatBot)
.addMessage("Is it me, " + id + ", you're looking for?",
SystemClock.currentThreadTimeMillis(), chatBot)
)
.setSmallIcon(R.drawable.ic_bubble);
}
private Notification.BubbleMetadata.Builder getBubbleBuilder(BubbleInfo info) {
Intent target = new Intent(mContext, BubbleActivity.class);
target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target,
PendingIntent.FLAG_UPDATE_CURRENT);
return new Notification.BubbleMetadata.Builder()
.setIntent(bubbleIntent)
.setIcon(info.icon)
.setDesiredHeight(info.height);
}
public void cancel(int id) {
mNotificationManager.cancel(id);
}
public void cancelAll() {
mNotificationManager.cancelAll();
}
public void cancelLast() {
StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
if (activeNotifications.length > 0) {
mNotificationManager.cancel(
activeNotifications[activeNotifications.length - 1].getId());
}
}
public void cancelFirst() {
StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
if (activeNotifications.length > 0) {
mNotificationManager.cancel(activeNotifications[0].getId());
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2022 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.server.wm.flicker.testapp;
import static com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION;
import android.os.Bundle;
public class FixedActivity extends SimpleActivity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Set the fixed orientation if requested
if (getIntent().hasExtra(EXTRA_FIXED_ORIENTATION)) {
final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_FIXED_ORIENTATION));
setRequestedOrientation(ori);
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2022 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.server.wm.flicker.testapp;
import android.app.Activity;
import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.view.View;
import java.util.Arrays;
public class LaunchBubbleActivity extends Activity {
private BubbleHelper mBubbleHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addInboxShortcut(getApplicationContext());
mBubbleHelper = BubbleHelper.getInstance(this);
setContentView(R.layout.activity_main);
findViewById(R.id.button_create).setOnClickListener(this::add);
findViewById(R.id.button_cancel).setOnClickListener(this::cancel);
findViewById(R.id.button_cancel_all).setOnClickListener(this::cancelAll);
}
private void add(View v) {
mBubbleHelper.addNewBubble(false /* autoExpand */, false /* suppressNotif */);
}
private void cancel(View v) {
mBubbleHelper.cancelLast();
}
private void cancelAll(View v) {
mBubbleHelper.cancelAll();
}
private void addInboxShortcut(Context context) {
Icon icon = Icon.createWithResource(this, R.drawable.bg);
Person[] persons = new Person[4];
for (int i = 0; i < persons.length; i++) {
persons[i] = new Person.Builder()
.setBot(false)
.setIcon(icon)
.setName("google" + i)
.setImportant(true)
.build();
}
ShortcutInfo shortcut = new ShortcutInfo.Builder(context, "BubbleChat")
.setShortLabel("BubbleChat")
.setLongLived(true)
.setIntent(new Intent(Intent.ACTION_VIEW))
.setIcon(Icon.createWithResource(context, R.drawable.ic_message))
.setPersons(persons)
.build();
ShortcutManager scmanager = context.getSystemService(ShortcutManager.class);
scmanager.addDynamicShortcuts(Arrays.asList(shortcut));
}
}

View File

@@ -22,7 +22,7 @@ import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Button;
public class ButtonActivity extends Activity {
public class LaunchNewActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -30,11 +30,11 @@ public class ButtonActivity extends Activity {
p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(p);
setContentView(R.layout.activity_button);
setContentView(R.layout.activity_launch_new);
Button button = findViewById(R.id.launch_second_activity);
button.setOnClickListener(v -> {
Intent intent = new Intent(ButtonActivity.this, SimpleActivity.class);
Intent intent = new Intent(LaunchNewActivity.this, SimpleActivity.class);
startActivity(intent);
});
}

View File

@@ -0,0 +1,307 @@
/*
* Copyright (C) 2022 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.server.wm.flicker.testapp;
import static android.media.MediaMetadata.METADATA_KEY_TITLE;
import static android.media.session.PlaybackState.ACTION_PAUSE;
import static android.media.session.PlaybackState.ACTION_PLAY;
import static android.media.session.PlaybackState.ACTION_STOP;
import static android.media.session.PlaybackState.STATE_PAUSED;
import static android.media.session.PlaybackState.STATE_PLAYING;
import static android.media.session.PlaybackState.STATE_STOPPED;
import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP;
import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_SET_REQUESTED_ORIENTATION;
import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.EXTRA_ENTER_PIP;
import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.EXTRA_PIP_ORIENTATION;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.RemoteAction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.util.Log;
import android.util.Rational;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.RadioButton;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class PipActivity extends Activity {
private static final String TAG = PipActivity.class.getSimpleName();
/**
* A media session title for when the session is in {@link STATE_PLAYING}.
* TvPipNotificationTests check whether the actual notification title matches this string.
*/
private static final String TITLE_STATE_PLAYING = "TestApp media is playing";
/**
* A media session title for when the session is in {@link STATE_PAUSED}.
* TvPipNotificationTests check whether the actual notification title matches this string.
*/
private static final String TITLE_STATE_PAUSED = "TestApp media is paused";
private static final Rational RATIO_DEFAULT = null;
private static final Rational RATIO_SQUARE = new Rational(1, 1);
private static final Rational RATIO_WIDE = new Rational(2, 1);
private static final Rational RATIO_TALL = new Rational(1, 2);
private static final String PIP_ACTION_NO_OP = "No-Op";
private static final String PIP_ACTION_OFF = "Off";
private static final String PIP_ACTION_ON = "On";
private static final String PIP_ACTION_CLEAR = "Clear";
private static final String ACTION_NO_OP = "com.android.wm.shell.flicker.testapp.NO_OP";
private static final String ACTION_SWITCH_OFF =
"com.android.wm.shell.flicker.testapp.SWITCH_OFF";
private static final String ACTION_SWITCH_ON = "com.android.wm.shell.flicker.testapp.SWITCH_ON";
private static final String ACTION_CLEAR = "com.android.wm.shell.flicker.testapp.CLEAR";
private final PictureInPictureParams.Builder mPipParamsBuilder =
new PictureInPictureParams.Builder()
.setAspectRatio(RATIO_DEFAULT);
private MediaSession mMediaSession;
private final PlaybackState.Builder mPlaybackStateBuilder = new PlaybackState.Builder()
.setActions(ACTION_PLAY | ACTION_PAUSE | ACTION_STOP)
.setState(STATE_STOPPED, 0, 1f);
private PlaybackState mPlaybackState = mPlaybackStateBuilder.build();
private final MediaMetadata.Builder mMediaMetadataBuilder = new MediaMetadata.Builder();
private final List<RemoteAction> mSwitchOffActions = new ArrayList<>();
private final List<RemoteAction> mSwitchOnActions = new ArrayList<>();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (isInPictureInPictureMode()) {
switch (intent.getAction()) {
case ACTION_SWITCH_ON:
mPipParamsBuilder.setActions(mSwitchOnActions);
break;
case ACTION_SWITCH_OFF:
mPipParamsBuilder.setActions(mSwitchOffActions);
break;
case ACTION_CLEAR:
mPipParamsBuilder.setActions(Collections.emptyList());
break;
case ACTION_NO_OP:
return;
default:
Log.w(TAG, "Unhandled action=" + intent.getAction());
return;
}
setPictureInPictureParams(mPipParamsBuilder.build());
} else {
switch (intent.getAction()) {
case ACTION_ENTER_PIP:
enterPip(null);
break;
case ACTION_SET_REQUESTED_ORIENTATION:
setRequestedOrientation(Integer.parseInt(intent.getStringExtra(
EXTRA_PIP_ORIENTATION)));
break;
default:
Log.w(TAG, "Unhandled action=" + intent.getAction());
}
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Window window = getWindow();
final WindowManager.LayoutParams layoutParams = window.getAttributes();
layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
window.setAttributes(layoutParams);
setContentView(R.layout.activity_pip);
findViewById(R.id.media_session_start)
.setOnClickListener(v -> updateMediaSessionState(STATE_PLAYING));
findViewById(R.id.media_session_stop)
.setOnClickListener(v -> updateMediaSessionState(STATE_STOPPED));
mMediaSession = new MediaSession(this, "WMShell_TestApp");
mMediaSession.setPlaybackState(mPlaybackStateBuilder.build());
mMediaSession.setCallback(new MediaSession.Callback() {
@Override
public void onPlay() {
updateMediaSessionState(STATE_PLAYING);
}
@Override
public void onPause() {
updateMediaSessionState(STATE_PAUSED);
}
@Override
public void onStop() {
updateMediaSessionState(STATE_STOPPED);
}
});
// Build two sets of the custom actions. We'll replace one with the other when 'On'/'Off'
// action is invoked.
// The first set consists of 3 actions: 1) Off; 2) No-Op; 3) Clear.
// The second set consists of 2 actions: 1) On; 2) Clear.
// Upon invocation 'Clear' action clear-off all the custom actions, including itself.
final Icon icon = Icon.createWithResource(this, android.R.drawable.ic_menu_help);
final RemoteAction noOpAction = buildRemoteAction(icon, PIP_ACTION_NO_OP, ACTION_NO_OP);
final RemoteAction switchOnAction =
buildRemoteAction(icon, PIP_ACTION_ON, ACTION_SWITCH_ON);
final RemoteAction switchOffAction =
buildRemoteAction(icon, PIP_ACTION_OFF, ACTION_SWITCH_OFF);
final RemoteAction clearAllAction = buildRemoteAction(icon, PIP_ACTION_CLEAR, ACTION_CLEAR);
mSwitchOffActions.addAll(Arrays.asList(switchOnAction, clearAllAction));
mSwitchOnActions.addAll(Arrays.asList(noOpAction, switchOffAction, clearAllAction));
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_NO_OP);
filter.addAction(ACTION_SWITCH_ON);
filter.addAction(ACTION_SWITCH_OFF);
filter.addAction(ACTION_CLEAR);
filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
filter.addAction(ACTION_ENTER_PIP);
registerReceiver(mBroadcastReceiver, filter);
handleIntentExtra(getIntent());
}
@Override
protected void onDestroy() {
unregisterReceiver(mBroadcastReceiver);
super.onDestroy();
}
@Override
protected void onUserLeaveHint() {
// Only used when auto PiP is disabled. This is to simulate the behavior that an app
// supports regular PiP but not auto PiP.
final boolean manuallyEnterPip =
((RadioButton) findViewById(R.id.enter_pip_on_leave_manual)).isChecked();
if (manuallyEnterPip) {
enterPictureInPictureMode();
}
}
private RemoteAction buildRemoteAction(Icon icon, String label, String action) {
final Intent intent = new Intent(action);
final PendingIntent pendingIntent =
PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
return new RemoteAction(icon, label, label, pendingIntent);
}
public void enterPip(View v) {
final boolean withCustomActions =
((CheckBox) findViewById(R.id.with_custom_actions)).isChecked();
mPipParamsBuilder.setActions(
withCustomActions ? mSwitchOnActions : Collections.emptyList());
enterPictureInPictureMode(mPipParamsBuilder.build());
}
public void onAutoPipSelected(View v) {
switch (v.getId()) {
case R.id.enter_pip_on_leave_manual:
// disable auto enter PiP
case R.id.enter_pip_on_leave_disabled:
mPipParamsBuilder.setAutoEnterEnabled(false);
setPictureInPictureParams(mPipParamsBuilder.build());
break;
case R.id.enter_pip_on_leave_autoenter:
mPipParamsBuilder.setAutoEnterEnabled(true);
setPictureInPictureParams(mPipParamsBuilder.build());
break;
}
}
public void onRatioSelected(View v) {
switch (v.getId()) {
case R.id.ratio_default:
mPipParamsBuilder.setAspectRatio(RATIO_DEFAULT);
break;
case R.id.ratio_square:
mPipParamsBuilder.setAspectRatio(RATIO_SQUARE);
break;
case R.id.ratio_wide:
mPipParamsBuilder.setAspectRatio(RATIO_WIDE);
break;
case R.id.ratio_tall:
mPipParamsBuilder.setAspectRatio(RATIO_TALL);
break;
}
}
private void updateMediaSessionState(int newState) {
if (mPlaybackState.getState() == newState) {
return;
}
final String title;
switch (newState) {
case STATE_PLAYING:
title = TITLE_STATE_PLAYING;
break;
case STATE_PAUSED:
title = TITLE_STATE_PAUSED;
break;
case STATE_STOPPED:
title = "";
break;
default:
throw new IllegalArgumentException("Unknown state " + newState);
}
mPlaybackStateBuilder.setState(newState, 0, 1f);
mPlaybackState = mPlaybackStateBuilder.build();
mMediaMetadataBuilder.putText(METADATA_KEY_TITLE, title);
mMediaSession.setPlaybackState(mPlaybackState);
mMediaSession.setMetadata(mMediaMetadataBuilder.build());
mMediaSession.setActive(newState != STATE_STOPPED);
}
private void handleIntentExtra(Intent intent) {
// Set the fixed orientation if requested
if (intent.hasExtra(EXTRA_PIP_ORIENTATION)) {
final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_PIP_ORIENTATION));
setRequestedOrientation(ori);
}
// Enter picture in picture with the given aspect ratio if provided
if (intent.hasExtra(EXTRA_ENTER_PIP)) {
mPipParamsBuilder.setActions(mSwitchOnActions);
enterPip(null);
}
}
}

View File

@@ -18,7 +18,7 @@ package com.android.server.wm.flicker.testapp;
import static android.os.SystemClock.sleep;
import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
import static com.android.server.wm.flicker.testapp.ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD;
import android.app.Activity;
import android.os.Bundle;

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2022 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.server.wm.flicker.testapp;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class SendNotificationActivity extends Activity {
private NotificationManager mNotificationManager;
private String mChannelId = "Channel id";
private String mChannelName = "Channel name";
private NotificationChannel mChannel;
private int mNotifyId = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notification);
findViewById(R.id.button_send_notification).setOnClickListener(this::sendNotification);
mChannel = new NotificationChannel(mChannelId, mChannelName,
NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager = getSystemService(NotificationManager.class);
mNotificationManager.createNotificationChannel(mChannel);
}
private void sendNotification(View v) {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, SendNotificationActivity.class),
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Notification notification = new Notification.Builder(this, mChannelId)
.setContentTitle("Notification App")
.setContentText("Notification content")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.ic_message)
.setContentIntent(pendingIntent)
.build();
mNotificationManager.notify(mNotifyId, notification);
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2022 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.server.wm.flicker.testapp;
import android.app.Activity;
import android.os.Bundle;
public class SplitScreenActivity extends Activity {
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_splitscreen);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2022 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.server.wm.flicker.testapp;
import android.app.Activity;
import android.os.Bundle;
public class SplitScreenSecondaryActivity extends Activity {
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_splitscreen_secondary);
}
}