Last week a reader wrote in with an interesting problem. They had a simple application that made use of PhoneGap's database support. They wanted to add camera support as well. Their idea was that a picture could be associated with their content. But then they ran into this issue:
iOS Quirks:
When destinationType.FILE_URI is used, photos are saved in the application's temporary directory. Developers may delete the contents of this directory using the navigator.fileMgr APIs if storage space is a concern.
So, right away, this brings up a useful reminder. Most things you do with PhoneGap will work cross platform, but it is crucial that you pay attention to the individual quirks for each platform. In this case, something that works fine in Android will cause problems in iOS. Even worse, because the images aren't removed immediately, you may think there isn't a problem at all. (As a side note, navigator.fileMgr is old code that has not been removed from the docs yet. I've filed a bug report to get this updated.)
I thought I'd help out the reader by building a simple application that made use of both technologies and then work out how I'd handle the iOS issue. I started off by building a simple Diary application. The Diary would allow you to write basic content entries, each with a title, body, and a creation date. I built a very simple "Single Page Architecture" framework to handle my application views and routing. I won't even call it a framework. Really it is just one simple JavaScript function that lets me load a page into the DOM.
Here's the home page - a simple list of entries with the ability to view an entry and add a new entry.

And the amazingly well-designed entry page:

Finally, the form to write entries:

I also built a wrapper for my Diary class that would abstract out the persistence for me. Here is that wrapper. Please don't laugh at my pitiful object-oriented-ish JavaScript code.
This wrapper class is used by index.js, which handles my views, adding and requesting data, etc. Basically, index.js acts like a controller in your typical MVC setup. I'm not going to share all of that file (to be clear, everything is shared via a Download link below), but here is an example.
So - nothing too terribly complex. I took this as a starting point (in the zip you can download, this may be found in www1) and then began to integrate camera functionality. I started by adding a new button to my entry field. This button, "Add Picture", would request the device camera and store the resulting image in a hidden form field. I began by using FILE_URIs even though I knew it would be an issue with iOS. In almost everything I do I start slowly, take baby steps, and try to build one thing at a time. So with that in mind, I went ahead and just built it in.
Here is that code:
Note that I've updated my form to include a preview and the entry detail view (not shown here) has been updated to display it.

Ok, so at this point, the core functionality is done. (You can find this version in the www2 folder.) I can add and view content (I didn't bother with edit/delete, but that would be trivial) and I can use the device camera to assign a picture to a diary entry. Now to look into the bug.
My initial thought was to make use of the File API to copy the image to a nice location. Even though Android didn't really need this feature, I thought I'd keep things simple and just use the same logic for all. (To be fair, when I say "all", I really was just testing Android and iOS.)
However, I ran into an interesting issue. When requesting the persistent file system, iOS gave me a directory that was customized for my application:
/var/mobile/Applications/362CC22A-BA60-4D81-876C-21072A06CE16/Documents
Unfortunately, the Android version of the exact same code returned a more generic folder. I could then make a folder for my Android app that was specific to the application itself, but that seemed to be a bit too much work. (Really, it wasn't, I was being lazy.)
I rang up Simon MacDonald, my goto guy for PhoneGap questions (or at least until I annoy him ;) and in discussions with him, I discovered that right now there isn't a simple way, cross platform, to ask the file system for "a safe application-specific place to store my crap." (My words, not his.)
I made a decision at this point. Even though it felt a bit wrong, I decided I'd write code just for iOS. I figured the 'forked' code would be pretty small and therefore it wouldn't "pollute" my code too much. This smells like one of those decisions I may regret later, but for now, it is what I'm going with.
I began by sniffing the device using PhoneGap's Device API.
Nothing too complex here. If I detect iOS, I request the persistent file system and remember the root directory it gives me.
Next - before I store the Diary entry, if I am on iOS, I simply copy the image over.
Overall, not as messy as I thought. You can find the complete source code attached below. Enjoy.
Archived Comments
This is a very interesting post and as always it is quite timely as I've been considering something similar to this for my karate app.
Thanks for sharing this!!
This is the exact thing I have been looking for to integrate with my Site Safety Inspector - Now all I need to do is figure out how to send the pic to my server for processing.
Read up on the FileTransfer API: http://docs.phonegap.com/en...
Raymond, you're a legend .... I've been meaning to figure this out for myself, but now I don't have any excuses to start writing my app ;)
Glad this is so useful to folks. :)
I got an error when I ran on Eclipse as below:
Uncaught Error: NOT_SUPPORTED_ERR: DOM Exception 9 at file:/
//android_asset/www/js/index.js:52
Any thoughts? Thanks in advance.
Line 52 for me is:
if(u.indexOf("?") >= 0) {
Is that what you see?
Thanks for sharing this post. it gives me another reason to develop a phonegap app. I hope you can share a topic about knockoutjs + jquerymobile + phonegap..
I'm not sure I'd mix Knockout w/ JQM. Then again, I've barely begun to learn Knockout.
I have noticed other differences from platform to platform. Accelerometer data does not output the same readings either because device accelerometers are calibrated differently and/or they are not reading the accelerations the same way.
Sorry for late reply.
The line 52 to me is:
var evt = document.createEvent('CustomEvent');
inside:
$.get(u,function(res,code) {
mainView.html(res);
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent("pageload",true,true,data);
var page = $("div", mainView);
page[0].dispatchEvent(evt);
});
}
Heb, that is the code from www2. That is one of the 'iterative' steps I worked on while creating this blog entry. You want to use the code in www.
Thanks Ray! I'll try www folder.
I actually got the same error at the same error with www folder as well. Should it be running on Android project or only on iOS?
Um, no. So - how exactly are you testing? Did you make a new PG project and then copy in my code?
Sort of. I copied my working project and overwrote your code in Eclipse. Permissions are already applied. When tested on my Android phone, it shows the first page but doesn't do anything when I click on the button. Console is not moving either. The error showed up right after the app was started.
I haven't used Eclipse for PhoneGap in a long time. Did you make it as a new 2.5.0 project and did you use the command line? If you didn't, then the JS file isn't being included properly.
I see that you are not calling jquery.js or jquery.mobile.js from index.html or any other html files. Is this intended?
I used your cordova.js file and, yes, I use the command line to create a project. Should I call jquery.js and jquery.mobile.js from index.html?
This app does not make use of jQuery Mobile nor does it use jQuery. You should remove cordova.js. I'm going to edit the zip to remove it too. When you do a build for a platform, the right cordova.js is added.
I have successfully run the Diary app in my HTC mobile phone using eclipse on windows and your cordova.js and it works good. My only concern was there's an occasional application crash, this only happened right after taking picture and click done to go back on the Diary app.
But overall this is one of my favorite phonegap tutorial that I have seen. Simple and yet practical.
How should i connect my phonegap app to mysql database with examples please?
Ice, I'm glad you like the tutorial. If you can figure out why it crashes for you I'd like to know. In theory, this is a pretty simple implementation so it shouldn't have stability issues. Try running the app while your device is connected to your machine and Eclipse can give you the error.
@hellogap: You would use Ajax to speak to a server proxy for MySQL. Something like ColdFusion or PHP on the server could listen for, and respond, to your Ajax requests. I've done some examples of this here - just search my blog for phonegap.
I see, the occasional crashes was due to out of memory error. Thanks Raymond.
Hmm. I would have suggested modifying the quality, but I see I have it at 50% already. Try perhaps setting it lower. Also try targetWidth and Height to values like 250 or so.
Hi Ray, Thanks for the insightful tutorial(s). I'm new to PG, so heres what I did to build/load app in iPhone Simulator but without luck:
- use command-line to create a new project using PG 2.5
- overwrite www folder with the one from the archive zip
- double-click on .xcodeproj and chose iPhone 6.1 simulator
I only got a pop-up with 'NetworkStatus125..."] and in xcode I notice a warning for "self.viewController.useSplashScreen = YES;" saying useSplashScreen is deprecated.
Can you pls let me know if that is the right way to build the app?
First off, while you can run the app in the iOS Sim, you can't take pictures with it. Unfortunately, while the sim shines pretty much everywhere, it doesn't let you fake the camera.
That being said, what you did was partially right. When you copy the files, be sure to then do a build (cordova build). That copies over the www files into the projects.
I no longer encountered the occasional crashes of the app. I just adjusted the quality by 40%. I tested it 20 times in two different scenarios (1. add picture first then filled up the input field; and 2. filled up the input fields then add a picture) and in two different android versions(android 2.2 and android 4..0.3... :D
Thanks Ray. After copying the files, I should do a build using build.phonegap.com or is there any command-line utility to do the cordova build (sorry if this is to naive).
@Ice: Thanks for sharing that.
@buntu: It sounds like you may not know how PG works. I'd recommend the Getting Started guide: http://docs.phonegap.com/en...
So great, i am just starting to play with phonegap on iphone, and this helped me a lot. thx (;
hi Ray,
is it possible to connect this phonegap app to parse.com, ?
Sure.
Hi Raymond,
Finally I found the reason why it didn't work. When using Eclipse, it doesn't work on Android 2.x version. I ran it on my galaxy tab with Android 4.0 something and it works beautifully. Not sure if this is true without Eclipse though. Is it?
Honestly I don't know. When I first did PhoneGap work, I used Eclipse, but since they improved the command line stuff, I avoid Eclipse like the plague.
Hi Raymond,
I can't find a way to connect this phonegap app to parse.com, do you know how to upload an image with associated parse objects from a phonegap app ?
Are you asking how to use data w/ Parse or just files? Files are a bit tricky with PhoneGap and Parse. Or it was in <2.4. This is a topic I've meant to look at with 2.5 (some things changed w/ regard to file handling) but I've yet to get a chance yet to test it.
Best I can say it is on my list.
wooww.. great..
in android, this code work..????
--->>> entry.html?id="+id
cause in my app i use similar method, and error..
a network occured error..
*thanks Mr Camden.. i will try this.. :D
oh i'm sorry..
--->>> entry.html?id="+id
i think its different with my app.. :D
What version of Android are you on? And how exactly isn't it working - is it not seeing the ID value in the query string?
android 4.04...
is this code for passing parameter in entry.html?id="+id ??
if(u.indexOf("?") >= 0) {
var qs = u.split("?")[1];
var parts = qs.split("&");
for(var i=0, len=parts.length; i<len; i++) {
var bits = parts[i].split("=");
data[bits[0]] = bits[1];
};
}
my problem same as http://community.phonegap.c...
In theory, this was patched a while ago. Are you using the latest rev of PhoneGap?
cordova 2.1.0
--> i try in 2.5.0 and its work..
--> thanks.. :D
but i found another error
--> the connection to the server was unsuccessful. (javascript: try{cordova.require(cordova/chanel).onDestroy.fire();}catch(e){console.log(exeption firing destroy event from native);};)
You got me there. Are you using the right phonegap.js/cordova.js for your platform?
then i just move down to cordova 2.4.0 ... and work well... i don't know why..
Would the user be able to backup the database file or should I use a different method for backup functionality? This pertains to ios and android. Thanks for the code!
No, there wouldn't be a way for the user to do this via settings or anything, but, you could build in a backup. You could export to a file and then email it, or copy it to the file system. You could also use a server, or a service like Parse.
That sounds like a nice way to do it. I was initially thinking that when the user went into itunes that there would be a db file associated with the app and they would be able to back it up if using ios for example. Do you think I would need to write logic to copy it somewhere for this option to work?
Honestly I've got no idea on that one.
hi how to deploy your code on server pls tell me i waana check how to do set up this things on server.
Your question doesn't make sense. This is a mobile app, not a server app.
i know it how can i chek it on mobile for that i have to give a url to check on my mobile i want to know it hows the application run on my mobile.
I've got no idea what you mean. If you are asking how to use this on your mobile device, the you need to learn how to use PhoneGap. I've got multiple PhoneGap presentations you can watch and you can read their official docs as well. This blog post assumes you know that already.
Excellent tutorial! This is very helpful. I have two questions though...first, I'm curious where I would begin to be able to make it so users could delete entries from the view where the entry is shown; and secondly, how difficult it would be to alter the JavaScript if I combine all the html pages into a single page.
No worries about answering these if you're busy, I'm sure I'll struggle through figuring them out eventually... :)
Thanks again for this tutorial, saves me A LOT of time!
This is my very first Phonegap and I am trying to reproduce what you did but recording sound and replaying it instead of taking picture I used the capture.captureAudio, media.startRecord and media.stopRecord but everytime I click on Record Audio button
<input type="hidden" id="entryAudio">
<img id="imgPreview" class="audioPlay">
<button id="recordAudio">Record Audio</button>
it goes to the main.html I'm not sure what am missing anyone has an idea how to modify this to record, save and replay sound
@Josh: I'd add a delete button on the view portion. As to making it all one page, um, sure, doable, just have to rework the logic a bit. There are multiple ways of handling a SPA (Single Page Architecture).
@Hans: I'd have to see your code. To me, it sounds like you don't have a good handler for your recordAudio button. Make sure you preventDefault on the event object.
@Raymond: this is what I have
function onCaptureSuccess(audiofile) {
//var i, path, len;
//for (i = 0, len = audiofile.length; i < len; i += 1) {
//path = audiofile[i].fullPath;
// }
console.log(audiofile);
$("#entryAudio").val(audiofile);
console.log('set the file');
$('#audioPreview').attr("src", audiofile);
console.log('set the attr');
}
function onCaptureError(e) {
navigator.notification.alert(msg, null, 'Uh oh!');
}
$("#recordAudio").on("touchstart", function(e){
e.preventDefault();
e.stopPropagation();
navigator.device.capture.captureAudio(onCaptureSuccess, onCaptureError, options);
});
$("#addEntrySubmit").on("touchstart", function(e) {
e.preventDefault();
//grab the values
var title = $("#entryTitle").val();
var body = $("#entryBody").val();
var img = $("#entryPicture").val();
var sound = $("#entryAudio").val();
and the other thing is how do I show and replay this file the same way you show and display the image
hi sir,
am beginner for phone gap.am having doubt on
how to use sqlite in phone gap?
am having confusion ..pls help me
Did you check the docs?
http://docs.phonegap.com/en...
yes i checked it sir.it work for me thanks. And i have one more query how to write join query in phonegap database?
It follows the SQLite standard. I'd google for SQLite, find their docs, and check. I believe they support normal join operations so it may just work. Have you tried just writing the query and seeing what happens?
to use sqlite do we need to install it or phonegap has inbuilt setup for that. I am developing a android app using sqlite and whenever runs a sqlite example it does not execute. i do not know where sqlite stores data.....
It should be built in. I'd recommend checking the PhoneGap docs. What version of Android are you using?
i m using android version 2.2 with api level 8 and the editor used is eclipse indigo . actually i m developing a location based mobile alerts app which will give me the reminder previously set by me for a particular location. i m able to locate the current location but can not proceed further without the database support. i tried some of the examples of phonegap.com but they are not working..... and one more thing i want to ask that where can i see the table created .
is there any jar file that is required to execute the sqlite code .
Nope, it should just work.
How about making sure that the horizon is level? The author apparently doesn’t consider that important and actually posted a couple of images that needed a bit of rotation. This is equally important with a DSLR and EVIL.
When i copy the www folder directly into a phonegap project in Eclipse and run it. I get a blank white screen.
You want to remove cordova.js since the build process should add it automatically. My cordova.js is for ios probably.
I can't get this one running :/
I've copied your www folder to my phonegap www folder and renamed it to "diary". I then linked to diary/index.html from my root folder. You know what i mean? But it just shows up in the usual style and says "undefined".
I'd have to see more of the code to help. If you can put it online where we can see, myself, or others, can chime in.
The code is excellent. I have just one problem
The code works fine in the emulator and all the links are clickable. but when i deployed it to real android and symbian devices all the links stop working. what could be the problem am using your www2
am using cordova 2.9.0
thank you
Check your access config to see if parse.com is whitelisted.
There is no parse.com in my config.xml files. below is how config.xml file is
<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns = "http://www.w3.org/ns/widgets"
id = "io.cordova.helloCordova"
version = "2.0.0">
<name>Hello Cordova</name>
<description>
A sample Apache Cordova application that responds to the deviceready event.
</description>
<author href="http://cordova.io" email="dev@cordova.apache.org">
Apache Cordova Team
</author>
<access origin="*"/>
<!-- <content src="http://mysite.com/myapp.html" /> for external pages -->
<content src="index.html" />
<preference name="loglevel" value="DEBUG" />
<!--
<preference name="splashscreen" value="resourceName" />
<preference name="backgroundColor" value="0xFFF" />
<preference name="loadUrlTimeoutValue" value="20000" />
<preference name="InAppBrowserStorageEnabled" value="true" />
<preference name="disallowOverscroll" value="true" />
-->
<feature name="App">
<param name="android-package" value="org.apache.cordova.App"/>
</feature>
<feature name="Geolocation">
<param name="android-package" value="org.apache.cordova.GeoBroker"/>
</feature>
<feature name="Device">
<param name="android-package" value="org.apache.cordova.Device"/>
</feature>
<feature name="Accelerometer">
<param name="android-package" value="org.apache.cordova.AccelListener"/>
</feature>
<feature name="Compass">
<param name="android-package" value="org.apache.cordova.CompassListener"/>
</feature>
<feature name="Media">
<param name="android-package" value="org.apache.cordova.AudioHandler"/>
</feature>
<feature name="Camera">
<param name="android-package" value="org.apache.cordova.CameraLauncher"/>
</feature>
<feature name="Contacts">
<param name="android-package" value="org.apache.cordova.ContactManager"/>
</feature>
<feature name="File">
<param name="android-package" value="org.apache.cordova.FileUtils"/>
</feature>
<feature name="NetworkStatus">
<param name="android-package" value="org.apache.cordova.NetworkManager"/>
</feature>
<feature name="Notification">
<param name="android-package" value="org.apache.cordova.Notification"/>
</feature>
<feature name="Storage">
<param name="android-package" value="org.apache.cordova.Storage"/>
</feature>
<feature name="FileTransfer">
<param name="android-package" value="org.apache.cordova.FileTransfer"/>
</feature>
<feature name="Capture">
<param name="android-package" value="org.apache.cordova.Capture"/>
</feature>
<feature name="Battery">
<param name="android-package" value="org.apache.cordova.BatteryListener"/>
</feature>
<feature name="SplashScreen">
<param name="android-package" value="org.apache.cordova.SplashScreen"/>
</feature>
<feature name="Echo">
<param name="android-package" value="org.apache.cordova.Echo"/>
</feature>
<feature name="Globalization">
<param name="android-package" value="org.apache.cordova.Globalization"/>
</feature>
<feature name="InAppBrowser">
<param name="android-package" value="org.apache.cordova.InAppBrowser"/>
</feature>
<!-- Deprecated plugins element. Remove in 3.0 -->
<plugins>
</plugins>
</widget>
Jon, nothing sticks out to me. Best I can suggest is to try debugging on the device using weinre.
Code very useful.
What happens to images when application is uninstalled?
I believe they persist. But - it should be easy for you to test this theory and report back to us. :)
I have similar code in my app, and The images do persist, at least on android , not quite sure about IOS.
My concern is that if user uninstalls app several times without removing images, then you get a build up of images.
When the app is installed it could check for existence of folder set aside for this, and if it exists delete all images in it, otherwise just create the folder.
What are your thoughts?
Hey, thank you very much for this great tutorial. Can you tell me how I could add buttons "edit entry" and "delete entry" with a code? That would be very helpful for me.
Well, I can't describe it all in a comment, but at a high level, you would add the buttons, of course, and delete would run a sql delete command and edit would fetch the record but provide the data to a form.
Hi, first of al great app for beginners in the phone build world :)
Have you perhaps had any time to work on this project any further, for example add an delete/edit button or something?
I'd love to see your code to learn a bit more about the apps functions and how to use those in my own.
Cheers
No, I haven't. I did build a new git repo just for PG samples - and I could see working on an update to this there. I just don't know when in the heck I would. ;)
I should add - I've made a note of this request in my Blog Ideas note - so at least I don't forget. I definitely see the merit in adding this as a sample app.
Hey Raymond.
I'm looking at your example and find it really great and easy to follow.
When that is said even when i have the right filesystem path and filename, when I try to use the copyTo/moveTo method it wont work. Any ideas?
Any specific error?
It just says error code 1
Thanks for replying so fast.
http://stackoverflow.com/qu...
Here you can see the code i made which doesnt work
Lasse, I have a solution - I'll post to SO though.
That sounds REALLY nice. What have taken me a day you sound like beeing able to solve in an hour. *amazed*
If the solution works, be sure to vote it up so I can get those SO points. ;)
obviously i will :) Working on it now
Hey Raymond.
Whenever i try to use NATIVE_URI nothing gets selected.
Still havent got it working, but i'll look more into it tomorrow.
Thanks a LOT for your time. I will get it working eventually.
Let me know, but please respond on SO as the thread is getting long here. If you want a complete copy of the JS file I made, it is here:
https://dl.dropboxuserconte...
Hi great app,
I'd like to link my hobby app to your html as sort of a sub-app inside my utilities-app but everytime i try to link to your index.html file or main.html file i get an error. Perhaps you know what i'm doing wrong (i'm kind of a first time phonegap user) and if it's possible?
Greets
You literally link to my url? I wouldn't do that. Just copy the source.
Oh no. sorry, i Copied your files into my won and linked to /diary/Main.html but every time i launche the app the new entry button doesnt wrok properly, but if i launch the two Apps seperately they work just fine?
Excuse the spelling, damn autocorrect
I think it has something to do with the deviceready and startApp functions in the index.js that handels the MainView, everytime i try to load the index.html from within the app i just get a white screen.
Is there anyway i can get this to work or is this a bit to complicated?
I can't really say what is wrong without more information. Have you tried Chrome Remote Debugging or Safari Remote Debugging (for Android or iOS) and checking to see what the console says?
What Development Tool or IDE you used to develop mobile application.I tried to run in Eclipse(Kepler) 4.3 but it wont run
In recent Cordova/PhoneGap apps, the command line handles it all and the editor is not important. I use Brackets for my work.
Hi Again :)
I was looking into this PGdiary example. First trying to run your code as is.
Phonegap 1.9.0 is not supported, so had to change this in the config file to 3.3.0.
But the camera part doesn't work anymore. Any ideas?
Twitter @madebyjohann
Did you add the plugin?
'Add picture' doesn't work for me. I added <plugin name="Camera" value="org.apache.cordova.CameraLauncher" /> (in 'res/xml/config.xml') and <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> (in
app/AndroidManifest)
after adding: cordova plugin add https://git-wip-us.apache.o...
What am i doing wrong? thanks in advance
When I add plugins, I do not modify the config xml by hand, so I'm not sure you should have done that.
Also - I'd do remote debugging w/ Chrome to see the JS error.
The white screen disappears when the "cordova plugin add org.apache.cordova.file" will be added. The "window.requestFileSystem" in Line 17 (index.js) needs this.
Greetings from Hamburg Germany
hi i need to add a thumbnail to sqlite ... can u tell how???
I don't remember offhand if SQLite supports binary data. You could try it and let us know. Base64 would work. You could also store the thumbnails to the file system and store the *path* in SQLite.
hmm tanx u for ur quick reply...i have converted into thumbnail image... but whle it gives error..ok..tanx i will try again...
I'm evaluating different aspects of PhoneGap/Cordova for a project.
This was noted when I tried to build and run your code for Android using PhoneGap 3.5:
When I substitute phonegap.js for cordova.js, device.platform is not set (whether it's device or device.platform I haven't checked yet). If I skip the iOS-specific condition where device.platform is used the app starts and "Write Entry" is shown on the Android phone. So far so good.
After taking a photo it seems there's a resetting of state so that "Write Entry" pops up again without any picture shown. Previously stored entries are still there, but it seems to have completely "forgotten" about the picture I just took.
I also noticed I get no warning for non-included plugins, and it's not always obvious what plugins are needed, but that's a general "couldn't PhoneGap handle this better?" issue.
I usually develop natively for Android, but as this project will heavily rely on Javascript yet provide installable apps for Android and iOS, PhoneGap still looks like a good way forward, provided I get it to work that is :).
Regards,
Anders
In general, folks should (hopefully) take note of the date of blog posts and recognize that things change over time. :)
"When I substitute phonegap.js for cordova.js, device.platform is not set"
In general, this isn't something you do willy nilly unless you are changing from using the PhoneGap CLI to Cordova CLI.
It sounds like you didn't install the plugins - which is something that changed in the 3.0 release. It was pretty significant and in more recent blog posts, I always try to call out what plugins are required so folks don't run into issues like this.
Extremely helpful! Thanks a lot :)
Download attached file. link is not working
You can find it here - http://static.raymondcamden....
The link above will be corrected next time I update the site.