Mini App SDK for Android

Provides functionality to show a Mini App in Android Applications. The SDK offers features like downloading, caching, updating, and displaying of a Mini App. Mini App SDK also facilitates communication between a mini app and the host app via a message bridge.

Table of Contents

Requirements

Getting Started

This section will guide you through setting up the Mini App SDK in your App and you will learn how to display your first mini app.

#1 Add dependency to your App

Add the following to your build.gradle file:

repositories {
    mavenCentral()
}

dependency {
    implementation 'io.github.rakutentech.miniapp:miniapp:${version}'
}

#2 Configure SDK settings in AndroidManifest.xml

The SDK is configured via meta-data tags in your AndroidManifest.xml. The following table lists the configurable values.

Field Datatype Manifest Key Optional Default
Base URL String com.rakuten.tech.mobile.miniapp.BaseUrl 🚫
Is Preview Mode Boolean com.rakuten.tech.mobile.miniapp.IsPreviewMode false
RAS Project ID String com.rakuten.tech.mobile.ras.ProjectId 🚫
RAS Project Subscription Key String com.rakuten.tech.mobile.ras.ProjectSubscriptionKey 🚫
Host App User Agent Info String com.rakuten.tech.mobile.miniapp.HostAppUserAgentInfo 🚫

Note:

Click to expand example AndroidManifest.xml
<manifest>
    <application>

        <!-- Base URL used for retrieving a Mini App -->
        <meta-data
            android:name="com.rakuten.tech.mobile.miniapp.BaseUrl"
            android:value="https://www.example.com" />

        <!-- Preview mode used for retrieving the Mini Apps -->
        <meta-data
            android:name="com.rakuten.tech.mobile.miniapp.IsPreviewMode"
            android:value="${isPreviewMode}" />

        <!-- Project ID for the Platform API -->
        <meta-data
            android:name="com.rakuten.tech.mobile.ras.ProjectId"
            android:value="your_project_id" />

        <!-- Subscription Key for the Platform API -->
        <meta-data
            android:name="com.rakuten.tech.mobile.ras.ProjectSubscriptionKey"
            android:value="your_subscription_key" />

        <!-- Optional User Agent Information relating to the host app -->
        <meta-data
            android:name="com.rakuten.tech.mobile.miniapp.HostAppUserAgentInfo"
            android:value="app_name/version_info" />

    </application>
</manifest>

#3 Create and display a Mini App

API Docs: MiniApp.create, MiniAppDisplay, MiniAppMessageBridge

MiniApp.create is used to create a View for displaying a specific mini app. Before calling MiniApp.create, the Host App should first get the manifest using MiniApp.getMiniAppManifest, show permission prompt to user, then set the result with MiniApp.setCustomPermissions. If Host App wants to launch/download the miniapp without granting the required permissions, the SDK will throw RequiredPermissionsNotGrantedException to notify Host App. You must provide the mini app ID which you wish to create (you can get the mini app ID by Fetching Mini App Info first). Calling MiniApp.create will do the following:

After calling MiniApp.create and all the “required” manifest permissions have been granted, you will obtain an instance of MiniAppDisplay which represents the downloaded mini app. You can call MiniAppDisplay.getMiniAppView to obtain a View for displaying the mini app.

The following is a simplified example:

try {
    val miniAppMessageBridge = object : MiniAppMessageBridge() {
        // implement methods for mini app bridge
    }

    val miniAppDisplay = withContext(Dispatchers.IO) {
        MiniApp.instance().create("MINI_APP_ID", miniAppMessageBridge)
    }
    val miniAppView = miniAppDisplay.getMiniAppView(this@YourActivity)

    // Add the view to your Activity
} catch (e: MiniAppSdkException) {
    // Handle exception
}

Note that this is a simplified example. See Mini App Features for the full functionality which you can provide to the mini app. The following is a more complete example:

