Talking Web Views


You got this one web view with Javascript and you want to talk to Javascript running in another web view, for reasons that I’m totally unsure of. Maybe you want to impress the girl in the coffee shop with your hard core coding expertise. Maybe you wanna go for a promotion and your manager is all like, “I need to see you innovate!” Maybe you think it’s just cool to load two web views that know one another. Hi, I’m Cliff, you’re here because you want to tote two web views. I’m here to show you how it’s done.

I haven’t posted anything in a month of Sundays. I never understood why people use dumb phrases like “a month of Sundays”. If there were only Sunday “days” in the month then it would be kind of short because you only get four of them… and that doesn’t seem like it makes much sense. I say that because people use that phrase to indicate long elapsed periods of time and a month of Sundays is only four days. People should just get over themselves and say I haven’t seen you in four days without all of the, “I need you to do the Maths and figure out what I’m talking about”. People are dumb. I digress.

I was beginning to tell you about this thing I’m doing with web views. I did it because I’ve been asked how to do it like two times. I got tired of pretending to know what I’m talking about so I prototyped it and I’m going to post my stuff here and just point to this page from now on. Plus, like I said earlier it’s been a month of Sundays (or four days if you don’t like Maths). So here goes. For those three of you who visit regularly I’m sorta back but not quite. Testing the waters a bit.

Back to these talking web views… the idea is simple. a mobile app with Javascript in one web view makes something happen in a second web view in the same app. To make it work you need to know two things, how to call from Javascript into native code and how to call from native code into Javascript. Tonight’s example will demonstrate how we will demonstrate how this is done on Android but I can show the same example on iOS.

Let’s start with main layout saved in a file call main.xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <WebView
            android:layout_width="fill_parent"
            android:layout_height="250dp"
            android:id="@+id/myWebView"
            android:layout_gravity="center"/>

        <WebView
            android:layout_width="fill_parent"
            android:layout_height="250dp"
            android:id="@+id/secondWebView"/>
    </LinearLayout>
</FrameLayout>

We have a linear layout set to vertical orientation. There are two nested WebViews defined, “myWebView” and “secondWebView”. Each will appear one above the other. Next we define a main activity.

package com.example.JavascriptBridge;




import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;




public class MainActivity extends Activity {
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        WebView myWebView = (WebView) this.findViewById(R.id.myWebView);
        myWebView.getSettings().setJavaScriptEnabled(true);
        //Magic Javascript handling class to be defined later
        final JavaScriptHandler scriptHandler = new JavaScriptHandler(this);
        myWebView.addJavascriptInterface(scriptHandler, "MyHandler");
        myWebView.loadUrl("file:///android_asset/view1.html");
        WebView otherWebView = (WebView) this.findViewById(R.id.secondWebView);
        scriptHandler.setRelayWebView(otherWebView);
        otherWebView.getSettings().setJavaScriptEnabled(true);
        otherWebView.addJavascriptInterface(scriptHandler, "MyHandler");
        otherWebView.loadUrl("file:///android_asset/view2.html");
    }
}

We set Javascript enabled on both views. We have a magic Javascript handler class which we will define next. This is a custom class which we stuff inside the first WebView using the addJavascriptInterface() method on the WebView class. This method makes ordinary Java objects appear as Javascript objects to code running inside a WebView. (We will soon add Javascript code that makes calls on the magic class.) Next we load an HTML file, “view1.html” into our first view. We grab our second WebView and give it to our JavascriptHandler which will relay information from the first WebView. We setJavascript enabled on the second WebView and put our magic JavascriptHandler object in it as well, just for good measure. We load view2.html as the content for the second view.

Let’s see our first HTML file, view1.html:

<!DOCTYPE html>
<html>
<head>
    <title>Javascript Bridge</title>
</head>
<script>

</script>
    <body>
        Java/Javascript Bridge example
        <form id="inputform">
            Value to pass: <input type="text" name="user_input">
            <input type="button" name="pass" value="relay!" onclick="window.MyHandler.setValue(inputform.user_input.value)">
        </form>
    </body>
</html>

Not much here. There’s a typical HTML form with an input text field and a button. The onClick() action of the button has Javascript that talks to a special object called “MyHandler”. This object was defined in the Activity above, if you recall these lines:

        final JavaScriptHandler scriptHandler = new JavaScriptHandler(this);
        myWebView.addJavascriptInterface(scriptHandler, "MyHandler");

The handler, which we’ll define next, exposes a setValue method. We call this method and supply the value from the earlier input text field. Let’s see the magic inside the custom JavaScriptHandler class definition.

package com.example.JavascriptBridge;

import android.webkit.WebView;

public class JavaScriptHandler {
    MainActivity parentActivity;
    private String value = "unset";
    private WebView relayWebView;

    public JavaScriptHandler(MainActivity activity) {
        parentActivity = activity;
    }

    public void setValue(String val){
        this.value = val;
        relayWebView.loadUrl("javascript:second_inputform.second_user_input.value='" + value + "'");
    }

    public String getValue() {
        return value;
    }

    public void setRelayWebView(WebView relayWebView) {
        this.relayWebView = relayWebView;
    }
}

Inside the JavaScriptHandler custom class we have a WebView property, relayWebView which we saw used in the MainActivity. There is also a value property but inside the property set method we have additional logic to relay the value to the relayWebView property. We do this by calling the loadUrl() method on the relayWebView and passing in javascript code using the “javascript:” protocol. (When you ask a WebView or web browser to load a Url prefixed with the “javascript:” protocol is will execute any javascript included after the protocol prefix.) The Javascript assumes there is a “second_inputform” with a “second_user_input” text field and attempts to set the value of this text input. Let’s look at the view2.html which we load into the relayWebView from the MainActivity.

<!DOCTYPE html>
<html>
<head>
    <title>Javascript 2nd Page</title>
</head>
    <body>
        Passed value shows here!
        <form id="second_inputform">
            Value Received: <input type="text" name="second_user_input">
            <input type="button" name="button" value="reset" onclick="second_inputform.second_user_input.value='enter text above'">
        </form>
    </body>
</html>

Here you see the “second_inputform” with the “second_user_input” text input. So when the earlier Javascript is injected and executed you will see the text from the first WebView relayed to the second WebView. That’s the entirety of it! Since I haven’t posted source in what we used to describe as a “month of Sundays” but now understand should be much more than a four day-long month, it’s going to take me a while to figure out how to glue my example project to this blog post.

I used to be so much better at gluing code to blog posts but these days I don’t even remember how to log in to my site as the administrator. With any luck, the code will be found as an attachment or maybe something with a little paper clip icon hanging on the side bar. I’m not really sure but I’ll figure that out now. There! I just remembered the Box widget on the right hand side. You can find my Jvascriptbridge.zip project there. Until next time… which might not take an entire month of Sundays if I get my act together…

Getting one’s act together is another one of those things people say. I’ve never acted nor do I belong to a drama club, so I would have a hard time getting my act together. For example, if I had an “act” how did it become disassembled in the first place? I would imagine various pieces of what would have been “my act” scattered randomly on the floor. I would probably hire somebody to piece it back together because I suck at act assembly. That would take a while because last time I looked in the Yellow Pages there was no section for “Act Assemblers”. I could find “Plumber”, and “Doctor” just fine. “Mechanic” was there, as well as “Actor” but not “Assembler”. It’s late, and I probably should go now. If you’re into programming stuff, check back. I promise I won’t ramble senselessly on my next post… unless you like my rambling. And if you do like it maybe I’ll put up one of those PayPal links so people can pay me for rambling. Yes it’s late. I should do other stuff like sleep. Thanks for reading! 🙂

There’s “no such method” to my madness, man…


So you’re writing a web application and it, like, needs to run on one of the many mobile devices invading society these days. You need to invoke a function or a method that doesn’t exist on the class you’re dealing with. You won’t know the name or arguments of the function until the Jaavscript begins executing on the mobile device. Hi, I’m Cliff. You’re here because you can’t predict your functions and methods in advance of coding your application. I’m here because I wanted to post a really quick tip for those of you dealing with crazy Javascript on a modern browser.

Ruby, Python, Groovy, and even Objective-C have a menas of catching and interpreting functions or methods that have not yet been defined. It’s one of those new cool-kid programming tricks that these languages employ which allow developers to define programming languages that take the shape of the problem they are trying to solve. (See write-ups on BDD, and DSL to understand further.) Javascript, historically, has lacked such features. Notice I say historically… I’ve been studying just such a feature enabled by a magic method called __noSuchMethod_. I’ll be spending the next few days understanding its feasibility and limitations on a mobile web browser. That along with the Java to Javascript bridge feature in Android. Check it out and you can thank me later…

Nexus 7 stuck in Reboot mode


You left your Nexus 7 on all day and, unbeknownst to you, there isn’t enough power left in the device to draw the letter “A” much less boot the device. You plug it in and while noticing the familiar black screen, instinctively hold the lock screen button in an attempt to power it on. The Google logo followed multi-colored “X” which is eventually followed the home screen whets your appetite for some advanced Droid play only to hear an unexpected chime followed a power down notice followed by  by static. Hi, I’m Cliff. You’re here because you’re locked out of your Nexus 7 and all of its robotic goodness. I’m here to comfort you, reassuring that everything is ok, there is always a way back in. Read below and you’ll be shaking hands with R2-D2 in no time.

