Using Web View in Android and Loading Data Over network

This is extension to previous Article i posted (This is my second article). I’m learning Android in process of creating these articles. All my examples are compiled and Integrated from various solutions over the web.

Note (This version of code dosen’t work in 2.3.2 version of emulator . There is a bug in Emulator for Webview and Javascript Interfacing)

In this example i have presented my Lessons in more systematic manner.

My First screen shows list of examples

AndroidLessonsMain is my Main activity and is starting point of my application. It just shows some List containing links to corresponding demo activities

package com.linkwithweb;
/*  Copyright 2011  Ashwin Kumar  (email : ashwin@linkwithweb.com)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

/**
 * 
 * @author Ashwin Kumar
 * 
 */
public class AndroidLessonsMain extends ListActivity {
	static final String[] COUNTRIES = new String[] { "GraphViewDemo",
			"WebViewDemo" };

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setListAdapter(new ArrayAdapter(this, R.layout.list_item,
				COUNTRIES));

		ListView lv = getListView();
		lv.setTextFilterEnabled(true);

		lv.setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView parent, View view,
					int position, long id) {
				// When clicked, show a toast with the TextView text
				if (((TextView) view).getText().equals("GraphViewDemo")) {
					Intent myIntent = new Intent(AndroidLessonsMain.this,
							GraphViewDemo.class);
					AndroidLessonsMain.this.startActivity(myIntent);
				} else {
					Intent myIntent = new Intent(AndroidLessonsMain.this,
							NetworkAndWebView.class);
					AndroidLessonsMain.this.startActivity(myIntent);
				}

				/*
				 * Toast.makeText(getApplicationContext(),
				 * ((TextView) view).getText(), Toast.LENGTH_SHORT).show();
				 */
			}
		});
	}
}
MainScreen
MainScreen


As you see the code each item in list has Trigger to Corresponding Demo Activities. First Activity is already Described in Previous Example
Now i’ll describe WebViewDemo

This activity starts with a screen which has a Textbox and a button to get the Data of the URL entered. I have uploaded a sample xml i’m going to parse in
http://www.northalley.com/SampleStats.xml

Once you click the get Button. I parse the response xml and create WebView which displays Graph usng flot (JQuery Graphing Library)
Network request is done as Asynchronous process here
Once the response is processed i start webview using Javascript Native Interface and rest is using Flot library in Javscript to draw the graph

It interacts with Android code using some buttons in bottom

NetworkAndWebViewMain
NetworkAndWebViewMain

Here is sample code

/*  Copyright 2011  Ashwin Kumar  (email : ashwin@linkwithweb.com)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package com.linkwithweb;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

/**
 * The main activity class
 * 
 * @author "Ashwin Kumar" (https://ashwinrayaprolu.wordpress.com)
 * 
 */
public class NetworkAndWebView extends Activity {
	private EditText mFeedUrl;
	private Button mGetButton;

	private Context mContext;
	private StatsGraphHandler mGraphHandler;

	private static final String FEEDBURNER_API_URL = "https://feedburner.google.com/awareness/1.0/GetFeedData?uri=";
	static final int PROGRESS_DIALOG = 0;
	static final int ABOUT_DIALOG = 1;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		mFeedUrl = (EditText) findViewById(R.id.feedUrl);
		mFeedUrl.setText("http://www.northalley.com/SampleStats.xml");
		mGetButton = (Button) findViewById(R.id.getGraph);
		mContext = this;

		// mFeedUrl.setText("http://feeds.feedburner.com/SudarBlogs"); //for debugging

		Button getGraph = (Button) findViewById(R.id.getGraph);
		getGraph.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				// get the feed url and validate

				String feedUrl = mFeedUrl.getText().toString();
				if (feedUrl == null || feedUrl.equals("http://")
						|| feedUrl.equals("")) {
					Toast.makeText(
							mContext,
							mContext.getResources().getString(
									R.string.feed_url_empty),
							Toast.LENGTH_SHORT).show();
					return;
				}

