How to print in Android?

Android 4.4 alias Kitkat came with a lot of improvement one worth mentioned is the native integration of the API for printing. This is very important for point of sale based on Android phone or tablets like our free pos for bar and restaurant. In general the integration for cloud printing was based in a webview, and it was composed of a class which was also an activity. This activity was in charge to print with Google cloud printing

Integration of Google Cloud printing before the KitKat

In case you don't run with enough luck to have an device with Android KitKat you still can use google cloud print within your application. Google Cloud Print has a nice documentation about How to integrate Google Cloud printing in Android. Unfortunately, there is a little error for Jelly Bean with this approach in the class PrintDialogActivity that they do not mention. Let's see the code:

final class PrintDialogJavaScriptInterface {
    public String getType() {
      return cloudPrintIntent.getType();
    }

    public String getTitle() {
      return cloudPrintIntent.getExtras().getString("title");
    }

    public String getContent() {
      try {
        ContentResolver contentResolver = getContentResolver();
        InputStream is = contentResolver.openInputStream(cloudPrintIntent.getData());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        byte[] buffer = new byte[4096];
        int n = is.read(buffer);
        while (n >= 0) {
          baos.write(buffer, 0, n);
          n = is.read(buffer);
        }
        is.close();
        baos.flush();

        return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }
      return "";
    }

    public String getEncoding() {
      return CONTENT_TRANSFER_ENCODING;
    }

    public void onPostMessage(String message) {
      if (message.startsWith(CLOSE_POST_MESSAGE_NAME)) {
        finish();
      }
    }
}
 

If we try to use this in Jelly Bean you will fail miserably! The problem is in the method addJavascriptInterface. The document clearly indicates that you need to specify @JavaScriptInterface for versions newer than JELLY_BEAN_MR1. For applications targeted to API level JELLY_BEAN_MR1 and above, only public methods that are annotated with JavascriptInterface can be accessed from JavaScript. Therefore, do not forget to specify in the following way:

@JavascriptInterface
public String getType() {
	return cloudPrintIntent.getType();
}

@JavascriptInterface
public String getTitle() {
	return cloudPrintIntent.getExtras().getString("title");
}

Once you have that detail fixed, following the guide steps are easy. However, the experience you would give to your users is with a webview that loads the address of the google cloud print https://www.google.com/cloudprint/dialog.html .

Integrate Google Cloud printing with Kitkat

Using Kitkat integration is more native and friendly, in this matter Google makes easy the access to printing through a print framework. However, there is not enough documentation. The best guide that you can follow it is from the official development site print content. In our case for our integration we create a class called SenderManager which handles the different files that we create in our point of sale (pos), and then send them. The most important thing is how to integrate with KitKat and we can do it in the following way:

@TargetApi(19)
public void printWithGoogleCloud() {

	File receiptFile = new File(getExternalFilesDir(null), getString(R.string.receipt_file_name) + ".pdf");
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

	final String jobName = getString(R.string.app_name) + " " + getString(R.string.receipt_file_name);
	// Start a print job, passing in a PrintDocumentAdapter implementation
	// to handle the generation of a print document

	WebView webView = new WebView(this);
	webView.setWebViewClient(new WebViewClient() {

public boolean shouldOverrideUrlLoading(WebView view, String url) {
	return false;
}

@Override
public void onPageFinished(WebView view, String url) {
	createWebPrintJob(jobName, view);
	mWebView = null;
}});

	// Generate an HTML document on the fly:
String htmlDocument = OutputHelper.html(this, receiptFile, details, subtotal, taxes,total);
webView.loadDataWithBaseURL(null, htmlDocument, "text/HTML", "UTF-8", null);
			
// Keep a reference to WebView object until you pass the PrintDocumentAdapter
// to the PrintManager
mWebView = webView;

} else {
	if (OutputHelper.save(this, receiptFile, details, subtotal, taxes, total)) {
	Uri file = Uri.fromFile(receiptFile);

	Intent printIntent = new Intent(this, PrintDialogActivity.class);
	printIntent.setDataAndType(file, "application/pdf");
	printIntent.putExtra("title", getString(R.string.subject_payment, total, KitchSettings.getInstance().getBusinessName()));
	startActivity(printIntent);
	callback.printingFinished();
}
	}
}

We create a webview because we made the printing using HTML format, in our pos, we create a webview with the context of the activity

 WebView webView = new WebView(this); 

We create the document and the we load the HTML

	// Generate an HTML document on the fly:
	String htmlDocument = OutputHelper.html(this, receiptFile, details, subtotal, taxes,total);
	webView.loadDataWithBaseURL(null, htmlDocument, "text/HTML", "UTF-8", null);

Once the document has been loaded, the method onPageFinished will invoke the PrintManager to get the printing parameters.

@Override
public void onPageFinished(WebView view, String url) {
		createWebPrintJob(jobName, view);
		mWebView = null;
}

@TargetApi(19)
private void createWebPrintJob(String jobName, WebView webView) {

// Get a PrintManager instance
PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
// Get a print adapter instance
PrintDocumentAdapter printAdapter = webView.createPrintDocumentAdapter();
final PrintJob printJob = printManager.print(jobName, printAdapter, new PrintAttributes.Builder().build());
		
new Thread(new Runnable() {
	@Override
	public void run() {
		while(!printJob.isCompleted()) {
			mHandler.sendEmptyMessage(0);
		}
	}}).start();;
}

and the result is similar to the following

Great thing about the new Kitkat API is the ability to connect with diverse printing providers, and external application that allow us to print. For instance, let's say that Epson has application to print and it is registered as a provider, with the new features in Kitkat this type of application will show in a dialog before printing.

Facebook icon Twitter icon LinkedIn icon e-mail icon