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.

15 comments:

  1. How about just in code ??:

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

    ReplyDelete
  2. @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.

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

    ReplyDelete
  4. @Juan,

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

    ReplyDelete
  5. 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.

    ReplyDelete
  6. @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.

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

    ReplyDelete
  8. @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.

    ReplyDelete
  9. 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..?

    ReplyDelete
  10. @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.

    ReplyDelete
  11. 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.

    ReplyDelete
  12. @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.

    ReplyDelete
  13. 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 ?

    ReplyDelete
  14. @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

    ReplyDelete
  15. Elegant, thank you. Check out hooks in case, your .json files get deleted after_build

    ReplyDelete