Mobile, Using Apache Cordova

KVision allows you to develop hybrid, mobile applications (for Android and iOS) with the Apache Cordova framework. You can use all KVision components in your mobile applications, and the kvision-cordova module gives you a complete set of Kotlin language bindings for core Cordova API. You can develop your applications in a productive way, with Webpack's HMR and instantaneous hot reload of your application code in the phone emulator.

Note: We aim to support both Android and iOS platforms. But at the time of writing only the Android platform and toolchain were properly tested.

KVision adds Kotlin type-safe bindings for most of core Cordova API, but you should get familiar with Cordova documentation to achieve best results.

Requirements

Before using KVision with Apache Cordova you have to install Cordova CLI. You will use cordova command the same way as in standard Cordova applications (JavaScript based).

Android platform

To develop for Android platform you have to install Android SDK. See Cordova documentation for details. Make sure you have set correctly all required environment variables.

iOS platform

To develop for iOS platform you have to install Xcode, and it's possible only on the OS X operating system on Intel-based Macs. See Cordova documentation for details.

Getting started

The easiest way to start is to clone template-cordova project from kvision-examples repository on GitHub. It contains the basic configuration for building and running your application.

This template is just a typical KVision application with some additional files and directories required for Cordova framework. You need to initialize Cordova project with cordova prepare command. After this you are ready to build and run your app.

The recommended way to work with the project is using the emulator/simulator and running your application from your local Webpack dev server. This way, you get all the benefits of Webpack's HMR and hot reload. Just like with the standard KVision app, you should run ./gradlew -t run command in the dedicated terminal window. When the webpack dev server is running, you should execute cordova emulate command inside the second terminal window. It will start the emulator/simulator and deploy your application inside the emulated device. The content of the main WebView will be served from your webpack dev server, so it will be reloaded when you make some changes to your source code.

Hint: when developing for the Android platform you can connect Chrome Dev Tools to your emulated phone and get full access (DOM, console, network, sources, storage etc.) to your app. Just open chrome://inspect/ URL in your Chrome browser and select your device.

Cordova configuration

Cordova project configuration is saved inside config.xml and package.json files. The template project includes all core Cordova plugins.

package.json
{
  "cordova": {
    "plugins": {
      "cordova-plugin-whitelist": {},
      "cordova-plugin-device": {},
      "cordova-plugin-battery-status": {},
      "cordova-plugin-camera": {},
      "cordova-plugin-dialogs": {},
      "cordova-plugin-network-information": {},
      "cordova-plugin-screen-orientation": {},
      "cordova-plugin-splashscreen": {},
      "cordova-plugin-statusbar": {},
      "cordova-plugin-vibration": {},
      "cordova-plugin-file": {},
      "cordova-plugin-geolocation": {},
      "cordova-plugin-inappbrowser": {},
      "cordova-plugin-media": {},
      "cordova-plugin-media-capture": {},
      "cordova-plugin-locationservices": {}
    },
    "platforms": [
      "android"
    ]
  }
}

You can use cordova plugin remove command to remove unused plugins or cordova plugin add command to add new plugins.

Note: kvision-cordova module offers Kotlin bindings for all core Cordova plugins. You can also use any external Cordova plugin in your application, but you need to manage JS interoperation on your own. You can declare external classes or use dynamic types, whatever suits you more.

Currently the template project targets only the Android platform. You can use cordova platform add ios command to add iOS target.

Cordova API

Most of the original, callback-based Cordova API has been wrapped into simple suspending functions. They can be used with the full power of Kotlin coroutines, although the Cordova's usage patterns are closely followed, so the standard Cordova documentation should give enough knowledge to work with KVision.

Device

This plugin allows to get information about the device, and also add listeners for standard Cordova events.

GlobalScope.launch {
    console.log(getDevice())
}
addResumeListener { resumeEvent ->
    console.log(resumeEvent)
}
addCordovaEventListener(CordovaEvent.BACKBUTTON) {
    console.log("backbutton")
}
addCordovaEventListener(CordovaEvent.VOLUMEDOWNBUTTON) {
    console.log("volume down")
}
addCordovaEventListener(CordovaEvent.VOLUMEUPBUTTON) {
    console.log("volume up")
}

