Adding Cordova APIs to Android via CLI: Prepopulated Database Storage

In this article, we are in part 5 of exploring the Cordova APIs, which started in Refer to that article to set up your Cordova PhoneGap project files. You’ll want to follow the “Set Up the Android Project” section through step 9 inclusive.

The code here creates all the database entries on this HTML page, not from a server. It is a pre-populated database, and the data can’t be erased by Android or iPhone (this code was tested in Android 4.3). It is not placed in a volatile memory area.

This is a brief database of five radio control vehicles — buggies and short-course trucks. When the page is accessed, you’ll see two buttons. Tapping them will display the data as innerHTML according to whether that keyword is present in the bodyType column of the database, either “Buggies” or “Short Course,” via the tx.executeSql() command:

tx.executeSql('CREATE TABLE IF NOT EXISTS CARS (id unique, bodyType TEXT NOT NULL, category TEXT NOT NULL, name TEXT NULL, photo TEXT NULL, resource TEXT NULL, caption TEXT NULL)');

Images are pulled from the external server, so an Internet connection is required to display them. Of course, your app can include all the images in-app if necessary (or as space allows).

You’ll want to change the following terms to fit your own needs:

CARS (name of the table)

bodyType, category, name, photo, resource, caption ( fields in the table)

The localStorage names for the fields, such as “formBodyStyleSet” and “formBodyStyleGet.”

The innerHTML field names.

Remember to escape the quotes in the table field contents so you don’t accidentally close off the starting double-quote. That is, field contents begin and end with double-quotes (“. . .”), so if your content includes a double-quote, then you’ll need to add a backslash to the left of the quotes you want to keep, like this: \”

Start a new page in the project and call it storage.html. Copy/paste from this page into that page. Link to it as a button from your index.html page. What you see below is the complete code. No other pages are required. You can even double-click on the file on your hard drive and test-drive it in a browser like Google Chrome. (Cordova.js, though referenced here, isn’t needed.)

There are no plugins to download nor editing of config.xml.

<!-- ********************* HMTL5 Storage begin *********************** -->
<!DOCTYPE html>
<title>Prepopulated DB (PG Storage Example)</title>
<meta name="viewport" content="width=device-width, initial-scale = 1.0, user-scalable = no">
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8">
// Wait for Cordova to load
document.addEventListener("deviceready", onDeviceReady, false);
// Cordova is ready
// If using <body onload="onDeviceReady();">, then this will be default data displayed 
 var type = "Buggy"; 

