Friday, November 4, 2011

Rendering pdf in android app

I was expecting this to be easy, but it got some complications. Android does not have native rendering for pdf,so you have different options. I decided to load the pdf in async task and then open the available pdf application. One nice feature with the Android framework is "the intents". You can launch application from another application with those intents. They have very nice way of managing different instances of same app. You can read more about this here.

You need to set manifest file first to enable file permissions.

Here is my AndroidManifest.xml:

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

    <uses-sdk android:minSdkVersion="14" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".PdfprintdemoActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

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

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
    </uses-permission>
    <uses-permission android:name="android.permission.INTERNET" >
    </uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
    </uses-permission>
    <uses-permission android:name="android.permission.READ_PHONE_STATE" >
    </uses-permission>
</manifest>


I put everything inside one activity to make this example simple. There are some important elements that need to be explained.

OnCreate method: It calls this method when creating the view. You should call the base method first then do your overrides.

/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        //call base
        super.onCreate(savedInstanceState);
        //set layout main.xml to be layout for this activity
        setContentView(R.layout.main);
        //create references to the controls
        startBtn = (Button) findViewById(R.id.startBtn);
        //attach listener in code or you can do that in layout file.
        startBtn.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                startDownload();
            }
        });         
    }




I used Async task to handle the download. It helps you to performasynchronous work on your user interface without halting the UI. You don't needto handle threads by yourself.

  • doInBackground() executes automatically on a worker thread if you call "execute" method from this class. 
  • onPreExecute(), onPostExecute(), onProgressUpdate() are all invoked on the UI thread. 
  • You can set the return value from the "doInBackround()" as input to onPostExecute()  
  • You can access publishProgress() from "doInBackround()" method to call "onProgressUpdate()" 


We are setting the dialog at onPreExecute():


@Override
        protected void onPreExecute() {
            super.onPreExecute();
            showDialog(DIALOG_DOWNLOAD_PROGRESS);
        }

        //do actual download
        @Override
        protected String doInBackground(String... aurl) {
            int count;
            String fullpath = "";
            try {
                URL url = new URL(aurl[0]);
                URLConnection conexion = url.openConnection();
                conexion.connect();
                int lenghtOfFile = conexion.getContentLength();
                Log.d("ANDRO_ASYNC", "Lenght of file: " + lenghtOfFile);
                //Get directory
                File root = Environment.getExternalStorageDirectory();
                InputStream input = new BufferedInputStream(url.openStream());
                OutputStream output =  new FileOutputStream(new File(root,
                        aurl[1]));
                byte data[] = new byte[1024];
                fullpath = root.getAbsolutePath()+"/"+aurl[1];
                Log.d("ANDRO_ASYNC","path:"+fullpath);
                long total = 0;
                while ((count = input.read(data)) != -1) {
                    total += count;
                    publishProgress("" + (int) ((total * 100) / lenghtOfFile));
                    output.write(data, 0, count);
                }
                output.flush();
                output.close();
                input.close();
                Log.d("ANDRO_ASYNC", "File closed1: " + lenghtOfFile);
            } catch (Exception e) {
                Log.d("error",e.getMessage());
            }
            return fullpath;
        }


/*We use this method to update progress bar*/
        protected void onProgressUpdate(String... progress) {
            Log.d("ANDRO_ASYNC", progress[0]);
            mProgressDialogWindow.setProgress(Integer.parseInt(progress[0]));
        }

        
        /* This method takes the return param from doinbackground call as input*/
        @Override
        protected void onPostExecute(String linkname) {
            mProgressDialogWindow.setMessage("Finished");
            dismissDialog(DIALOG_DOWNLOAD_PROGRESS);
            Log.d("onPostExecute","Send:"+linkname);
            OpenFile(linkname);
        }
        
        /*We are firing intent to open this file with avaialble pdf viewer*/
        protected void OpenFile(String linkname)
        {
            File file = new File(linkname);

            if (file.exists()) {
                Log.d("OpenFile","Exists:"+linkname);
                Uri path = Uri.fromFile(file);
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(path, "application/pdf");
                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

                try {
                    startActivity(intent);
                } 
                catch (ActivityNotFoundException e) {
                    Toast.makeText(PdfprintdemoActivity.this, 
                        "No Application Available to View PDF", 
                        Toast.LENGTH_SHORT).show();
                }
            }else
            {
                Log.d("OpenFile","DOES NOT Exists:"+linkname);
            }
        }

No comments:

Post a Comment

Hey!
Let me know what you think?