• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
peekInstance()78     public static MyPrintService peekInstance() {
79         synchronized (sLock) {
80             return sInstance;
81         }
82     }
83 
84     @Override
onConnected()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
onDisconnected()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
onCreatePrinterDiscoverySession()105     protected PrinterDiscoverySession onCreatePrinterDiscoverySession() {
106         Log.i(LOG_TAG, "#onCreatePrinterDiscoverySession()");
107         return new FakePrinterDiscoverySession();
108     }
109 
110     @Override
onRequestCancelPrintJob(final PrintJob printJob)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
onPrintJobQueued(final PrintJob printJob)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 
handleRequestCancelPrintJob(PrintJobId printJobId)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 
handleFailPrintJobDelayed(PrintJobId printJobId)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 
handleFailPrintJob(PrintJobId printJobId)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 
handleBlockPrintJobDelayed(PrintJobId printJobId)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 
handleBlockPrintJob(PrintJobId printJobId)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 
handleBlockAndDelayedUnblockPrintJob(PrintJobId printJobId)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 
handleUnblockPrintJob(PrintJobId printJobId)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 
handleQueuedPrintJobDelayed(PrintJobId printJobId)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 
handleQueuedPrintJob(PrintJobId printJobId)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 
MyHandler(Looper looper)306         public MyHandler(Looper looper) {
307             super(looper);
308         }
309 
310         @Override
handleMessage(Message message)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 
FakePrinterDiscoverySession()341         public FakePrinterDiscoverySession() {
342             for (int i = 0; i < 2; 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
onDestroy()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
onStartPrinterDiscovery(List<PrinterId> priorityList)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
onStopPrinterDiscovery()372         public void onStopPrinterDiscovery() {
373             Log.i(LOG_TAG, "FakePrinterDiscoverySession#onStopPrinterDiscovery()");
374             cancellAddingFakePrinters();
375         }
376 
377         @Override
onStartPrinterStateTracking(PrinterId printerId)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
onValidatePrinters(List<PrinterId> printerIds)407         public void onValidatePrinters(List<PrinterId> printerIds) {
408             Log.i(LOG_TAG, "FakePrinterDiscoverySession#onValidatePrinters() " + printerIds);
409         }
410 
411         @Override
onStopPrinterStateTracking(PrinterId printerId)412         public void onStopPrinterStateTracking(PrinterId printerId) {
413             Log.i(LOG_TAG, "FakePrinterDiscoverySession#onStopPrinterStateTracking()");
414         }
415 
addFirstBatchFakePrinters()416         private void addFirstBatchFakePrinters() {
417             List<PrinterInfo> printers = mFakePrinters.subList(0, mFakePrinters.size() / 2);
418             addPrinters(printers);
419         }
420 
addSecondBatchFakePrinters()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 
findPrinterInfo(PrinterId printerId)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 
cancellAddingFakePrinters()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 
SessionHandler(Looper looper)454             public SessionHandler(Looper looper) {
455                 super(looper);
456             }
457 
458             @Override
handleMessage(Message message)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