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