Activation Spawn on Android SDK

Jun 26, 2023

This tutorial contains information on how to implement both parts of activation spawn (main and secondary, read the activation spawn overview for more information) on Android.

Installation

Add new Maven repositories pointing to Wultra Artifactory.

repositories {
    maven {
        url 'https://wultra.jfrog.io/artifactory/activation-spawn-android/'
        credentials {
            username = wultraArtifactoryUsername
            password = wultraArtifactoryPass
        }
    }
    maven {
        url 'https://wultra.jfrog.io/artifactory/device-fingerprint-android/'
        credentials {
            username = wultraArtifactoryUsername
            password = wultraArtifactoryPass
        }
    }
}

Once you add the Maven repository, you can add activation spawn dependency to your Android project.

android {
    dependencies {
        implementation "com.wultra.android.activationspawn:activation-spawn:${WULTRA_ACTIVATION_SPAWN_MANAGER}"
    }
}

Main Application

Secondary App Definition

If your application targets Android 11+ (SDK 30+), you need to declare the query “permissions” for the package name of Target App in the AndroidManifest.xml of the Source App. Otherwise, you won’t be able to detect if the app is already installed.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourcompany.yourapp">

    <queries>
         <!-- package name of your secondary app -->
        <package android:name="com.mycompany.secondaryapplication"/>
    </queries>

    <!-- rest of your manifest -->

</manifest>

Define the secondary apps that are available for activation. You can create the app representation by instantiating the SpawnableApplication class. It provides a package name, a deep link scheme, and a backend ID.

import com.wultra.android.activationspawn.SpawnableApplication

val application = SpawnableApplication(
    // package name of the app
    "com.company.android.application",
    // url deeplink scheme (for example from myAppScheme://settings)
    "myAppScheme",
    // needs to be provided by the powerauth backend administrator at your company
    "AA1122XYZ"
)

Obtaining ActivationSpawnManager

You can create ActivationSpawnManager instance in the following manner:

import com.wultra.android.devicefingerprint.DeviceFingerprintGenerator
import com.wultra.android.activationspawn.createSpawnManager

// Additional data for generator (must be the same for both Source and Target App).
val additionalData: ByteArray? = null

// Note that the generator configuration must be the same
// for both Target and Source App. Please consult with Wultra
// what configuration suits your needs.
private val generator = DeviceFingerprintGenerator.getSemiStable(appContext, false, 10, additionalData)

// manager that handles the activation
// powerAuth is configured PowerAuthSDK instance
// appContext is application `Context` instance
private val manager = powerAuth.createSpawnManager(appContext, SSLValidationStrategy.default(), generator, "https://your-domain.com/your-app")

Checking for Application Installation

At any point in time, you can check if the secondary app is installed and if not, request the installation:

// manager is instance of ActivationSpawnManager

try {
    if (manager.isInstalled(application)) {
        // app is installed
    } else {
        // when app is not installed, open it's store page
        manager.openStore(app)

    }
} catch (t: Throwable) {
   //handle isInstalled exception
}

Obtaining Activation Data

In case you are using your own authentication scheme, you can fetch the data using your authenticated service.

When using PowerAuth for authentication, you can retrieve activation data for the user in the following manner:

import io.getlime.security.powerauth.sdk.PowerAuthAuthentication
import com.wultra.android.activationspawn.IRetrieveActivationDataListener
import com.wultra.android.activationspawn.ActivationSpawnData
import com.wultra.android.powerauth.networking.error.ApiError

val auth = PowerAuthAuthentication()
// prepare authentication object for 2FA
// ..
// ..

// manager is instance of ActivationSpawnManager
manager.retrieveActivationData(app, auth, object : IRetrieveActivationDataListener {
    override fun onSuccess(data: ActivationSpawnData) {
        // activation data retrieved
    }

    override fun onError(error: ApiError) {
        // process the error
    }
})
  1. Activate the app by forwarding the activation data via URL scheme.
try {
    // sharedInfo is a pre-shared value
    manager.transportDataToApp(data, application, sharedInfo)
} catch (t: Throwable) {
    // process the error
}

Secondary App

Declare a deep link scheme in the AndroidManifest.xml for the application to enable digesting the deep link.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mycompany.secondaryapplication">
    <application>
        <activity
            android:name=".MyActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data
                    android:scheme="myAppScheme" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Performing Device Activation

When the secondary app is opened from the activation deep link, it needs to parse it using our helper methods. If the data is correctly retrieved, the app can proceed with a standard PowerAuth activation (under the hood, it is a standard activation via activation code and activation OTP):

import android.app.Activity
import android.os.Bundle
import com.wultra.android.activationspawn.*
import io.getlime.security.powerauth.networking.response.CreateActivationResult
import io.getlime.security.powerauth.networking.response.ICreateActivationListener
import io.getlime.security.powerauth.sdk.PowerAuthSDK

class TestActivity: Activity() {

    // dependencies you need to prepare
    private lateinit var manager: ActivationSpawnManager
    private lateinit var powerAuth: PowerAuthSDK

    // Additional data for transport (must be the same for both Source and Target App).
    private val sharedInfo: ByteArray? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val data = intent.data
        if (data != null) {
            try {
                val activationData = manager.processDeeplink(data, sharedInfo)
                powerAuth.createActivation(activationData, "Simon's phone", object : ICreateActivationListener {
                    override fun onActivationCreateSucceed(result: CreateActivationResult) {
                        // continue with the activation
                    }

                    override fun onActivationCreateFailed(t: Throwable) {
                        // process the error
                    }
                })
            } catch (t: Throwable) {
                // deeplink cannot be processed
            }
        }
    }
}

Continue Reading

Reference

Last updated on Jun 26, 2023 (22:49) Edit on Github Send Feedback
Petr Dvořák

Petr Dvořák

CEO & Founder
Search

develop