1 /* 2 * Copyright (C) 2014 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.print.cts; 18 19 import static org.mockito.Matchers.any; 20 import static org.mockito.Matchers.argThat; 21 import static org.mockito.Matchers.eq; 22 import static org.mockito.Mockito.doAnswer; 23 import static org.mockito.Mockito.doCallRealMethod; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.when; 26 27 import android.content.Context; 28 import android.content.pm.PackageManager; 29 import android.content.res.Configuration; 30 import android.content.res.Resources; 31 import android.cts.util.SystemUtil; 32 import android.graphics.pdf.PdfDocument; 33 import android.os.Bundle; 34 import android.os.CancellationSignal; 35 import android.os.LocaleList; 36 import android.os.ParcelFileDescriptor; 37 import android.os.SystemClock; 38 import android.print.PageRange; 39 import android.print.PrintAttributes; 40 import android.print.PrintDocumentAdapter; 41 import android.print.PrintDocumentAdapter.LayoutResultCallback; 42 import android.print.PrintDocumentAdapter.WriteResultCallback; 43 import android.print.PrintManager; 44 import android.print.PrinterId; 45 import android.print.cts.services.PrintServiceCallbacks; 46 import android.print.cts.services.PrinterDiscoverySessionCallbacks; 47 import android.print.cts.services.StubbablePrinterDiscoverySession; 48 import android.print.pdf.PrintedPdfDocument; 49 import android.printservice.CustomPrinterIconCallback; 50 import android.printservice.PrintJob; 51 import android.printservice.PrintService; 52 import android.support.test.uiautomator.By; 53 import android.support.test.uiautomator.UiDevice; 54 import android.support.test.uiautomator.UiObject; 55 import android.support.test.uiautomator.UiObjectNotFoundException; 56 import android.support.test.uiautomator.UiSelector; 57 import android.test.InstrumentationTestCase; 58 import android.util.DisplayMetrics; 59 import android.util.Log; 60 61 import org.hamcrest.BaseMatcher; 62 import org.hamcrest.Description; 63 import org.mockito.InOrder; 64 import org.mockito.stubbing.Answer; 65 66 import java.io.BufferedReader; 67 import java.io.ByteArrayOutputStream; 68 import java.io.FileInputStream; 69 import java.io.FileOutputStream; 70 import java.io.IOException; 71 import java.io.InputStreamReader; 72 import java.util.ArrayList; 73 import java.util.List; 74 import java.util.Locale; 75 import java.util.concurrent.TimeoutException; 76 /** 77 * This is the base class for print tests. 78 */ 79 public abstract class BasePrintTest extends InstrumentationTestCase { 80 private final static String LOG_TAG = "BasePrintTest"; 81 82 protected static final long OPERATION_TIMEOUT_MILLIS = 60000; 83 private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler"; 84 protected static final String PRINT_JOB_NAME = "Test"; 85 private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success"; 86 private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s"; 87 private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable "; 88 private static final String COMMAND_PREFIX_DISABLE_IME = "ime disable "; 89 private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT 90 91 private static PrintDocumentActivity sActivity; 92 private UiDevice mUiDevice; 93 94 /** 95 * Return the UI device 96 * 97 * @return the UI device 98 */ getUiDevice()99 public UiDevice getUiDevice() { 100 return mUiDevice; 101 } 102 103 private LocaleList mOldLocale; 104 105 private CallCounter mCancelOperationCounter; 106 private CallCounter mLayoutCallCounter; 107 private CallCounter mWriteCallCounter; 108 private CallCounter mFinishCallCounter; 109 private CallCounter mPrintJobQueuedCallCounter; 110 private CallCounter mCreateSessionCallCounter; 111 private CallCounter mDestroySessionCallCounter; 112 private static CallCounter sDestroyActivityCallCounter = new CallCounter(); 113 private static CallCounter sCreateActivityCallCounter = new CallCounter(); 114 115 private String[] mEnabledImes; 116 getEnabledImes()117 private String[] getEnabledImes() throws IOException { 118 List<String> imeList = new ArrayList<>(); 119 120 ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() 121 .executeShellCommand(COMMAND_LIST_ENABLED_IME_COMPONENTS); 122 try (BufferedReader reader = new BufferedReader( 123 new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) { 124 125 String line; 126 while ((line = reader.readLine()) != null) { 127 imeList.add(line); 128 } 129 } 130 131 String[] imeArray = new String[imeList.size()]; 132 imeList.toArray(imeArray); 133 134 return imeArray; 135 } 136 disableImes()137 private void disableImes() throws Exception { 138 mEnabledImes = getEnabledImes(); 139 for (String ime : mEnabledImes) { 140 String disableImeCommand = COMMAND_PREFIX_DISABLE_IME + ime; 141 SystemUtil.runShellCommand(getInstrumentation(), disableImeCommand); 142 } 143 } 144 enableImes()145 private void enableImes() throws Exception { 146 for (String ime : mEnabledImes) { 147 String enableImeCommand = COMMAND_PREFIX_ENABLE_IME + ime; 148 SystemUtil.runShellCommand(getInstrumentation(), enableImeCommand); 149 } 150 mEnabledImes = null; 151 } 152 153 @Override runTest()154 protected void runTest() throws Throwable { 155 // Do nothing if the device does not support printing. 156 if (supportsPrinting()) { 157 super.runTest(); 158 } 159 } 160 161 @Override setUp()162 public void setUp() throws Exception { 163 Log.d(LOG_TAG, "setUp()"); 164 165 super.setUp(); 166 if (!supportsPrinting()) { 167 return; 168 } 169 170 mUiDevice = UiDevice.getInstance(getInstrumentation()); 171 172 // Make sure we start with a clean slate. 173 Log.d(LOG_TAG, "clearPrintSpoolerData()"); 174 clearPrintSpoolerData(); 175 Log.d(LOG_TAG, "disableImes()"); 176 disableImes(); 177 178 // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2 179 // Dexmaker is used by mockito. 180 System.setProperty("dexmaker.dexcache", getInstrumentation() 181 .getTargetContext().getCacheDir().getPath()); 182 183 // Set to US locale. 184 Log.d(LOG_TAG, "set locale"); 185 Resources resources = getInstrumentation().getTargetContext().getResources(); 186 Configuration oldConfiguration = resources.getConfiguration(); 187 if (!oldConfiguration.getLocales().get(0).equals(Locale.US)) { 188 mOldLocale = oldConfiguration.getLocales(); 189 DisplayMetrics displayMetrics = resources.getDisplayMetrics(); 190 Configuration newConfiguration = new Configuration(oldConfiguration); 191 newConfiguration.setLocale(Locale.US); 192 resources.updateConfiguration(newConfiguration, displayMetrics); 193 } 194 195 // Initialize the latches. 196 Log.d(LOG_TAG, "init counters"); 197 mCancelOperationCounter = new CallCounter(); 198 mLayoutCallCounter = new CallCounter(); 199 mFinishCallCounter = new CallCounter(); 200 mWriteCallCounter = new CallCounter(); 201 mFinishCallCounter = new CallCounter(); 202 mPrintJobQueuedCallCounter = new CallCounter(); 203 mCreateSessionCallCounter = new CallCounter(); 204 mDestroySessionCallCounter = new CallCounter(); 205 206 // Create the activity for the right locale. 207 Log.d(LOG_TAG, "createActivity()"); 208 createActivity(); 209 Log.d(LOG_TAG, "setUp() done"); 210 } 211 212 @Override tearDown()213 public void tearDown() throws Exception { 214 Log.d(LOG_TAG, "tearDown()"); 215 216 if (!supportsPrinting()) { 217 return; 218 } 219 220 // Done with the activity. 221 Log.d(LOG_TAG, "finish activity"); 222 if (!getActivity().isFinishing()) { 223 getActivity().finish(); 224 } 225 226 Log.d(LOG_TAG, "enableImes()"); 227 enableImes(); 228 229 // Restore the locale if needed. 230 Log.d(LOG_TAG, "restore locale"); 231 if (mOldLocale != null) { 232 Resources resources = getInstrumentation().getTargetContext().getResources(); 233 DisplayMetrics displayMetrics = resources.getDisplayMetrics(); 234 Configuration newConfiguration = new Configuration(resources.getConfiguration()); 235 newConfiguration.setLocales(mOldLocale); 236 mOldLocale = null; 237 resources.updateConfiguration(newConfiguration, displayMetrics); 238 } 239 240 // Make sure the spooler is cleaned, this also un-approves all services 241 Log.d(LOG_TAG, "clearPrintSpoolerData()"); 242 clearPrintSpoolerData(); 243 244 super.tearDown(); 245 Log.d(LOG_TAG, "tearDown() done"); 246 } 247 print(final PrintDocumentAdapter adapter, final PrintAttributes attributes)248 protected void print(final PrintDocumentAdapter adapter, final PrintAttributes attributes) { 249 // Initiate printing as if coming from the app. 250 getInstrumentation().runOnMainSync(new Runnable() { 251 @Override 252 public void run() { 253 PrintManager printManager = (PrintManager) getActivity() 254 .getSystemService(Context.PRINT_SERVICE); 255 printManager.print("Print job", adapter, attributes); 256 } 257 }); 258 } 259 print(PrintDocumentAdapter adapter)260 protected void print(PrintDocumentAdapter adapter) { 261 print(adapter, null); 262 } 263 onCancelOperationCalled()264 protected void onCancelOperationCalled() { 265 mCancelOperationCounter.call(); 266 } 267 onLayoutCalled()268 protected void onLayoutCalled() { 269 mLayoutCallCounter.call(); 270 } 271 getWriteCallCount()272 protected int getWriteCallCount() { 273 return mWriteCallCounter.getCallCount(); 274 } 275 onWriteCalled()276 protected void onWriteCalled() { 277 mWriteCallCounter.call(); 278 } 279 onFinishCalled()280 protected void onFinishCalled() { 281 mFinishCallCounter.call(); 282 } 283 onPrintJobQueuedCalled()284 protected void onPrintJobQueuedCalled() { 285 mPrintJobQueuedCallCounter.call(); 286 } 287 onPrinterDiscoverySessionCreateCalled()288 protected void onPrinterDiscoverySessionCreateCalled() { 289 mCreateSessionCallCounter.call(); 290 } 291 onPrinterDiscoverySessionDestroyCalled()292 protected void onPrinterDiscoverySessionDestroyCalled() { 293 mDestroySessionCallCounter.call(); 294 } 295 waitForCancelOperationCallbackCalled()296 protected void waitForCancelOperationCallbackCalled() { 297 waitForCallbackCallCount(mCancelOperationCounter, 1, 298 "Did not get expected call to onCancel for the current operation."); 299 } 300 waitForPrinterDiscoverySessionCreateCallbackCalled()301 protected void waitForPrinterDiscoverySessionCreateCallbackCalled() { 302 waitForCallbackCallCount(mCreateSessionCallCounter, 1, 303 "Did not get expected call to onCreatePrinterDiscoverySession."); 304 } 305 waitForPrinterDiscoverySessionDestroyCallbackCalled(int count)306 protected void waitForPrinterDiscoverySessionDestroyCallbackCalled(int count) { 307 waitForCallbackCallCount(mDestroySessionCallCounter, count, 308 "Did not get expected call to onDestroyPrinterDiscoverySession."); 309 } 310 waitForServiceOnPrintJobQueuedCallbackCalled(int count)311 protected void waitForServiceOnPrintJobQueuedCallbackCalled(int count) { 312 waitForCallbackCallCount(mPrintJobQueuedCallCounter, count, 313 "Did not get expected call to onPrintJobQueued."); 314 } 315 waitForAdapterFinishCallbackCalled()316 protected void waitForAdapterFinishCallbackCalled() { 317 waitForCallbackCallCount(mFinishCallCounter, 1, 318 "Did not get expected call to finish."); 319 } 320 waitForLayoutAdapterCallbackCount(int count)321 protected void waitForLayoutAdapterCallbackCount(int count) { 322 waitForCallbackCallCount(mLayoutCallCounter, count, 323 "Did not get expected call to layout."); 324 } 325 waitForWriteAdapterCallback(int count)326 protected void waitForWriteAdapterCallback(int count) { 327 waitForCallbackCallCount(mWriteCallCounter, count, "Did not get expected call to write."); 328 } 329 waitForCallbackCallCount(CallCounter counter, int count, String message)330 private static void waitForCallbackCallCount(CallCounter counter, int count, String message) { 331 try { 332 counter.waitForCount(count, OPERATION_TIMEOUT_MILLIS); 333 } catch (TimeoutException te) { 334 fail(message); 335 } 336 } 337 338 /** 339 * Indicate the print activity was created. 340 */ onActivityCreateCalled(PrintDocumentActivity activity)341 static void onActivityCreateCalled(PrintDocumentActivity activity) { 342 sActivity = activity; 343 sCreateActivityCallCounter.call(); 344 } 345 346 /** 347 * Indicate the print activity was destroyed. 348 */ onActivityDestroyCalled()349 static void onActivityDestroyCalled() { 350 sDestroyActivityCallCounter.call(); 351 } 352 353 /** 354 * Get the number of ties the print activity was destroyed. 355 * 356 * @return The number of onDestroy calls on the print activity. 357 */ getActivityDestroyCallbackCallCount()358 protected static int getActivityDestroyCallbackCallCount() { 359 return sDestroyActivityCallCounter.getCallCount(); 360 } 361 362 /** 363 * Get the number of ties the print activity was created. 364 * 365 * @return The number of onCreate calls on the print activity. 366 */ getActivityCreateCallbackCallCount()367 protected static int getActivityCreateCallbackCallCount() { 368 return sCreateActivityCallCounter.getCallCount(); 369 } 370 371 /** 372 * Wait until create was called {@code count} times. 373 * 374 * @param count The number of create calls to expect. 375 */ waitForActivityCreateCallbackCalled(int count)376 private static void waitForActivityCreateCallbackCalled(int count) { 377 waitForCallbackCallCount(sCreateActivityCallCounter, count, 378 "Did not get expected call to create."); 379 } 380 381 /** 382 * Reset all counters. 383 */ resetCounters()384 protected void resetCounters() { 385 mCancelOperationCounter.reset(); 386 mLayoutCallCounter.reset(); 387 mWriteCallCounter.reset(); 388 mFinishCallCounter.reset(); 389 mPrintJobQueuedCallCounter.reset(); 390 mCreateSessionCallCounter.reset(); 391 mDestroySessionCallCounter.reset(); 392 sDestroyActivityCallCounter.reset(); 393 sCreateActivityCallCounter.reset(); 394 } 395 selectPrinter(String printerName)396 protected void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException { 397 try { 398 long delay = 100; 399 while (true) { 400 try { 401 UiObject destinationSpinner = mUiDevice.findObject(new UiSelector().resourceId( 402 "com.android.printspooler:id/destination_spinner")); 403 404 destinationSpinner.click(); 405 406 // Give spinner some time to expand 407 try { 408 Thread.sleep(delay); 409 } catch (InterruptedException e) { 410 // ignore 411 } 412 413 // try to select printer 414 UiObject printerOption = mUiDevice 415 .findObject(new UiSelector().text(printerName)); 416 printerOption.click(); 417 } catch (UiObjectNotFoundException e) { 418 Log.e(LOG_TAG, "Could not select printer " + printerName, e); 419 } 420 421 // Make sure printer is selected 422 if (getUiDevice().hasObject(By.text(printerName))) { 423 break; 424 } else { 425 if (delay <= OPERATION_TIMEOUT_MILLIS) { 426 Log.w(LOG_TAG, "Cannot find printer " + printerName + ", retrying."); 427 delay *= 2; 428 continue; 429 } else { 430 throw new UiObjectNotFoundException("Could find printer " + printerName + 431 " even though we retried"); 432 } 433 } 434 } 435 } catch (UiObjectNotFoundException e) { 436 dumpWindowHierarchy(); 437 throw e; 438 } 439 } 440 answerPrintServicesWarning(boolean confirm)441 protected void answerPrintServicesWarning(boolean confirm) throws UiObjectNotFoundException { 442 UiDevice uiDevice = UiDevice.getInstance(getInstrumentation()); 443 UiObject button; 444 if (confirm) { 445 button = uiDevice.findObject(new UiSelector().resourceId("android:id/button1")); 446 } else { 447 button = uiDevice.findObject(new UiSelector().resourceId("android:id/button2")); 448 } 449 button.click(); 450 } 451 changeOrientation(String orientation)452 protected void changeOrientation(String orientation) 453 throws UiObjectNotFoundException, IOException { 454 try { 455 UiObject orientationSpinner = mUiDevice.findObject(new UiSelector().resourceId( 456 "com.android.printspooler:id/orientation_spinner")); 457 orientationSpinner.click(); 458 UiObject orientationOption = mUiDevice.findObject(new UiSelector().text(orientation)); 459 orientationOption.click(); 460 } catch (UiObjectNotFoundException e) { 461 dumpWindowHierarchy(); 462 throw e; 463 } 464 } 465 getOrientation()466 protected String getOrientation() throws UiObjectNotFoundException, IOException { 467 try { 468 UiObject orientationSpinner = mUiDevice.findObject(new UiSelector().resourceId( 469 "com.android.printspooler:id/orientation_spinner")); 470 return orientationSpinner.getText(); 471 } catch (UiObjectNotFoundException e) { 472 dumpWindowHierarchy(); 473 throw e; 474 } 475 } 476 changeMediaSize(String mediaSize)477 protected void changeMediaSize(String mediaSize) throws UiObjectNotFoundException, IOException { 478 try { 479 UiObject mediaSizeSpinner = mUiDevice.findObject(new UiSelector().resourceId( 480 "com.android.printspooler:id/paper_size_spinner")); 481 mediaSizeSpinner.click(); 482 UiObject mediaSizeOption = mUiDevice.findObject(new UiSelector().text(mediaSize)); 483 mediaSizeOption.click(); 484 } catch (UiObjectNotFoundException e) { 485 dumpWindowHierarchy(); 486 throw e; 487 } 488 } 489 getMediaSize()490 protected String getMediaSize() throws UiObjectNotFoundException, IOException { 491 try { 492 UiObject mediaSizeSpinner = mUiDevice.findObject(new UiSelector().resourceId( 493 "com.android.printspooler:id/paper_size_spinner")); 494 return mediaSizeSpinner.getText(); 495 } catch (UiObjectNotFoundException e) { 496 dumpWindowHierarchy(); 497 throw e; 498 } 499 } 500 changeColor(String color)501 protected void changeColor(String color) throws UiObjectNotFoundException, IOException { 502 try { 503 UiObject colorSpinner = mUiDevice.findObject(new UiSelector().resourceId( 504 "com.android.printspooler:id/color_spinner")); 505 colorSpinner.click(); 506 UiObject colorOption = mUiDevice.findObject(new UiSelector().text(color)); 507 colorOption.click(); 508 } catch (UiObjectNotFoundException e) { 509 dumpWindowHierarchy(); 510 throw e; 511 } 512 } 513 getColor()514 protected String getColor() throws UiObjectNotFoundException, IOException { 515 try { 516 UiObject colorSpinner = mUiDevice.findObject(new UiSelector().resourceId( 517 "com.android.printspooler:id/color_spinner")); 518 return colorSpinner.getText(); 519 } catch (UiObjectNotFoundException e) { 520 dumpWindowHierarchy(); 521 throw e; 522 } 523 } 524 changeDuplex(String duplex)525 protected void changeDuplex(String duplex) throws UiObjectNotFoundException, IOException { 526 try { 527 UiObject duplexSpinner = mUiDevice.findObject(new UiSelector().resourceId( 528 "com.android.printspooler:id/duplex_spinner")); 529 duplexSpinner.click(); 530 UiObject duplexOption = mUiDevice.findObject(new UiSelector().text(duplex)); 531 duplexOption.click(); 532 } catch (UiObjectNotFoundException e) { 533 dumpWindowHierarchy(); 534 throw e; 535 } 536 } 537 getDuplex()538 protected String getDuplex() throws UiObjectNotFoundException, IOException { 539 try { 540 UiObject duplexSpinner = mUiDevice.findObject(new UiSelector().resourceId( 541 "com.android.printspooler:id/duplex_spinner")); 542 return duplexSpinner.getText(); 543 } catch (UiObjectNotFoundException e) { 544 dumpWindowHierarchy(); 545 throw e; 546 } 547 } 548 getCopies()549 protected String getCopies() throws UiObjectNotFoundException, IOException { 550 try { 551 UiObject copies = mUiDevice.findObject(new UiSelector().resourceId( 552 "com.android.printspooler:id/copies_edittext")); 553 return copies.getText(); 554 } catch (UiObjectNotFoundException e) { 555 dumpWindowHierarchy(); 556 throw e; 557 } 558 } 559 clickPrintButton()560 protected void clickPrintButton() throws UiObjectNotFoundException, IOException { 561 try { 562 UiObject printButton = mUiDevice.findObject(new UiSelector().resourceId( 563 "com.android.printspooler:id/print_button")); 564 printButton.click(); 565 } catch (UiObjectNotFoundException e) { 566 dumpWindowHierarchy(); 567 throw e; 568 } 569 } 570 dumpWindowHierarchy()571 protected void dumpWindowHierarchy() throws IOException { 572 ByteArrayOutputStream os = new ByteArrayOutputStream(); 573 mUiDevice.dumpWindowHierarchy(os); 574 575 Log.w(LOG_TAG, "Window hierarchy:"); 576 for (String line : os.toString("UTF-8").split("\n")) { 577 Log.w(LOG_TAG, line); 578 } 579 } 580 getActivity()581 protected PrintDocumentActivity getActivity() { 582 return sActivity; 583 } 584 createActivity()585 protected void createActivity() { 586 int createBefore = getActivityCreateCallbackCallCount(); 587 588 launchActivity(getInstrumentation().getTargetContext().getPackageName(), 589 PrintDocumentActivity.class, null); 590 591 waitForActivityCreateCallbackCalled(createBefore + 1); 592 } 593 openPrintOptions()594 protected void openPrintOptions() throws UiObjectNotFoundException { 595 UiObject expandHandle = mUiDevice.findObject(new UiSelector().resourceId( 596 "com.android.printspooler:id/expand_collapse_handle")); 597 expandHandle.click(); 598 } 599 openCustomPrintOptions()600 protected void openCustomPrintOptions() throws UiObjectNotFoundException { 601 UiObject expandHandle = mUiDevice.findObject(new UiSelector().resourceId( 602 "com.android.printspooler:id/more_options_button")); 603 expandHandle.click(); 604 } 605 clearPrintSpoolerData()606 protected void clearPrintSpoolerData() throws Exception { 607 assertTrue("failed to clear print spooler data", 608 SystemUtil.runShellCommand(getInstrumentation(), String.format( 609 "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME)) 610 .contains(PM_CLEAR_SUCCESS_OUTPUT)); 611 } 612 verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock, PrintAttributes oldAttributes, PrintAttributes newAttributes, final boolean forPreview)613 protected void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock, 614 PrintAttributes oldAttributes, PrintAttributes newAttributes, 615 final boolean forPreview) { 616 inOrder.verify(mock).onLayout(eq(oldAttributes), eq(newAttributes), 617 any(CancellationSignal.class), any(LayoutResultCallback.class), argThat( 618 new BaseMatcher<Bundle>() { 619 @Override 620 public boolean matches(Object item) { 621 Bundle bundle = (Bundle) item; 622 return forPreview == bundle.getBoolean( 623 PrintDocumentAdapter.EXTRA_PRINT_PREVIEW); 624 } 625 626 @Override 627 public void describeTo(Description description) { 628 /* do nothing */ 629 } 630 })); 631 } 632 createMockPrintDocumentAdapter(Answer<Void> layoutAnswer, Answer<Void> writeAnswer, Answer<Void> finishAnswer)633 protected PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer, 634 Answer<Void> writeAnswer, Answer<Void> finishAnswer) { 635 // Create a mock print adapter. 636 PrintDocumentAdapter adapter = mock(PrintDocumentAdapter.class); 637 if (layoutAnswer != null) { 638 doAnswer(layoutAnswer).when(adapter).onLayout(any(PrintAttributes.class), 639 any(PrintAttributes.class), any(CancellationSignal.class), 640 any(LayoutResultCallback.class), any(Bundle.class)); 641 } 642 if (writeAnswer != null) { 643 doAnswer(writeAnswer).when(adapter).onWrite(any(PageRange[].class), 644 any(ParcelFileDescriptor.class), any(CancellationSignal.class), 645 any(WriteResultCallback.class)); 646 } 647 if (finishAnswer != null) { 648 doAnswer(finishAnswer).when(adapter).onFinish(); 649 } 650 return adapter; 651 } 652 653 @SuppressWarnings("unchecked") createMockPrinterDiscoverySessionCallbacks( Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery, Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking, Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking, Answer<Void> onDestroy)654 protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks( 655 Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery, 656 Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking, 657 Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking, 658 Answer<Void> onDestroy) { 659 PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class); 660 661 doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class)); 662 when(callbacks.getSession()).thenCallRealMethod(); 663 664 if (onStartPrinterDiscovery != null) { 665 doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery( 666 any(List.class)); 667 } 668 if (onStopPrinterDiscovery != null) { 669 doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery(); 670 } 671 if (onValidatePrinters != null) { 672 doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters( 673 any(List.class)); 674 } 675 if (onStartPrinterStateTracking != null) { 676 doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking( 677 any(PrinterId.class)); 678 } 679 if (onRequestCustomPrinterIcon != null) { 680 doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon( 681 any(PrinterId.class), any(CancellationSignal.class), 682 any(CustomPrinterIconCallback.class)); 683 } 684 if (onStopPrinterStateTracking != null) { 685 doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking( 686 any(PrinterId.class)); 687 } 688 if (onDestroy != null) { 689 doAnswer(onDestroy).when(callbacks).onDestroy(); 690 } 691 692 return callbacks; 693 } 694 createMockPrintServiceCallbacks( Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks, Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob)695 protected PrintServiceCallbacks createMockPrintServiceCallbacks( 696 Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks, 697 Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) { 698 final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class); 699 700 doCallRealMethod().when(service).setService(any(PrintService.class)); 701 when(service.getService()).thenCallRealMethod(); 702 703 if (onCreatePrinterDiscoverySessionCallbacks != null) { 704 doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service) 705 .onCreatePrinterDiscoverySessionCallbacks(); 706 } 707 if (onPrintJobQueued != null) { 708 doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class)); 709 } 710 if (onRequestCancelPrintJob != null) { 711 doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob( 712 any(PrintJob.class)); 713 } 714 715 return service; 716 } 717 writeBlankPages(PrintAttributes constraints, ParcelFileDescriptor output, int fromIndex, int toIndex)718 protected void writeBlankPages(PrintAttributes constraints, ParcelFileDescriptor output, 719 int fromIndex, int toIndex) throws IOException { 720 PrintedPdfDocument document = new PrintedPdfDocument(getActivity(), constraints); 721 final int pageCount = toIndex - fromIndex + 1; 722 for (int i = 0; i < pageCount; i++) { 723 PdfDocument.Page page = document.startPage(i); 724 document.finishPage(page); 725 } 726 FileOutputStream fos = new FileOutputStream(output.getFileDescriptor()); 727 document.writeTo(fos); 728 document.close(); 729 } 730 731 protected static final class CallCounter { 732 private final Object mLock = new Object(); 733 734 private int mCallCount; 735 call()736 public void call() { 737 synchronized (mLock) { 738 mCallCount++; 739 mLock.notifyAll(); 740 } 741 } 742 getCallCount()743 public int getCallCount() { 744 synchronized (mLock) { 745 return mCallCount; 746 } 747 } 748 reset()749 public void reset() { 750 synchronized (mLock) { 751 mCallCount = 0; 752 } 753 } 754 waitForCount(int count, long timeoutMillis)755 public void waitForCount(int count, long timeoutMillis) throws TimeoutException { 756 synchronized (mLock) { 757 final long startTimeMillis = SystemClock.uptimeMillis(); 758 while (mCallCount < count) { 759 try { 760 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 761 final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; 762 if (remainingTimeMillis <= 0) { 763 throw new TimeoutException(); 764 } 765 mLock.wait(timeoutMillis); 766 } catch (InterruptedException ie) { 767 /* ignore */ 768 } 769 } 770 } 771 } 772 } 773 774 775 /** 776 * Make {@code printerName} the default printer by adding it to the history of printers by 777 * printing once. 778 * 779 * @param adapter The {@link PrintDocumentAdapter} used 780 * @throws Exception If the printer could not be made default 781 */ makeDefaultPrinter(PrintDocumentAdapter adapter, String printerName)782 protected void makeDefaultPrinter(PrintDocumentAdapter adapter, String printerName) 783 throws Exception { 784 // Perform a full print operation on the printer 785 Log.d(LOG_TAG, "print"); 786 print(adapter); 787 Log.d(LOG_TAG, "waitForWriteAdapterCallback"); 788 waitForWriteAdapterCallback(1); 789 Log.d(LOG_TAG, "selectPrinter"); 790 selectPrinter(printerName); 791 Log.d(LOG_TAG, "clickPrintButton"); 792 clickPrintButton(); 793 Log.d(LOG_TAG, "answerPrintServicesWarning"); 794 answerPrintServicesWarning(true); 795 Log.d(LOG_TAG, "waitForPrinterDiscoverySessionDestroyCallbackCalled"); 796 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 797 798 // Switch to new activity, which should now use the default printer 799 Log.d(LOG_TAG, "getActivity().finish()"); 800 getActivity().finish(); 801 802 Log.d(LOG_TAG, "createActivity"); 803 createActivity(); 804 } 805 supportsPrinting()806 protected boolean supportsPrinting() { 807 return getInstrumentation().getContext().getPackageManager() 808 .hasSystemFeature(PackageManager.FEATURE_PRINTING); 809 } 810 } 811