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