Skip to content

Android 集成指南

本文介绍了如何在 Android 应用程序中使用 InkView 集成 Ink 引擎

前置条件

  • Android Studio (Ladybug 或更高版本)
  • Android SDK (API Level 24+)
  • NDK (r25c 或更高版本)
  • Rust Toolchain (1.70+)

1. 添加依赖

首先,你需要获取 Ink Android 库 (AAR)。

  1. Android Build Workflow 下载最新的 AAR 构建产物。
  2. 将下载的 AAR 文件(例如 ink-debug.aar)放置在应用的 libs 目录下。

Gradle 设置

在应用的 build.gradle.kts 中:

kotlin
dependencies {
    // Ink Android 核心库
    implementation(files("libs/ink-debug.aar"))

    // 用于 Java 和 Native 层之间 IPC 通信的高效 CBOR 序列化
    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.14.2")

    // 为 Jackson 序列化提供 Kotlin 支持
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.2")
}

2. 添加 InkView 到布局

你可以将 InkView 直接添加到 XML 布局文件中,或通过代码动态创建。

XML 布局

xml
<com.rokid.jsai.ink.InkView
    android:id="@+id/inkView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

动态创建

kotlin
val inkView = InkView(context)
layout.addView(inkView)

3. 初始化并打开小程序

在你的 Activity 或 Fragment 中,初始化 InkView 并打开小程序。

kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var inkView: InkView

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

        inkView = findViewById(R.id.inkView)
        
        // 从本地目录打开小程序
        val appPath = filesDir.absolutePath + "/mini-app"
        inkView.open(appPath)
    }
}

4. 实现系统能力

Ink 依赖宿主应用提供多媒体、网络和语音等系统能力。你需要实现相应的接口。

4.1 实现能力

以下是实现 MediaCapability 接口来处理多媒体请求的示例。其他能力(网络、语音)遵循类似的模式。

kotlin
inkView.addCapability(object : InkView.MediaCapability {
    override fun startAudioRecording(
        reqId: String, 
        targetId: String, 
        options: InkView.AudioRecordingOptions, 
        callback: InkView.VoidCallback
    ) {
        try {
            // 初始化并开始录音
            myAudioRecorder.start(options.sampleRate)
            callback.success()
        } catch (e: Exception) {
            callback.error(-1, e.message ?: "未知错误")
        }
    }

    override fun stopAudioRecording(reqId: String, callback: InkView.VoidCallback) {
        myAudioRecorder.stop()
        callback.success()
    }

    // 类似地实现其他方法(暂停、恢复、拍照)...
})

4.2 分发事件

Ink 支持两种类型的事件分发:特定目标事件 (Target-Specific Events)全局事件 (Global Events)

特定目标事件

这些事件被分发到特定目标(例如录音会话或网络 Socket)。你需要提供 targetId(或 handle),它通常通过能力实现方法传递给你(例如,startAudioRecording 提供 targetId)。

dispatchEvent(targetId: String, eventData: MediaEventData)

向特定媒体会话分发媒体相关事件。

  • 参数:
    • targetId: 目标组件/会话的 UUID(在 startAudioRecording 中接收)。
    • eventData: 特定的媒体事件负载(例如 MediaEventData.AudioRecordingStartedMediaEventData.AudioFrameRecorded)。
dispatchEvent(targetId: String, eventData: NetworkingEventData)

向特定 Socket 连接分发网络相关事件。

  • 参数:
    • targetId: Socket 句柄的 UUID(在 openSocket 中接收)。
    • eventData: 网络事件负载(例如 NetworkingEventData.Data)。

示例:

kotlin
// 分发媒体事件(例如,录音开始)
inkView.dispatchEvent(recordingSessionId, InkView.MediaEventData.AudioRecordingStarted)

// 分发网络事件(例如,收到数据)
val data = "Hello".toByteArray()
inkView.dispatchEvent(socketHandle, InkView.NetworkingEventData.Data(data))

全局事件

这些事件不绑定到特定目标,影响全局状态或是通用输入事件。

dispatchEvent(type: InputEventType, code: String, timestamp: Long)

分发全局输入事件(例如键盘按键)。

  • 参数:
    • type: 输入事件类型(InputEventType.KeyUpInputEventType.KeyDown)。
    • code: 键值字符串(例如 "KeyA", "Enter")。
    • timestamp: 毫秒级事件时间戳。

示例:

kotlin
// 分发全局输入事件
inkView.dispatchEvent(InkView.InputEventType.KeyDown, "KeyA", System.currentTimeMillis())

4.3 系统能力参考

本节详细介绍了可用的系统能力、其方法以及应分发的事件。

多媒体 (InkView.MediaCapability)

处理录音和相机操作。

kotlin
inkView.addCapability(object : InkView.MediaCapability {
    override fun startAudioRecording(
        reqId: String, 
        targetId: String, 
        options: InkView.AudioRecordingOptions, 
        callback: InkView.VoidCallback
    ) {
        // 开始录音...
        callback.success()
    }
    
    // 实现其他方法...
})

方法:

startAudioRecording(reqId: String, targetId: String, options: AudioRecordingOptions, callback: VoidCallback)

请求开始录音。

  • 参数:
    • reqId: 唯一请求 ID。
    • targetId: 录音会话的 UUID。
    • options: 配置(采样率、通道数等)。
    • callback: 报告成功或错误。
stopAudioRecording(reqId: String, callback: VoidCallback)

请求停止录音。

pauseAudioRecording(reqId: String, callback: VoidCallback)

请求暂停录音。

resumeAudioRecording(reqId: String, callback: VoidCallback)

请求恢复录音。

takePhoto(reqId: String, options: TakePhotoOptions, callback: PhotoCallback)

请求拍照。

  • 回调: success(data: ByteArray, mimeType: String)

事件 (InkView.MediaEventData):

事件名称数据类型描述
AudioRecordingStartedUnit录音已开始。
AudioRecordingStoppedString录音已停止(带有可选原因/ID)。
AudioRecordingPausedUnit录音已暂停。
AudioRecordingResumedUnit录音已恢复。
AudioFrameRecordedByteArray新的音频帧数据。
AudioRecordingErrorString发生错误。
AudioRecordingInterruptionBeginUnit录音中断(例如来电)。
AudioRecordingInterruptionEndUnit中断结束。

网络 (InkView.NetworkingCapability)

处理 Socket 操作。

kotlin
inkView.addCapability(object : InkView.NetworkingCapability {
    override fun openSocket(
        reqId: String, 
        handle: String, 
        name: String, 
        port: Int, 
        isSsl: Boolean, 
        callback: InkView.VoidCallback
    ) {
        // 打开 Socket...
        callback.success()
    }

    // 实现其他方法...
})

方法:

openSocket(reqId: String, handle: String, name: String, port: Int, isSsl: Boolean, callback: VoidCallback)

请求打开 Socket 连接。

  • 参数:
    • reqId: 唯一请求 ID。
    • handle: 此 Socket 的唯一 UUID。
    • name: 主机名或 IP 地址。
    • port: 端口号。
    • isSsl: 是否使用 SSL/TLS。
    • callback: 报告成功或错误。
closeSocket(reqId: String, handle: String, callback: VoidCallback)

请求关闭 Socket。

send(reqId: String, handle: String, data: ByteArray, callback: VoidCallback)

请求通过 Socket 发送数据。

事件 (InkView.NetworkingEventData):

事件名称数据类型描述
DataByteArray从 Socket 接收到的数据。

语音 (InkView.SpeechCapability)

处理文本转语音 (TTS) 和自动语音识别 (ASR)。

kotlin
inkView.addCapability(object : InkView.SpeechCapability {
    override fun playTts(
        reqId: String, 
        text: String, 
        callback: InkView.VoidCallback
    ) {
        // 播放 TTS...
        callback.success()
    }

    override fun startAsr(
        reqId: String, 
        callback: InkView.StringCallback
    ) {
        // 开始 ASR...
        callback.success("识别到的文本")
    }
})

方法:

playTts(reqId: String, text: String, callback: VoidCallback)

请求播放 TTS。

  • 参数:
    • reqId: 唯一请求 ID。
    • text: 要朗读的文本。
    • callback: 报告完成。
startAsr(reqId: String, callback: StringCallback)

请求开始语音识别。

  • 回调: success(text: String) 返回识别到的文本。

事件:

  • 无特定事件;结果通过回调返回。

6. 生命周期管理

确保正确处理 InkView 的生命周期。

kotlin
override fun onDestroy() {
    super.onDestroy()
    // SurfaceView 销毁时会自动清理原生资源,
    // 但你可以在这里确保其他清理工作。
}

Released under the Apache-2.0 License.