374 lines
12 KiB
Kotlin
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
|
|
}
|
|
|
|
}
|
|
|