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:

hazelvisions said...

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

Simon Mac Donald said...

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

ccliley said...

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.

Simon Mac Donald said...

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

ccliley said...

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?

Robert (aka Defused) said...

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?

Simon Mac Donald said...

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

Simon Mac Donald said...

@ccliley

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

Sven said...

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

Simon Mac Donald said...

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

Scholar said...

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

Brian Kalbster said...

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

Simon Mac Donald said...

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

Anusha Kadambala said...

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.

Simon Mac Donald said...

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

Anusha Kadambala said...

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.

Simon Mac Donald said...

@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

__talles__ said...

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

Simon Mac Donald said...

@talles Can you show some code?

ChuK0i said...

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.

Simon Mac Donald said...

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

clemens said...

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?

Simon Mac Donald said...

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

ddanone said...

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

Simon Mac Donald said...

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

ddanone said...

ok thank you Simon.

siva said...

If it possiable to load two url in childbrowser.

Simon Mac Donald said...

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

Matthias Lynch said...

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?

Simon Mac Donald said...

@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().

mikhailjacob said...

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.

mikhailjacob said...

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

Thank you,

Raj said...

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?

Raj said...

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

Simon Mac Donald said...

@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?

Raj said...

@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?

Raj said...

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

lingala said...

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

Simon Mac Donald said...

@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?

Douglas Alves said...

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

Simon Mac Donald said...

@Douglas Alves

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

Vaibhav said...

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.. :)

Michael Tamony said...

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.

Simon MacDonald said...

@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

Simon MacDonald said...

@Vaibhav

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

Vaibhav said...

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.

Simon MacDonald said...

@Vaibhav

Where did you add those lines?

Vaibhav said...

just above window.openDatabase() command...

Demo application can be found at

https://github.com/vaibhavbparikh/TestDBandCB

Rajeev Dave said...

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.

Rajeev Dave said...

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.

Vaibhav said...

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

Michael Tamony said...

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?

Michael Tamony said...

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

João Colucas said...

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).

Simon MacDonald said...

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

MULSANIYA said...

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.

Simon MacDonald said...

@MULSANIYA

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

Scott Adams said...

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.

Simon MacDonald said...

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

Alex Diepeveen said...

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)

Simon MacDonald said...

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

Rajesh said...

Can we view Pdf file in Android using childbrowser

Simon MacDonald said...

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

pinkphp said...

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 });

Simon MacDonald said...

@pinkphp

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

pinkphp said...

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

Simon MacDonald said...

@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?

pinkphp said...

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

Simon MacDonald said...

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

Jefry Pozo said...

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?

Simon MacDonald said...

@Jefry Pozo

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

Yann SOUDY said...

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

Simon MacDonald said...

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

mo@mopages.co.uk said...

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

Simon MacDonald said...

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

mo@mopages.co.uk said...

@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?

Simon MacDonald said...

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

mo@mopages.co.uk said...

@Simon
Genius Simon!