feat: 初始化 SDK

This commit is contained in:
yangxisong 2025-11-10 17:20:15 +08:00
parent 41726d5006
commit 22ff4384ca
9 changed files with 348 additions and 13 deletions

Binary file not shown.

View File

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".app.RokidApplication"
android:name=".RokidApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"

View File

@ -1,10 +1,15 @@
package com.yuanxuan.rokid
import android.os.Bundle
import androidx.activity.addCallback
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import com.yuanxuan.rokid.dependencies.AppDependencies
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -16,5 +21,9 @@ class MainActivity : AppCompatActivity() {
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
AppDependencies.deviceServiceManager.playTTS("测试一下")
onBackPressedDispatcher.addCallback {
}
}
}

View File

@ -0,0 +1,36 @@
package com.yuanxuan.rokid
import android.app.Application
import android.content.Intent
import com.yuanxuan.rokid.dependencies.AppDependencies
import com.yuanxuan.rokid.dependencies.ApplicationDependencyProvider
import com.yuanxuan.rokid.keeplive.KeepLiveService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import timber.log.Timber
class RokidApplication : Application() {
private val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
override fun onCreate() {
super.onCreate()
Timber.plant(Timber.DebugTree())
AppDependencies.init(this, ApplicationDependencyProvider(this))
/**
* 启动APP必须先获取到SN 后面网络依赖
*/
applicationScope.launch {
val sn = AppDependencies.deviceServiceManager.getSn()
Timber.d("sn = $sn")
}
/**
* 这个服务保证心跳包准时发
*/
val serviceIntent = Intent(this, KeepLiveService::class.java)
startForegroundService(serviceIntent)
}
}

View File

@ -1,11 +0,0 @@
package com.yuanxuan.rokid.app
import android.app.Application
class RokidApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}

View File

@ -0,0 +1,31 @@
package com.yuanxuan.rokid.dependencies
import android.app.Application
import com.yuanxuan.rokid.device.DeviceServiceManager
/**
* 项目的服务管理器所有依赖集中管理
*/
object AppDependencies {
private lateinit var _application: Application
private lateinit var provider: Provider
fun init(application: Application, provider: Provider) {
if (this::_application.isInitialized || this::provider.isInitialized) {
return
}
_application = application
AppDependencies.provider = provider
}
val deviceServiceManager by lazy {
provider.provideDeviceServiceManager()
}
interface Provider {
fun provideDeviceServiceManager(): DeviceServiceManager
}
}

View File

@ -0,0 +1,10 @@
package com.yuanxuan.rokid.dependencies
import android.app.Application
import com.yuanxuan.rokid.device.DeviceServiceManager
class ApplicationDependencyProvider(val context: Application) : AppDependencies.Provider {
override fun provideDeviceServiceManager(): DeviceServiceManager {
return DeviceServiceManager(context)
}
}

View File