Click here to expand a full code example
class MiniAppActivity : Activity(), CoroutineScope {

    override val coroutineContext = Dispatchers.Main

    private var miniAppDisplay: MiniAppDisplay

    override fun onCreate(savedInstanceState: Bundle?) {
        super(savedInstanceState)
        setContentView(R.layout.loading)

        launch {
            try {
                miniAppDisplay = withContext(Dispatchers.IO) {
                    MiniApp.instance().create("MINI_APP_ID", createMessageBridge())
                }
                val miniAppView = miniAppDisplay.getMiniAppView(this@MiniAppActivity)

                setContentView(miniAppView)
            } catch (e: MiniAppSdkException) {
                setContentView(R.layout.error_screen)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        miniAppDisplay.destroyView()
    }

    fun createMessageBridge() = object : MiniAppMessageBridge() {
        override fun getUniqueId() {
            // Implementation details to generate a Unique ID

            return "your-unique-id"
        }

        override fun requestPermission(
            miniAppPermissionType: MiniAppPermissionType,
            callback: (isGranted: Boolean) -> Unit
        ) {
            // Implementation details to request device permissions

            callback.invoke(true)
        }
        
        // You can additionally implement other MiniAppMessageBridge methods
    }
}

Note:

Mini App Features

API Docs: MiniAppMessageBridge

The MiniAppMessageBridge is used for passing messages between the Mini App (JavaScript) and the Host App (your native Android App) and vice versa. Your App must provide the implementation for these functions and pass this implementation to the MiniApp.create function.

There are some methods have a default implementation but the host app can override them to fully control.

Method Default
getUniqueId 🚫
requestPermission 🚫
requestDevicePermission 🚫
requestCustomPermissions
shareContent

The UserInfoBridgeDispatcher:

Method Default
getUserName 🚫
getProfilePhoto 🚫
getAccessToken 🚫
getContacts 🚫

The sections below explain each feature in more detail.

The following is a full code example of using MiniAppMessageBridge.

Click here to expand full code example of MiniAppMessageBridge
val miniAppMessageBridge = object: MiniAppMessageBridge() {
    override fun getUniqueId() {
        var id: String = ""
        // Implementation details to generate a Unique ID
        // .. .. ..

        return id
    }

    override fun requestDevicePermission(
        miniAppPermissionType: MiniAppDevicePermissionType,
        callback: (isGranted: Boolean) -> Unit
    ) {
        // Implementation details to request device permission for location
        // .. .. ..

        callback.invoke(true)
    }

    // Implement requestCustomPermissions if HostApp wants to show their own UI for managing permissions
    override fun requestCustomPermissions(
        permissionsWithDescription: List<Pair<MiniAppCustomPermissionType, String>>,
        callback: (List<Pair<MiniAppCustomPermissionType, MiniAppCustomPermissionResult>>) -> Unit
    ) {
        // Implementation details to request custom permissions
        // .. .. ..
        // pass a list of Pair of MiniAppCustomPermissionType and MiniAppCustomPermissionResult in callback 
        callback.invoke(list) 
    }

    override fun shareContent(
        content: String,
        callback: (isSuccess: Boolean, message: String?) -> Unit
    ) {
        // Share content implementation.
        // .. .. ..
        
        callback.invoke(true, null) // or callback.invoke(false, "error message")
    }
}

val userInfoBridgeDispatcher = object : UserInfoBridgeDispatcher {

    override fun getUserName(
        onSuccess: (userName: String) -> Unit,
        onError: (message: String) -> Unit
    ) {
        val name: String?
        // Check if there is any valid username in HostApp
        // .. .. ..
        if (isNameValid) // Check if name is valid
            onSuccess(name) // allow miniapp to get user name.
        else
            onError(message) // reject miniapp to get user name with message explanation.
    }

    override fun getProfilePhoto(
        onSuccess: (profilePhoto: String) -> Unit,
        onError: (message: String) -> Unit
    ) {
        val photoUrl: String?
        // Check if there is any valid photo url in HostApp
        // .. .. ..
        if (isPhotoUrlValid) // Check if photoUrl is valid
            onSuccess(photoUrl) // allow miniapp to get photo url.
        else
            onError(message) // reject miniapp to get photo url with message explanation.
    }

    override fun getAccessToken(
        miniAppId: String,
        accessTokenScope: AccessTokenScope,
        onSuccess: (tokenData: TokenData) -> Unit,
        onError: (message: String) -> Unit
    ) {
        var allowToken: Boolean = false
        // Check if you want to allow this Mini App ID to use the Access Token based on AccessTokenScope.
        // .. .. ..
        if (allowToken)
            onSuccess(tokenData) // allow miniapp to get token and return TokenData value.
        else
            onError(message) // reject miniapp to get token with message explanation.
    }

    override fun getContacts(
        onSuccess: (contacts: ArrayList<Contact>) -> Unit,
        onError: (message: String) -> Unit
    ) {
        // Check if there is any contact id in HostApp
        // .. .. ..
        if (hasContact)
            onSuccess(contacts) // allow miniapp to get contacts.
        else
            onError(message) // reject miniapp to get contacts with message explanation.
    }
}

// set UserInfoBridgeDispatcher object to miniAppMessageBridge
miniAppMessageBridge.setUserInfoBridgeDispatcher(userInfoBridgeDispatcher)

Unique ID

API Docs: MiniAppMessageBridge.getUniqueId

Your App should provide an ID to the mini app which is unique to each user or device. The mini app can use this ID for storing session information for each user.

Device Permission Requests

API Docs: MiniAppMessageBridge.requestPermission

The mini app is able to request some device permissions. Your App should be able to handle requests from the mini app for the following device permissions by ensuring that the Android permission dialog is displayed. Alternatively, if your App is not able to request certain device permissions, you can just deny that permission to all mini apps.

Custom Permission Requests

API Docs: MiniAppMessageBridge.requestCustomPermissions

Mini apps are able to make requests for custom permission types which are defined by the Mini App SDK. These permissions include things like user data. When a mini app requests a permission, your App should display a dialog to the user asking them to accept or deny the permissions. You can also choose to always deny some permissions if your App is not capable of providing that type of data. The following custom permission types are supported:

Note: The Mini App SDK has a default UI built-in for the custom permission dialog, but you can choose to override this and use a custom UI. The Mini App SDK will handle caching the permission accept/deny state, and your requestCustomPermission function will only receive permissions which have not yet been granted to the mini app.

Share Content

API Docs: MiniAppMessageBridge.shareContent

The mini app can share text content to either your App or another App. The default functionality for this will create a text type Intent which shows the Android chooser and allows the user to share the content to any App which accepts text.

You can also choose to override the default functionality and instead share the text content to some feature within your own App.

User Info

API Docs: MiniAppMessageBridge.setUserInfoBridgeDispatcher

The mini app is able to request data about the current user from your App. Each of these types of data is associated with a MiniAppCustomPermissionType (except where noted). The mini app should have requested the permission before requesting the user data type. Note that the Mini App SDK will handle making sure that the permission has been granted, so if the permission has not been granted, then these functions will not be called within your App.

The following user data types are supported. If your App does not support a certain type of data, you do not have to implement the function for it.

Ads Integration

API Docs: MiniAppMessageBridge.setAdMobDisplayer

It is optional to set AdMob for mini apps to show advertisement. The below implementation will allow ads to be shown when mini apps trigger a request.

Configure the Android Ads SDK from here. Don’t forget to initialize the Ads SDK.

Note: We only support AdMob usage on Android 7.0+. Some ads from AdMob have inconsistent behavior on Android 6.0 due to the older webview implementation on those devices.

AdMob

API Docs: AdMobDisplayer

Set the AdMobDisplayer provided by MiniApp SDK. This controller will handle the display of ad so no work is required from host app.

miniAppMessageBridge.setAdMobDisplayer(AdMobDisplayer(activityContext))

Custom Ads Provider

API Docs: MiniAppAdDisplayer

In case the host app wants to take control of the ad display, there is the interface MiniAppAdDisplayer to implement.

class CustomAdDisplayer: MiniAppAdDisplayer { 

    override fun loadInterstitialAd(adUnitId: String, onLoaded: () -> Unit, onFailed: (String) -> Unit) {
      // load the ad
    }
    
    override fun showInterstitialAd(adUnitId: String, onClosed: () -> Unit, onFailed: (String) -> Unit) {
      // show the ad
    }
    //...more ad implementations.
}

miniAppMessageBridge.setAdMobDisplayer(CustomAdDisplayer())

Screen Orientation

API Docs: MiniAppMessageBridge.allowScreenOrientation

The default setting does not allow miniapp to change hostapp screen orientation. Hostapp can allow miniapp to control the screen orientation for better experience by calling

miniAppMessageBridge.allowScreenOrientation(true)

In case miniapp is allowed to control, please ensure that your activity handles screen orientation. There are several ways to prevent the view from being reset. In our Demo App, we set the config on activity android:configChanges="orientation|screenSize". See here.

Fetching Mini App Info

API Docs: MiniApp.listMiniApp, MiniApp.fetchInfo, MiniAppInfo

Information about Mini Apps can be fetched in two different ways: by using MiniApp.listMiniApp to get a list of info for all Mini Apps, or by using MiniApp.fetchInfo to get info for a single Mini App. Either method will return MiniAppInfo objects with info about the Mini App such as name, icon URL, ID, version, etc.

Use MiniApp.listMiniApp if you want a list of all Mini Apps:

CoroutineScope(Dispatchers.IO).launch {
    try {
        val miniAppList = MiniApp.instance().listMiniApp()
    } catch(e: MiniAppSdkException) {
        Log.e("MiniApp", "There was an error retrieving the list", e)
    }
}

Or use MiniApp.fetchInfo if you want info for a single Mini App and already know the Mini App’s ID:

CoroutineScope(Dispatchers.IO).launch {
    try {
        val miniAppInfo = MiniApp.instance().fetchInfo("MINI_APP_ID")
    } catch(e: MiniAppSdkException) {
        Log.e("MiniApp", "There was an error when retrieving the Mini App info", e)
    }
}

Fetching Mini App Meta data

API Docs: MiniApp.getMiniAppManifest

MiniApp developers need to add the following attributes in manifest.json. Host App can require Mini Apps to set meta data here using “customMetaData”, such as the first time launch screen options.

{
   "reqPermissions":[
      {
         "name":"rakuten.miniapp.user.USER_NAME",
         "reason":"Describe your reason here."
      },
      {
         "name":"rakuten.miniapp.user.PROFILE_PHOTO",
         "reason":"Describe your reason here."
      }
   ],
   "optPermissions":[
      {
         "name":"rakuten.miniapp.user.CONTACT_LIST",
         "reason":"Describe your reason here."
      },
      {
         "name":"rakuten.miniapp.device.LOCATION",
         "reason":"Describe your reason here."
      }
   ],
   "customMetaData":{
      "hostAppRandomTestKey":"metadata value"
   }
}

In Host App, we can get the manifest information as following:

CoroutineScope(Dispatchers.IO).launch {
    try {
        val miniAppManifest = MiniApp.instance().getMiniAppManifest("MINI_APP_ID", "VERSION_ID")

        // Host App can set it's own metadata key in manifest.json to retrieve the value
        miniAppManifest.customMetaData["hostAppRandomTestKey"]
    } catch(e: MiniAppSdkException) {
        Log.e("MiniApp", "There was an error when retrieving the Mini App manifest", e)
    }
}

Getting downloaded Mini App Meta data

In Host App, we can get the downloaded manifest information as following:

  val downloadedManifest = MiniApp.instance().getDownloadedManifest("MINI_APP_ID")

HostApp can compare between the downloadedManifest and the latest manifest by MiniApp.getMiniAppManifest to detect any new changes.

Advanced Features

Clearing up mini app display

API Docs: MiniAppDisplay.destroyView

For a mini app, it is required to destroy necessary view state and any services registered with. The automatic way can be used only if we want to end the Activity container along with mini app display. MiniAppDisplay complies to Android’s LifecycleObserver contract. It is quite easy to setup for automatic clean up of resources.

class MiniAppActivity : Activity(), CoroutineScope {

    override fun onCreate(savedInstanceState: Bundle?) {
    //...
        launch {
            val miniAppDisplay = withContext(Dispatchers.IO) {
                MiniApp.instance().create("mini_app_id", miniAppMessageBridge)
            }
            lifeCycle.addObserver(miniAppDisplay)
    //...
        }
    }
}

To read more about Lifecycle please see link.

On the other hand, when the consuming app manages resources manually or where it has more control on the lifecycle of views MiniAppDisplay.destroyView should be called upon e.g. when removing a view from the view system, yet within the same state of parent’s lifecycle.

API Docs: MiniAppDisplay.navigateBackward, MiniAppDisplay.navigateForward

MiniAppDisplay.navigateBackward and MiniAppDisplay.navigateForward facilitates the navigation inside a mini app if the history stack is available in it. A common usage pattern could be to link it up to the Android Back Key navigation.

override fun onBackPressed() {
    if(!miniAppDisplay.navigateBackward()) {
        super.onBackPressed()
    }
}

External url loader

API Docs: MiniAppNavigator, MiniAppExternalUrlLoader, ExternalResultHandler

The mini app is loaded with the specific custom scheme and custom domain in mini app view.

In default, the external link will be opened in custom tab. See this.

HostApp also can implement their own way by passing MiniAppNavigator object to MiniApp.create(appId: String, miniAppMessageBridge: MiniAppMessageBridge, miniAppNavigator: MiniAppNavigator).

miniAppNavigator = object : MiniAppNavigator {
    override fun openExternalUrl(url: String, externalResultHandler: ExternalResultHandler) {
        // Load external url with own webview.
    }
}

There are two approaches to return mini app url from host app webview to mini app view:

Automatic check in WebView which belongs to separated Activity

If the external webview Activity is different from the Activity running mini app, our SDK provide the auto check and Activity closing by overriding the WebViewClient.

val miniAppExternalUrlLoader = MiniAppExternalUrlLoader(miniAppId, externalWebViewActivity)
class MyWebViewClient(private val miniAppExternalUrlLoader: MiniAppExternalUrlLoader): WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
        val url = request.url.toString()
        return miniAppExternalUrlLoader.shouldOverrideUrlLoading(url)
    }
}

