1 /*
2  * Copyright (C) 2021 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 android.photopicker.cts.util;
18 
19 import static com.google.common.truth.Truth.assertWithMessage;
20 
21 import android.text.format.DateUtils;
22 
23 import androidx.annotation.NonNull;
24 import androidx.test.uiautomator.UiDevice;
25 import androidx.test.uiautomator.UiObject;
26 import androidx.test.uiautomator.UiObjectNotFoundException;
27 import androidx.test.uiautomator.UiScrollable;
28 import androidx.test.uiautomator.UiSelector;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 
33 /**
34  * Photo Picker Utility methods for finding UI elements.
35  */
36 public class PhotoPickerUiUtils {
37     public static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
38 
39     private static final long TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
40 
41     public static final String REGEX_PACKAGE_NAME =
42             "com(.google)?.android.providers.media(.module)?";
43 
44     /**
45      * Get the list of items from the photo grid list.
46      * @param itemCount if the itemCount is -1, return all matching items. Otherwise, return the
47      *                  item list that its size is not greater than the itemCount.
48      * @throws Exception
49      */
findItemList(int itemCount)50     public static List<UiObject> findItemList(int itemCount) throws Exception {
51         final List<UiObject> itemList = new ArrayList<>();
52         final UiSelector gridList = new UiSelector().resourceIdMatches(
53                 REGEX_PACKAGE_NAME + ":id/picker_tab_recyclerview");
54 
55         // Wait for the first item to appear
56         assertWithMessage("Timed out while waiting for first item to appear")
57                 .that(new UiObject(gridList.childSelector(new UiSelector())).waitForExists(TIMEOUT))
58                 .isTrue();
59 
60         final UiSelector itemSelector = new UiSelector().resourceIdMatches(
61                 REGEX_PACKAGE_NAME + ":id/icon_thumbnail");
62         final UiScrollable grid = new UiScrollable(gridList);
63         final int childCount = grid.getChildCount();
64         final int count = itemCount == -1 ? childCount : itemCount;
65 
66         for (int i = 0; i < childCount; i++) {
67             final UiObject item = grid.getChildByInstance(itemSelector, i);
68             if (item.exists()) {
69                 itemList.add(item);
70             }
71             if (itemList.size() == count) {
72                 break;
73             }
74         }
75         return itemList;
76     }
77 
findPreviewAddButton()78     public static UiObject findPreviewAddButton() {
79         return new UiObject(new UiSelector().resourceIdMatches(
80                 REGEX_PACKAGE_NAME + ":id/preview_add_button"));
81     }
82 
findPreviewAddOrSelectButton()83     public static UiObject findPreviewAddOrSelectButton() {
84         return new UiObject(new UiSelector().resourceIdMatches(
85                 REGEX_PACKAGE_NAME + ":id/preview_add_or_select_button"));
86     }
87 
findAddButton()88     public static UiObject findAddButton() {
89         return new UiObject(new UiSelector().resourceIdMatches(
90                 REGEX_PACKAGE_NAME + ":id/button_add"));
91     }
92 
findProfileButton()93     public static UiObject findProfileButton() {
94         return new UiObject(new UiSelector().resourceIdMatches(
95                 REGEX_PACKAGE_NAME + ":id/profile_button"));
96     }
97 
findAndClickBrowse(UiDevice uiDevice)98     public static void findAndClickBrowse(UiDevice uiDevice) throws Exception {
99         final UiObject overflowMenu = getOverflowMenuObject(uiDevice);
100         clickAndWait(uiDevice, overflowMenu);
101 
102         final UiObject browseButton = new UiObject(new UiSelector().textContains("Browse"));
103         clickAndWait(uiDevice, browseButton);
104     }
105 
findSettingsOverflowMenuItem(UiDevice uiDevice)106     public static UiObject findSettingsOverflowMenuItem(UiDevice uiDevice) throws Exception {
107         final UiObject overflowMenu = getOverflowMenuObject(uiDevice);
108         clickAndWait(uiDevice, overflowMenu);
109         return new UiObject(new UiSelector().textContains("Cloud media app"));
110     }
111 
getOverflowMenuObject(UiDevice uiDevice)112     public static UiObject getOverflowMenuObject(UiDevice uiDevice)  {
113         // Wait for overflow menu to appear.
114         verifyOverflowMenuExists(uiDevice);
115         return new UiObject(new UiSelector().description("More options"));
116     }
117 
isPhotoPickerVisible()118     public static boolean isPhotoPickerVisible() {
119         return new UiObject(new UiSelector().resourceIdMatches(
120                 PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/bottom_sheet")).waitForExists(TIMEOUT);
121     }
122 
verifySettingsActionBarIsVisible()123     public static void verifySettingsActionBarIsVisible() {
124         assertWithMessage("Timed out waiting for action bar to appear")
125                 .that(new UiObject(new UiSelector()
126                         .resourceIdMatches(REGEX_PACKAGE_NAME + ":id/picker_settings_toolbar"))
127                         .waitForExists(TIMEOUT))
128                 .isTrue();
129     }
130 
verifySettingsTitleIsVisible()131     public static void verifySettingsTitleIsVisible() {
132         assertWithMessage("Timed out waiting for settings page title to appear")
133                 .that(new UiObject(new UiSelector()
134                         .resourceIdMatches(REGEX_PACKAGE_NAME + ":id/picker_settings_title"))
135                         .waitForExists(TIMEOUT))
136                 .isTrue();
137     }
138 
verifySettingsDescriptionIsVisible()139     public static void verifySettingsDescriptionIsVisible() {
140         assertWithMessage("Timed out waiting for settings page description to appear")
141                 .that(new UiObject(new UiSelector()
142                         .resourceIdMatches(REGEX_PACKAGE_NAME + ":id/picker_settings_description"))
143                         .waitForExists(TIMEOUT))
144                 .isTrue();
145     }
146 
147     /**
148      * Verify if the app label of the {@code sTargetPackageName} is visible on the UI.
149      */
verifySettingsCloudProviderOptionIsVisible(@onNull String cmpLabel)150     public static void verifySettingsCloudProviderOptionIsVisible(@NonNull String cmpLabel) {
151         assertWithMessage("Timed out waiting for cloud provider option on settings activity")
152                 .that(new UiObject(new UiSelector().textContains(cmpLabel))
153                         .waitForExists(TIMEOUT))
154                 .isTrue();
155     }
156 
verifySettingsFragmentContainerExists()157     public static void verifySettingsFragmentContainerExists() {
158         assertWithMessage("Timed out waiting for settings fragment container to appear")
159                 .that(new UiObject(new UiSelector()
160                         .resourceIdMatches(REGEX_PACKAGE_NAME + ":id/settings_fragment_container"))
161                         .waitForExists(TIMEOUT))
162                 .isTrue();
163     }
164 
verifyOverflowMenuExists(UiDevice uiDevice)165     private static void verifyOverflowMenuExists(UiDevice uiDevice) {
166         assertWithMessage("Timed out waiting for overflow menu to appear")
167                 .that(new UiObject(new UiSelector().description("More options"))
168                         .waitForExists(TIMEOUT))
169                 .isTrue();
170     }
171 
verifySettingsActivityIsVisible()172     public static void verifySettingsActivityIsVisible() {
173         // id/settings_activity_root is the root layout in activity_photo_picker_settings.xml
174         assertWithMessage("Timed out waiting for settings activity to appear")
175                 .that(new UiObject(new UiSelector()
176                 .resourceIdMatches(REGEX_PACKAGE_NAME + ":id/settings_activity_root"))
177                 .waitForExists(TIMEOUT))
178                 .isTrue();
179     }
180 
clickAndWait(UiDevice uiDevice, UiObject uiObject)181     public static void clickAndWait(UiDevice uiDevice, UiObject uiObject) throws Exception {
182         uiObject.click();
183         uiDevice.waitForIdle();
184     }
185 
186     /**
187      * Verifies whether the selected tab is the one with the provided title
188      */
isSelectedTabTitle( @onNull String tabTitle, @NonNull String tabResourceId, UiDevice device)189     public static boolean isSelectedTabTitle(
190             @NonNull String tabTitle, @NonNull String tabResourceId, UiDevice device)
191             throws UiObjectNotFoundException {
192         final UiObject tabLayout = findObject(tabResourceId, device);
193         final UiObject tab = tabLayout.getChild(new UiSelector().textContains(tabTitle));
194         return tab.isSelected();
195     }
196 
197     /**
198      * Returns the UI object corresponding to the specified resourceId
199      */
findObject(@onNull String resourceId, UiDevice device)200     public static UiObject findObject(@NonNull String resourceId, UiDevice device) {
201         return device.findObject(new UiSelector().resourceIdMatches(resourceId));
202     }
203 }
204