All Cordova API is available only after deviceready event is dispatched. Most of KVision functions already handle this event internally, so usually there is no need to wrap your code into deviceready listener.

Battery status

This plugin allows you to get information about the current battery level.

Battery.addStatusListener(BatteryEvent.BATTERY_STATUS) {
    console.log("Battery level: it.level")
}
Battery.addStatusListener(BatteryEvent.BATTERY_LOW) {
    console.log("Battery low level: ${it.level}")
}
Battery.addStatusListener(BatteryEvent.BATTERY_CRITICAL) {
    console.log("Battery critical level: ${it.level}")
}

Camera

This plugin allows you to use the phone camera to get a picture or a video.

button("Take a photo", "fa-camera") {
    onClick {
        GlobalScope.launch {
            val result = Camera.getPicture(
                CameraOptions(
                    mediaType = Camera.MediaType.PICTURE,
                    destinationType = Camera.DestinationType.FILE_URI
                )
            )
            processCameraResult(result)
            Camera.cleanup()
        }
    }
}
Camera.addCameraResultCallback {
    processCameraResult(it)
}
fun processCameraResult(result: Result<String>) {
    result.fold(
        onSuccess = {
            GlobalScope.launch {
                File.resolveLocalFileSystemURLForFile(it).success {
                    store.dispatch(ImageAction.Image(it.toInternalURL()))
                }
            }
        },
        onFailure = {
            store.dispatch(ImageAction.Error(it.message ?: "No data"))
        }
    )
}

When using the camera on the Android platform, use addCameraResultCallback method to correctly support Android lifecycle.

Dialogs

The dialog plugin allows to display native notifications - alert, prompt and confirmation dialog windows and also the beep sound.

Notification.alert("The message", "Title", "OK") {
    console.log("You pressed OK")
}
Notification.confirm("Are you sure?", "Delete", listOf("Yes", "No")) { index ->
    console.log("You pressed button number $index")
}
Notification.prompt("Enter your name:", "Register", listOf("OK", "Cancel"), "John Snow") { res ->
    console.log("You pressed button number ${res.buttonIndex} and your name is ${res.input1}")
}
Notification.beep(3)

Network information

This plugin allows to get information about the current network connection and add listeners for the network status events.

GlobalScope.launch {
    console.log(Network.getConnectionType().toString())
}
Network.addStatusListener(NetworkEvent.OFFLINE) {
    console.log("Going offline")
}
Network.addStatusListener(NetworkEvent.ONLINE) {
    console.log("Going online")
}

Screen orientation

This plugin allows to get current screen orientation, lock and unlock the specified orientation and also add listeners for the screen orientation change events.

console.log(Screen.getOrientation())

button("Lock landscape").onClick {
    Screen.lock(Screen.Orientation.LANDSCAPE)
}
button("Lock portrait").onClick {
    Screen.lock(Screen.Orientation.PORTRAIT)
}
button("Unlock").onClick {
    Screen.unlock()
}
Screen.addOrientationChangeListener {
    console.log("Orientation changed to ${it}")
}

Splash screen

Cordova allows to configure a splash screen for your application, which is displayed before the application is fully loaded. Check Cordova documentation for details. This plugin has also simple methods to show and hide the splash screen at runtime.

button("Show splash screen for 3 seconds").onClick {
    Splashscreen.show()
    window.setTimeout({
        Splashscreen.hide()
    }, 3000)
}

Status bar

This plugin allow to style the native status bar with a few predefined color schemes.

button("Show status bar").onClick {
    StatusBar.show()
}
button("Hide status bar").onClick {
    StatusBar.hide()
}
button("Set overlay").onClick {
    StatusBar.overlaysWebView(true)
}
button("Set no overlay").onClick {
    StatusBar.overlaysWebView(false)
}
button("Set style to light content").onClick {
    StatusBar.styleLightContent()
}
button("Set style to black opaque").onClick {
    StatusBar.styleBlackOpaque()
}
button("Set style to black translucent").onClick {
    StatusBar.styleBlackTranslucent()
}
button("Set style to default").onClick {
    StatusBar.styleDefault()
}
button("Set background color").onClick {
    StatusBar.setBackgroundColorByHexString("#88FF0000")
}

Vibration

This plugin provides a way to vibrate the device with a single vibration or with a given pattern