Return the url result to mini app view:

// externalResultHandler is from MiniAppNavigator implementation.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == externalWebViewReqCode && resultCode == Activity.RESULT_OK) {
            data?.let { intent -> externalResultHandler.emitResult(intent) }
        }
}

Manual check by host app

Host app can take full control and transmit the url back to mini app view.

val miniAppExternalUrlLoader = MiniAppExternalUrlLoader(miniAppId, null)

Using miniAppExternalUrlLoader.shouldClose(url) which returns Boolean to check if it is mini app scheme and should close external webview.

Using #ExternalResultHandler.emitResult(String) to transmit the url string to mini app view.

File choosing

API Docs: MiniAppFileChooser

The mini app is able to choose the file which is requested using HTML forms with ‘file’ input type whenever users press a “Select file” button. HostApp can use a default class provided by the SDK e.g. MiniAppFileChooserDefault to choose the files.

val fileChoosingReqCode = REQUEST_CODE // define a request code in HostApp
val miniAppFileChooser = MiniAppFileChooserDefault(requestCode = fileChoosingReqCode)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    // HostApp can cancel the file choosing operation when resultCode doesn't match
    if (Activity.RESULT_OK != resultCode) {
         miniAppFileChooser.onCancel()
    }

    if (requestCode == fileChoosingReqCode && resultCode == Activity.RESULT_OK) {
        data?.let { intent ->
            miniAppFileChooser.onReceivedFiles(intent)
        }
    }
}

Alternatively, HostApp can use MiniAppFileChooser interface to override onShowFileChooser for customizing file choosing mode and other options.

val miniAppFileChooser = object : MiniAppFileChooser {

        override fun onShowFileChooser(
            filePathCallback: ValueCallback<Array<Uri>>?,
            fileChooserParams: WebChromeClient.FileChooserParams?,
            context: Context
        ): Boolean {
           // write own implementation here.
        }
    }

In both case, HostApp needs to pass MiniAppFileChooser through MiniApp.create(appId: String, miniAppMessageBridge: MiniAppMessageBridge, miniAppFileChooser: MiniAppFileChooser).

Custom Permissions

API Docs: MiniApp.getCustomPermissions, MiniApp.setCustomPermissions, MiniApp.listDownloadedWithCustomPermissions

MiniApp Android SDK supports list of Custom Permissions (MiniAppCustomPermissionType) and these can be stored and retrieved using the following public interfaces.

Retrieving the Mini App Custom Permissions using MiniAppID

Custom permissions and its status can be retrieved using the following interface. getCustomPermissions will return MiniAppCustomPermission that contains the meta-info as a Pair of name and grant result (ALLOWED or DENIED). The custom permissions are stored per each miniAppId.

val permissions = miniapp.getCustomPermissions(miniAppId)