I found the key to Nexus lockout on another forum and it totally restored my ability to power up in peace:

– Assuming your unit can boot long enough to do this, press and hold all the buttons (that is the lock button and both volume buttons) to give the device a hard reset.
– Keep holding the buttons till it reboots to the bootloader.
– Using the volume buttons, cycle through the menu selections and choose to power off the device. (The power off option will point to the lock button which you click to power it off)
– Unplug then re-plug the charger.
– You should now see the battery-charging icon. It shouldn’t boot up on its own anymore. Just let it sit and charge to full capacity.

Talking TCP to a server on an Android emulator


You had a really slick idea. You wrote this TCP server that accepts socket connections and it runs on an Android device! You’ve been testing it all day and debugging it until it was bullet proof. You arrive home ready to test the last 4 liner modification you made just before leaving the office when you realize you left your device in the office! No worries, fire up the emulator and connect the test client on your mac to…? You test client expects an IP address and a port! Hi, I’m Cliff. You’re here because you left your Android phone on your desk underneath the 5oz bag of Lays BBQ chips. I’m here to give you some brief info on how to get your emulated Droid talking TCP  to its host environment via a telnet session and a redirect command.

The first thing you need to do is determine which port your emulator is bound to. The port number is usually listed in the title bar of the Window your emulator runs in. (Unless you’ve specified an override, the 1st emulator instance you start will bind to port 5554, and the next will bind to 5555 and so forth.)

Emulator Screen

Next, open your terminal (or “cmd” command prompt on Windows) and start a telnet session on localhost. This will connect you the emulator control console where you can enter port redirect commands. The command for redirect is:

redir add <protocol>:<host-port>:<guest-port>

So let’s say your socket server is running on port 8080 on the emulator and you want to redirect all traffic going to port 8081 on your Mac (or Windows PC), you would enter:

redir add tcp:8081:808

You can now “exit” the telnet session and from thenceforth all traffic hitting localhost:8081 will be forwarded directly to the emulator on its port 8080! When you are done with port forwarding you can connect to the emulator control console again via telnet and remove the port forward with the following command:

redir del tcp:8081

These comands work equally well with UDP. Use:

redir add udp:9001:9000

To forward UDP traffic from 9001 on localhost to the emulator’s port 9000 and use:

redir del udp:9001

To undo the change when you are done. There are various other things you can do with the emulator console. For more info see the Android developer docs on Using the Emulator Console.

IntelliJ Android Test java.lang.NoClassDefFoundError: junit/textui/ResultPrinter


A short post for those who are wondering if I’m still alive. I got an error trying to run unit tests in an Android project under IntelliJ Idea and I got stumped. After a few minutes of Googling it dawned on me that I had chosen to run the test using a JUnit run configuration instead of an Android run configuration. That is when I right clicked the test method, my mind insisted on chosing the more familiar white window-looking icon with the red and green arrows instead of the Android icon, also with red and green arrows. I was associating the android icon with execute apk and not run tests. I now notice the little chevron-looking arrows and realize they refers to the red, green, refactor life cycle of development.. duh!

Cross Platform Mobile Development


Just when I thought I reached my peak for number of programming languages I could fit in my skull I get bitten by the Lua bug! Not exactly sure what to make of it at first, I rolled up my sleeves and dug deep to see what the big deal was. Why Lua? Was it yet another language, or could there be something I was missing. Hi, I’m Cliff. You’re here because you thought Lua was one of those ornaments you hang on your neck when going on vacation. I’m here for quite the same reason, only I figured out something more.

I found a product called, Gideros Studio, which is based on Lua technology. It facilitates powerful cross platform mobile development for iOS and Android, allowing you to prototype apps in a mobile agnostic emulator while offering instant deployment to device. Development with Gideros is fast! Apps are pushed to device and launched instantly, much quicker than Xcode, IntelliJ, or Eclipse. What’s more, apps are pushed without wires! So now that I found something extremely useful that could be done with the new programming language I am psyched to push the limits. I’ve been mostly quiet on my blog but more active these days on Stackoverflow with questions and answers as I explore.

Side note
If you are getting an error “attempt to call method ‘getContentWidth'” while trying to follow one of the various tutorials or examples out there note that version 2012 of the studio has recently been released and you should upgrade. Some of the examples were built against a beta of the newer SDK and you will no doubt hit the issue unless you’ve upgraded within the last couple of weeks. Also note that you should rebuild and redeploy the player on your device using the newer SDK to avoid the error.