Lessons Learned: CrossWalk and Enhanced Webviews

I recently had a problem (seems to be a very common one [1][2][3]) when building a hybrid HTML5 mobile app. As can be seen in this online demo (https://mobilehtml5.org/ts/?id=23), one can use the input html tag with

type=”file” accept=”image/*”

to quickly and easily bring up the user’s camera if they visit the site from a mobile web browser like Chrome. But, embed the above in an Android webview and… nothing. We could fiddle around with intents and Java – Javascript bridges, but there has to be an easier way. Reading through some solutions someone suggested simply replacing the webview with one that actually works consistently.

The original suggestion pointed towards an “Android Advanced Webview” by a company named “Delight”. It is an excellent piece of code:

https://github.com/delight-im/Android-AdvancedWebView

And now clicking on the input tag brings up the native android chooser, allowing a user to upload an image they had already taken. Good, but not perfect. Ideally we allow the user to choose if they would like to upload a saved image they already took, or allow them to choose the “camera” option and take a picture on the spot. Out of the box, Android Advanced Webview doesn’t support this [4]. Bummer.

But this solution reminded me of something similar I played with years ago: the Intel-funded Crosswalk Project. It has definitely matured and gotten a lot better since I last checked it out. It proved relatively simple to embed it in my native android framework by following their documentation:

Embedding the Crosswalk Project

Note: if you’re using Android Studio and maven/gradle, a much – very much – easier way of embedding Crosswalk can be found here:

Embedding Crosswalk in Android Studio [5]

That did the trick! Now clicking on the input tag gives the user a choice:

Screenshot_20160513-104253

Almost there. In my case, clicking on the camera option didn’t do anything :(. After some debugging and logging the intents being passed around, I noticed the android subsystem complaining of “revoked permission” even though I had the appropriate CAMERA permission in my manifest file:

<uses-permission android:name=”android.permission.CAMERA” />

UPDATE: If you see only the “camcoder” option as in the screenshot above, adding the “android.permission.WRITE_EXTERNAL_STORAGE” resolved the issue

Since I’m using Android 6.x, the permission scheme has changed and now requires you to ask for user permission at runtime . Following that realization, the below did the trick:

References

[1] http://stackoverflow.com/questions/18568566/webview-input-of-type-file-camera-and-image

[2] http://stackoverflow.com/questions/13284903/upload-camera-photo-and-filechooser-from-webview-input-field

[3] http://stackoverflow.com/questions/29290940/open-camera-for-input-type-file-in-webview-not-opening-android

[4] https://github.com/delight-im/Android-AdvancedWebView/issues/10

[5] Reproduced here: https://docs.google.com/document/d/15Lhg8de6hXmv0FVImu5OuWeqF1Dx5rcWUI0Hi08E78s/edit?usp=sharing

Advertisements

BLE Health Devices: First Steps with Android

Bluetooth Low Energy (also known as Bluetooth v4) is the current standard in Bluetooth Technology. It is particularly interesting to me when applied to healthcare devices, for a number of reasons:

  1. No pairing necessary. These healthcare devices are normally handled by carers or vulnerable people who do not want to go through the hassle of pairing bluetooth devices, or entering PIN codes, etc. They just want an easy, quick connection process – BLE does this wonderfully. The BLE device acts as a server which broadcasts a number of services [1]. Different devices offer different services – for example a thermometer would advertise a “Health Thermometer” service (0x1809), and a SP02 device would advertise a “Heart Rate” service (0x180D). A BLE client (an android smartphone in this case) can scan for BLE device, determine which one offers the service it needs, connect to it, and get a reading
  2. “Push” model. In healthcare devices it’s easier to use a push model… that is, a device will notify the smartphone that a reading is ready and send the reading. This in contrast to early versions where the smartphone had to poll the device
  3. Standards. The bluetooth standard covers services [1] so BLE devices from different manufacturers present their data in the same format – which is a big plus since it avoids developers having to hassle with reverse engineering data representations

This article outlines the steps I took as a newbie android BLE developer to get a basic Android app talk to a couple of BLE devices.

  • First, download the extremely handy “BLE Scanner” from BluePixel here:

https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner&hl=en

This app helps save a lot of time by displaying which services and characteristics are present on a device. Connect to your BLE device and snoop around which services are being offered. See if you can read any values

  • Second, read the fantastic article by Marcelle Gibble on toastdroid regarding android BLE here:

http://toastdroid.com/2014/09/22/android-bluetooth-low-energy-tutorial/

I had to pay special attention to the “Configure Descriptor for Notify” and “Receive Notifications” sections…

  • Understand the basic android code on how to connect and read information from a BLE device in “Android Bluetooth Low Energy (BLE) Example” by Mohit Gupt here:

http://www.truiton.com/2015/04/android-bluetooth-low-energy-ble-example/

  • Using the last link above to provide me with a skeleton app, I then set about modifying the code to:
    • Receive notifications as described by the toastdroid article, i.e. by configuring the descriptor for notify and overriding the “onCharacteristicChanged” [2] method

The full code can be found here [3]:

  • Figuring out which service to subscribe to takes some fiddling around with the Android Studio debugger, by setting a breakpoint on line 267 above and inspecting the “services” object
  • In line 294 in the code above we read the float value with an offset of “1”, since this is a temperature record as described in [4], we see the first byte is reserved for flags, and the actual temperature reading starts at the second byte.

The resulting app automatically detects which device is sending data to it, and displays the information read on screen:

device-2015-09-02-150827 device-2015-09-02-151028

References

[1] Bluetooth developer portal, https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx

[2] Android Developers, https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html#onCharacteristicChanged

[3] BLE demo app gist, https://gist.github.com/dvas0004/e78cd9f73a331d856bec

[4] Bluetooth Temperature Measurement, https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml