Android Gets A Print Framework

Written by: on November 14, 2013

Have you ever tried adding the option to print a document to your Android application?

Prior to the release of Android 4.4, KitKat, there were no Android APIs specifically for printing. In order to implement printing, you had to rely on a third party solution or implement protocols such as SMB/CIFS to directly communicate with the printer via Wi-Fi, Bluetooth or USB. Since most of us won’t be attempting to directly communicate with a printer, relying on a solution which already exists would be the best choice. In fact, it is essentially what the new additions to the framework allow you to do, albeit a bit more cleanly and reliably.

Cloud Printing

Google Cloud Print service first debuted in April, 2010. GCP is a web-based printer and print job management system which enables printing from any web-connected device to any printer setup with the GCP service. A user can add just about any printer to their GCP service, even older, non-‘Cloud Ready’ printers, provided that the printer is sharable from a web-connected computer with Google Chrome installed. Using GCP, in this author’s opinion, is the easiest way to allow printing from an Android device running an OS prior to 4.4. GCP is also the primary print service that you should expect to be on any device with Android 4.4, so go ahead and check it out here.

Printing Prior to KitKat

The Google Cloud Print service provides a set of APIs for Google account authentication, submitting print jobs and receiving print jobs. You can check out the developer guide for Google Cloud Print here. I won’t go into any implementation details because this approach is a bit long winded and gains you little. You still rely on the user having a properly set up GCP account with printers attached, but you avoid relying on either of the two approaches I am about to discuss.

Google Cloud Print App

Cloud Print by Paulo Fernandes was the first Android app to leverage the GCP APIs and enable printing from an Android device with the GCP service. Two years later, in June of 2013, Google released it’s own version called Google Cloud Print. Since Google Cloud Print will likely be more popular going forward, I will use that as an example, though I suspect many cloud print apps could be leveraged in a similar fashion. First, we need to use the PackageManager to ensure the device your app is running on has Google Cloud Print installed using private boolean hasGoogleCloudPrint() {

If the device does have the Google Cloud Print app installed, we can go ahead and use an intent to start printing:

If the device does not have Google Cloud Print installed, you can either send the user to the Play Store to download it:

or you can open up the Google Cloud Print web interface in a WebView. It is omitted for brevity, but you can find a complete example here.

Printing With KitKat

With the addition of the new print APIs, printing has become a bit easier and more reliable. Here is a breakdown of the API updates organized by package:

android.support.v4.print

  • PrintHelper – Useful for creating print jobs to print bitmaps

android.print

  • PrintDocumentAdapter – base class for providing custom print documents
  • PrintManager – allows access to print jobs for this application and facilitates printing with a PrintDocumentAdapter

android.print.pdf

  • PrintedPdfDocument – helper for creating PDFs based on the specified PrintAttributes

android.webkit

  • WebView.createPrintDocumentAdapter – creates a PrintDocumentAdapter for printing the contents of the WebView

android.printservice

  • Contains classes for implementing your own PrintService

Let’s start with android.printservice. This package contains classes relevant to implementing your own print service. A print service abstracts communication with the actual printer (or another intermediary such as GCP). The Google Cloud Print app installed on KitKat devices implements this service for creating print jobs on GCP. The actual implementation of a print service is beyond the scope of this article, but it is useful to be aware of PrintService, as at least one is required to do any actual printing with the other classes mentioned later on. (Note: Most KitKat devices come with Google Cloud Print, Chrome, Drive, Gallery and Quickoffice pre-installed, all of which provide print services)

Next, let’s look at PrintHelper which is located in android.support.v4.print. You might think that, since this class is in the support package, you can leverage it to print on older devices; unfortunately this does not seem to be the case. The PrintHelper class has a static method called systemSupportsPrint(). From my tests, I found that this method returns true only on KitKat devices. Also, trying to call the printBitmap() method on any device where systemSupportsPrint() returns false does nothing. So, for now we are left to wonder why the PrintHelper class was included in the support package, given that it seems to have no use, at least not currently.

Anyway, the PrintHelper is useful for printing on KitKat devices and provides a simple and straightforward method for printing Bitmaps.

  • You can specify a scale mode, FILL or FIT, which behave as expected. FILL will fill the print area with the given image, maintaining aspect ratio but potentially cropping. FIT will fill either the horizontal or vertical area depending on your printed document size and the size of your image, once again maintaining aspect ratio but with no cropping, so there will be some whitespace unless your image is the same ratio as your printed document.
  • You can also specify the color mode as either COLOR or MONOCHROME.

Another useful addition is the method createPrintDocumentAdapter() for WebView. This method, used in conjunction with PrintManager, allows for easy printing of the contents of your WebView.

There are a few caveats to take note of when printing the contents of a WebView:

  • The user will not be able to specify page ranges, so the entire contents of the WebView will be printed.
  • The PrintDocumentAdapter created by the WebView will provide the contents of the WebView as they currently are. If the page isn’t finished loading, or if you start loading another page, you may not get the expected result, or the PrintDocumentAdapter could fail altogether.
  • An instance of a WebView can only process one print job at a time.

The PrintManager only has two public methods: getPrintJobs() and print(). getPrintJobs() will return a List of PrintJob objects which were started by your application. A PrintJob object is useful for checking the status and potentially canceling or restarting the job. The print method will create a PrintJob with the specified title, PrintDocumentAdapter and PrintAttributes. PrintAttributes allow you to specify a color mode, MediaSize, Margins and Resolution, although it is important to note that how these attributes are handled is entirely up to the PrintService the user selects to handle the job. It is quite possible that some of the attributes are either irrelevant to the specific service or will simply be ignored. One example of this is that the ‘Save as PDF’ print service ignores color mode.

The last class we are going to take a look at is the PrintDocumentAdapter. The PrintDocumentAdapter is the base class to extend for creating custom layouts and content. Although you can easily print anything you can draw to a canvas using the PrintHelper and Bitmap classes, the PrintDocumentAdapter gives you much more flexibility in terms of layout and attributes. When implementing a PrintDocumentAdapter, there are two optional methods and two required methods. The optional methods, onStart() and onFinish(), are basically opportunities for your implementation to allocate and release any additional resources it may require. The first required method we will take a look at is onLayout(), which is called anytime the PrintAttributes change. The main goal of this method is to create a PrintDocumentInfo object describing your content and to call one of the three methods on the LayoutResultCallback object. It is also a good place to create your PrintedPdfDocument object, so you can pass in the PrintAttributes.

The other required method is onWrite(). This method is called when there are changes which will need to be written to the PDF file specified by the file descriptor. It is important to note that this method (as well as all of the PrintDocumentAdapter methods) is called on the main thread; it is mentioned here because this method in particular may be a good candidate for backgrounding, especially given that we will be doing some file IO. The main goal of this method will be draw content to the PDF, write the PDF to file and call one of the callback methods.

Overall, the addition of the print framework to Android does not add much new functionality, but it does allow significantly easier and more reliable printing through apps which provide a print service, several of which come pre-installed on KitKat devices.

This piece is the fourth of eight in our KitKat Developer’s Guide. Check back later this week for new updates or follow us on twitter.

Michael Wally

Michael Wally

Michael Wally is a Mobile Software Engineer at Double Encore. He specializes in Android Development and being really, really tall.
Article


Add your voice to the discussion: