Custom Content Provider In Android

I’ve been searching over internet to find out one good example for Custom Content Provider but found very little help in trying to run examples rightaway from web. Lots of examples are 1/2 done or 1/2 written. I wanted to come out with a runnign example and here is the example. Again as before i’ve compiled and modified this example from code i have found over web so that some UI is added to it.

Content providers store and retrieve data and make it accessible to all applications. They’re the only way to share data across applications; there’s no common storage area that all Android packages can access.
Each content provider exposes a public URI (wrapped as a Uri object) that uniquely identifies its data set. A content provider that controls multiple data sets (multiple tables) exposes a separate URI for each one. All URIs for providers begin with the string “content://”. The content: scheme identifies the data as being controlled by a content provider

Querying a Content Provider

You need three pieces of information to query a content provider:

The URI that identifies the provider
The names of the data fields you want to receive
The data types for those fields
If you’re querying a particular record, you also need the ID for that record.

Below is the code that creates content provider (Here content provider acts like a wrapper to sqllite database)

package com.linkwithweb.providers;

import android.net.Uri;
import android.provider.BaseColumns;

/**
 * @author Ashwin Kumar
 *
 */
public class MyUsers {

	public static final String AUTHORITY = "com.linkwithweb.providers.MyContentProvider";

	// BaseColumn contains _id.
	public static final class User implements BaseColumns {

		public static final Uri CONTENT_URI = Uri
				.parse("content://com.linkwithweb.providers.MyContentProvider");

		// Table column
		public static final String USER_NAME = "USER_NAME";
	}
}
package com.linkwithweb.providers;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;

/**
 * @author Ashwin Kumar
 *
 */
public class MyContentProvider extends ContentProvider {

	private SQLiteDatabase sqlDB;

	private DatabaseHelper dbHelper;

	private static final String DATABASE_NAME = "Users.db";

	private static final int DATABASE_VERSION = 1;

	private static final String TABLE_NAME = "User";

	private static final String TAG = "MyContentProvider";

	private static class DatabaseHelper extends SQLiteOpenHelper {

		DatabaseHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			// create table to store user names
			db.execSQL("Create table "
					+ TABLE_NAME
					+ "( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);");
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
			onCreate(db);
		}
	}

	@Override
	public int delete(Uri uri, String s, String[] as) {
		return 0;
	}

	@Override
	public String getType(Uri uri) {
		return null;
	}

	@Override
	public Uri insert(Uri uri, ContentValues contentvalues) {
		// get database to insert records
		sqlDB = dbHelper.getWritableDatabase();
		// insert record in user table and get the row number of recently inserted record
		long rowId = sqlDB.insert(TABLE_NAME, "", contentvalues);
		if (rowId > 0) {
			Uri rowUri = ContentUris.appendId(
					MyUsers.User.CONTENT_URI.buildUpon(), rowId).build();
			getContext().getContentResolver().notifyChange(rowUri, null);
			return rowUri;
		}
		throw new SQLException("Failed to insert row into " + uri);
	}

	@Override
	public boolean onCreate() {
		dbHelper = new DatabaseHelper(getContext());
		return (dbHelper == null) ? false : true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		SQLiteDatabase db = dbHelper.getReadableDatabase();
		qb.setTables(TABLE_NAME);
		Cursor c = qb.query(db, projection, selection, null, null, null,
				sortOrder);
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

	@Override
	public int update(Uri uri, ContentValues contentvalues, String s,
			String[] as) {
		return 0;
	}
}

Now lets create an Activity and Corresponding view to display this in GUI

/**
 * 
 */
package com.linkwithweb;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.linkwithweb.providers.MyUsers;

/**
 * @author Ashwin Kumar
 * 
 */
public class CustomProviderDemo extends Activity {
	private EditText mContactNameEditText;
	private TextView mContactsText;
	private Button mContactSaveButton;

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

		setContentView(R.layout.custom_provider);