				// start the dialog
				showDialog(PROGRESS_DIALOG);
				new GetStatsTask().execute(feedUrl);
			}
		});
	}

	/**
	 * Before the dialog is created
	 * 
	 * @see android.app.Activity#onCreateDialog(int)
	 */
	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case PROGRESS_DIALOG:
			ProgressDialog dialog = ProgressDialog.show(mContext, "",
					getResources().getString(R.string.loading_msg), true);
			return dialog;
		case ABOUT_DIALOG:
			AlertDialog.Builder builder;
			Dialog dialog2;

			LayoutInflater inflater = (LayoutInflater) mContext
					.getSystemService(LAYOUT_INFLATER_SERVICE);
			View layout = inflater.inflate(R.layout.about,
					(ViewGroup) findViewById(R.id.layout_root));

			builder = new AlertDialog.Builder(mContext);
			builder.setView(layout);
			builder.setMessage("").setPositiveButton(
					this.getString(R.string.ok),
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog, int id) {
							dialog.cancel();
						}
					});

			dialog2 = builder.create();

			View projectUrl = layout.findViewById(R.id.project_url);
			projectUrl.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
					// When the project url is clicked
					Uri uri = Uri.parse(getString(R.string.about_project_url));
					Intent intent = new Intent(Intent.ACTION_VIEW, uri);
					startActivity(intent);
				}
			});

			return dialog2;
		default:
			return null;
		}
	}

	/**
	 * Create options menu
	 * 
	 * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu, menu);
		return true;
	}

	/**
	 * When the menu item is selected
	 * 
	 * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.about:
			displayAboutBox();
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	/**
	 * When the activity is resumed
	 */
	@Override
	protected void onResume() {
		super.onResume();
		// TODO: Need to handle screen orientation changes properly.
	}

	/**
	 * Display About box
	 */
	protected void displayAboutBox() {
		showDialog(ABOUT_DIALOG);
	}

	/**
	 * Task to fetch and parse feeds
	 * 
	 * @author "Sudar Muthu"
	 * 
	 */
	private class GetStatsTask extends
			AsyncTask<String, Void, Map> {
		private String errorMsg;

		/**
		 * Before the task is started
		 */
		@Override
		protected void onPreExecute() {
			mGetButton.setEnabled(false);
		}

		/**
		 * Start the background process
		 */
		protected Map doInBackground(String... feedUrl) {
			Map stats = new HashMap();
			FeedStatsHandler feedStatsHandler = new FeedStatsHandler(stats);

			// Add date query

			Calendar c = Calendar.getInstance();
			c.add(Calendar.DATE, -1); // we should start with previous day
			String endDate = c.get(Calendar.YEAR) + "-"
					+ (c.get(Calendar.MONTH) + 1) + "-" + c.get(Calendar.DATE);
			c.add(Calendar.DATE, -30);
			String startDate = c.get(Calendar.YEAR) + "-"
					+ (c.get(Calendar.MONTH) + 1) + "-" + c.get(Calendar.DATE);

			// get data from feedburner
			try {
				// URL url = new URL(FEEDBURNER_API_URL + feedUrl[0] + "&dates=" + startDate + "," + endDate);
				URL url = new URL("http://www.northalley.com/SampleStats.xml");

				/* Get a SAXParser from the SAXPArserFactory. */
				SAXParserFactory spf = SAXParserFactory.newInstance();
				SAXParser sp = spf.newSAXParser();

				/* Get the XMLReader of the SAXParser we created. */
				XMLReader xr = sp.getXMLReader();
				/* Create a new ContentHandler and apply it to the XML-Reader */
				xr.setContentHandler(feedStatsHandler);

				/* Parse the xml-data from our URL. */
				xr.parse(new InputSource(url.openStream()));
				/* Parsing has finished. */

				if (!feedStatsHandler.isError()) {
					stats = feedStatsHandler.getStats();
				} else {
					errorMsg = feedStatsHandler.getErrorMsg();
					stats = null;
				}

			} catch (MalformedURLException e) {
				handleError(e);
				stats = null;
			} catch (IOException e) {
				handleError(e);
				stats = null;
			} catch (ParserConfigurationException e) {
				handleError(e);
				stats = null;
			} catch (SAXException e) {
				handleError(e);
				stats = null;
			} catch (Exception e) {
				handleError(e);
				stats = null;
			}

			return stats;
		}

		/**
		 * When the background process is complete
		 */
		protected void onPostExecute(Map stats) {
			if (stats != null && stats.size() > 0) {

				// Show the webview
				WebView wv = (WebView) findViewById(R.id.wv1);

				mGraphHandler = new StatsGraphHandler(wv, stats);

				wv.getSettings().setJavaScriptEnabled(true);
				wv.loadUrl("file:///android_asset/flot/stats_graph.html");
				wv.addJavascriptInterface(mGraphHandler, "testhandler");
			} else {
				// show error message
				Toast.makeText(mContext, errorMsg, Toast.LENGTH_LONG).show();
			}
			mGetButton.setEnabled(true);
			dismissDialog(PROGRESS_DIALOG);
		}

		/**
		 * Print Error message
		 * 
		 * @param e
		 */
		private void handleError(Exception e) {
			Log.d(this.getClass().getSimpleName(), "Caught some exceiton");
			e.printStackTrace();
			errorMsg = e.getMessage();
		}
	}
}

