Rokid/app/src/main/java/com/yuanxuan/rokid/device/DeviceServiceManager.kt

374 lines
12 KiB
Kotlin

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>(InstructState.None)
val instructState = _instructState.asStateFlow()
/**
* 系统电量
*/
private val _batteryPercentage = MutableStateFlow(getBatteryLevel(context))
val batteryPercentage = _batteryPercentage.asStateFlow()
/**
* wifi
*/
private val _wifiState: MutableStateFlow<WifiState> = MutableStateFlow(WifiState.Unconnected)
val wifiState = _wifiState.asStateFlow()
private val wifiManager by lazy {
context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
}
/**
* 音量
*/
private val _volume: MutableStateFlow<Int> = MutableStateFlow(0)
val volume = _volume.asStateFlow()
/**
* 亮度
*/
private val _brightness: MutableStateFlow<Int> = 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
}
}