MyPrintService.java revision 7ae2878f5219d55e842348eb44ffb5070b9d708d
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.print.PrintAttributes; 26import android.print.PrintAttributes.Margins; 27import android.print.PrintAttributes.MediaSize; 28import android.print.PrintAttributes.Resolution; 29import android.print.PrintAttributes.Tray; 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.FileDescriptor; 44import java.io.FileInputStream; 45import java.io.FileOutputStream; 46import java.io.IOException; 47import java.io.InputStream; 48import java.io.OutputStream; 49import java.util.ArrayList; 50import java.util.List; 51 52import libcore.io.IoUtils; 53 54public class MyPrintService extends PrintService { 55 56 private static final String LOG_TAG = "MyPrintService"; 57 58 private static final long STANDARD_DELAY_MILLIS = 10000; 59 60 static final String INTENT_EXTRA_ACTION_TYPE = "INTENT_EXTRA_ACTION_TYPE"; 61 static final String INTENT_EXTRA_PRINT_JOB_ID = "INTENT_EXTRA_PRINT_JOB_ID"; 62 63 static final int ACTION_TYPE_ON_PRINT_JOB_PENDING = 1; 64 static final int ACTION_TYPE_ON_REQUEST_CANCEL_PRINT_JOB = 2; 65 66 private static final Object sLock = new Object(); 67 68 private static MyPrintService sInstance; 69 70 private Handler mHandler; 71 72 private AsyncTask<FileDescriptor, Void, Void> mFakePrintTask; 73 74 private FakePrinterDiscoverySession mSession; 75 76 private final SparseArray<PrintJob> mProcessedPrintJobs = new SparseArray<PrintJob>(); 77 78 public static MyPrintService peekInstance() { 79 synchronized (sLock) { 80 return sInstance; 81 } 82 } 83 84 @Override 85 protected void onConnected() { 86 Log.i(LOG_TAG, "#onConnected()"); 87 mHandler = new MyHandler(getMainLooper()); 88 synchronized (sLock) { 89 sInstance = this; 90 } 91 } 92 93 @Override 94 protected void onDisconnected() { 95 Log.i(LOG_TAG, "#onDisconnected()"); 96 if (mSession != null) { 97 mSession.cancellAddingFakePrinters(); 98 } 99 synchronized (sLock) { 100 sInstance = null; 101 } 102 } 103 104 @Override 105 protected PrinterDiscoverySession onCreatePrinterDiscoverySession() { 106 Log.i(LOG_TAG, "#onCreatePrinterDiscoverySession()"); 107 return new FakePrinterDiscoverySession(); 108 } 109 110 @Override 111 protected void onRequestCancelPrintJob(final PrintJob printJob) { 112 Log.i(LOG_TAG, "#onRequestCancelPrintJob()"); 113 mProcessedPrintJobs.put(printJob.getId(), printJob); 114 Intent intent = new Intent(this, MyDialogActivity.class); 115 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 116 intent.putExtra(INTENT_EXTRA_PRINT_JOB_ID, printJob.getId()); 117 intent.putExtra(INTENT_EXTRA_ACTION_TYPE, ACTION_TYPE_ON_REQUEST_CANCEL_PRINT_JOB); 118 startActivity(intent); 119 } 120 121 @Override 122 public void onPrintJobQueued(final PrintJob printJob) { 123 Log.i(LOG_TAG, "#onPrintJobQueued()"); 124 mProcessedPrintJobs.put(printJob.getId(), printJob); 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 handleQueuedPrintJobDelayed(int printJobId) { 163 Message message = mHandler.obtainMessage( 164 MyHandler.MSG_HANDLE_DO_PRINT_JOB, printJobId, 0); 165 mHandler.sendMessageDelayed(message, STANDARD_DELAY_MILLIS); 166 } 167 168 void handleQueuedPrintJob(int printJobId) { 169 final PrintJob printJob = mProcessedPrintJobs.get(printJobId); 170 if (printJob == null) { 171 return; 172 } 173 174 if (printJob.isQueued()) { 175 printJob.start(); 176 } 177 178 final PrintJobInfo info = printJob.getInfo(); 179 final File file = new File(getFilesDir(), info.getLabel() + ".pdf"); 180 181 mFakePrintTask = new AsyncTask<FileDescriptor, Void, Void>() { 182 @Override 183 protected Void doInBackground(FileDescriptor... params) { 184 InputStream in = new BufferedInputStream(new FileInputStream(params[0])); 185 OutputStream out = null; 186 try { 187 out = new BufferedOutputStream(new FileOutputStream(file)); 188 final byte[] buffer = new byte[8192]; 189 while (true) { 190 if (isCancelled()) { 191 break; 192 } 193 final int readByteCount = in.read(buffer); 194 if (readByteCount < 0) { 195 break; 196 } 197 out.write(buffer, 0, readByteCount); 198 } 199 } catch (IOException ioe) { 200 throw new RuntimeException(ioe); 201 } finally { 202 IoUtils.closeQuietly(in); 203 IoUtils.closeQuietly(out); 204 if (isCancelled()) { 205 file.delete(); 206 } 207 } 208 return null; 209 } 210 211 @Override 212 protected void onPostExecute(Void result) { 213 if (printJob.isStarted()) { 214 printJob.complete(); 215 } 216 217 file.setReadable(true, false); 218 219 // Quick and dirty to show the file - use a content provider instead. 220 Intent intent = new Intent(Intent.ACTION_VIEW); 221 intent.setDataAndType(Uri.fromFile(file), "application/pdf"); 222 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 223 startActivity(intent, null); 224 225 mFakePrintTask = null; 226 } 227 228 @Override 229 protected void onCancelled(Void result) { 230 if (printJob.isStarted()) { 231 printJob.cancel(); 232 } 233 } 234 }; 235 mFakePrintTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, 236 printJob.getDocument().getData()); 237 } 238 239 private final class MyHandler extends Handler { 240 public static final int MSG_HANDLE_DO_PRINT_JOB = 1; 241 public static final int MSG_HANDLE_FAIL_PRINT_JOB = 2; 242 243 public MyHandler(Looper looper) { 244 super(looper, null, true); 245 } 246 247 @Override 248 public void handleMessage(Message message) { 249 switch (message.what) { 250 case MSG_HANDLE_DO_PRINT_JOB: { 251 final int printJobId = message.arg1; 252 handleQueuedPrintJob(printJobId); 253 } break; 254 255 case MSG_HANDLE_FAIL_PRINT_JOB: { 256 final int printJobId = message.arg1; 257 handleFailPrintJob(printJobId); 258 } break; 259 } 260 } 261 } 262 263 private final class FakePrinterDiscoverySession extends PrinterDiscoverySession { 264 private final Handler mSesionHandler = new SessionHandler(getMainLooper()); 265 266 private final List<PrinterInfo> mFakePrinters = new ArrayList<PrinterInfo>(); 267 268 public FakePrinterDiscoverySession() { 269 for (int i = 0; i < 1000; i++) { 270 String printerName = "Printer " + i; 271 PrinterInfo printer = new PrinterInfo.Builder(generatePrinterId(printerName), 272 printerName, PrinterInfo.STATUS_READY).create(); 273 mFakePrinters.add(printer); 274 } 275 } 276 277 @Override 278 public void onDestroy() { 279 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onDestroy()"); 280 mSesionHandler.removeMessages(SessionHandler.MSG_ADD_FIRST_BATCH_FAKE_PRINTERS); 281 mSesionHandler.removeMessages(SessionHandler.MSG_ADD_SECOND_BATCH_FAKE_PRINTERS); 282 } 283 284 @Override 285 public void onStartPrinterDiscovery(List<PrinterId> priorityList) { 286 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onStartPrinterDiscovery()"); 287 Message message1 = mSesionHandler.obtainMessage( 288 SessionHandler.MSG_ADD_FIRST_BATCH_FAKE_PRINTERS, this); 289 mSesionHandler.sendMessageDelayed(message1, 0); 290 291 Message message2 = mSesionHandler.obtainMessage( 292 SessionHandler.MSG_ADD_SECOND_BATCH_FAKE_PRINTERS, this); 293 mSesionHandler.sendMessageDelayed(message2, 10000); 294 } 295 296 @Override 297 public void onStopPrinterDiscovery() { 298 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onStopPrinterDiscovery()"); 299 cancellAddingFakePrinters(); 300 } 301 302 @Override 303 public void onRequestPrinterUpdate(PrinterId printerId) { 304 Log.i(LOG_TAG, "FakePrinterDiscoverySession#onRequestPrinterUpdate()"); 305 PrinterInfo printer = findPrinterInfo(printerId); 306 if (printer != null) { 307 PrinterCapabilitiesInfo capabilities = 308 new PrinterCapabilitiesInfo.Builder(printerId) 309 .setMinMargins(new Margins(0, 0, 0, 0), new Margins(0, 0, 0, 0)) 310 .addMediaSize(MediaSize.createMediaSize(getPackageManager(), 311 MediaSize.ISO_A4), true) 312 .addMediaSize(MediaSize.createMediaSize(getPackageManager(), 313 MediaSize.ISO_A5), false) 314 .addResolution(new Resolution("R1", getString( 315 R.string.resolution_200x200), 200, 200), true) 316 .addResolution(new Resolution("R2", getString( 317 R.string.resolution_300x300), 300, 300), false) 318 .addInputTray(new Tray("FirstInputTray", getString( 319 R.string.input_tray_first)), false) 320 .addInputTray(new Tray("SecondInputTray", getString( 321 R.string.input_tray_second)), true) 322 .addOutputTray(new Tray("FirstOutputTray", getString( 323 R.string.output_tray_first)), false) 324 .addOutputTray(new Tray("SecondOutputTray", getString( 325 R.string.output_tray_second)), true) 326 .setDuplexModes(PrintAttributes.DUPLEX_MODE_NONE 327 | PrintAttributes.DUPLEX_MODE_LONG_EDGE 328 | PrintAttributes.DUPLEX_MODE_SHORT_EDGE, 329 PrintAttributes.DUPLEX_MODE_SHORT_EDGE) 330 .setColorModes(PrintAttributes.COLOR_MODE_COLOR 331 | PrintAttributes.COLOR_MODE_MONOCHROME, 332 PrintAttributes.COLOR_MODE_MONOCHROME) 333 .setFittingModes(PrintAttributes.FITTING_MODE_FIT_TO_PAGE 334 | PrintAttributes.FITTING_MODE_NONE, 335 PrintAttributes.FITTING_MODE_FIT_TO_PAGE) 336 .setOrientations(PrintAttributes.ORIENTATION_PORTRAIT 337 | PrintAttributes.ORIENTATION_LANDSCAPE, 338 PrintAttributes.ORIENTATION_LANDSCAPE) 339 .create(); 340 341 printer = new PrinterInfo.Builder(printer) 342 .setCapabilities(capabilities) 343 .create(); 344 345 List<PrinterInfo> printers = new ArrayList<PrinterInfo>(); 346 printers.add(printer); 347 updatePrinters(printers); 348 } 349 } 350 351 private void addFirstBatchFakePrinters() { 352 List<PrinterInfo> printers = mFakePrinters.subList(0, mFakePrinters.size() / 2); 353 addPrinters(printers); 354 } 355 356 private void addSecondBatchFakePrinters() { 357 List<PrinterInfo> printers = mFakePrinters.subList(mFakePrinters.size() / 2, 358 mFakePrinters.size()); 359 addPrinters(printers); 360 } 361 362 private PrinterInfo findPrinterInfo(PrinterId printerId) { 363 List<PrinterInfo> printers = getPrinters(); 364 final int printerCount = getPrinters().size(); 365 for (int i = 0; i < printerCount; i++) { 366 PrinterInfo printer = printers.get(i); 367 if (printer.getId().equals(printerId)) { 368 return printer; 369 } 370 } 371 return null; 372 } 373 374 private void cancellAddingFakePrinters() { 375 mSesionHandler.removeMessages(SessionHandler.MSG_ADD_FIRST_BATCH_FAKE_PRINTERS); 376 mSesionHandler.removeMessages(SessionHandler.MSG_ADD_SECOND_BATCH_FAKE_PRINTERS); 377 } 378 379 final class SessionHandler extends Handler { 380 public static final int MSG_ADD_FIRST_BATCH_FAKE_PRINTERS = 1; 381 public static final int MSG_ADD_SECOND_BATCH_FAKE_PRINTERS = 2; 382 383 public SessionHandler(Looper looper) { 384 super(looper, null, true); 385 } 386 387 @Override 388 public void handleMessage(Message message) { 389 switch (message.what) { 390 case MSG_ADD_FIRST_BATCH_FAKE_PRINTERS: { 391 addFirstBatchFakePrinters(); 392 } break; 393 394 case MSG_ADD_SECOND_BATCH_FAKE_PRINTERS: { 395 addSecondBatchFakePrinters(); 396 } break; 397 } 398 } 399 } 400 } 401} 402