Browse in : |
All
> Topics
> Programming
All > Journals > CVu > 271 Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: Simple Android programming with WebKit
Author: Martin Moene
Date: 05 March 2015 21:06:26 +00:00 or Thu, 05 March 2015 21:06:26 +00:00
Summary: Silas S. Brown shares his trials with developing for mobile devices.
Body:
I don’t like modern smartphones. It seems the manufacturers are trying to trick people into thinking that the lack of real buttons is some kind of progress, as a lame excuse for their having become too lazy to make keyboards. Nowadays if you want a keyboard you are expected to buy it as a separate add-on and perform a juggling act on the streets.
I’m still waiting for the great keyboard revival, but meanwhile other people are asking me to develop for touch devices and I reluctantly bought a Sony Xperia Z Ultra for a project. Much to my annoyance, within one month of purchase it started registering false screen-presses by detecting moisture from my breath: they obviously didn’t realise people with limited eyesight might hold it close. And needless to say I can hardly type on the thing (an application called ‘Hacker’s Keyboard’ with its good Dvorak layout helped a bit, but it’s still not as good as a real keyboard); the pocket Bluetooth keyboard I ordered never turned up.
Anyway, the canonical way to program these things is to download the Android Developer Tools (ADT) [1]. The Eclipse-based ADT is a bit – how should I put this? – ‘wobbly’. Not every version installs OK. One version I downloaded ended up not being able to create new projects (well, it could create new projects, but the empty project wouldn’t build, although it could still open projects created with an earlier version of ADT). Sometimes when using ADT you have to wait for it to finish its background operations before it’ll do what you want without errors, because the code that’s supposed to do this automatically doesn’t always work. Sometimes there are other timing-related bugs, so you generally have to act nice and slowly as if not to confuse the poor computer. Sometimes you have to press F5 on the Package Explorer to force a refresh, although that’s supposed to happen by itself when necessary but it doesn’t always work. And every version of the ADT is different, so if I tell you what to do in one version then the instructions likely won’t work in another. Bring back the command line!
If you do manage to create a project that builds, I’d suggest the first thing you should do is to create a WebView and write some HTML for it. That way you can get an application up and running quite quickly. The HTML can include Javascript that calls back into the Java code (and, as it turns out, C code: more on this later), or you could just write a Javascript-based Web application and use ADT to package it into an ‘app’ as a convenience for offline use (but please don’t write an app that just browses an online site with no additional functionality: apps like that are generally annoying, as users tend to think ‘I can browse the web myself thank you very much’; to justify a dedicated app, it either has to include its own offline content or else apply some kind of offline processing to the pages it retrieves so it’s not just a poor imitation of the general Web browser).
The project’s res/layout/activity_main.xml file should look like Listing 1, and the code itself should be something like Listing 2. Then you can place the HTML into the assets folder, the main file being index.html: I would suggest making it ‘mobile friendly’ as in Listing 3. Then refresh the ADT (highlight the Package Explorer and press F5), wait, and try it out with Run / Run As / Android application. ADT will use a real device if connected, otherwise it will start an emulator, and either way, if it works, you’ll be left with an APK file in the bin directory which you can distribute to others if they have ‘Unknown sources’ enabled in their ‘Application settings’ or ‘Security’.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical"> <TextView android:layout_height="wrap_content" android:layout_width="fill_parent" /> <WebView android:id="@+id/browser" android:layout_height="fill_parent" android:layout_width="fill_parent" /> </LinearLayout> |
Listing 1 |
import android.webkit.WebView; import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { WebView browser; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); browser = (WebView)findViewById(R.id.browser); browser.getSettings().setJavaScriptEnabled(true); int size=Math.round(16*getResources().getConfiguration() .fontScale); browser.getSettings().setDefaultFontSize(size); browser.getSettings().setDefaultFixedFontSize(size); browser.getSettings().setDefaultTextEncodingName("utf-8"); browser.loadUrl("file:///android_asset/index.html"); } } |
Listing 2 |
<html> <head> <meta name="mobileoptimized" content="0"> <meta name="viewport" content="width=device-width"> </head> <body> You should not have to zoom in to see this. </body> </html> |
Listing 3 |
Notice Listing 2 has three ‘size’ lines to set the font size according to the system’s font size setting. I’m putting this in ‘right off the bat’ because I believe getting it right is important (some people need big print, and, if you don’t, you probably will when you’re older). It’s a pity Google didn’t make this the default behaviour with their WebView component.
There are quite a few things missing from our application: the Back button won’t work, ‘pinch to zoom’ won’t work, Javascript alerts won’t work, local storage won’t work, the page state is reset when you rotate your device, and there is so far no way to specify that certain links should open in the ‘real’ browser rather than our app. All of these can be fixed by adding more code to ‘switch things on’, and it gets tedious, so if you’re interested I’ll refer you to my ‘html2apk’ code [2], which addresses all of these issues and also adds a Javascript object for clipboard interaction.
Adding Java and C functionality
You can provide extra Javascript objects implemented in Java by writing something like Listing 4. Then you can call these from the Javascript in your web pages, e.g. document.write(myObject.test('hi'));
class MyObject { public MyObject() {} @android.webkit.JavascriptInterface public String test(String in) { return "You called test with "+in; } } browser.addJavascriptInterface(new MyObject(), "myObject"); |
Listing 4 |
To write in C, you’ll additionally need to download the Android NDK (Native Development Kit?) [3]. You’ll then need a JNI directory, containing an Android.mk file like Listing 5:
LOCAL_PATH:= $(call my-dir) LOCAL_SRC_FILES := my_program.c LOCAL_MODULE := MyProgram LOCAL_MODULE_FILENAME := MyProgram include $(BUILD_SHARED_LIBRARY) |
Listing 5 |
and an Application.mk file like this:
APP_PLATFORM := android-1 APP_ABI := armeabi
Then your my_program.c should start with #include <jni.h>
and should incorporate functions like Listing 6.
JNIEXPORT jstring JNICALL Java_your_package_name_here_MainActivity_jniMyFunc (JNIEnv *env, jclass theClass, jstring jIn) { char *i=(char*)(*env)->GetStringUTFChars(env,jIn,NULL); char *o=malloc(strlen(s) + 20); // TODO: check o != NULL strcpy(o, "You wrote "); strcat(o, i); (*env)->ReleaseStringUTFChars(env,jIn,startPtr); jstring ret=(*env)->NewStringUTF(env,o); free(o); return ret; } |
Listing 6 |
Then back in MainActivity.java you’ll need something like this to make your C function available to Java:
static { System.loadLibrary("MyProgram"); } static synchronized native String jniMyFunc (String in);
After that, you can make it available to your Javascript by changing Listing 4 to call your newly-available C function. I’d suggest not bothering with C on Android unless you have either a lot of existing C code or a dire need for speed: Java or even JavaScript is enough for most simple purposes and involves less setup on this platform.
Publishing
When you’re ready to ship your ‘app’ (I tend to use the full word ‘application’, but these 4-syllable words are hard I know) on the so-called ‘Play Store’ (are we all children now? are all Android versions named after sweets as a childish rebellion against some other company using healthy fruit?), you’ll have to use File / Export / Export Android Application (it lets you create a keystore and private signing key), then pay for your Play Store account (I really think it should be free to upload if you’re doing the platform a favour by contributing a completely free and no-adverts app for the benefit of the community, but at least the payment is only a one-off; Apple have a higher payment and they make you renew every year, which is why they won’t be getting any iOS apps off me anytime soon), and finally upload the APK and wait for it to be published on the Store. You could just skip all this and have your users download the APK file from your own site with ‘Unknown Sources’ enabled, and you might wish to do it this way for a private app that you don’t want the entire public to see, but having your app on the Play Store is a significant convenience for many users.
When uploading future versions, you’ll need to increase the version number in the AndroidManifest.xml file (otherwise Play Store won’t accept it), and I’d suggest mirroring that change in the second copy of AndroidManifest that’s in the bin directory (the mirroring is supposed to be automatic, but due to general ‘wobbliness’ it seems that doesn’t always happen). I’d also suggest using the Play Store ‘beta test’ facility just to double-check your app did not somehow get corrupted during the upload process as one of mine did. I don’t see how, as they’re cryptographically signed, but somehow an app which worked perfectly well for me didn’t work after my friend had uploaded it to his account on the Store: the Javascript-to-Java callback wouldn’t run. By that time we had over 1000 users, most of whom had automatic updates enabled on their devices, so I got several phone calls, making me feel like Star Trek’s engineer Scottie having to fix the engines quickly before the ship explodes, and all I did was to increase the version number one more time and ask my friend to re-upload, after which the problem mysteriously disappeared. But not before several users had turned off automatic updates on their devices (which is a worry: I hope their devices will still get the security-critical updates). After that we decided, no matter how well an APK file works for us, we’ll always use the Play Store’s own Beta Test facility to double-check nothing bad somehow happened during the upload process. I still don’t know what happened (do Google servers introduce bit-flips sometimes?) – everything about this platform seems to be ‘wobbly’. Please don’t run anything safety critical on it.
References:
[1] http://developer.android.com/sdk/
[2] http://people.ds.cam.ac.uk/ssb22/gradint/html2apk.html
[3] https://developer.android.com/tools/sdk/ndk/
Notes:
More fields may be available via dynamicdata ..