Thursday, October 24, 2013

Android Printer Integration | Google Cloud Printing in Android Application | Printer demo in Android

Hello Dear Friends,

My this post is based on "How to Integrate printer in android app". the best way of doing that Google cloud printing. Google provide us Print Dialog code, we can use it in our application. Important steps are given below-

1)Login with your gamil-id in your desktop and add your printers in your browser.
2)Create a demo app and put below code.
3)Add Internet permissions and read storage.
<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />





1)MainActivity.java
package com.manish.googleprintdemo;

import java.io.File;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
/**
 *
 * @author manish
 *
 */

public class MainActivity extends Activity {
       Button btnPrint;
       @Override
       protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              btnPrint=(Button)findViewById(R.id.button1);
             
              btnPrint.setOnClickListener(new OnClickListener() {
                    
                     @Override
                     public void onClick(View v) {
                           // TODO Auto-generated method stub

                            if (isNetworkAvailable() == false) {
                                         Toast.makeText(MainActivity.this,
                                                       "Network connection not available, Please try later",
                                                       Toast.LENGTH_SHORT).show();
                                  } else {
                                         File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/personal/xyz.pdf");
                                         Intent printIntent = new Intent(MainActivity.this, PrintDialogActivity.class);
                                         printIntent.setDataAndType(Uri.fromFile(file), "application/pdf");
                                         printIntent.putExtra("title", "Android print demo");
                                         startActivity(printIntent);
                                  }
                     }
              });
       }

       public boolean isNetworkAvailable() {

              ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
              NetworkInfo networkInfo = cm.getActiveNetworkInfo();
              // if no network is available networkInfo will be null
              // otherwise check if we are connected
              if (networkInfo != null && networkInfo.isConnected()) {
                     Log.e("Network Testing", "***Available***");
                     return true;
              }
              Log.e("Network Testing", "***Not Available***");
              return false;
       }

}

2)PrintDialogActivity.java

package com.manish.googleprintdemo;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
import android.util.Base64;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class PrintDialogActivity extends Activity {
  private static final String PRINT_DIALOG_URL = "https://www.google.com/cloudprint/dialog.html";
  private static final String JS_INTERFACE = "AndroidPrintDialog";
  private static final String CONTENT_TRANSFER_ENCODING = "base64";

  private static final String ZXING_URL = "http://zxing.appspot.com";
  private static final int ZXING_SCAN_REQUEST = 65743;

  /**
   * Post message that is sent by Print Dialog web page when the printing dialog
   * needs to be closed.
   */
  private static final String CLOSE_POST_MESSAGE_NAME = "cp-dialog-on-close";

  /**
   * Web view element to show the printing dialog in.
   */
  private WebView dialogWebView;

  /**
   * Intent that started the action.
   */
  Intent cloudPrintIntent;

  @SuppressLint("JavascriptInterface") @Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);

    setContentView(R.layout.print_dialog);
    dialogWebView = (WebView) findViewById(R.id.webview);
    cloudPrintIntent = this.getIntent();

    WebSettings settings = dialogWebView.getSettings();
    settings.setJavaScriptEnabled(true);

    dialogWebView.setWebViewClient(new PrintDialogWebClient());
    dialogWebView.addJavascriptInterface(
      new PrintDialogJavaScriptInterface(), JS_INTERFACE);

    dialogWebView.loadUrl(PRINT_DIALOG_URL);
  }

  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (requestCode == ZXING_SCAN_REQUEST && resultCode == RESULT_OK) {
      dialogWebView.loadUrl(intent.getStringExtra("SCAN_RESULT"));
    }
  }

  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();
      }
    }
  }

  private final class PrintDialogWebClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
      if (url.startsWith(ZXING_URL)) {
        Intent intentScan = new Intent("com.google.zxing.client.android.SCAN");
        intentScan.putExtra("SCAN_MODE", "QR_CODE_MODE");
        try {
          startActivityForResult(intentScan, ZXING_SCAN_REQUEST);
        } catch (ActivityNotFoundException error) {
          view.loadUrl(url);
        }
      } else {
        view.loadUrl(url);
      }
      return false;
    }

    @Override
    public void onPageFinished(WebView view, String url) {
      if (PRINT_DIALOG_URL.equals(url)) {
        // Submit print document.
        view.loadUrl("javascript:printDialog.setPrintDocument(printDialog.createPrintDocument("
          + "window." + JS_INTERFACE + ".getType(),window." + JS_INTERFACE + ".getTitle(),"
          + "window." + JS_INTERFACE + ".getContent(),window." + JS_INTERFACE + ".getEncoding()))");

        // Add post messages listener.
        view.loadUrl("javascript:window.addEventListener('message',"
            + "function(evt){window." + JS_INTERFACE + ".onPostMessage(evt.data)}, false)");
      }
    }
  }
}



