Skip to content

Calindra/eitri-android-contracts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 

Repository files navigation

eitri-android-contracts

Contracts for integrating Eitri Android modules with the Eitri Machine runtime. The package defines lightweight Kotlin interfaces and data classes that standardize how native modules report their capabilities, expose callable methods, and gain access to runtime context supplied by the Eitri SDK.

Requirements

  • Kotlin 1.8.10 or later
  • Java 11 or later
  • Android 5.0 (API level 21) minimum deployment target

Core Concepts

  • EitriModule: Implement this interface in every native module exposed to the Eitri runtime. The runtime calls start(contextProvider:), and the module must synchronously finish registering the methods it intends to expose. Defer long-running work, as blocking start will stall the host application's bootstrapping process.

  • EitriContextProvider: A runtime bridge returned to modules during start. Use it to read the immutable AppContext and to register suspending functions via exposeMethod(methodName:method:).

  • AppContext: Immutable metadata about the host environment (Eitri SDK version, debug flag, package name, and the Android Context). Treat it as read-only state that describes the runtime.

  • ModuleExposedMethod: A typealias for suspending functions that modules expose. These functions receive ModuleMethodParams and may return any result, which will be serialized and sent back to the caller. Throwing an exception will propagate a failure.

  • ModuleMethodParams: The invocation payload containing a structured data payload (JSONObject), the EitriAppInvokingMethod descriptor for the requesting eitri-app, and the androidUIContext available to present UI.

  • ActivityResultHandler: A contract interface for modules to dynamically register handlers for activity results from startActivityForResult operations. Modules can cast androidUIContext to this interface and register callbacks without requiring tight coupling to specific activity implementations. Handlers are automatically removed after invocation to prevent memory leaks and allow request code reuse.

  • PermissionRequesterActivity: A contract interface for requesting Android runtime permissions. Modules can cast androidUIContext to this interface to check and request permissions asynchronously.

The runtime also exposes an EitriModuleRegistry interface that host applications implement to accept modules. Modules should not retain the registry; it should only be used at the initial registration time.

Using ActivityResultHandler

The ActivityResultHandler contract allows modules to handle activity results without tight coupling to the host activity implementation.

Request Code Requirements

  • Request codes must be in the range 0-65535 (16-bit values)
  • The following request codes are reserved for internal SDK use and cannot be registered:
    • 2019: Document picker operations
    • 2026: Web flow operations
    • 3020: Camera operations
  • Request codes are automatically released after the handler is invoked, allowing reuse

Example Usage

// In your module's exposed method
suspend fun openCustomActivity(params: ModuleMethodParams): String {
    val activityResultHandler = params.androidUIContext as? ActivityResultHandler
        ?: throw Exception("Activity must implement ActivityResultHandler")

    return suspendCancellableCoroutine { continuation ->
        val requestCode = 5000 // Use a unique code not in the reserved range

        // Register handler before launching activity
        activityResultHandler.registerActivityResultHandler(requestCode) { resultCode, data ->
            when (resultCode) {
                Activity.RESULT_OK -> {
                    val result = data?.getStringExtra("result") ?: "success"
                    continuation.resume(result)
                }
                Activity.RESULT_CANCELED -> {
                    continuation.resumeWithException(Exception("User cancelled"))
                }
                else -> {
                    continuation.resumeWithException(Exception("Unexpected result code: $resultCode"))
                }
            }
        }

        // Clean up handler if continuation is cancelled before result arrives
        continuation.invokeOnCancellation {
            activityResultHandler.unregisterActivityResultHandler(requestCode)
        }

        // Launch the activity
        val intent = Intent(params.androidUIContext, CustomActivity::class.java)
        params.androidUIContext.startActivityForResult(intent, requestCode)
    }
}

Best Practices

  • Always register handlers before calling startActivityForResult
  • Use invokeOnCancellation to clean up handlers if the coroutine is cancelled
  • Choose request codes outside the reserved range (avoid 2019, 2026, 3020)
  • Validate and sanitize any data received from external activities to prevent injection attacks
  • Handlers are automatically removed after invocation, so you don't need to manually unregister them in the success path

Project Structure

  • eitri-android-contracts/src/main/java/tech/eitri/android/contracts: Contains the Kotlin interfaces and data classes that define the native contract surface.
  • eitri-android-contracts/src/test: Unit tests for the contracts module.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •