11// Copyright 2013 The Flutter Authors. All rights reserved.
2- // Use of this source code is governed by a BSD-style license that can be
3- // found in the LICENSE file.
2+ // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
43
54package io .flutter .plugins .quickactionsexample ;
65
7- import static org .junit .Assert .assertTrue ;
6+ import static org .junit .Assert .* ;
87
8+ import android .content .Context ;
9+ import android .content .pm .ShortcutInfo ;
10+ import android .content .pm .ShortcutManager ;
11+ import android .util .Log ;
12+ import androidx .lifecycle .Lifecycle ;
913import androidx .test .core .app .ActivityScenario ;
14+ import androidx .test .core .app .ApplicationProvider ;
15+ import androidx .test .ext .junit .runners .AndroidJUnit4 ;
16+ import androidx .test .platform .app .InstrumentationRegistry ;
17+ import androidx .test .uiautomator .*;
1018import io .flutter .plugins .quickactions .QuickActionsPlugin ;
19+ import java .util .ArrayList ;
20+ import java .util .List ;
21+ import java .util .concurrent .atomic .AtomicReference ;
22+ import org .junit .After ;
23+ import org .junit .Assert ;
24+ import org .junit .Before ;
1125import org .junit .Test ;
26+ import org .junit .runner .RunWith ;
1227
28+ @ RunWith (AndroidJUnit4 .class )
1329public class QuickActionsTest {
30+ private Context context ;
31+ private UiDevice device ;
32+ private ActivityScenario <QuickActionsTestActivity > scenario ;
33+
34+ @ Before
35+ public void setUp () {
36+ context = ApplicationProvider .getApplicationContext ();
37+ device = UiDevice .getInstance (InstrumentationRegistry .getInstrumentation ());
38+ scenario = ensureAppRunToView ();
39+ }
40+
41+ @ After
42+ public void tearDown () {
43+ scenario .close ();
44+ Log .i (QuickActionsTest .class .getSimpleName (), "Run to completion" );
45+ }
46+
1447 @ Test
1548 public void imagePickerPluginIsAdded () {
1649 final ActivityScenario <QuickActionsTestActivity > scenario =
@@ -20,4 +53,108 @@ public void imagePickerPluginIsAdded() {
2053 assertTrue (activity .engine .getPlugins ().has (QuickActionsPlugin .class ));
2154 });
2255 }
56+
57+ @ Test
58+ public void appShortcutsAreCreated () {
59+ // Arrange
60+ List <Shortcut > expectedShortcuts = createMockShortcuts ();
61+
62+ // Act
63+ ShortcutManager shortcutManager =
64+ (ShortcutManager ) context .getSystemService (Context .SHORTCUT_SERVICE );
65+ List <ShortcutInfo > dynamicShortcuts = shortcutManager .getDynamicShortcuts ();
66+ Object [] shortcuts = dynamicShortcuts .stream ().map (Shortcut ::new ).toArray ();
67+
68+ // Assert the app shortcuts defined in ../lib/main.dart.
69+ assertFalse (dynamicShortcuts .isEmpty ());
70+ assertEquals (2 , dynamicShortcuts .size ());
71+ assertArrayEquals (expectedShortcuts .toArray (), shortcuts );
72+ }
73+
74+ @ Test
75+ public void appShortcutExistsAfterLongPressingAppIcon () throws UiObjectNotFoundException {
76+ // Arrange
77+ List <Shortcut > shortcuts = createMockShortcuts ();
78+ String appName = context .getApplicationInfo ().loadLabel (context .getPackageManager ()).toString ();
79+
80+ // Act
81+ findAppIcon (device , appName ).longClick ();
82+
83+ // Assert
84+ for (Shortcut shortcut : shortcuts ) {
85+ Assert .assertTrue (
86+ "The specified shortcut label '" + shortcut .shortLabel + "' does not exists." ,
87+ device .hasObject (By .text (shortcut .shortLabel )));
88+ }
89+ }
90+
91+ @ Test
92+ public void appShortcutLaunchActivityAfterPressing () throws UiObjectNotFoundException {
93+ // Arrange
94+ List <Shortcut > shortcuts = createMockShortcuts ();
95+ String appName = context .getApplicationInfo ().loadLabel (context .getPackageManager ()).toString ();
96+ Shortcut firstShortcut = shortcuts .get (0 );
97+ AtomicReference <QuickActionsTestActivity > initialActivity = new AtomicReference <>();
98+ scenario .onActivity (initialActivity ::set );
99+
100+ // Act
101+ findAppIcon (device , appName ).longClick ();
102+ UiObject appShortcut = device .findObject (new UiSelector ().text (firstShortcut .shortLabel ));
103+ appShortcut .clickAndWaitForNewWindow ();
104+ AtomicReference <QuickActionsTestActivity > currentActivity = new AtomicReference <>();
105+ scenario .onActivity (currentActivity ::set );
106+
107+ // Assert
108+ Assert .assertTrue (
109+ "AppShortcut:" + firstShortcut .type + " does not launch the correct activity" ,
110+ // We can only find the shortcut type in content description while inspecting it in Ui
111+ // Automator Viewer.
112+ device .hasObject (By .desc (firstShortcut .type )));
113+ // This is Android SingleTop behavior in which Android does not destroy the initial activity and
114+ // launch a new activity.
115+ Assert .assertEquals (initialActivity .get (), currentActivity .get ());
116+ }
117+
118+ private List <Shortcut > createMockShortcuts () {
119+ List <Shortcut > expectedShortcuts = new ArrayList <>();
120+ String actionOneLocalizedTitle = "Action one" ;
121+ expectedShortcuts .add (
122+ new Shortcut ("action_one" , actionOneLocalizedTitle , actionOneLocalizedTitle ));
123+
124+ String actionTwoLocalizedTitle = "Action two" ;
125+ expectedShortcuts .add (
126+ new Shortcut ("action_two" , actionTwoLocalizedTitle , actionTwoLocalizedTitle ));
127+
128+ return expectedShortcuts ;
129+ }
130+
131+ private ActivityScenario <QuickActionsTestActivity > ensureAppRunToView () {
132+ final ActivityScenario <QuickActionsTestActivity > scenario =
133+ ActivityScenario .launch (QuickActionsTestActivity .class );
134+ scenario .moveToState (Lifecycle .State .STARTED );
135+ return scenario ;
136+ }
137+
138+ private UiObject findAppIcon (UiDevice device , String appName ) throws UiObjectNotFoundException {
139+ device .pressHome ();
140+
141+ // Swipe up to open App Drawer
142+ UiScrollable homeView = new UiScrollable (new UiSelector ().scrollable (true ));
143+ homeView .scrollForward ();
144+
145+ if (!device .hasObject (By .text (appName ))) {
146+ Log .i (
147+ QuickActionsTest .class .getSimpleName (),
148+ "Attempting to scroll App Drawer for App Icon..." );
149+ UiScrollable appDrawer = new UiScrollable (new UiSelector ().scrollable (true ));
150+ // The scrollTextIntoView scrolls to the beginning before performing searching scroll; this
151+ // causes an issue in a scenario where the view is already in the beginning. In this case, it
152+ // scrolls back to home view. Therefore, we perform a dummy forward scroll to ensure it is not
153+ // in the beginning.
154+ appDrawer .scrollForward ();
155+ appDrawer .scrollTextIntoView (appName );
156+ }
157+
158+ return device .findObject (new UiSelector ().text (appName ));
159+ }
23160}
0 commit comments