And Here is code which handles Javascript and Android Code Interfacing


/*  Copyright 2011  Ashwin Kumar  (email : ashwin@linkwithweb.com)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

/**
 * Graph Handler
 */
package com.linkwithweb;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;
import android.webkit.WebView;

/**
 * Handles data between JavaWorld and JavaScript World 
 * 
 *
 */
public class StatsGraphHandler {
	private WebView mAppView;
	private Map mStats;
	
	public StatsGraphHandler(WebView appView, Map stats) {
		mAppView = appView;
		mStats = stats;
	}
	
	/**
	 * Set the title of the graph
	 * 
	 * @return
	 */
	public String getGraphTitle() {
		//TODO: Move it to string.xml
		return "Your Feed stats";
	}

	/**
	 * Load the default graph
	 */
	public void loadGraph() {
		JSONArray data = new JSONArray();
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		
		Object[] key = mStats.keySet().toArray();
		Arrays.sort(key); // keys should be sorted, otherwise the graph will not work properly.
		
		for (int i =0; i < key.length; i++) {
			JSONArray entry = new JSONArray();
			
			try {
				entry.put(formatter.parse(key[i] + " 00:00:00").getTime());
			} catch (ParseException e) {
				Log.d(this.getClass().getSimpleName(), "Some problem in parsing dates");
				e.printStackTrace();
			}
			
			entry.put(Integer.parseInt(mStats.get(key[i])));
			data.put(entry);
		}
		
		loadGraph(data);
	}
	
	/**
	 * Load Graph data
	 */
	private void loadGraph(JSONArray data) {
		JSONArray arr = new JSONArray();

		JSONObject result = new JSONObject();
			 try {
				result.put("data", data);//will ultimately look like: {"data": p[x1,y1],[x2,y2],[x3,y3],[]....]},
				result.put("lines", getLineOptionsJSON()); // { "lines": { "show" : true }},
				result.put("points", getPointOptionsJSON()); // { "points": { "show" : true }}
			} catch (JSONException e) {
				Log.d(this.getClass().getSimpleName(), "Got an exception while trying to parse JSON");
				e.printStackTrace();
			} 
			arr.put(result);
			
		// return arr.toString(); //This _WILL_ return the data in a good looking JSON string, but if you pass it straight into the Flot Plot method, it will not work!
			Log.d(this.getClass().getSimpleName(), arr.toString());
		mAppView.loadUrl("javascript:GotGraph(" + arr.toString() + ")"); // this callback works!
	}

	/**
	 * Get Points action
	 * @return
	 */
	private JSONArray getPointOptionsJSON() {
		JSONArray pointOption = new JSONArray();
		pointOption.put("show");
		pointOption.put(true);
		
		return pointOption;		
	}

	/**
	 * Get Lines option
	 * 
	 * @return
	 */
	private JSONArray getLineOptionsJSON() {
		JSONArray lineOption = new JSONArray();
		lineOption.put("show");
		lineOption.put(true);
		
		return lineOption;
	}
}

And Here is how it looks finally

WebViewGraph
WebViewGraph

And here is eclipse Project Attached. Rename to .zip to extract project

AndroidLessons

Advertisements

3 thoughts on “Using Web View in Android and Loading Data Over network

  1. Interesting piece, I am looking for a good example on how to pass data to/from a webview so I can build a web interface in asp.net, and just point my phone to it for user registration/license management. I can’t tell exactly where the point is where you get the data from the .xml file.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s