Store the Mini App Custom Permissions

Custom permissions for a mini app are cached by the SDK and you can use the following interface to store permissions when needed.

var permissionPairs = listOf<Pair<MiniAppCustomPermissionType, MiniAppCustomPermissionResult>>()
// .. .. ..
val permissionsToSet = MiniAppCustomPermission(miniAppId, permissionPairs)
miniapp.setCustomPermissions(permissionsToSet)

List of downloaded Mini apps with Custom Permissions

val downloadedMiniApps = miniapp.listDownloadedWithCustomPermissions()
downloadedMiniApps.forEach {
    val miniApp = it.first
    val permissions = it.second
    // Display permissions in view, etc....
}

Passing parameters to a miniapp

For a mini app, you can pass query parameters as String using MiniApp.create to be appended with miniapp’s url. For example: https://mscheme.1234/miniapp/index.html?param1=value1&param2=value2

class MiniAppActivity : Activity(), CoroutineScope {

    override fun onCreate(savedInstanceState: Bundle?) {
    //...
        launch {
            val miniAppDisplay = withContext(Dispatchers.IO) {
                MiniApp.instance().create(
                    appId = "mini_app_id",
                    miniAppMessageBridge = miniAppMessageBridge,
                    miniAppNavigator = miniAppNavigator,
                    queryParams = "param1=value1&param2=value2"
                )
            }
    //...
        }
    }
}

