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 android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.net.wifi.WifiManager import android.os.BatteryManager 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 com.yuanxuan.rokid.toast.ToastUtils 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 import kotlin.coroutines.resumeWithException class DeviceServiceManager(val context: Application) : ConnectivityManager.NetworkCallback() { companion object { /** * 语音指令等待超时 */ private const val INSTRUCT_WAITE_TIME = 15000L const val MAX_VOLUME = 15 const val MAX_BRIGHTNESS = 15 } 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.None) val instructState = _instructState.asStateFlow() /** * 系统电量 */ private val _batteryPercentage = MutableStateFlow(getBatteryLevel(context)) val batteryPercentage = _batteryPercentage.asStateFlow() /** * wifi */ private val _wifiState: MutableStateFlow = MutableStateFlow(WifiState.Unconnected) val wifiState = _wifiState.asStateFlow() private val wifiManager by lazy { context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager } /** * 音量 */ private val _volume: MutableStateFlow = MutableStateFlow(0) val volume = _volume.asStateFlow() /** * 亮度 */ private val _brightness: MutableStateFlow = MutableStateFlow(0) val brightness = _brightness.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().apply { addAction(SysKeyAction.SPRITE_BUTTON_DOWN) addAction(SysKeyAction.SPRITE_BUTTON_UP) addAction(SysKeyAction.SPRITE_BUTTON_CLICK) addAction(SysKeyAction.SPRITE_BUTTON_LONG_PRESS) addAction(SysKeyAction.SPRITE_BUTTON_VERY_VERY_LONG_PRESS) addAction(SysKeyAction.TOUCH_BAR_LONG_PRESS) addAction(Intent.ACTION_BATTERY_CHANGED) // 监听电量 } context.registerReceiver(SysKeyReceiver(), filter) scope.launch { instructState.collect { Timber.d("语音指令状态 $it") when (it) { InstructState.None -> instructService?.unregisterListener(instructListener) InstructState.WaitingInstructState -> { instructService?.registerListener(instructListener) startWakeTimeoutCountdown() } } } } /** * 监听wifi状态 */ val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val request = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .build() connectivityManager.registerNetworkCallback(request, this) } 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 -> { volumeAdd() } IInstructService.INSTRUCT_VOLUME_DOWN -> { volumeSubtract() } IInstructService.INSTRUCT_LIGHT_UP -> { brightnessAdd() } IInstructService.INSTRUCT_LIGHT_DOWN -> { brightnessSubtract() } 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 quitInstructReceived() { _instructState.update { InstructState.None } } fun playTTS(msg: String) { ttsServiceTodo { ttsService?.playTtsMsg(msg) } } fun volumeAdd() { systemFuncServiceTodo { service -> val volume = (service.volumeSpecified + 1).coerceIn(0, 15) service.volumeSpecified = volume _volume.update { volume } } } fun volumeSubtract() { systemFuncServiceTodo { service -> val volume = (service.volumeSpecified - 1).coerceIn(0, 15) service.volumeSpecified = volume _volume.update { volume } } } fun brightnessAdd() { systemFuncServiceTodo { service -> val brightness = (service.brightnessSpecified + 1).coerceIn(0, 15) service.brightnessSpecified = brightness _brightness.update { brightness } } } fun brightnessSubtract() { systemFuncServiceTodo { service -> val brightness = (service.brightnessSpecified - 1).coerceIn(0, 15) service.brightnessSpecified = brightness _brightness.update { brightness } } } suspend fun getSn() = suspendCancellableCoroutine { continuation -> ServiceManager.getSystemFuncService(context) { service -> if (service == null) { continuation.resumeWithException(Throwable("没有拿到SN 要重启APP")) return@getSystemFuncService } systemFuncService = service sn = service.sn _volume.update { service.volumeSpecified } _brightness.update { service.brightnessSpecified } 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) } } 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 getBatteryLevel(context: Context): Int { val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager return bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) } inner class SysKeyReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val action = intent?.action ?: return when (action) { Intent.ACTION_BATTERY_CHANGED -> { val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) val scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1) if (level == -1 || scale == -1) return val batteryPercentage = (level / scale.toFloat() * 100).toInt() _batteryPercentage.update { batteryPercentage } } SysKeyAction.SPRITE_BUTTON_DOWN -> { Timber.d("按下拍照键 SPRITE_BUTTON_DOWN") } SysKeyAction.SPRITE_BUTTON_UP -> { Timber.d("抬起拍照键 SPRITE_BUTTON_UP") } SysKeyAction.SPRITE_BUTTON_CLICK -> { ToastUtils.showLong("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 } } } } } } override fun onLost(network: Network) { _wifiState.update { WifiState.Unconnected } } override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { _wifiState.update { val level = wifiManager.calculateSignalLevel(networkCapabilities.signalStrength) WifiState.Connected( /** * maxSignalLevel 是 4 level区间应该是 0-3 这里却返回了4 限制一下 * 参考 [com.yuanxuan.rokid.R.drawable.wifi_level_list] 0 为wifi断开 */ level = level.coerceAtMost(wifiManager.maxSignalLevel - 1) + 1 ) } } sealed interface InstructState { data object None : InstructState data object WaitingInstructState : InstructState } sealed interface WifiState { data object Unconnected : WifiState data class Connected(val level: Int) : WifiState } }