Wednesday, January 4, 2012

On the Eleventh Day of PhoneGapping: Configuration Defaults

As much as we'd like to make PhoneGap 100% cross platform there are some differences between platforms that sometimes need to be smoothed over. Two items that I can think of off the top of my head are:

1) Android's persistent file system defaults to the SD Card which everyone can read from while in iOS it defaults to a directory that only the app can read.

2) In order to play a Media file the path to a file in the www directory is different for Android and iOS.

So to smooth these difference over you can create a .json file that is included in your www directory which will hold platform specific configuration details and load it via XHR. Here's how to do it:


Your .json file on Android could look like:

{ 
    "mediaPath": "/android_asset/www/"
}

and on iOS it would look like;

{ 
    "mediaPath": ""
}

So now you can replace clunky if statements like:
if (device.platform == "Android") {
    var my_media = new Media("/android_asset/www/test.mp3");
} else {
    var my_media = new Media("test.mp3");
}
with:
var my_media = new Media(Config.mediaPath + "test.mp3");
and continue to access everything in a cross platform way.

14 comments:

Jesse said...

How about just in code ??:

function onDeviceReady(){
Config.mediaPath':(device.platform == "Android") ? "/android_asset/www/" : "";
}

Simon Mac Donald said...

@Jesse

Yeah, you could do that with the ternary operator but as you start to support more and more platforms like BB and WP7 it begins to be a pain.

Juan Alberto López Cavallotti said...

Simon, what is the advantage of loading the extra configuration using XHR and not as a static file in the index.html?

Simon MacDonald said...

@Juan,

There isn't really. I was just trying to illustrate how you could do this with XHR.

Zuko said...

Hello Simon,
Great Post. Thanks.

What i m trying to do is download a file from a remore server and save it locally on the android device.
Using this:
window.requestFileSystem (with a PERSISTENT requst)
the rootPath that i get is /sdcard
(when an sdcard is available)

But what i would like to do is always get /data/{my package} as the rootPath. Wether there is an sdcard or not.

Is this possible by altering the configurations, as you did before?
Could you explain a little bit what i need to do?

Thanks.

Simon MacDonald said...

@Zuko

Probably the easiest thing for you go do is to:

window.resolveLocalFileSystemURI("file:///data/data/{package name}", onSuccess, onError);

and the onSuccess method would be called with a DirectoryEntry that represents your internal storage. If you don't want to hard code the path in your html put it in a JSON file like I specify in this post.

Pankaj Sharma said...

How to store file(like doc, pdf) in the internal storage in android phonegap.And how to read that file from internal storage.?

Simon MacDonald said...

@Pankaj Sharma

I would use the FileTransfer object to download the file to the file system. Then using the path you can load the file.

Pankaj Sharma said...

thanx simon :) i used the file transfer object to download that file and save it to the internal storage , i used the plugin (https://github.com/markeeftb/FileOpener) to read that file. but it giving me message that "this Document can not be opened". i think this plugin is only for teh local storage. can you tellme how to read the file if it is in the internal storage..?

Simon MacDonald said...

@Pankaj Sharma

I did a quick look at that plugin and it should work just fine as long as you have an application installed that can open a file of that type.

Pankaj Sharma said...

thanx Simon for the reply , but it still not working for me.
i used these line of code to open downloaded file window.plugins.fileOpener.open("file:///data/data/com.example.cordove_filedownload/ATT.pdf"); or
window.plugins.fileOpener.open("/data/data/com.example.cordove_filedownload/ATT.pdf"); then its giving me Toast message with "This document can not be opened."

if i use window.plugins.fileOpener.open("file:///mnt/sdcard/ATT.pdf"); then it will opening that pdf file in the reader. but not in case of internal storage.

Simon MacDonald said...

@Pankaj Sharma

Oh, I see now. Yes, this makes perfect sense. When you fire an intent from an Android program you are actually starting a whole new program. Your app and the app that reads the pdf are run as two separate user ids. When the file is on the /sdcard the file permissions are world readable but when you store the file in /data/data/{package name} the files are only readable by your app.

If you want to be able to serve files from the apps internal directory you will need to either create a content resolver or write a PDF viewer that executes inside your app instead of being an external program.

Pankaj Sharma said...

thanx Simon :) Is their any plugin for Content Resolver , that i directly use in my application to open a file from the internal storage. oe else i have to create my own content resolver.
plz give me some idea How to create Content Resolver ?

Simon MacDonald said...

@Pankaj Sharma

No, there is not Content Provider plugin that I know of, you'll have to expose one yourself.

https://developer.android.com/guide/topics/providers/content-providers.html