Analytics events

When Analytics SDK is integrated, MiniApp SDK sends analytics data from your app when some events are triggered:

Troubleshooting & FAQs

Exception: “Network requests must not be performed on the main thread.”

Some of the suspending functions in this SDK will perform network requests (MiniApp.create, MiniApp.fetchInfo, MiniApp.listMiniApp). Network requests should not be performed on the main thread, so the above exception will occur if your Coroutine is running in the Dispatchers.Main CoroutineContext. To avoid this exception, please use the Dispatchers.IO or Dispatchers.Default context instead. You can use withContext to make sure your code is running in the appropriate CoroutineContext.

CoroutineScope(Dispatchers.Main).launch {
    withContext(Dispatchers.IO) {
        // Call MiniApp suspending function i.e. `MiniApp.create`
        // This runs in a background thread
    }
        
    // Update your UI - i.e. `setContentView(miniAppView)`
    // This runs on the main thread
}
Exception: MiniAppVerificationException

This exception will be thrown when the SDK cannot verify the security check on local storage using keystore which means that users are not allowed to use miniapp. Some keystores within devices are tampered or OEM were shipped with broken keystore from the beginning.

Build Error: java.lang.RuntimeException: Duplicate class com.rakuten.tech.mobile.manifestconfig.annotations.ManifestConfig