3)activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Print" />

</RelativeLayout>


4)print_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <WebView
        android:id="@+id/webview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</RelativeLayout>


5)AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.manish.googleprintdemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.manish.googleprintdemo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".PrintDialogActivity" />
    </application>

</manifest>




Thanks!

28 comments :

  1. This is awesome. I learnt a lot from your blogs. hope all will learn. Thanks for posting this.

    ReplyDelete
  2. Hey manish, I tried using your code, but everytime I try to print a document, it says "Document missing". There is no error in my address, I have checked repeatedly. Instead of using Environment.getExternalStorageDirectory().getAbsolutePath() + "/personal/xyz.pdf", i have hard - coded the path. Can you please help me?

    ReplyDelete
    Replies
    1. Hi Nancy, I think some thing wrong in your path because it is working code as you can see log screen shot. well let me suggest some thing-
      1)are you try to print pdf file?
      2)file is inside your sdcard or in phone memory? if in sdcard please move to phone memory.

      And can you print your file path log and error please? So I can suggest right way..

      Thanks

      Delete
    2. Hey Manish, I wanted to print a pdf stored in some URL, since this option seemed to be the best for the app I'm making, any suggestions on how to print a pdf stored online ?
      Thanks a lot!

      Delete
  3. Hi Manish Srivastava, Thank you for sharing. I have a question: Are there other way to print by wifi in android?

    ReplyDelete
    Replies
    1. Hi Luan,
      As this needs an internet connection, it depends at end user whether it is SIM Card internet or a wi-fi.internet. So I don't think wi-fi matters. Yes it matters if we want an internet connection. At code perspective, I don't think so. I hope you understand now.

      Delete
    2. And If you're talking about printing through wi-fi printer, you'll have to follow some steps, like scan the wi-fi device , then make a connection, and then make a request for printing. Below are some link that may help you:
      http://stackoverflow.com/questions/11344572/how-to-send-a-file-from-android-to-wifi-printer-programatically-and-print-the-fi
      http://stackoverflow.com/questions/7145787/bluetooth-printer-issue-in-android

      Delete
  4. hi Manish,
    i have added a classic Epson printer to Google drive and ur code is running as well and give the message print job added.
    But problem is I have'nt get print from printer.
    could u suggest me?
    My email-nishantgupta1205@gmail.com
    ph no:+919810680536

    ReplyDelete
    Replies
    1. follow below steps hope it will help you-
      1) login with gmail account in your desktop google chrome ans use same gmail account in your mobile device for printing.
      2)go to setting option in your chrome and from there add cloud printer which one attached to your desktop.
      3)now print from mobile device it will show you all attached printer list and choose one from them.

      Thanks,

      Delete
  5. Hello Manish,
    i have done these steps already. Problem is my file sometimes added in print jobs with error sign.
    have u any idea?

    ReplyDelete
    Replies
    1. I think you are getting data==null because of big size of attachment or any other reason. Please test on any other device is it working fine or same issue you are facing?

      Delete
  6. Hi Manish,
    Do you know how to print data by using USB hosting?. If you know please post that too.

    ReplyDelete
  7. Dear Manish, I wanted to ask you a few questions about your kind posts above for Goolge Cloud Print. I am using Android "Persistent Storage" rather than doing File/IO using Local Data Storage (which appears to be how you have created that PDF file that you are printing in your demo above). I have not studied your code above too much, but right away I realized that my approach is a bit different (since I am using an ArrayList of Notes that are simply TextView String Items). What I want to kindly ask you is... in my case, when my user presses and holds a ListView item (a note item)...I present a Context Menu with some options they can perform. I would like to use that same context menu to add a feature called "Google Cloud Print" and have that Context Menu trigger something similar to what you are doing above. But as I say, I am not loading any PDF files or anything like that... all my data is already loaded from Persistent Storage, and I simply want to dynamically print that note item that the user has selected from my custom ListView Adapter. How can I utitlize your sample code above, for my own project ? What has to change ? And also, do I still need to use ALL of those Manifest Permissions ? You have four of them. two of them I am certainly not sure I still need (since I use Persisnte Storage) are: [android.permission.READ_EXTERNAL_STORAGE] and the other one which is related called: [android.permission.READ_INTERNAL_STORAGE]. I am also not too sure about whether I need the other two permissions you have as well ... the (android.permission.INTERNET) and the (android.permission.ACCESS_NETWORK_STATE)

    Anyhow...please give me some guidance on how best (easily) to adapt your sample codes above, to make them work in my own application which is utitlizing "persistent storage" and ListView custom adapter to allow user to select their notes from a list, and be able to print them.

    Ps. Manish, I tried to also ask about this on a "Hangout" on Google+ community. I am new to Google + as I am to this androidHub forum so please excuse anything from my posts that seem a bit strange, long or out of place. I just wanted to be clear as to where I am with my own Android Project and how best you can help me. I also did not know that Google+ "Hangout" posts are limited by the number of characters you can post. So thats why I came here and decided to elaborate on my own issue.

    Thanks so much for your immediate and kind reply to this.

    Sincerely yours,
    Alexander

    ReplyDelete
  8. Hello Manish,

    I tried to implement your code into my project...and did all the necessary debugging to get most (if not all the bugs out). To try and implement your code...i show below (all the following changes I had to make).... and now when I try to use the context menu option to Print out a note item to my Printer, nothing happens. I am not getting any errors, but Nothing happens and nothing is printed on my printer whatsoever). I am testing this on a Samsung Galaxy Note 3 --Android Kitkat operating system.

    Please keep in mind that I have already registered my HP LaserJet P2035n printer with Google Cloud Print (on my Google + account using the Chrome Browser --successfully. In fact, when I go to a different Note Taking application that you may have heard of called "S Note... I am able to use the "Print" option on a Note from that application, and I am able to Print the item just fine. So I seem to have setup my Printer as well as my Android Smartphone device, to utitlize Google Cloud Print successfuly, as I am able to print a S Note item. However, using your code, nothing is printing and I need to figure out why. Here is what I have done in my own application thusfar.

    1) In my public boolean onContextItemSelected(MenuItem item) , I have created a new Context menu item and check for when it is selected when the user is pressing on a Note Item in my custom ListView. Here is what the code looks like:

    if (item.getItemId() == MENU_CLOUD_PRINT_ID) //if user selected to Cloud Print their note, we handle that
    {
    NoteItem note = notesList.get(currentNoteId); //grab the NoteItem that was selected from list
    String message = note.getText(); //get the message of the note that we wish to print

    if (isNetworkAvailable() == false) {

    Toast.makeText(MainActivity.this,

    "Network connection not available, Please try later",

    Toast.LENGTH_SHORT).show();}


    else
    {
    Intent printIntent = new Intent (MainActivity.this, PrintDialogActivity.class);
    printIntent.putExtra("title", message); // pass the message the user slected , to Print activity
    }

    }

    2) I have created my PrintDialogActivity.java exactly as you have prescribed. I have also bound that activity to the print_dialog.xml layout file as you suggest. Which means my print_dialog.xml file is also exactly setup like yours.

    The only thing I did not do (*since I dont think I need to in my case*) was to creaet a "Print" button inside my activity_main.xml ..... the reason is because I am using a context menu to allow the user to initiate the print job. And you are using a button.

    So please tell me why Nothing is printing out on my printer ? I am totally lost.

    Thanks

    ReplyDelete
  9. Dear Manish,

    After some more careful analysis of my code, I realized that in my intent call from my MainActivity.java, I was misssing some critical lines that you actually HAD , in your else clause. Let me show you what my current else clause looks like.

    else
    {
    File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/personal/xyz.pdf");

    Intent printIntent = new Intent (MainActivity.this, PrintDialogActivity.class);
    printIntent.setDataAndType(Uri.fromFile(file), "application/pdf");
    printIntent.putExtra("title", message);
    startActivity(printIntent);

    }// end of else clause


    My problem now is that my Activity Crashes when I run my code. As soon as I add the lines of:

    File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/personal/xyz.pdf");
    printIntent.setDataAndType(Uri.fromFile(file), "application/pdf");

    and started my activity, with:
    startActivity(printIntent);


    Now my MainActivity.java crashes. I have a feeling its because this PDF file (xyz.pdf)
    at the path == > /personal/xyz.pdf DOES NOT EXIST

    Can you please tell me how I can adapt your code to work with a case like MINE where I AM NOT reading any PDF file that I wish to print. In my case, as I stated earlier, I am allowing the user to Select a ListView Note Item from a LIST and I display a Conext Menu with an option allowing them to HOPEFULLY be able to Print their Note to their Cloud Printer (which has already been setup presumably ahead of time by that user). I dont want my Application to be responsible for helping them setup (configure) their cloud printer (as far as registering it with their Google + account in Chrome -- meaning finding it on their network and setting it in their Google + account for Cloud Printing). I also dont want to be responsible for them downloading and setting up the Google Cloud Print (free app from market place). I ONLY WANT that they should be able to do a print.

    So given that I dont have a file xyz.pdf (which is not created by my app and is not existing)... How do I modify your code to make it work in my situation ?

    Please reply ASAP.

    Thanks
    Alexander


    ReplyDelete
    Replies
    1. Hey friend, thanks for your onwer. And i am sorry this time your problem seems big and i have tight dead line so i cant able to look on it..

      Delete
    2. Hey friend, thanks for your onwer. And i am sorry this time your problem seems big and i have tight dead line so i cant able to look on it..

      Delete
    3. Hello Manish. Thanks for the kind and honest reply. Actually, my challenge is not that complicated if I explain to you my question in another way. Suppose you have a String stored in a variable called "statement" - - which may be a statement about let's say the cruelty of killing elephants for their tusks. Now, let's say you want to print this statement. How can you achieve (hopefully without the requirement to first create an external PDF file out of the statement, then storing it to disk, and the reloading it and passing it into your PrintDialogActivity.class

      I mean Manish, should there not be some simple way to print a string to a printer directly (without resorting to fancy File/IO requirements?)

      This is my real question from anyone who can help me please.
      Thank you all very much.

      Delete
    4. I can understand what you are trying to print. But as per my knowledge you need a file at-least in any format HTML, PDF, DOC any but you need. In android there are only two type of printing option-

      1) Wireless Bluetooth printing
      2)Using cloud printer what I am doing.

      But in both case you need a file like you have a listview nd want to print it so you need to write this data into a file then only you can print.

      Well why you are thinking like that? Make it at your end user will not know about your internal code. After print the file just remove that from disc.

      Just forget android for a minute and let me know can you print a variable in java or in any language using desktop when there a printer attach with your desktop? You need to print an object, file etc..

      Please don't mind try over the world and do Google there are some chargeable library and they have own printing machine, might be they help you. Well if you got anything please let me know I am interested to know.

      Thank you!

      Delete
  10. Since you're writing to the file system you also need to add .

    When using Android 4.4 you do not need to use above code to print, you can also use android.print

    ReplyDelete
  11. Also add following permission to the manifest: android.permission.WRITE_EXTERNAL_STORAGE

    ReplyDelete