function onDeviceReady() { 
// save memory button's value to localStorage for later insertion into query
function persistBodyStyle(type) {
 var formBodyStyle = type;
 localStorage.setItem('formBodyStyleSet', formBodyStyle); // key, value storage
function startDB() {
 var db = window.openDatabase("Database", "1.0", "CARS", 200000);
 db.transaction(populateDB, errorCB, successCB);
// Create data here
function populateDB(tx) {
 tx.executeSql('DROP TABLE IF EXISTS CARS');
 tx.executeSql('CREATE TABLE IF NOT EXISTS CARS (id unique, bodyType TEXT NOT NULL, category TEXT NOT NULL, name TEXT NULL, photo TEXT NULL, resource TEXT NULL, caption TEXT NULL)');
 tx.executeSql('INSERT INTO CARS (id, bodyType, category, name, photo, resource, caption) VALUES (1, "Short Course", "SC18", "SC18 Ready-To-Run", "", "","Now you can enjoy Team Associated\'s world-class performance with true scale authenticity in a 1:18 scale electric truck! The SC18 features a newly designed chassis with a fully enclosed, 2-belt drive train system that is capable of handling the extreme amount of power that today\’s brushless motors and LiPo batteries can dish out. Along with durability, the drive train is sealed to help keep rocks and dirt away from the gears and pulleys.")');
 tx.executeSql('INSERT INTO CARS (id, bodyType, category, name, photo, resource, caption) VALUES (2, "Short Course", "SC10GT", "SC10GT Ready-To-Run", "", "","For many people in the RC world, nothing beats a 2-stroke nitro-breathing engine. From the sound, to the smoke, to the brutal power, nitro delivers an experience in a RC truck like nothing else can. Now you can experience that awesome nitro power in the short-course class with the SC10GT!")');
 tx.executeSql('INSERT INTO CARS (id, bodyType, category, name, photo, resource, caption) VALUES (3, "Short Course", "SC10 4x4", "SC10 4x4 RTR Combo", "", "","The SC10 4x4 Ready-To-Runs are RC replicas of the 800+ horsepower short course trucks driven in the Lucas Oil Off Road Racing Series.")');
 tx.executeSql('INSERT INTO CARS (id, bodyType, category, name, photo, resource, caption) VALUES (4, "Buggy", "RC8", "RC8RS RTR", "", "","The RC8RS Race Spec RTR is based on the Factory Team RC8, making it the only 1:8 buggy with the high-performance qualities that can live up to Team Associated\'s toughest standards. The RC8RS is a winner right out of the box, having been designed for the highest level of performance and off-road fun, with more suspension travel than any other buggy in its class.")');
 tx.executeSql('INSERT INTO CARS (id, bodyType, category, name, photo, resource, caption) VALUES (5, "Buggy", "RC10 Classic", "RC10 Classic - Limited Release", "", "","The RC10 started as the vision of Team Associated\'s founder Roger Curtis (RC) and became one of the most iconic RC cars in history. In celebration of the 30th anniversary of the original RC10 we are proud to bring to you the RC10 Classic Kit, a faithful reproduction of Roger\'s world championship-winning design that changed the world of RC off-road racing forever.")');
// Form the query
function queryDB(tx) {
 var formBodyStyleGet = localStorage.getItem('formBodyStyleSet'); 
 tx.executeSql("SELECT * FROM 'CARS' WHERE bodyType == '" + formBodyStyleGet + "'", [], querySuccess, errorCB);
// Display the results within <span id="output"></span>
function querySuccess(tx, results) {
 var len = results.rows.length;
 console.log("CARS table: " + len + " rows found."); // get the number of results
 document.getElementById("output").innerHTML = ""; // remove previous content
 for (var i = 0; i < len; i++) { // loop as many times as there are row results
 document.getElementById("output").innerHTML +=
 "<div class='segment'><p class='title'>" + results.rows.item(i).bodyType + ": " + results.rows.item(i).name + "</p>" +
 "<img class='pic' src='" + results.rows.item(i).photo + "' width='150px'>" +
 "<p class='caption'>" + results.rows.item(i).caption + "</p>" +
 "<p class='more'><a href='" + results.rows.item(i).resource + "'>More</a></p>";
 } document.getElementById("output").innerHTML += "</div>";
// Transaction error callback
function errorCB(err) {
console.log("Error processing SQL: " + err.code);
// Transaction success callback
function successCB() {

<style type="text/css">
.buttonClass, h2, p {
 font-family: "Helvetica"; color: #000; font-size:1em;
.buttonClass {
 border-radius:8px; background-color:#fff;
 border:#878787 solid 1px; padding:0 1em;margin:.5em;
 height: 3em; width: 46%;
.segment {
 display:block; border-radius:8px; background-color:#eee;
 border:#878787 solid 1px; padding:1em; margin:.5em;
 height: auto; 
h2 {
 font-size:1.3em; font-weight: bold;
@media screen and (max-width:800px) {
 .buttonClass { width: 100%;}
/* style the query output */
p { 
 text-align:left; margin-left: 170px; 
p.title { 
 font-weight:bold;font-size:1em;text-align:center; color:#000
p.more { 
a { 
img { 
 width:150px;-webkit-border-radius: 5px;
 -moz-border-radius: 5px;
 border-radius: 5px;
 -webkit-box-shadow: 0px 0px 6px 6px #ddd; /* Safari 3-4, iOS 4.0.2 – 4.2, Android 2.3+ */
 box-shadow: 0px 0px 6px 6px #ddd; /* Opera 10.5, IE9, Firefox 4+, Chrome 6+, iOS 5 */
 float: left;
<!-- Can use <body onload="onDeviceReady();"> to start page with default table rows -->
<input type="button" class="buttonClass" onclick='window.location="index.html"' value="Return">
<!-- "value" = the "bodyType" column values --> 
 <input type="button" class="buttonClass" id="buggy" value="Buggy" onclick='persistBodyStyle("Buggy");'> 
 <input type="button" class="buttonClass" id="shortCourse" value="Short Course" onclick="persistBodyStyle('Short Course');">
 <input type="button" class="buttonClass" onclick='window.location=""' value="View Code on iPhoneDevLog">
 <span id="output"></span> <!-- show the database here -->
<!-- ********************* HMTL5 Storage end *********************** -->

1. After you’ve got the above page saved in your project’s www/ folder, prepare the files in the terminal:

cordova prepare android

2. Compile the apk file:

cordova compile android

3. Locate the apk file at platforms/android/bin/CordovaAPI-debug.apk and put it on your device.

4. When the page opens, you’ll see two buttons, Buggies and Short Course. Tap them to show their data in the database.

4 thoughts on “Adding Cordova APIs to Android via CLI: Prepopulated Database Storage

  1. Hi Steve,

    Thanks for the above post, very informative and interesting. I’ve been looking at the Cordova-Prepopulated-SQLitePlugin for my own project which is not too different from the use case in your example. I in fact found your post through a response you left on the Google Group’s forum for the project. My use case is a much larger scale of the same thing though, cars, full scale cars. I would like to have all makes and models for all major manufacturers for about a 20 year range in my offline database.

    I noticed you mentioned in your example the database gets stored in local storage. While what I’m working with is only string data I’m sure the volume I’m dealing with wouldn’t fit in what Safari or possibly even Chrome allocate for local storage. The Cordova-Prepopulated-SQLitePlugin doesn’t put it there though, correct? The ReadMe states that they avoid the 5mb limit iOS imposes. I have been considering both this plugin and Couchbase-Lite’s JSON based solution( but have been pressed to find any solid code examples of either that align with the pre-populated nature of what I’m trying to accomplish. If you could point me in the right direction for either it would be really appreciated. Thanks for your time.


    • “Isn’t localstorage where your table is being put in this example? ” No. When you tap a button, the keyword is entered into localStorage: localStorage.setItem(‘formBodyStyleSet’, formBodyStyle); Then it is recalled to be a filter for the DB: WHERE bodyType == ‘” + formBodyStyleGet. The DB is not in localstorage.

  2. I’m taking pre-populated to mean that the database table installs with the app populated with all the data you believe you may need for the immediate future. You mention localstorage though ‘The localStorage names for the fields, such as “formBodyStyleSet” and “formBodyStyleGet.”’. Localstorage is memory that the browser sets aside for site/app usage, kind of like a cookie but with a bit more space. Isn’t localstorage where your table is being put in this example? Safari to my understanding only allocates 5mb of localstorage, Chrome uses a calculation involving usage by previously installed apps and remaining unused storage. What I want, and what I took the Cordova-Prepopulated-SQLitePlugin to be providing, is unrestricted access to the 16-126GB of storage most mobile devices come with today.

    • I believe Cordova’s API allows for up to 5MB of storage. For more storage, look to Brody’s SQLite’s DB. (My article on it is no longer useful, however, since the codebase has changed.)

Leave a Reply

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

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

Google photo

You are commenting using your Google 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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.