This build error could occur if you are using older versions of other libraries from com.rakuten.tech.mobile. Some of the dependencies in this SDK have changed to a new Group ID of io.github.rakutentech (due to the JCenter shutdown). This means that if you have another library in your project which depends on the older dependencies using the Gropu ID com.rakuten.tech.mobile, then you will have duplicate classes.

To avoid this, please add the following to your build.gradle in order to exclude the old com.rakuten.tech.mobile dependencies from your project.

configurations.all {
    exclude group: 'com.rakuten.tech.mobile', module: 'manifest-config-processor'
    exclude group: 'com.rakuten.tech.mobile', module: 'manifest-config-annotations'
    exclude group: 'com.rakuten.tech.mobile.sdkutils', module: 'sdk-utils'
}

How do I use snapshot versions of this SDK?

We may periodically publish snapshot versions for testing pre-release features. These versions will always end in -SNAPSHOT, for example 1.0.0-SNAPSHOT. If you wish to use a snapshot version, you will need to add the snapshot repo to your Gradle configuration.

repositories {
    maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
}

dependency {
    implementation 'io.github.rakutentech.miniapp:miniapp:X.X.X-SNAPSHOT'
}
How can I use this SDK in a Java project?

We don’t support usage of the Mini App SDK in a Java project. This is because this SDK uses Kotlin specific features such as suspend functions and Coroutines.

However, it is possible to use this SDK in a Java project if you wrap the calls to suspend functions into something that Java can use such as a Future or a normal callback interface.

To do this, you will need to integrate Kotlin and Kotlin Coroutines into your project:

// In your top level "build.gradle" file:
buildscript {
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.50"
    }
}

// In your project "myApp/build.gradle" file
apply plugin: 'kotlin-android'

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.3.0" // Needed if you want to use CoroutineScope.future
}

Then, you will need to create wrapper functions for the Mini App SDK functionality which you wish to use.

If your minimum Android API level is 24 or higher, then you can use Java CompletableFuture:

// MiniAppAsync.kt

private val coroutineScope = CoroutineScope(Dispatchers.Default)

fun createMiniAppAsync(
    appId: String,
    miniAppMessageBridge: MiniAppMessageBridge,
    miniAppNavigator: MiniAppNavigator?
): CompletableFuture<MiniAppDisplay> = coroutineScope.future {
    MiniApp.instance().create(appId, miniAppMessageBridge, miniAppNavigator)
}

If your minimum Android API level is lower than 24, then you can use a normal Java callback interface:

// MiniAppAsync.kt

interface MiniAppCallback<T> {
    fun onSuccess(result: T)
    fun onError(error: MiniAppSdkException)
}

val coroutineScope = CoroutineScope(Dispatchers.Default)

fun createMiniAppAsync(
    appId: String,
    miniAppMessageBridge: MiniAppMessageBridge,
    miniAppNavigator: MiniAppNavigator?,
    callback: MiniAppCallback<MiniAppDisplay>
) {
    coroutineScope.launch {
        try {
            val display = MiniApp.instance().create(appId, miniAppMessageBridge, miniAppNavigator)
            callback.onSuccess(display)
        } catch (error: MiniAppSdkException) {
            callback.onError(error)
        }
    }
}

</detail>

Changelog

See the full CHANGELOG.