MyPrintService.java revision 9981caa9c40383230c2e08fade7cbe03e2d96125
1/* 2 * Copyright (C) 2013 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 17package foo.bar.printservice; 18 19import android.annotation.NonNull; 20import android.content.Intent; 21import android.net.Uri; 22import android.os.AsyncTask; 23import android.os.Handler; 24import android.os.Looper; 25import android.os.Message; 26import android.os.ParcelFileDescriptor; 27import android.print.PrintAttributes; 28import android.print.PrintAttributes.Margins; 29import android.print.PrintAttributes.MediaSize; 30import android.print.PrintAttributes.Resolution; 31import android.print.PrintJobId; 32import android.print.PrintJobInfo; 33import android.print.PrinterCapabilitiesInfo; 34import android.print.PrinterId; 35import android.print.PrinterInfo; 36import android.printservice.PrintJob; 37import android.printservice.PrintService; 38import android.printservice.PrinterDiscoverySession; 39import android.util.ArrayMap; 40import android.util.Log; 41import android.widget.ProgressBar; 42 43import java.io.BufferedInputStream; 44import java.io.BufferedOutputStream; 45import java.io.File; 46import java.io.FileInputStream; 47import java.io.FileOutputStream; 48import java.io.IOException; 49import java.io.InputStream; 50import java.io.OutputStream; 51import java.util.ArrayList; 52import java.util.List; 53import java.util.Map; 54 55public class MyPrintService extends PrintService { 56 57 private static final String LOG_TAG = "MyPrintService"; 58 59 private static final long STANDARD_DELAY_MILLIS = 10000000; 60 61 static final String INTENT_EXTRA_ACTION_TYPE = "INTENT_EXTRA_ACTION_TYPE"; 62 static final String INTENT_EXTRA_PRINT_JOB_ID = "INTENT_EXTRA_PRINT_JOB_ID"; 63 64 static final int ACTION_TYPE_ON_PRINT_JOB_PENDING = 1; 65 static final int ACTION_TYPE_ON_REQUEST_CANCEL_PRINT_JOB = 2; 66 67 private static final Object sLock = new Object(); 68 69 private static MyPrintService sInstance; 70 71 private Handler mHandler; 72 73 private AsyncTask<ParcelFileDescriptor, Void, Void> mFakePrintTask; 74 75 private FakePrinterDiscoverySession mSession; 76 77 private final Map<PrintJobId, PrintJob> mProcessedPrintJobs = 78 new ArrayMap<PrintJobId, PrintJob>(); 79 80 public static MyPrintService peekInstance() { 81 synchronized (sLock) { 82 return sInstance; 83 } 84 } 85 86 @Override 87 protected void onConnected() { 88 Log.i(LOG_TAG, "#onConnected()"); 89 mHandler = new MyHandler(getMainLooper()); 90 synchronized (sLock) { 91 sInstance = this; 92 } 93 } 94 95 @Override 96 protected void onDisconnected() { 97 Log.i(LOG_TAG, "#onDisconnected()"); 98 if (mSession != null) { 99 mSession.cancellAddingFakePrinters(); 100 } 101 synchronized (sLock) { 102 sInstance = null; 103 } 104 } 105 106 @Override 107 protected PrinterDiscoverySession onCreatePrinterDiscoverySession() { 108 Log.i(LOG_TAG, "#onCreatePrinterDiscoverySession()"); 109 return new FakePrinterDiscoverySession(); 110 } 111 112 @Override 113 protected void onRequestCancelPrintJob(final PrintJob printJob) { 114 Log.i(LOG_TAG, "#onRequestCancelPrintJob()"); 115 mProcessedPrintJobs.put(printJob.getId(), printJob); 116 Intent intent = new Intent(this, MyDialogActivity.class); 117 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 118 intent.putExtra(INTENT_EXTRA_PRINT_JOB_ID, printJob.getId()); 119 intent.putExtra(INTENT_EXTRA_ACTION_TYPE, ACTION_TYPE_ON_REQUEST_CANCEL_PRINT_JOB); 120 startActivity(intent); 121 } 122 123 @Override 124 public void onPrintJobQueued(final PrintJob printJob) { 125 Log.i(LOG_TAG, "#onPrintJobQueued()"); 126 mProcessedPrintJobs.put(printJob.getId(), printJob); 127 if (printJob.isQueued()) { 128 printJob.start(); 129 } 130 131 Intent intent = new Intent(this, MyDialogActivity.class); 132 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 133 intent.putExtra(INTENT_EXTRA_PRINT_JOB_ID, printJob.getId()); 134 intent.putExtra(INTENT_EXTRA_ACTION_TYPE, ACTION_TYPE_ON_PRINT_JOB_PENDING); 135 startActivity(intent); 136 } 137 138 void handleRequestCancelPrintJob(PrintJobId printJobId) { 139 PrintJob printJob = mProcessedPrintJobs.get(printJobId); 140 if (printJob == null) { 141 return; 142 } 143 mProcessedPrintJobs.remove(printJobId); 144 if (printJob.isQueued() || printJob.isStarted() || printJob.isBlocked()) { 145 mHandler.removeMessages(MyHandler.MSG_HANDLE_DO_PRINT_JOB); 146 mHandler.removeMessages(MyHandler.MSG_HANDLE_FAIL_PRINT_JOB); 147 printJob.cancel(); 148 } 149 } 150 151 void handleFailPrintJobDelayed(PrintJobId printJobId) { 152 Message message = mHandler.obtainMessage( 153 MyHandler.MSG_HANDLE_FAIL_PRINT_JOB, printJobId); 154 mHandler.sendMessageDelayed(message, STANDARD_DELAY_MILLIS); 155 } 156 157 void handleFailPrintJob(PrintJobId printJobId) { 158 PrintJob printJob = mProcessedPrintJobs.get(printJobId); 159 if (printJob == null) { 160 return; 161 } 162 mProcessedPrintJobs.remove(printJobId); 163 if (printJob.isQueued() || printJob.isStarted()) { 164 printJob.fail(getString(R.string.fail_reason)); 165 } 166 } 167 168 void handleBlockPrintJobDelayed(PrintJobId printJobId) { 169 Message message = mHandler.obtainMessage( 170 MyHandler.MSG_HANDLE_BLOCK_PRINT_JOB, printJobId); 171 mHandler.sendMessageDelayed(message, STANDARD_DELAY_MILLIS); 172 } 173 174 void handleBlockPrintJob(PrintJobId printJobId) { 175 final PrintJob printJob = mProcessedPrintJobs.get(printJobId); 176 if (printJob == null) { 177 return; 178 } 179 180 if (printJob.isStarted()) { 181 printJob.block("Gimme some rest, dude"); 182 } 183 } 184 185 void handleBlockAndDelayedUnblockPrintJob(PrintJobId printJobId) { 186 handleBlockPrintJob(printJobId); 187 188 Message message = mHandler.obtainMessage( 189 MyHandler.MSG_HANDLE_UNBLOCK_PRINT_JOB, printJobId); 190 mHandler.sendMessageDelayed(message, STANDARD_DELAY_MILLIS); 191 } 192 193 void handleUnblockPrintJob(PrintJobId printJobId) { 194 final PrintJob printJob = mProcessedPrintJobs.get(printJobId); 195 if (printJob == null) { 196 return; 197 } 198 199 if (printJob.isBlocked()) { 200 printJob.start(); 201 } 202 } 203 204 void handleQueuedPrintJobDelayed(PrintJobId printJobId) { 205 final PrintJob printJob = mProcessedPrintJobs.get(printJobId); 206 if (printJob == null) { 207 return; 208 } 209 210 if (printJob.isQueued()) { 211 printJob.start(); 212 } 213 Message message = mHandler.obtainMessage( 214 MyHandler.MSG_HANDLE_DO_PRINT_JOB, printJobId); 215 mHandler.sendMessageDelayed(message, STANDARD_DELAY_MILLIS); 216 } 217 218 /** 219 * Pretend that the print job has progressed. 220 * 221 * @param printJobId ID of the print job to progress 222 * @param progress the new value to progress to 223 */ 224 void handlePrintJobProgress(@NonNull PrintJobId printJobId, int progress) { 225 final PrintJob printJob = mProcessedPrintJobs.get(printJobId); 226 if (printJob == null) { 227 return; 228 } 229 230 if (printJob.isQueued()) { 231 printJob.start(); 232 } 233 234 if (progress == 100) { 235 handleQueuedPrintJob(printJobId); 236 } else { 237 printJob.setProgress((float)progress / 100); 238 printJob.setStatus("Printing progress: " + progress + "%"); 239 240 Message message = mHandler.obtainMessage( 241 MyHandler.MSG_HANDLE_PRINT_JOB_PROGRESS, progress + 10, 0, printJobId); 242 mHandler.sendMessageDelayed(message, 1000); 243 } 244 } 245 246 void handleQueuedPrintJob(PrintJobId printJobId) { 247 final PrintJob printJob = mProcessedPrintJobs.get(printJobId); 248 if (printJob == null) { 249 return; 250 } 251 252 if (printJob.isQueued()) { 253 printJob.start(); 254 } 255 256 final PrintJobInfo info = printJob.getInfo(); 257 final File file = new File(getFilesDir(), info.getLabel() + ".pdf"); 258 259 mFakePrintTask = new AsyncTask<ParcelFileDescriptor, Void, Void>() { 260 @Override 261 protected Void doInBackground(ParcelFileDescriptor... params) { 262 InputStream in = new BufferedInputStream(new FileInputStream( 263 params[0].getFileDescriptor())); 264 OutputStream out = null; 265 try { 266 out = new BufferedOutputStream(new FileOutputStream(file)); 267 final byte[] buffer = new byte[8192]; 268 while (true) { 269 if (isCancelled()) { 270 break; 271 } 272 final int readByteCount = in.read(buffer); 273 if (readByteCount < 0) { 274 break; 275 } 276 out.write(buffer, 0, readByteCount); 277 } 278 } catch (IOException ioe) { 279 throw new RuntimeException(ioe); 280 } finally { 281 if (in != null) { 282 try { 283 in.close(); 284 } catch (IOException ioe) { 285 /* ignore */ 286 } 287 } 288 if (out != null) { 289 try { 290 out.close(); 291 } catch (IOException ioe) { 292 /* ignore */ 293 } 294 } 295 if (isCancelled()) { 296 file.delete(); 297 } 298 } 299 return null; 300 } 301 302 @Override 303 protected void onPostExecute(Void result) { 304 if (printJob.isStarted()) { 305 printJob.complete(); 306 } 307 308 file.setReadable(true, false); 309 310 // Quick and dirty to show the file - use a content provider instead. 311 Intent intent = new Intent(Intent.ACTION_VIEW); 312 intent.setDataAndType(Uri.fromFile(file), "application/pdf"); 313 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 314 startActivity(intent, null); 315 316 mFakePrintTask = null; 317 } 318 319 @Override 320 protected void onCancelled(Void result) { 321 if (printJob.isStarted()) { 322 printJob.cancel(); 323 } 324 } 325 }; 326 mFakePrintTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, 327 printJob.getDocument().getData()); 328 } 329 330 private final class MyHandler extends Handler { 331 public static final int MSG_HANDLE_DO_PRINT_JOB = 1; 332 public static final int MSG_HANDLE_FAIL_PRINT_JOB = 2; 333 public static final int MSG_HANDLE_BLOCK_PRINT_JOB = 3; 334 public static final int MSG_HANDLE_UNBLOCK_PRINT_JOB = 4; 335 public static final int MSG_HANDLE_PRINT_JOB_PROGRESS = 5; 336 337 public MyHandler(Looper looper) { 338 super(looper); 339 } 340 341 @Override 342 public void handleMessage(Message message) { 343 switch (message.what) { 344 case MSG_HANDLE_DO_PRINT_JOB: { 345 PrintJobId printJobId = (PrintJobId) message.obj; 346 handleQueuedPrintJob(printJobId); 347 } break; 348 349 case MSG_HANDLE_FAIL_PRINT_JOB: { 350 PrintJobId printJobId = (PrintJobId) message.obj; 351 handleFailPrintJob(printJobId); 352 } break; 353 354 case MSG_HANDLE_BLOCK_PRINT_JOB: { 355 PrintJobId printJobId = (PrintJobId) message.obj; 356 handleBlockPrintJob(printJobId); 357 } break; 358 359 case MSG_HANDLE_UNBLOCK_PRINT_JOB: { 360 PrintJobId printJobId = (PrintJobId) message.obj; 361 handleUnblockPrintJob(printJobId); 362 } break; 363 364 case MSG_HANDLE_PRINT_JOB_PROGRESS: { 365 PrintJobId printJobId = (PrintJobId) message.obj; 366 handlePrintJobProgress(printJobId, message.arg1); 367 } break; 368 } 369 } 370 } 371 372 private final class FakePrinterDiscoverySession extends PrinterDiscoverySession { 373 private final Handler mSesionHandler = new SessionHandler(getMainLooper()); 374 375 private final List<PrinterInfo> mFakePrinters = new ArrayList<PrinterInfo>(); 376 377 public FakePrinterDiscoverySession() { 378 for (int i = 0; i < 2; i++) { 379 String name = "Printer " + i; 380 PrinterInfo printer = new PrinterInfo 381 .Builder(generatePrinterId(name), name, (i % 2 == 1) 382 ? PrinterInfo.STATUS_UNAVAILABLE : PrinterInfo.STATUS_IDLE) 383 .build(); 384 mFakePrinters.add(printer); 385 } 386 } 387 388 @Override 389 public void onDestroy() { 390 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onDestroy()"); 391 mSesionHandler.removeMessages(SessionHandler.MSG_ADD_FIRST_BATCH_FAKE_PRINTERS); 392 mSesionHandler.removeMessages(SessionHandler.MSG_ADD_SECOND_BATCH_FAKE_PRINTERS); 393 } 394 395 @Override 396 public void onStartPrinterDiscovery(List<PrinterId> priorityList) { 397 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onStartPrinterDiscovery()"); 398 Message message1 = mSesionHandler.obtainMessage( 399 SessionHandler.MSG_ADD_FIRST_BATCH_FAKE_PRINTERS, this); 400 mSesionHandler.sendMessageDelayed(message1, 0); 401 402 Message message2 = mSesionHandler.obtainMessage( 403 SessionHandler.MSG_ADD_SECOND_BATCH_FAKE_PRINTERS, this); 404// mSesionHandler.sendMessageDelayed(message2, 10000); 405 } 406 407 @Override 408 public void onStopPrinterDiscovery() { 409 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onStopPrinterDiscovery()"); 410 cancellAddingFakePrinters(); 411 } 412 413 @Override 414 public void onStartPrinterStateTracking(PrinterId printerId) { 415 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onStartPrinterStateTracking()"); 416 PrinterInfo printer = findPrinterInfo(printerId); 417 if (printer != null) { 418 PrinterCapabilitiesInfo capabilities = 419 new PrinterCapabilitiesInfo.Builder(printerId) 420 .setMinMargins(new Margins(200, 200, 200, 200)) 421 .addMediaSize(MediaSize.ISO_A4, true) 422 .addMediaSize(MediaSize.NA_GOVT_LETTER, false) 423 .addMediaSize(MediaSize.JPN_YOU4, false) 424 .addResolution(new Resolution("R1", getString( 425 R.string.resolution_200x200), 200, 200), false) 426 .addResolution(new Resolution("R2", getString( 427 R.string.resolution_300x300), 300, 300), true) 428 .setColorModes(PrintAttributes.COLOR_MODE_COLOR 429 | PrintAttributes.COLOR_MODE_MONOCHROME, 430 PrintAttributes.COLOR_MODE_MONOCHROME) 431 .setDuplexModes(PrintAttributes.DUPLEX_MODE_LONG_EDGE 432 | PrintAttributes.DUPLEX_MODE_NONE, 433 PrintAttributes.DUPLEX_MODE_LONG_EDGE) 434 .build(); 435 436 printer = new PrinterInfo.Builder(printer) 437 .setCapabilities(capabilities) 438 .build(); 439 440 List<PrinterInfo> printers = new ArrayList<PrinterInfo>(); 441 printers.add(printer); 442 addPrinters(printers); 443 } 444 } 445 446 @Override 447 public void onValidatePrinters(List<PrinterId> printerIds) { 448 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onValidatePrinters() " + printerIds); 449 } 450 451 @Override 452 public void onStopPrinterStateTracking(PrinterId printerId) { 453 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onStopPrinterStateTracking()"); 454 } 455 456 private void addFirstBatchFakePrinters() { 457 List<PrinterInfo> printers = mFakePrinters.subList(0, mFakePrinters.size() / 2); 458 addPrinters(printers); 459 } 460 461 private void addSecondBatchFakePrinters() { 462 List<PrinterInfo> printers = mFakePrinters.subList(0, mFakePrinters.size() / 2 463 /* mFakePrinters.size() / 2, mFakePrinters.size()*/); 464 final int printerCount = mFakePrinters.size(); 465 for (int i = printerCount - 1; i >= 0; i--) { 466 PrinterInfo printer = new PrinterInfo.Builder(mFakePrinters.get(i)) 467 .setStatus(PrinterInfo.STATUS_UNAVAILABLE).build(); 468 printers.add(printer); 469 } 470 addPrinters(printers); 471 } 472 473 private PrinterInfo findPrinterInfo(PrinterId printerId) { 474 List<PrinterInfo> printers = getPrinters(); 475 final int printerCount = getPrinters().size(); 476 for (int i = 0; i < printerCount; i++) { 477 PrinterInfo printer = printers.get(i); 478 if (printer.getId().equals(printerId)) { 479 return printer; 480 } 481 } 482 return null; 483 } 484 485 private void cancellAddingFakePrinters() { 486 mSesionHandler.removeMessages(SessionHandler.MSG_ADD_FIRST_BATCH_FAKE_PRINTERS); 487 mSesionHandler.removeMessages(SessionHandler.MSG_ADD_SECOND_BATCH_FAKE_PRINTERS); 488 } 489 490 final class SessionHandler extends Handler { 491 public static final int MSG_ADD_FIRST_BATCH_FAKE_PRINTERS = 1; 492 public static final int MSG_ADD_SECOND_BATCH_FAKE_PRINTERS = 2; 493 494 public SessionHandler(Looper looper) { 495 super(looper); 496 } 497 498 @Override 499 public void handleMessage(Message message) { 500 switch (message.what) { 501 case MSG_ADD_FIRST_BATCH_FAKE_PRINTERS: { 502 addFirstBatchFakePrinters(); 503 } break; 504 505 case MSG_ADD_SECOND_BATCH_FAKE_PRINTERS: { 506 addSecondBatchFakePrinters(); 507 } break; 508 } 509 } 510 } 511 } 512} 513