		// Obtain handles to UI objects
		mContactNameEditText = (EditText) findViewById(R.id.contactNameEditText);
		mContactSaveButton = (Button) findViewById(R.id.contactSaveButton);
		mContactsText = (TextView) findViewById(R.id.contactEntryText);

		
		mContactSaveButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
            	String name = mContactNameEditText.getText().toString();
            	insertRecord(name);
            	mContactsText.append("\n"+name);
            }
        });
		
		//insertRecord("MyUser");
		displayRecords();
	}

	private void insertRecord(String userName) {
		ContentValues values = new ContentValues();
		values.put(MyUsers.User.USER_NAME, userName);
		getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
	}

	private void displayRecords() {
		// An array specifying which columns to return.
		String columns[] = new String[] { MyUsers.User._ID,
				MyUsers.User.USER_NAME };
		Uri myUri = MyUsers.User.CONTENT_URI;
		Cursor cur = managedQuery(myUri, columns, // Which columns to return
				null, // WHERE clause; which rows to return(all rows)
				null, // WHERE clause selection arguments (none)
				null // Order-by clause (ascending by name)
		);
		if (cur.moveToFirst()) {
			String id = null;
			String userName = null;
			do {
				// Get the field values
				id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
				userName = cur.getString(cur
						.getColumnIndex(MyUsers.User.USER_NAME));
				Toast.makeText(this, id + " " + userName, Toast.LENGTH_LONG)
						.show();
			} while (cur.moveToNext());
		}
	}
}

Below is the view code

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    <TableLayout android:layout_width="match_parent"
                 android:layout_height="match_parent">

        <TableRow>
            <TextView android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:text="@string/contactNameLabel"/>
        </TableRow>
        <TableRow>
            <EditText android:id="@+id/contactNameEditText"
                      android:layout_height="wrap_content"
                      android:layout_width="wrap_content"
                      android:layout_weight="1"/>
        </TableRow>
        <TableRow>
            <Button android:layout_height="wrap_content"
                    android:text="@string/save"
                    android:id="@+id/contactSaveButton"
                    android:layout_width="match_parent"
                    android:layout_weight="1"/>
        </TableRow>
        <TableRow>
	        <TextView android:text="@+id/contactEntryText"
	              android:id="@+id/contactEntryText"
	              android:layout_width="match_parent"
	              android:layout_height="wrap_content"/>
   		 </TableRow>
        
    </TableLayout>
</ScrollView>

Below is the configuration you can define in Manifest file

		<activity android:name=".CustomProviderDemo" android:label="CustomProviderDemo">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.SAMPLE_CODE" />
			</intent-filter>
		</activity>	
		
		
		<provider android:name="com.linkwithweb.providers.MyContentProvider" android:authorities="com.linkwithweb.providers.MyContentProvider" />		

Code has been checked in to below url. You can checkout from the below svn url for workign code of whole project with all other lessons
https://linkwithweb.googlecode.com/svn/trunk/Android/AndroidLessons

Integrating Google Maps in Android

This is my third article in sequence. I have used same Code base which i’ve been using. Added one more menu for maps

Adding maps to Android requires that your application Android Target is set to GoogleAPI’s(These contain Google map addon’s) Rather than Android version one’s

Remember : Adding Maps.jar externally to target will not work at runtime so always change Build Target to Google Addon’s

Create a Virtual Device for your version tergetting Google API’s

Now once the above steps are completed you need to follow the below steps

  1. 1. Either use debug.keystore or create keystore certificate using android tools(Debug.keystore is located in “C:\Documents and Settings\\.android” Directory.
  2. Now generate MD5 using keytool command of java
  3. Generate Google API key using this MD5

Finally build your application and Run in debug mode (As debug mode automatically sign’s you application using debug.keystore which we configured for maps

Remember to add <uses-library android:name=”com.google.android.maps” /> inside application tag else system will exit with some errors

Attached is the AndroidLessions with new Demo added for google maps. (Rename to .zip to extract eclipse project)
You can learn about more features in google maps from Android website
AndroidLessons(Rename to .zip to extract eclipse project)

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