@ -0,0 +1,260 @@
package com.yuanxuan.rokid.device
import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.rokid.dcg.sprite.service.IInstructListener
import com.rokid.dcg.sprite.service.IInstructService
import com.rokid.dcg.sprite.service.ISpriteMediaService
import com.rokid.dcg.sprite.service.ISystemFuncService
import com.rokid.dcg.sprite.service.ITTSService
import com.rokid.dcg.sprite.service.ServiceManager
import com.rokid.dcg.sprite.syskey.SysKeyAction
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import timber.log.Timber
import kotlin.coroutines.resume
class DeviceServiceManager(val context: Application) {
companion object {
/**
* 语音指令等待超时
*/
private const val INSTRUCT_WAITE_TIME = 15000L
}
private var instructService: IInstructService? = null
private var ttsService: ITTSService? = null
private var systemFuncService: ISystemFuncService? = null
private var spriteMediaService: ISpriteMediaService? = null
private val _instructState = MutableStateFlow<InstructState>(InstructState.None)
val instructState = _instructState.asStateFlow()
lateinit var sn: String
private set
val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception ->
Timber.e(exception, "全局捕获协程异常:${exception.message}")
}
private val scope =
CoroutineScope(SupervisorJob() + Dispatchers.Main + coroutineExceptionHandler)
private var wakeTimeoutJob: Job? = null
init {
val filter = IntentFilter()
filter.addAction(SysKeyAction.SPRITE_BUTTON_DOWN)
filter.addAction(SysKeyAction.SPRITE_BUTTON_UP)
filter.addAction(SysKeyAction.SPRITE_BUTTON_CLICK)
filter.addAction(SysKeyAction.SPRITE_BUTTON_LONG_PRESS)
filter.addAction(SysKeyAction.SPRITE_BUTTON_VERY_VERY_LONG_PRESS)
filter.addAction(SysKeyAction.TOUCH_BAR_LONG_PRESS)
context.registerReceiver(SysKeyReceiver(), filter)
scope.launch {
instructState.collect {
Timber.d("语音指令状态 $it")
when (it) {
InstructState.None -> instructService?.unregisterListener(instructListener)
InstructState.WaitingInstructState -> {
instructService?.registerListener(instructListener)
startWakeTimeoutCountdown()
}
}
}
}
}
val instructListener = object : IInstructListener.Stub() {
override fun onInstructReceived(p0: Int) {
Timber.d("接受到语音指令$p0")
if (p0 != IInstructService.INSTRUCT_QUIT) {
startWakeTimeoutCountdown()
}
when (p0) {
IInstructService.INSTRUCT_WAKEUP -> {
}
IInstructService.INSTRUCT_VOLUME_UP -> {
systemFuncServiceTodo { service ->
setVolumeSpecified(service.volumeSpecified + 1)
}
}
IInstructService.INSTRUCT_VOLUME_DOWN -> {
systemFuncServiceTodo { service ->
setVolumeSpecified(service.volumeSpecified - 1)
}
}
IInstructService.INSTRUCT_LIGHT_UP -> {
systemFuncServiceTodo { service ->
setBrightnessSpecified(service.brightnessSpecified + 1)
}
}
IInstructService.INSTRUCT_LIGHT_DOWN -> {
systemFuncServiceTodo { service ->
setBrightnessSpecified(service.brightnessSpecified - 1)
}
}
IInstructService.INSTRUCT_TAKE_PHOTO -> {
}
IInstructService.INSTRUCT_START_AUDIO_RECORD -> {
}
IInstructService.INSTRUCT_STOP_AUDIO_RECORD -> {
}
IInstructService.INSTRUCT_START_VIDEO_RECORD -> {
}
IInstructService.INSTRUCT_STOP_VIDEO_RECORD -> {
}
IInstructService.INSTRUCT_QUIT -> {
_instructState.update { InstructState.None }
}
}
}
}
/**
* [INSTRUCT_WAITE_TIME] 一段时间无新指令 退出等待状态
*/
private fun startWakeTimeoutCountdown() {
wakeTimeoutJob?.cancel()
wakeTimeoutJob = scope.launch {
delay(INSTRUCT_WAITE_TIME)
_instructState.update { InstructState.None }
}
}
fun playTTS(msg: String) {
ttsServiceTodo {
ttsService?.playTtsMsg(msg)
}
}
suspend fun getSn() = suspendCancellableCoroutine { continuation ->
systemFuncServiceTodo { service ->
sn = service.sn
continuation.resume(sn)
}
}
private inline fun ttsServiceTodo(crossinline todo: (ITTSService) -> Unit) {
if (ttsService == null) {
ServiceManager.getTTSService(context) { service ->
if (service == null) {
Timber.d("设备初始化TTS服务失败")
return@getTTSService
}
ttsService = service
todo.invoke(service)
}
} else {
todo.invoke(ttsService!!)
}
}
private inline fun systemFuncServiceTodo(crossinline todo: (ISystemFuncService) -> Unit) {
if (systemFuncService == null) {
ServiceManager.getSystemFuncService(context) { service ->
if (service == null) {
Timber.d("设备初始化系统功能服务失败")
return@getSystemFuncService
}
systemFuncService = service
todo.invoke(service)
Timber.d(" ${service.sn}")
}
} else {
todo.invoke(systemFuncService!!)
}
}
private inline fun instructServiceTodo(crossinline todo: (IInstructService) -> Unit) {
if (instructService == null) {
ServiceManager.getInstructService(context) { service ->
if (service == null) {
Timber.d("设备初始化语音指令服务失败")
return@getInstructService
}
instructService = service
todo.invoke(service)
}
} else {
todo.invoke(instructService!!)
}
}
private fun setVolumeSpecified(volume: Int) {
systemFuncService?.volumeSpecified = volume.coerceIn(0, 15)
}
private fun setBrightnessSpecified(brightness: Int) {
systemFuncService?.brightnessSpecified = brightness.coerceIn(0, 15)
}
inner class SysKeyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent?.action ?: return
when (action) {
SysKeyAction.SPRITE_BUTTON_DOWN -> {
Timber.d("按下拍照键 SPRITE_BUTTON_DOWN")
}
SysKeyAction.SPRITE_BUTTON_UP -> {
Timber.d("抬起拍照键 SPRITE_BUTTON_UP")
}
SysKeyAction.SPRITE_BUTTON_CLICK -> {
Timber.d("点击拍照键 SPRITE_BUTTON_CLICK")
}
SysKeyAction.SPRITE_BUTTON_LONG_PRESS -> {
Timber.d("长按拍照键 SPRITE_BUTTON_LONG_PRESS")
}
SysKeyAction.SPRITE_BUTTON_VERY_VERY_LONG_PRESS -> {
Timber.d("超级长的长按拍照键 SPRITE_BUTTON_VERY_VERY_LONG_PRESS")
}
SysKeyAction.TOUCH_BAR_LONG_PRESS -> {
Timber.d("长按触摸板 TOUCH_BAR_LONG_PRESS")
instructServiceTodo {
_instructState.update { InstructState.WaitingInstructState }
}
}
}
}
}
sealed interface InstructState {
data object None : InstructState
data object WaitingInstructState : InstructState
}
}

View File

@ -20,7 +20,7 @@ class KeepLiveService : Service() {
companion object {
const val NOTIFICATION_CHANNEL_ID = "KeepLiveServiceChannel"
const val NOTIFICATION_CHANNEL_NAME = NOTIFICATION_CHANNEL_ID
const val NOTIFICATION_ID = 101 // 使用一个唯一的ID
const val NOTIFICATION_ID = 101
const val WAKE_LOCK_TAG = "KeepLiveService:WakeLock"
}