feat: 服务消息UI交互
This commit is contained in:
parent
84961a367c
commit
9c29fee24e
|
|
@ -1,5 +1,6 @@
|
|||
package com.yuanxuan.rokid
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import androidx.activity.addCallback
|
||||
|
|
@ -11,9 +12,15 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import com.yuanxuan.rokid.databinding.ActivityMainBinding
|
||||
import com.yuanxuan.rokid.extension.fadeIn
|
||||
import com.yuanxuan.rokid.extension.fadeOut
|
||||
import com.yuanxuan.rokid.keeplive.KeepLiveService
|
||||
import com.yuanxuan.rokid.network.websocket.WebSocketEvent
|
||||
import com.yuanxuan.rokid.ui.NoticeFragment
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
|
@ -39,8 +46,33 @@ class MainActivity : AppCompatActivity() {
|
|||
/**
|
||||
* 拦截返回键事件,防止返回到桌面
|
||||
*/
|
||||
onBackPressedDispatcher.addCallback {}
|
||||
onBackPressedDispatcher.addCallback {
|
||||
viewModel.wakeUp()
|
||||
navigationToast("TestMessage")
|
||||
}
|
||||
/**
|
||||
* 这个服务保证心跳包准时发
|
||||
*/
|
||||
val serviceIntent = Intent(this, KeepLiveService::class.java)
|
||||
startForegroundService(serviceIntent)
|
||||
|
||||
/**
|
||||
* 出路服务端事件
|
||||
*/
|
||||
lifecycleScope.launch {
|
||||
viewModel.socketEvent.collect { event ->
|
||||
when (event) {
|
||||
is WebSocketEvent.Notice -> {
|
||||
viewModel.wakeUp()
|
||||
delay(500)
|
||||
navigationToast(event.msg)
|
||||
}
|
||||
|
||||
WebSocketEvent.Unknow -> {}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -55,6 +87,23 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun navigationToast(toast: String) {
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
|
||||
|
||||
val options = NavOptions.Builder()
|
||||
.setEnterAnim(android.R.anim.fade_in)
|
||||
.setExitAnim(android.R.anim.fade_out)
|
||||
.setPopEnterAnim(android.R.anim.fade_in)
|
||||
.setPopExitAnim(android.R.anim.fade_out)
|
||||
.build()
|
||||
val bundle = Bundle().apply {
|
||||
putString(NoticeFragment.TOAST_MESSAGE, toast)
|
||||
}
|
||||
val navController = navHostFragment.navController
|
||||
navController.navigate(R.id.notice_fragment, bundle, options)
|
||||
}
|
||||
|
||||
private fun observer() {
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.yuanxuan.rokid
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.yuanxuan.rokid.dependencies.AppDependencies
|
||||
import com.yuanxuan.rokid.dependencies.AppDependencies.deviceServiceManager
|
||||
import com.yuanxuan.rokid.device.DeviceServiceManager
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
|
@ -37,7 +38,9 @@ class MainViewModel : ViewModel() {
|
|||
started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds),
|
||||
)
|
||||
|
||||
fun quitInstructReceived(){
|
||||
val socketEvent = AppDependencies.webSocketManager.socketEventFlow
|
||||
|
||||
fun quitInstructReceived() {
|
||||
deviceServiceManager.quitInstructReceived()
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +50,14 @@ class MainViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
fun wakeUp() {
|
||||
deviceServiceManager.wakeup()
|
||||
}
|
||||
|
||||
fun playTTS(tts: String) {
|
||||
deviceServiceManager.playTTS(tts)
|
||||
}
|
||||
|
||||
sealed interface KeyEvent {
|
||||
data object DpadRight : KeyEvent //前滑
|
||||
data object DpadLeft : KeyEvent //后滑
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
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 com.yuanxuan.rokid.network.websocket.WebSocketReconnectWorker
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -33,11 +31,6 @@ class RokidApplication : Application() {
|
|||
Timber.e(it)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 这个服务保证心跳包准时发
|
||||
*/
|
||||
val serviceIntent = Intent(this, KeepLiveService::class.java)
|
||||
startForegroundService(serviceIntent)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -196,6 +196,13 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo
|
|||
_instructState.update { InstructState.None }
|
||||
}
|
||||
|
||||
|
||||
fun wakeup() {
|
||||
systemFuncServiceTodo { service ->
|
||||
service.wakeUp()
|
||||
}
|
||||
}
|
||||
|
||||
fun playTTS(msg: String) {
|
||||
ttsServiceTodo {
|
||||
ttsService?.playTtsMsg(msg)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import java.net.NetworkInterface
|
|||
|
||||
object NetUtils {
|
||||
|
||||
// fun getBaseUrl() = "http://${getLocalIPV4address()}.70:7070"
|
||||
fun getBaseUrl() = "http://${getLocalIPV4address()}.52:8765"
|
||||
fun getBaseUrl() = "http://${getLocalIPV4address()}.70:7070"
|
||||
// fun getBaseUrl() = "http://${getLocalIPV4address()}.52:8765"
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.yuanxuan.rokid.network.bean
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class WebSocketResponse(
|
||||
@SerializedName("MsgType")
|
||||
val msgType: Int,
|
||||
@SerializedName("Msg")
|
||||
val msg: JsonElement
|
||||
) {
|
||||
|
||||
enum class MsgType(val value: Int) {
|
||||
Notice(0);
|
||||
|
||||
companion object {
|
||||
fun fromValue(value: Int) = entries.firstOrNull { it.value == value }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +1,17 @@
|
|||
package com.yuanxuan.rokid.network.websocket
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.yuanxuan.rokid.network.NetUtils
|
||||
import com.yuanxuan.rokid.network.bean.WebSocketResponse
|
||||
import com.yuanxuan.rokid.network.websocket.WebSocketConnection.Companion.DEFAULT_SEND_TIMEOUT
|
||||
import com.yuanxuan.rokid.network.websocket.WebSocketConnection.Companion.PING_INTERVAL_TIME
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
|
@ -13,12 +19,16 @@ import okhttp3.WebSocket
|
|||
import okhttp3.WebSocketListener
|
||||
import timber.log.Timber
|
||||
|
||||
class OkHttpWebSocketConnection() : WebSocketListener(), WebSocketConnection {
|
||||
class OkHttpWebSocketConnection(val scope: CoroutineScope) : WebSocketListener(),
|
||||
WebSocketConnection {
|
||||
|
||||
private val _webSocketConnectionStateFlow: MutableStateFlow<WebSocketConnectionState> =
|
||||
MutableStateFlow(WebSocketConnectionState.DISCONNECTED)
|
||||
val webSocketConnectionStateFlow = _webSocketConnectionStateFlow.asStateFlow()
|
||||
|
||||
private val _eventFlow = MutableSharedFlow<WebSocketEvent>()
|
||||
val eventFlow = _eventFlow.asSharedFlow()
|
||||
|
||||
private var client: WebSocket? = null
|
||||
|
||||
@Synchronized
|
||||
|
|
@ -38,6 +48,20 @@ class OkHttpWebSocketConnection() : WebSocketListener(), WebSocketConnection {
|
|||
|
||||
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||
Timber.d(text)
|
||||
val response = Gson().fromJson(text, WebSocketResponse::class.java)
|
||||
val msgType = WebSocketResponse.MsgType.fromValue(response.msgType)
|
||||
val event = when (msgType) {
|
||||
WebSocketResponse.MsgType.Notice -> {
|
||||
WebSocketEvent.Notice(response.msg.asString)
|
||||
}
|
||||
|
||||
null -> {
|
||||
WebSocketEvent.Unknow
|
||||
}
|
||||
}
|
||||
scope.launch {
|
||||
_eventFlow.emit(event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOpen(webSocket: WebSocket, response: Response) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package com.yuanxuan.rokid.network.websocket
|
||||
|
||||
sealed interface WebSocketEvent {
|
||||
data class Notice(val msg: String) : WebSocketEvent
|
||||
|
||||
data object Unknow : WebSocketEvent
|
||||
}
|
||||
|
|
@ -7,7 +7,9 @@ import kotlinx.coroutines.launch
|
|||
|
||||
class WebSocketManager(context: Context, scope: CoroutineScope) {
|
||||
|
||||
private val webSocketConnection = OkHttpWebSocketConnection()
|
||||
private val webSocketConnection = OkHttpWebSocketConnection(scope = scope)
|
||||
|
||||
val socketEventFlow = webSocketConnection.eventFlow
|
||||
|
||||
init {
|
||||
scope.launch {
|
||||
|
|
|
|||
|
|
@ -2,23 +2,27 @@ package com.yuanxuan.rokid.network.websocket
|
|||
|
||||
import android.content.Context
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.OutOfQuotaPolicy
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.yuanxuan.rokid.dependencies.AppDependencies
|
||||
import kotlinx.coroutines.delay
|
||||
import timber.log.Timber
|
||||
|
||||
class WebSocketReconnectWorker(context: Context, workerParams: WorkerParameters) : Worker(
|
||||
class WebSocketReconnectWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(
|
||||
context,
|
||||
workerParams
|
||||
) {
|
||||
override fun doWork(): Result {
|
||||
override suspend fun doWork(): Result {
|
||||
/**
|
||||
* 第一次启动给点时间连wifi、重连等5s
|
||||
*/
|
||||
delay(5000)
|
||||
Timber.d("尝试开始重连")
|
||||
AppDependencies.deviceServiceManager.playTTS("开始重连")
|
||||
AppDependencies.webSocketManager.connect(AppDependencies.deviceServiceManager.sn)
|
||||
return Result.success()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
package com.yuanxuan.rokid.network.websocket
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class WebSocketResponseMessage<T>(
|
||||
@SerializedName("MsgType")
|
||||
val msgType: Int,
|
||||
@SerializedName("Msg")
|
||||
val msg: T
|
||||
)
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.yuanxuan.rokid.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.yuanxuan.rokid.databinding.FragmentNoticeBinding
|
||||
|
||||
class NoticeFragment : Fragment() {
|
||||
|
||||
companion object {
|
||||
const val TOAST_MESSAGE = "toast_message"
|
||||
}
|
||||
|
||||
private lateinit var binding: FragmentNoticeBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentNoticeBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val toast = arguments?.getString(TOAST_MESSAGE)
|
||||
binding.toast.text = toast
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/toast"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="30.dp"
|
||||
android:paddingEnd="30.dp"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据测试数据" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ffffff"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
|||
|
|
@ -39,4 +39,10 @@
|
|||
android:label="sn"
|
||||
tools:layout="@layout/fragment_sn" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/notice_fragment"
|
||||
android:name="com.yuanxuan.rokid.ui.NoticeFragment"
|
||||
android:label="notice"
|
||||
tools:layout="@layout/fragment_notice" />
|
||||
|
||||
</navigation>
|
||||
Loading…
Reference in New Issue