Friday, September 9, 2011

PhoneGap Android ChildBrowser Revamp

Recently I've been spending quite a bit of time revamping the Android ChildBrowser so that it is more in line with the ChildBrowser available for iOS. The big problem was that on Android we started a new Intent to view the web page and there was no way to communicate back to the original application. This new approach uses a dialog with an embedded web view to show the new web page but don't worry you can still use the old way if you really want.

Installation of the new plugin is much the same as it was previously.

1. To install the plugin, move www/childbrowser.js to your project's www folder and include a reference to it in your html file after phonegap.{ver}.js.

<script type="text/javascript" charset="utf-8" src="phonegap.js"></script>

<script type="text/javascript" charset="utf-8" src="childbrowser.js"></script>

2. Copy the image files folder www/childbrowser to your project'w www folder. Note you need the entire folder not just the images. Feel free to provide your own images, these are only some default place holders we are providing.

3. Create a directory within your project called "src/com/phonegap/plugins/childBrowser" and copy "src/com/phonegap/plugins/childBrowser/ChildBrowser.java" into it.

4. Add the following activity to your AndroidManifest.xml file. It should be added inside the <application> tag.

<activity android:name="com.phonegap.DroidGap" android:label="@string/app_name">
<intent-filter>
</intent-filter>
</activity>

5. In your res/xml/plugins.xml file add the following line:

<plugin name="ChildBrowser" value="com.phonegap.plugins.childBrowser.ChildBrowser"/>

Once installed properly, you should be able to browse external sites. I've provided the full listing of my test html at the end of this post but for now here's a screen shot of what the app would look like:


As you can see there are three buttons. The first "Show Page" is going to give you a dialog that looks like this:


This is accomplished by calling the following JavaScript:
window.plugins.childBrowser.showWebPage(
            "http://www.phonegap.com", {
            showLocationBar: true
        });
The second button "Show Page No Toolbar" once pressed gives you a dialog that looks like this:


Which is accomplished by calling the following JavaScript:
window.plugins.childBrowser.showWebPage(
            "http://www.phonegap.com", {
            showLocationBar: false
        });
As you can see the only difference between it and the first example is that the showLocationBar parameter has been set to false. Currently this is the only optional parameter we support right now but more can be added easily if required.

Finally if you push the last button "Old ChildBrowser" you would have a new Intent being launched to handle the page view:


Now the syntax for getting the old ChildBrowser behaviour has changed a bit. It is now:
window.plugins.childBrowser.openExternal("http://www.phonegap.com");
Now this is all well and good but how does it help us communicate between the application and the child browser. To do this we provide a couple of event handler on the ChildBrowser for onClose and onLocationChanged. You provide your own functions which are called when the ChildBrowser gets a close event or a locationChanged event. For instance:
function showPage(locbar) {
    window.plugins.childBrowser.onLocationChange = locationChanged;
    window.plugins.childBrowser.onClose = closed;
    window.plugins.childBrowser.showWebPage(
        "http://www.phonegap.com", {
        showLocationBar: locbar
    });
}

function locationChanged(newurl) {
    console.log("The JS got this url = " + newurl);
}
    
function closed() {
    console.log("The JS got a close event");
}
Well I hope people find the changes useful. It should make doing oauth a lot easier. No, I don't have an example for that yet.

