1 /*
2  * 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.cts.helpers.aosp;
18 
19 import android.app.Instrumentation;
20 import android.app.UiAutomation;
21 import android.os.RemoteException;
22 
23 import android.os.SystemClock;
24 import android.platform.helpers.exceptions.TestHelperException;
25 import android.support.test.uiautomator.By;
26 import android.support.test.uiautomator.UiDevice;
27 import android.support.test.uiautomator.UiObject;
28 import android.support.test.uiautomator.UiObject2;
29 import android.support.test.uiautomator.UiObjectNotFoundException;
30 import android.support.test.uiautomator.UiSelector;
31 import android.support.test.uiautomator.Until;
32 import android.util.Log;
33 
34 import com.android.cts.helpers.ICtsPrintHelper;
35 
36 import java.io.ByteArrayOutputStream;
37 import java.io.IOException;
38 
39 public class DefaultCtsPrintHelper implements ICtsPrintHelper {
40     private static final String LOG_TAG = DefaultCtsPrintHelper.class.getSimpleName();
41 
42     protected static final long OPERATION_TIMEOUT_MILLIS = 60000;
43     private static final long GET_UIAUTOMATION_TIMEOUT_MS = 60000;
44 
45     protected Instrumentation mInstrumentation;
46     protected UiDevice mDevice;
47     protected UiAutomation mAutomation;
48 
DefaultCtsPrintHelper(Instrumentation instrumentation)49     public DefaultCtsPrintHelper(Instrumentation instrumentation) {
50         mInstrumentation = instrumentation;
51         mDevice = UiDevice.getInstance(mInstrumentation);
52 
53         long start = SystemClock.uptimeMillis();
54         while (SystemClock.uptimeMillis() - start < GET_UIAUTOMATION_TIMEOUT_MS) {
55             UiAutomation ui = mInstrumentation.getUiAutomation();
56             if (ui != null) {
57                 mAutomation = ui;
58                 break;
59             }
60         }
61         if (mAutomation == null) {
62             throw new AssertionError("Failed to get UiAutomation");
63         }
64     }
65 
dumpWindowHierarchy()66     protected void dumpWindowHierarchy() throws TestHelperException {
67         try {
68             ByteArrayOutputStream os = new ByteArrayOutputStream();
69             mDevice.dumpWindowHierarchy(os);
70 
71             Log.w(LOG_TAG, "Window hierarchy:");
72             for (String line : os.toString("UTF-8").split("\n")) {
73                 Log.w(LOG_TAG, line);
74             }
75         } catch (IOException e) {
76             throw new TestHelperException(e);
77         }
78     }
79 
80     @Override
setUp()81     public void setUp() throws TestHelperException {
82         try {
83             // Prevent rotation
84             mDevice.freezeRotation();
85             while (!mDevice.isNaturalOrientation()) {
86                 mDevice.setOrientationNatural();
87                 mDevice.waitForIdle();
88             }
89         } catch (RemoteException e) {
90             throw new TestHelperException("Failed to freeze device rotation", e);
91         }
92     }
93 
94     @Override
tearDown()95     public void tearDown() throws TestHelperException {
96         try {
97             // Allow rotation
98             mDevice.unfreezeRotation();
99         } catch (RemoteException e) {
100             throw new TestHelperException("Failed to unfreeze device rotation", e);
101         }
102     }
103 
104     @Override
submitPrintJob()105     public void submitPrintJob() throws TestHelperException {
106         Log.d(LOG_TAG, "Clicking print button");
107 
108         mDevice.waitForIdle();
109 
110         UiObject2 printButton =
111                 mDevice.wait(
112                         Until.findObject(By.res("com.android.printspooler:id/print_button")),
113                         OPERATION_TIMEOUT_MILLIS);
114         if (printButton == null) {
115             dumpWindowHierarchy();
116             throw new TestHelperException("print button not found");
117         }
118 
119         printButton.click();
120     }
121 
122     @Override
retryPrintJob()123     public void retryPrintJob() throws TestHelperException {
124         try {
125             UiObject retryButton = mDevice.findObject(new UiSelector().resourceId(
126                     "com.android.printspooler:id/action_button"));
127             retryButton.click();
128         } catch (UiObjectNotFoundException e) {
129             dumpWindowHierarchy();
130             throw new TestHelperException("Retry button not found", e);
131         }
132     }
133 
134     @Override
canSubmitJob()135     public boolean canSubmitJob() {
136         return mDevice.hasObject(By.res("com.android.printspooler:id/print_button"));
137     }
138 
139     @Override
answerPrintServicesWarning(boolean confirm)140     public void answerPrintServicesWarning(boolean confirm) throws TestHelperException {
141         try {
142             mDevice.waitForIdle();
143             UiObject button;
144             if (confirm) {
145                 button = mDevice.findObject(new UiSelector().resourceId("android:id/button1"));
146             } else {
147                 button = mDevice.findObject(new UiSelector().resourceId("android:id/button2"));
148             }
149             button.click();
150         } catch (UiObjectNotFoundException e) {
151             dumpWindowHierarchy();
152             throw new TestHelperException("Unable to find print service dialog button", e);
153         }
154     }
155 
156     @Override
selectPrinter(String printerName, long timeout)157     public void selectPrinter(String printerName, long timeout) throws TestHelperException {
158         Log.d(LOG_TAG, "Selecting printer " + printerName);
159         try {
160             UiObject2 destinationSpinner =
161                     mDevice.wait(
162                             Until.findObject(
163                                     By.res("com.android.printspooler:id/destination_spinner")),
164                             timeout);
165 
166             if (destinationSpinner != null) {
167                 destinationSpinner.click();
168                 mDevice.waitForIdle();
169             }
170 
171             UiObject2 printerOption = mDevice.wait(Until.findObject(By.text(printerName)), timeout);
172             if (printerOption == null) {
173                 throw new UiObjectNotFoundException(printerName + " not found");
174             }
175 
176             printerOption.click();
177             mDevice.waitForIdle();
178         } catch (Exception e) {
179             dumpWindowHierarchy();
180             throw new TestHelperException("Failed to select printer", e);
181         }
182     }
183 
184     @Override
selectPrinterWhenAvailable(String printerName)185     public void selectPrinterWhenAvailable(String printerName) throws TestHelperException {
186         try {
187             while (true) {
188                 UiObject printerItem = mDevice.findObject(
189                         new UiSelector().text(printerName));
190 
191                 if (printerItem.isEnabled()) {
192                     printerItem.click();
193                     break;
194                 } else {
195                     Thread.sleep(100);
196                 }
197             }
198         } catch (UiObjectNotFoundException e) {
199             throw new TestHelperException("Failed to find printer label", e);
200         } catch (InterruptedException e) {
201             throw new TestHelperException("Interruped while waiting for printer", e);
202         }
203     }
204 
205     @Override
setPageOrientation(String orientation)206     public void setPageOrientation(String orientation) throws TestHelperException {
207         try {
208             UiObject orientationSpinner = mDevice.findObject(new UiSelector().resourceId(
209                     "com.android.printspooler:id/orientation_spinner"));
210             orientationSpinner.click();
211             UiObject orientationOption = mDevice.findObject(new UiSelector().text(orientation));
212             orientationOption.click();
213         } catch (UiObjectNotFoundException e) {
214             dumpWindowHierarchy();
215             throw new TestHelperException("Failed to set page orientation to " + orientation, e);
216         }
217     }
218 
219     @Override
getPageOrientation()220     public String getPageOrientation() throws TestHelperException {
221         try {
222             UiObject orientationSpinner = mDevice.findObject(new UiSelector().resourceId(
223                     "com.android.printspooler:id/orientation_spinner"));
224             return orientationSpinner.getText();
225         } catch (UiObjectNotFoundException e) {
226             dumpWindowHierarchy();
227             throw new TestHelperException("Failed to get page orientation", e);
228         }
229     }
230 
231     @Override
setMediaSize(String mediaSize)232     public void setMediaSize(String mediaSize) throws TestHelperException {
233         try {
234             UiObject mediaSizeSpinner = mDevice.findObject(new UiSelector().resourceId(
235                     "com.android.printspooler:id/paper_size_spinner"));
236             mediaSizeSpinner.click();
237             UiObject mediaSizeOption = mDevice.findObject(new UiSelector().text(mediaSize));
238             mediaSizeOption.click();
239         } catch (UiObjectNotFoundException e) {
240             dumpWindowHierarchy();
241             throw new TestHelperException("Unable to set media size to " + mediaSize, e);
242         }
243     }
244 
245     @Override
setColorMode(String color)246     public void setColorMode(String color) throws TestHelperException {
247         try {
248             UiObject colorSpinner = mDevice.findObject(new UiSelector().resourceId(
249                     "com.android.printspooler:id/color_spinner"));
250             colorSpinner.click();
251             UiObject colorOption = mDevice.findObject(new UiSelector().text(color));
252             colorOption.click();
253         } catch (UiObjectNotFoundException e) {
254             dumpWindowHierarchy();
255             throw new TestHelperException("Unable to set color mode to " + color, e);
256         }
257     }
258 
259     @Override
getColorMode()260     public String getColorMode() throws TestHelperException {
261         try {
262             UiObject colorSpinner = mDevice.findObject(new UiSelector().resourceId(
263                     "com.android.printspooler:id/color_spinner"));
264             return colorSpinner.getText();
265         } catch (UiObjectNotFoundException e) {
266             dumpWindowHierarchy();
267             throw new TestHelperException("Unable to get color mode", e);
268         }
269     }
270 
271     @Override
setDuplexMode(String duplex)272     public void setDuplexMode(String duplex) throws TestHelperException {
273         try {
274             UiObject duplexSpinner = mDevice.findObject(new UiSelector().resourceId(
275                     "com.android.printspooler:id/duplex_spinner"));
276             duplexSpinner.click();
277             UiObject duplexOption = mDevice.findObject(new UiSelector().text(duplex));
278             duplexOption.click();
279         } catch (UiObjectNotFoundException e) {
280             dumpWindowHierarchy();
281             throw new TestHelperException("Unable to set duplex mode to " + duplex, e);
282         }
283     }
284 
285     @Override
setCopies(int newCopies)286     public void setCopies(int newCopies) throws TestHelperException {
287         try {
288             UiObject copies = mDevice.findObject(new UiSelector().resourceId(
289                     "com.android.printspooler:id/copies_edittext"));
290             copies.setText(Integer.valueOf(newCopies).toString());
291         } catch (UiObjectNotFoundException e) {
292             dumpWindowHierarchy();
293             throw new TestHelperException("Unable to set copies to " + newCopies, e);
294         }
295     }
296 
297     @Override
getCopies()298     public int getCopies() throws TestHelperException {
299         try {
300             UiObject copies = mDevice.findObject(new UiSelector().resourceId(
301                     "com.android.printspooler:id/copies_edittext"));
302             return Integer.parseInt(copies.getText());
303         } catch (UiObjectNotFoundException e) {
304             dumpWindowHierarchy();
305             throw new TestHelperException("Unable to get number of copies", e);
306         }
307     }
308 
309     @Override
setPageRange(String pages, int expectedPages)310     public void setPageRange(String pages, int expectedPages) throws TestHelperException {
311         try {
312             mDevice.waitForIdle();
313             UiObject pagesSpinner = mDevice.findObject(new UiSelector().resourceId(
314                     "com.android.printspooler:id/range_options_spinner"));
315             pagesSpinner.click();
316 
317             mDevice.waitForIdle();
318             UiObject rangeOption = mDevice.findObject(new UiSelector().textContains("Range of "
319                     + expectedPages));
320             rangeOption.click();
321 
322             mDevice.waitForIdle();
323             UiObject pagesEditText = mDevice.findObject(new UiSelector().resourceId(
324                     "com.android.printspooler:id/page_range_edittext"));
325             pagesEditText.setText(pages);
326 
327             mDevice.waitForIdle();
328             // Hide the keyboard.
329             mDevice.pressBack();
330         } catch (UiObjectNotFoundException e) {
331             dumpWindowHierarchy();
332             throw new TestHelperException("Unable to set page range", e);
333         }
334     }
335 
336     @Override
getPageRange(int docPages)337     public String getPageRange(int docPages) throws TestHelperException {
338         final String fullRange = "All " + docPages;
339 
340         try {
341             if (mDevice.hasObject(By.text(fullRange))) {
342                 return fullRange;
343             }
344 
345             UiObject pagesEditText = mDevice.findObject(new UiSelector().resourceId(
346                     "com.android.printspooler:id/page_range_edittext"));
347 
348             return pagesEditText.getText();
349         } catch (UiObjectNotFoundException e) {
350             dumpWindowHierarchy();
351             throw new TestHelperException("Unable to get page range", e);
352         }
353     }
354 
355     @Override
getStatusMessage()356     public String getStatusMessage() throws TestHelperException {
357         UiObject2 message = mDevice.wait(Until.findObject(
358                 By.res("com.android.printspooler:id/message")), OPERATION_TIMEOUT_MILLIS);
359 
360         if (message == null) {
361             dumpWindowHierarchy();
362             throw new TestHelperException("Cannot find status message");
363         }
364 
365         return message.getText();
366     }
367 
368     @Override
openPrintOptions()369     public void openPrintOptions() throws TestHelperException {
370         try {
371             UiObject expandHandle = mDevice.findObject(new UiSelector().resourceId(
372                     "com.android.printspooler:id/expand_collapse_handle"));
373             expandHandle.click();
374         } catch (UiObjectNotFoundException e) {
375             dumpWindowHierarchy();
376             throw new TestHelperException("Unable to find print options handle", e);
377         }
378     }
379 
380     @Override
openCustomPrintOptions()381     public void openCustomPrintOptions() throws TestHelperException {
382         try {
383             UiObject expandHandle = mDevice.findObject(new UiSelector().resourceId(
384                     "com.android.printspooler:id/more_options_button"));
385             expandHandle.click();
386         } catch (UiObjectNotFoundException e) {
387             dumpWindowHierarchy();
388             throw new TestHelperException("Unable to find print options handle", e);
389         }
390     }
391 
392     @Override
displayPrinterList()393     public void displayPrinterList() throws TestHelperException {
394         try {
395             // Open destination spinner
396             UiObject destinationSpinner = mDevice.findObject(new UiSelector().resourceId(
397                     "com.android.printspooler:id/destination_spinner"));
398             destinationSpinner.click();
399 
400             // Wait until spinner is opened
401             mDevice.waitForIdle();
402         } catch (UiObjectNotFoundException e) {
403             throw new TestHelperException("Unable to find destination spinner", e);
404         }
405     }
406 
407     @Override
displayMoreInfo()408     public void displayMoreInfo() throws TestHelperException {
409         mDevice.waitForIdle();
410         mDevice.wait(
411                 Until.findObject(By.res("com.android.printspooler:id/more_info")),
412                 OPERATION_TIMEOUT_MILLIS).click();
413     }
414 
415     @Override
closePrinterList()416     public void closePrinterList() {
417         mDevice.pressBack();
418     }
419 
420     @Override
closeCustomPrintOptions()421     public void closeCustomPrintOptions() {
422         mDevice.pressBack();
423     }
424 
425     @Override
closePrintOptions()426     public void closePrintOptions() {
427         mDevice.pressBack();
428     }
429 
430     @Override
cancelPrinting()431     public void cancelPrinting() throws TestHelperException {
432         try {
433             mDevice.wakeUp();
434             mDevice.pressBack();
435             mDevice.waitForIdle();
436         } catch (RemoteException e) {
437             throw new TestHelperException("Failed to cancel printing", e);
438         }
439     }
440 }
441