button("Vibrate for 1 second").onClick {
    Vibration.vibrate(1000)
}
button("Vibrate with a given pattern").onClick {
    Vibration.vibrate(1000, 2000, 1000, 2000)
}

File

The File plugin provides a comprehensive API to access the device's file system. KVision bindings for the original, callback based API are designed as suspending functions and suspending extension functions.

GlobalScope.launch {
    // Get external data directory root
    File.getSystemDirectories().externalDataDirectory?.toDirectoryEntry()?.getOrNull()?.let { root ->
        // Print root directory information.
        console.log(root)
        root.getMetadata().getOrNull()?.let { console.log(it) }
        // List root directory entries.
        root.readEntries().getOrNull()?.let {
            console.log(it)
        }
        // Create new file from a Blob.
        root.getFile("test.txt").getOrNull()?.let { it.write(Blob(arrayOf("A test content."))) }
        // List root directory entries (with new file on the list).
        root.readEntries().getOrNull()?.let {
            console.log(it)
        }
        // Read file content as text.
        root.getFile("test.txt").getOrNull()?.let { console.log(it.readAsText()) }
        // Append content to the file.
        root.getFile("test.txt").getOrNull()?.let { it.append(Blob(arrayOf("two"))) }
        // Access the file.
        root.getFile("test.txt").getOrNull()?.let {
            // Read file content as a buffer.
            val buf = it.readAsArrayBuffer().component1()
            // Create a blob from a buffer.
            val blob = Blob(arrayOf(Uint8Array(buf!!)))
            // Write blob to a new file.
            root.getFile("test2.txt").getOrNull()?.let {
                it.write(blob)
            }
        }
        // Access the file.
        root.getFile("test.txt").getOrNull()?.let {
            // Get native file URL.
            console.log(it.toURL())
            // Get Cordova cdvfile:// file URL.
            console.log(it.toInternalURL())
        }
    }
}

Geolocation

This plugin provides information about the device's location, such as latitude and longitude.

GlobalScope.launch {
    Geolocation.getCurrentPosition().getOrNull()?.let {
        console.log("Timestamp: ${it.timestamp}")
        console.log("Lat: ${it.coords.latitude}")
        console.log("Lng: ${it.coords.longitude}")
    }
    val watchId = Geolocation.watchPosition(timeout = 5000, maximumAge = 3000) {
        it.fold(
            onSuccess = {
                console.log("Timestamp: ${it.timestamp}")
                console.log("Lat: ${it.coords.latitude}")
                console.log("Lng: ${it.coords.longitude}")
            }
            onFailure =, {
                console.log(it)
            }
        )
    }
    window.setTimeout({
        Geolocation.clearWatch(watchId)
    }, 30000)
}

KVision has support for an additional Cordova plugin, dedicated for the Android platform: cordova-plugin-locationservices. Just use Locationservices instead of Geolocation object. The API usage is similar, but the functions have some additional parameters.

In app browser

The browser plugin allows to open external URL address inside your application.

GlobalScope.launch {
    val ref = InAppBrowser.open("https://www.google.com")
    console.log(ref)
}

Media

The media plugin allows to record and play back audio files.

addDeviceReadyListener {
    val media = Media("https://archive.org/download/testmp3testfile/mpthreetest.mp3", {
        console.log("Media play success")
    }, { e ->
        console.log("Media play error", e)
    }) { status ->
        console.log(status)
    }
    media.play()
}

Media capture

This plugin provides access to the device's audio, image, and video capture capabilities.

GlobalScope.launch {
    val media = MediaCapture.captureImage()
    media.getOrNull()?.let {
        it.forEach {
            console.log(it)
            console.log(it.lastModifiedDate)
            val mediaDetail = it.getFormatData()
            console.log(mediaDetail)
        }
    }
}

Building for production

To build for production, first you have to build the distribution version of your KVision application. Run the dedicated Gradle task by calling:

./gradlew distCordova

This task will compile, generate and copy the distribution files to the Cordova's www directory.

After that you are ready to sign the code and build the final package, by calling:

cordova build --release --buildConfig=signingConfig.json

You have to create signingConfig.json file containing private signing information.

Refer to Cordova documentation for details regarding all supported platforms.

Last updated