feat: 组件解耦

This commit is contained in:
yangxisong 2025-11-27 16:52:43 +08:00
parent 4712855f6c
commit 0c122b9352
14 changed files with 96 additions and 61 deletions

View File

@ -1,25 +1,23 @@
package com.yuanxuan.rokid
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import androidx.activity.addCallback
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import com.yuanxuan.rokid.databinding.ActivityMainBinding
import com.yuanxuan.rokid.dependencies.AppDependencies
import com.yuanxuan.rokid.device.DeviceServiceManager
import com.yuanxuan.rokid.extension.fadeIn
import com.yuanxuan.rokid.extension.fadeOut
import kotlinx.coroutines.launch
import timber.log.Timber
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -45,11 +43,9 @@ class MainActivity : AppCompatActivity() {
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
return if (AppDependencies.deviceServiceManager.instructState.value ==
DeviceServiceManager.InstructState.WaitingInstructState
) {
return if (viewModel.uiState.value.isVoiceInputVisible) {
if (event.keyCode == KeyEvent.KEYCODE_BACK) {
AppDependencies.deviceServiceManager.quitInstructReceived()
viewModel.quitInstructReceived()
}
true
} else {
@ -59,36 +55,17 @@ class MainActivity : AppCompatActivity() {
private fun observer() {
lifecycleScope.launch {
/**
* 监听电量
*/
AppDependencies.deviceServiceManager.batteryPercentage.collect {
binding.batteryLevel.text = resources.getString(R.string.status_bar_battery, it)
binding.batteryLevelIv.setImageLevel(it)
}
}
lifecycleScope.launch {
AppDependencies.deviceServiceManager.wifiState.collect {
when (it) {
is DeviceServiceManager.WifiState.Connected -> binding.wifiIv.setImageLevel(it.level)
DeviceServiceManager.WifiState.Unconnected -> binding.wifiIv.setImageLevel(0)
}
}
}
lifecycleScope.launch {
AppDependencies.deviceServiceManager.instructState.collect {
when (it) {
DeviceServiceManager.InstructState.None -> {
binding.lottieVoiceInput.cancelAnimation()
binding.lottieVoiceInput.fadeOut(300)
}
DeviceServiceManager.InstructState.WaitingInstructState -> {
viewModel.uiState.collect { uiState ->
binding.batteryLevel.text =
resources.getString(R.string.status_bar_battery, uiState.batteryLevel)
binding.batteryLevelIv.setImageLevel(uiState.batteryLevel)
binding.wifiIv.setImageLevel(uiState.wifiLevel)
if (uiState.isVoiceInputVisible) {
binding.lottieVoiceInput.playAnimation()
binding.lottieVoiceInput.fadeIn(300)
}
} else {
binding.lottieVoiceInput.cancelAnimation()
binding.lottieVoiceInput.fadeOut(300)
}
}
}

View File

@ -1,10 +1,14 @@
package com.yuanxuan.rokid
import androidx.activity.result.launch
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.yuanxuan.rokid.dependencies.AppDependencies.deviceServiceManager
import com.yuanxuan.rokid.device.DeviceServiceManager
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class MainViewModel : ViewModel() {
@ -12,6 +16,30 @@ class MainViewModel : ViewModel() {
private val _keyEventFlow = MutableSharedFlow<KeyEvent>()
val keyEventFlow = _keyEventFlow.asSharedFlow()
val uiState = combine(
deviceServiceManager.batteryPercentage,
deviceServiceManager.wifiState,
deviceServiceManager.instructState
) { battery, wifi, instruct ->
MainUiState(
batteryLevel = battery,
wifiLevel = if (wifi is DeviceServiceManager.WifiState.Connected) wifi.level else 0,
isVoiceInputVisible = instruct == DeviceServiceManager.InstructState.WaitingInstructState
)
}.stateIn(
scope = viewModelScope,
initialValue = MainUiState(
batteryLevel = 0,
wifiLevel = 0,
isVoiceInputVisible = false
),
started = SharingStarted.Eagerly,
)
fun quitInstructReceived(){
deviceServiceManager.quitInstructReceived()
}
fun onKeyEventDispatched(event: KeyEvent) {
viewModelScope.launch {
_keyEventFlow.emit(event)
@ -24,4 +52,10 @@ class MainViewModel : ViewModel() {
data object Enter : KeyEvent //前滑
}
data class MainUiState(
val batteryLevel: Int,
val wifiLevel: Int,
val isVoiceInputVisible: Boolean,
)
}

View File

@ -2,10 +2,10 @@ package com.yuanxuan.rokid
import android.app.Application
import android.content.Intent
import android.os.Build
import com.yuanxuan.rokid.dependencies.AppDependencies
import com.yuanxuan.rokid.dependencies.ApplicationDependencyProvider
import com.yuanxuan.rokid.keeplive.KeepLiveService
import com.yuanxuan.rokid.network.websocket.WebSocketReconnectWorker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@ -28,7 +28,7 @@ class RokidApplication : Application() {
runCatching {
AppDependencies.deviceServiceManager.getSn()
}.onSuccess {
AppDependencies.webSocketManager.connect()
WebSocketReconnectWorker.schedule(this@RokidApplication)
}.onFailure {
Timber.e(it)
}

View File

@ -57,7 +57,7 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo
/**
* 系统电量
*/
private val _batteryPercentage = MutableStateFlow(-1)
private val _batteryPercentage = MutableStateFlow(getBatteryLevel(context))
val batteryPercentage = _batteryPercentage.asStateFlow()
/**
@ -293,6 +293,11 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo
}
}
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

View File

@ -6,8 +6,8 @@ import java.net.NetworkInterface
object NetUtils {
fun getBaseUrl() = "http://${getLocalIPV4address()}.70:7070"
// fun getBaseUrl() = "http://${getLocalIPV4address()}.83:8765"
// fun getBaseUrl() = "http://${getLocalIPV4address()}.70:7070"
fun getBaseUrl() = "http://${getLocalIPV4address()}.52:8765"
/**

View File

@ -22,13 +22,14 @@ class OkHttpWebSocketConnection() : WebSocketListener(), WebSocketConnection {
private var client: WebSocket? = null
@Synchronized
override fun connect() {
override fun connect(sn: String) {
val okHttpClient = OkHttpClient.Builder()
.readTimeout(DEFAULT_SEND_TIMEOUT)
.pingInterval(PING_INTERVAL_TIME)
.build()
val request = Request.Builder().url(NetUtils.getBaseUrl()).build()
val request = Request.Builder().url(NetUtils.getBaseUrl())
.addHeader("sn", sn).build()
_webSocketConnectionStateFlow.update {
WebSocketConnectionState.CONNECTING
}
@ -55,6 +56,7 @@ class OkHttpWebSocketConnection() : WebSocketListener(), WebSocketConnection {
}
override fun onOpen(webSocket: WebSocket, response: Response) {
_webSocketConnectionStateFlow.update { WebSocketConnectionState.CONNECTED }
Timber.d("websocket onOpen")
}

View File

@ -1,6 +1,5 @@
package com.yuanxuan.rokid.network.websocket
import kotlinx.coroutines.flow.MutableStateFlow
import kotlin.time.Duration.Companion.seconds
interface WebSocketConnection {
@ -10,7 +9,7 @@ interface WebSocketConnection {
val PING_INTERVAL_TIME = 30.seconds
}
fun connect()
fun connect(sn: String)
fun isDead(): Boolean

View File

@ -29,8 +29,8 @@ class WebSocketManager(context: Context, scope: CoroutineScope) {
}
}
fun connect() {
webSocketConnection.connect()
fun connect(sn: String) {
webSocketConnection.connect(sn)
}

View File

@ -19,7 +19,7 @@ class WebSocketReconnectWorker(context: Context, workerParams: WorkerParameters)
override fun doWork(): Result {
Timber.d("尝试开始重连")
AppDependencies.deviceServiceManager.playTTS("开始重连")
AppDependencies.webSocketManager.connect()
AppDependencies.webSocketManager.connect(AppDependencies.deviceServiceManager.sn)
return Result.success()
}

View File

@ -10,7 +10,6 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.yuanxuan.rokid.R
import com.yuanxuan.rokid.databinding.FragmentSettingBinding
import com.yuanxuan.rokid.dependencies.AppDependencies
import kotlinx.coroutines.launch
class BrightnessFragment : Fragment() {
@ -37,7 +36,6 @@ class BrightnessFragment : Fragment() {
binding.tip.text = resources.getString(R.string.setting_brightness_tip)
binding.recyclerView.adapter = adapter
viewLifecycleOwner.lifecycleScope.launch {
viewModel.brightnessFlow.collect { data ->
adapter.submitList(data)
@ -49,12 +47,12 @@ class BrightnessFragment : Fragment() {
return@setOnKeyListener if (event.action == KeyEvent.ACTION_UP) {
when (event.keyCode) {
KeyEvent.KEYCODE_DPAD_RIGHT -> {
AppDependencies.deviceServiceManager.brightnessAdd()
viewModel.brightnessAdd()
true
}
KeyEvent.KEYCODE_DPAD_LEFT -> {
AppDependencies.deviceServiceManager.brightnessSubtract()
viewModel.brightnessSubtract()
true
}

View File

@ -43,7 +43,8 @@ class HomeFragment : Fragment() {
binding.recyclerView.adapter = adapter
binding.recyclerView.itemAnimator = null
lifecycleScope.launch {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.homeMenuBeans.collect {
adapter.submitList(it)
}

View File

@ -47,4 +47,22 @@ class SettingViewModel : ViewModel() {
replay = 1
)
fun brightnessAdd(){
AppDependencies.deviceServiceManager.brightnessAdd()
}
fun brightnessSubtract(){
AppDependencies.deviceServiceManager.brightnessSubtract()
}
fun volumeAdd(){
AppDependencies.deviceServiceManager.volumeAdd()
}
fun volumeSubtract(){
AppDependencies.deviceServiceManager.volumeSubtract()
}
fun deviceSn()= AppDependencies.deviceServiceManager.sn
}

View File

@ -5,14 +5,16 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.yuanxuan.rokid.BuildConfig
import com.yuanxuan.rokid.R
import com.yuanxuan.rokid.databinding.FragmentSnBinding
import com.yuanxuan.rokid.dependencies.AppDependencies
class SnFragment : Fragment() {
private lateinit var binding: FragmentSnBinding
private val viewModel by viewModels<SettingViewModel>()
override fun onCreateView(
inflater: LayoutInflater,
@ -26,7 +28,7 @@ class SnFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.sn.text =
resources.getString(R.string.device_info_sn, AppDependencies.deviceServiceManager.sn)
resources.getString(R.string.device_info_sn, viewModel.deviceSn())
binding.appVersion.text =
resources.getString(R.string.device_info_app_version, BuildConfig.VERSION_NAME)
}

View File

@ -10,7 +10,6 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.yuanxuan.rokid.R
import com.yuanxuan.rokid.databinding.FragmentSettingBinding
import com.yuanxuan.rokid.dependencies.AppDependencies
import kotlinx.coroutines.launch
class VolumeFragment : Fragment() {
@ -49,12 +48,12 @@ class VolumeFragment : Fragment() {
return@setOnKeyListener if (event.action == KeyEvent.ACTION_DOWN) {
when (event.keyCode) {
KeyEvent.KEYCODE_DPAD_RIGHT -> {
AppDependencies.deviceServiceManager.volumeAdd()
viewModel.volumeAdd()
true
}
KeyEvent.KEYCODE_DPAD_LEFT -> {
AppDependencies.deviceServiceManager.volumeSubtract()
viewModel.volumeSubtract()
true
}