78 comments:

  1. Thank you so much for this, including a great write-up!

    ReplyDelete
  2. I've recently made the layout for the toolbar a bit better. If anyone had problems with the earlier version give it another try.

    ReplyDelete
  3. I love the new look. One problem I've run into is that now I can't get a keyboard to come up on external html form pages... I had to go back to the old CB version for it to work.

    ReplyDelete
  4. @ccliley Yeah, there is a bug open on that. I will get to it ASAP.

    ReplyDelete
  5. Thanks Simon! I also noticed that the external pages load zoomed in instead of scaling to the browser. Is there any way to address this?

    ReplyDelete
  6. Thank you for your tutorial.
    But can you advise me, I've done everything as you describe, but in Emulator address bar not hiding when I'm use showLocationBar: false.

    How can I resolve it?

    ReplyDelete
  7. @Robert

    Not sure what to tell you. It works great for me. Make sure when you pass the parameter in you wrap it in {} as we are expecting and object.

    ReplyDelete
  8. @ccliley

    Can you give me an example of an external page that produces this behaviour so I can check it out?

    ReplyDelete
  9. When I installed ChildBrowser I got the following Error:
    Error: Status=2 Message=Class not found

    Also described here:
    https://github.com/phonegap/phonegap-plugins/issues/26

    Nothing helped me but moving the ChildBrowser line in plugins.xml from the last to first line. Now it works like a charm.

    Thanks for your amazing work.

    Sven

    ReplyDelete
  10. @Sven well you really shouldn't need to do that but I'm glad it is working for you and you've shared the work around with others.

    ReplyDelete
  11. I will extend this more as I did iOS ChildBrowser to execute Javascript in the browser. If anyone need it too you can ping me at you.mere@gmail.com

    ReplyDelete
  12. I am having trouble using childBrowser and openExternal with Phonegap 1.1.0

    When I use:

    window.plugins.childBrowser.openExternal('http://www.google.com', true);

    The childbrowser pops up, but it is just a black screen.

    I can use

    window.plugins.childBrowser.showWebPage("http://www.google.com");

    and that works fine.


    I have double checked my plugins.xml, AndroidManifest.xml, etc. I have also tested on a G2 and Droid 2 with the same results.

    Is there something else I need to add to allow the openExternal with phonegap=true to work?

    Thanks

    ReplyDelete
  13. @Brian

    I can reproduce the bug. You can do an openExternal("http://www.google.com"); and omit the boolean parameter and it will work for you as well. I'm going to open an issue on phonegap-plugins to track this.

    ReplyDelete
  14. Thanks for this tutorial, i gone ahead in this task because of this. Now, I got stuck in closing the child browser when it is successful. I tried using " window.plugins.childBrowser.onClose",but somehow its not working. Please let me know how to close child browser whenever we require.

    ReplyDelete
  15. @Anusha Kadambala

    Slight misunderstanding in the methods. The onClose is an event handler so you can react to the ChildBrowser closing if it is been done by the user. To close it programmatically you use the close() method.

    ReplyDelete
  16. Thanks for help simon :) ... but now i want to how to logout from facebook. I tried on net but hardly any luck...i also tried the code
    $.get("https://www.facebook.com/logout.php?next=sitedomain&access_token="+localStorage.facebook_token,function(){alert('success');
    localStorage.removeItem('facebook_token');
    window.plugins.childBrowser.close();});

    It is showing me the alert success but it is not closing out the session. Please need help on this.

    ReplyDelete
  17. @Anusha Kadambala

    I think you should check out Dave Johnson's Facebook connect plugin as it may make your life a lot easier:

    https://github.com/davejohnson/phonegap-plugin-facebook-connect

    ReplyDelete
  18. hi , well , im using child browser to open an external page made by me, the address bar is set by off , i need to make a close buton , but when i use window.plugins.childBrowser.close() it didnt close the page but refreshes it

    ReplyDelete
  19. I'm having trouble getting childbrowser to run. I'm done all instructions as given. Even Sven's comment about moving the line in plugins.xml. But no luck. Keep getting the error class not found.

    ReplyDelete
  20. @ChuK0i you should come over to the PhoneGap Google group so I can get some more info from you.

    ReplyDelete
  21. I just customized the plugin to avoid the black border:

    dialog = new Dialog(ctx, android.R.style.Theme_Translucent_NoTitleBar);

    Maybe this would be a good default or another possible optional param?

    ReplyDelete
  22. @clemens good idea, I've created an issue in GitHub to track your suggestion. If you want you can go ahead and make the code changes yourself and I can review them.

    ReplyDelete
  23. Hi Simon,
    is it possible to run javascript on an external page?
    i've tested and it doesn't executes any javascript.
    Thanks

    ReplyDelete
  24. @ddanone Yes, it should be possible to run JavaScript on the external page. No, you can't make PhoneGap API calls.

    ReplyDelete
  25. If it possiable to load two url in childbrowser.

    ReplyDelete
  26. @siva

    I don't think I understand. Can you give me more details? Loading two urls at the same time wouldn't be possible but two in a row would be.

    ReplyDelete
  27. When navigating in the child browser, the phone's back button brings one straight back to index.html (out of the child browser). Would there be a way to have the phone's back button navigate one page back like it does in a normal browser?

    ReplyDelete
  28. @Matthias

    Well I wrote it so the back button would close the dialog that the ChildBrowser. It's open source so you are free to change that behaviour. You could get rid of the onDismissListener at line 218 and then add a HW back button handler that calls goBack().

    ReplyDelete
  29. Simon, thanks for this excellent write-up. I am a newb and I was able to get this working. I was just wondering how I could remove this right scroll bar from the dialog window.

    ReplyDelete
  30. I was asking about removing the scroll bar. I fixed it by adding this. webview.setVerticalScrollBarEnabled(false);

    Thank you,

    ReplyDelete
  31. Hey Simon - I implemented all the steps as described above, but i'm getting this error -
    "Cannot read property 'childBrowser' of undefined at file:///android_asset/www/index.js"

    In Index.js file, It's giving error at this line -
    window.plugins.childBrowser.showWebPage(
    "http://www.phonegap.com", {
    showLocationBar: true
    });

    Any ideas for fixing this?

    ReplyDelete
  32. Simon, FYI - I'm using phone gap version 1.3.0

    ReplyDelete
  33. @Raj

    That's hard to debug. Are you sure you are loading the .js files in the correct order and you are waiting for the "deviceready" event before you call showWebPage?

    ReplyDelete
  34. @Simon - I started from scratch and followed the steps again and it worked. Thank you

    One more question - I am using child browser for displaying the jpg images located on a different server. It works great but the image loads as zoomed in instead of scaling it the the page. I don't see this problem with PDF's.

    Any idea how to fix this?

    ReplyDelete
  35. @Simon, FYI - The jpg image zoom problem is only with android devices. It works good on iPhone

    ReplyDelete
  36. Thanks a lot dude, It is very helpful for me...:)

    ReplyDelete
  37. @Raj

    I have no idea what is going on as I don't see that behaviour. Can you send me a site I can test?

    ReplyDelete
  38. Simon,

    thank you very much for your effort on helping us.

    I did everything you explained above, but I can't make it work. I click on the buttons, but the webpages does not open. What do you think I'm doing wrong?

    Thanks again!

    Douglas

    ReplyDelete
  39. @Douglas Alves

    Run "adb logcat" to see what errors are being shown and I'll better be able to help you.

    ReplyDelete
  40. Hi Simon,

    Great contribution of yours.
    i am facing a problem in ChildBrowser implementation in Phonegap 1.3.0 with Android 3.0 and higher.

    After opening links in child browser ,pressing back button i am not able to fire window.openDatabase(),it throws exception of SECURITY_ERR : DOM Exception 18.

    FYI,the same code is working in 2.3.x and below versions of Android and even iOS and BB.The problem is with Android 3.0 and above.

    i am trying to dig the problem from last 3 days but not get that much clue.
    i think issue is with back button handling or with webview's goBack method .

    Any help would be appreciated .

    Thanks in advance.. :)

    ReplyDelete
  41. Simon I ran into a problem with this using PhoneGap Build I think. It seems that Build doesn't like:

    function showPage(locbar) {
    window.plugins.childBrowser.onLocationChange = locationChanged;
    window.plugins.childBrowser.onClose = closed;
    window.plugins.childBrowser.showWebPage( "url", {
    showLocationBar: locbar
    });
    }

    function openExternal() {
    window.plugins.childBrowser.openExternal("url");
    }

    You might check when you can. I could also be wrong.

    ReplyDelete
  42. @Michael

    I don't have anything to do with PhoneGap build. You'll have to contact them directly to see what's wrong with the ChildBrowser they are using. Possibly it hasn't been updated to fix any issues with 1.5.0.

    http://community.phonegap.com/nitobi/products/nitobi_phonegap_build?from_gsfn=true

    ReplyDelete
  43. @Vaibhav

    Hmmmm...I'll have to put that on my to do list.

    ReplyDelete
  44. Hi Simon,

    Thanks for your attention.

    i finally got the work around for the issue.

    I just put following two lines above window.opendatabse function and now it works fine.

    navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
    window.droiddb = new DroidDB();


    Hope this will help others also.

    ReplyDelete
  45. @Vaibhav

    Where did you add those lines?

    ReplyDelete
  46. just above window.openDatabase() command...

    Demo application can be found at

    https://github.com/vaibhavbparikh/TestDBandCB

    ReplyDelete
  47. Vaibhav/Simon,

    When I am putting your logic
    navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
    window.droiddb = new DroidDB();

    It is still now working with prepopulated database.

    It is giving Warning if I select row from any table. and db.transaction does not work.

    ReplyDelete
  48. Vaibhav/Simon,

    When I am putting your logic
    navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
    window.droiddb = new DroidDB();

    It is still now working with prepopulated database.

    It is giving Warning if I select row from any table. and db.transaction does not work.

    ReplyDelete
  49. @Rajiv,

    Yaa true ,this will wipe off context/session.
    To make db.transaction() command work , u need to reopen the database and have to get the new context/session.

    ReplyDelete
  50. Simon,

    I don't know if this is considered a bug with CB, but it won't allow for opening(downloading) a .pdf with showWebPage. I can open(download) .pdf files fine with openExternal('urlofsite/somefile.pdf') but when I change to showWebPage it does nothing. I am using showWebPage to open our sites mobile page that has Board agendas. Users would click to either download or view. I can use openExternal, but it takes people out of the application.

    Any thought? Am I using wrong plugin?

    ReplyDelete
  51. You can remove my previous comment. Relative links............doh!

    ReplyDelete
  52. hi Simon! thanks for the plugin, it works well with webpages, unfortunately it doesn't seems to work with pdf's, don't know why.. it simply opens a blank page. is it a problem with my cellphone? (using a sony xperia sola).

    ReplyDelete
  53. @João Colucas

    The ChildBrowser would need to be enhanced to be able to display PDF files. It is not a full featured browser. You'd be better off calling openExternal rather than showWebPage.

    ReplyDelete
  54. webview time out error when child browser open and webpage not shown message display on the screen and then application force close.

    what is happen i dont know.

    and also back key not work when we switch to childbrowser.

    ReplyDelete
  55. @MULSANIYA

    I need an example. What do you see in "adb logcat"?

    ReplyDelete
  56. Hi Simon,

    Really like the blog, and find it very useful. Whilst creating an Android version of my app (the iOS is already live and working) it never created a plugin xml file, and so I can't follow one of the steps above.

    I'm using eclipse to create the app, and I've followed every other step, and ideas of how I can create a plugins.xml file myself and link it correctly?

    Thanks in advance.

    ReplyDelete
  57. @Scott Adams

    Well the instructions are a bit old. If you are using a later version of PhoneGap then check to see if you have the res/xml/config.xml file. If that file exists that is where you need to put the plugin line.

    ReplyDelete
  58. Hi, is there a way of modifying the top bar in the childbrowser?
    On android (dont know about IOS) the arrows and cross are hard to see (Light color on light background)

    ReplyDelete
  59. @Alex Diepeveen

    Yes, you'd have to modify the Java code though. Go into the showWebPage method find where the buttons are created then you can call setBackgroundColor on the button to give it a different color. Alternatively you can provide your own images for the buttons.

    ReplyDelete
  60. Can we view Pdf file in Android using childbrowser

    ReplyDelete
  61. @Rajesh

    If you use the openExternal command from the child browser it should download the file then launch the PDF viewer already installed on your device.

    ReplyDelete
  62. Hello, there is an issue, i think, on android when i open an etherpad lite with plugin childbrowser, wait a moment and keyboard changes continously to key up and key down, for example:
    window.plugins.childBrowser.showWebPage("http://beta.etherpad.org/p/brankia", { showLocationBar: false });

    ReplyDelete
  63. @pinkphp

    I did a quick test and couldn't reproduce. What OS are you running on?

    ReplyDelete
  64. video issue: http://youtu.be/dqwBK28nMMs

    ReplyDelete
  65. @pinkphp

    Oh, that's brilliant. The keyboard flashes with the blinking cursor. Can you load the page in the Android Browser and not Chrome to see if it can be reproduced?

    ReplyDelete
  66. Exactly Dear Simon. With Android browser, keyboard blinking, with chrome, keyboard is good.
    Thank you.

    ReplyDelete
  67. @pinkphp

    Yeah, so if it is a bug in the Android Browser it'll be a bug in the Android WebView. Not sure if we can do much about it.

    ReplyDelete
  68. I'm trying to do a Twitter OAuth, but it seems childBrowser only fires onLocationChange event the first time, and never again.

    Any idea what could be causing this?

    ReplyDelete
  69. @Jefry Pozo

    Nope, it should fire on each change to the location. Any code you can share?

    ReplyDelete
  70. Hi. I'm using Childbrowser with PhoneGap Build : how to avoid black borders ? thx in advance !

    ReplyDelete
  71. @Yann SOUDY

    I don't use PG Build but to the best of my knowledge there is no way to modify the plugin on their server.

    ReplyDelete
  72. @Simon @Matthias Could you explain the changes needed to set the phone back button to goBack() instead of close? As a noob im struggling!

    ReplyDelete
  73. @mo@mopages.co.uk

    Do you mean make it so the ChildBrowser doesn't close when you hit the back button? If that is the question, then no it is not possible. The ChildBrowser is in a dialog so it closes when you hit the back button by default.

    ReplyDelete
  74. @Simon
    Yeah that is what I was hoping to do - I thought we could just write a function/handler within the childbrowser java file for the back button to run a different function? Or is there a way we can disable the back button from functioning when the ChildBrowser dialogue box is open?

    ReplyDelete
  75. @mo@mopages.co.uk

    If you go into the ChildBrowser code you can setCancellable to false and the dialog won't close on the back button.

    ReplyDelete