feat: home

This commit is contained in:
yangxisong 2025-11-18 15:24:16 +08:00
parent e5d17bc970
commit 699bc11c25
27 changed files with 671 additions and 30 deletions

View File

@ -44,6 +44,7 @@ dependencies {
implementation(fileTree("libs") { include("*.aar", "*.jar") })
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.leanback)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
@ -52,7 +53,9 @@ dependencies {
implementation(libs.androidx.work.ktx)
implementation(libs.retrofit)
implementation(libs.retrofit.converter.gson)
// implementation(libs.gson)
implementation(libs.lottie)
implementation(libs.androidx.navigation.fragment)
implementation(libs.androidx.navigation.ui)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

View File

@ -1,6 +1,7 @@
package com.yuanxuan.rokid
import android.os.Bundle
import android.view.KeyEvent
import androidx.activity.addCallback
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
@ -10,6 +11,8 @@ 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
class MainActivity : AppCompatActivity() {
@ -29,6 +32,46 @@ class MainActivity : AppCompatActivity() {
insets
}
observer()
/**
* 拦截返回键事件防止返回到桌面
*/
onBackPressedDispatcher.addCallback {
if (AppDependencies.deviceServiceManager.instructState.value
== DeviceServiceManager.InstructState.WaitingInstructState
) {
AppDependencies.deviceServiceManager.quitInstructReceived()
}
}
}
/**
* 触摸板事件
* [KeyEvent.KEYCODE_DPAD_DOWN] [KeyEvent.KEYCODE_DPAD_RIGHT] 同时响应
* 所以直接消费掉一个
*/
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
return if (event.action == KeyEvent.ACTION_DOWN) {
when (event.keyCode) {
KeyEvent.KEYCODE_DPAD_DOWN -> {
true
}
KeyEvent.KEYCODE_DPAD_UP -> {
true
}
else -> super.dispatchKeyEvent(event)
}
} else {
super.dispatchKeyEvent(event)
}
}
private fun observer() {
lifecycleScope.launch {
/**
* 监听电量
@ -48,10 +91,20 @@ class MainActivity : AppCompatActivity() {
}
}
/**
* 拦截返回键事件防止返回到桌面
*/
onBackPressedDispatcher.addCallback {
lifecycleScope.launch {
AppDependencies.deviceServiceManager.instructState.collect {
when (it) {
DeviceServiceManager.InstructState.None -> {
binding.lottieVoiceInput.cancelAnimation()
binding.lottieVoiceInput.fadeOut(300)
}
DeviceServiceManager.InstructState.WaitingInstructState -> {
binding.lottieVoiceInput.playAnimation()
binding.lottieVoiceInput.fadeIn(300)
}
}
}
}
}
}

View File

@ -0,0 +1,27 @@
package com.yuanxuan.rokid
import androidx.activity.result.launch
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
class MainViewModel : ViewModel() {
private val _keyEventFlow = MutableSharedFlow<KeyEvent>()
val keyEventFlow = _keyEventFlow.asSharedFlow()
fun onKeyEventDispatched(event: KeyEvent) {
viewModelScope.launch {
_keyEventFlow.emit(event)
}
}
sealed interface KeyEvent {
data object DpadRight : KeyEvent //前滑
data object DpadLeft : KeyEvent //后滑
data object Enter : KeyEvent //前滑
}
}

View File

@ -67,6 +67,19 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo
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
@ -124,27 +137,19 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo
}
IInstructService.INSTRUCT_VOLUME_UP -> {
systemFuncServiceTodo { service ->
setVolumeSpecified(service.volumeSpecified + 1)
}
volumeAdd()
}
IInstructService.INSTRUCT_VOLUME_DOWN -> {
systemFuncServiceTodo { service ->
setVolumeSpecified(service.volumeSpecified - 1)
}
volumeSubtract()
}
IInstructService.INSTRUCT_LIGHT_UP -> {
systemFuncServiceTodo { service ->
setBrightnessSpecified(service.brightnessSpecified + 1)
}
brightnessAdd()
}
IInstructService.INSTRUCT_LIGHT_DOWN -> {
systemFuncServiceTodo { service ->
setBrightnessSpecified(service.brightnessSpecified - 1)
}
brightnessSubtract()
}
IInstructService.INSTRUCT_TAKE_PHOTO -> {
@ -185,12 +190,48 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo
}
}
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) {
@ -199,6 +240,8 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo
}
systemFuncService = service
sn = service.sn
_volume.update { service.volumeSpecified }
_brightness.update { service.brightnessSpecified }
continuation.resume(sn)
}
}
@ -248,14 +291,6 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo
}
}
private fun setVolumeSpecified(volume: Int) {
systemFuncService?.volumeSpecified = volume.coerceIn(0, 15)
}
private fun setBrightnessSpecified(brightness: Int) {
systemFuncService?.brightnessSpecified = brightness.coerceIn(0, 15)
}
inner class SysKeyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent?.action ?: return

View File

@ -0,0 +1,12 @@
package com.yuanxuan.rokid.extension
import android.content.res.Resources
import android.util.TypedValue
fun Number.dpToPx(): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this.toFloat(),
Resources.getSystem().displayMetrics
)
}

View File

@ -0,0 +1,37 @@
package com.yuanxuan.rokid.extension
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.view.View
import androidx.core.view.isGone
import androidx.core.view.isVisible
fun View.animateTranslationY(translationY: Float, duration: Int) {
animate()
.translationY(translationY)
.setDuration(250)
.start()
}
fun View.fadeIn(duration: Long) {
if (this.isVisible) return
this.alpha = 0f
this.isVisible = true
this.animate()
.alpha(1f)
.setDuration(duration)
.setListener(null)
}
fun View.fadeOut(duration: Long) {
if (this.isVisible.not()) return
this.animate()
.alpha(0f)
.setDuration(duration)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
this@fadeOut.isGone = true
}
})
}

View File

@ -1,4 +1,4 @@
package com.yuanxuan.rokid.network.http
package com.yuanxuan.rokid.network.bean
data class ApiResponse<T>(
val code: String,

View File

@ -1,5 +1,7 @@
package com.yuanxuan.rokid.network.http
import com.yuanxuan.rokid.network.bean.Test
class ApiRepository(private val apiService: ApiService) {
suspend fun test(): Test? {

View File

@ -1,6 +1,8 @@
package com.yuanxuan.rokid.network.http
import com.yuanxuan.rokid.network.NetUtils
import com.yuanxuan.rokid.network.bean.ApiResponse
import com.yuanxuan.rokid.network.bean.Test
import retrofit2.http.GET
import retrofit2.http.Url

View File

@ -2,11 +2,8 @@ package com.yuanxuan.rokid.network.websocket
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import java.util.Date
import kotlin.time.Duration.Companion.seconds
class WebSocketManager(context: Context, scope: CoroutineScope) {

View File

@ -0,0 +1,55 @@
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.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.yuanxuan.rokid.R
import com.yuanxuan.rokid.databinding.FragmentHomeBinding
import kotlinx.coroutines.launch
import timber.log.Timber
class HomeFragment : Fragment() {
private lateinit var binding: FragmentHomeBinding
private val viewModel by viewModels<HomeViewModel>()
private val adapter by lazy {
HomeMenuAdapter { item ->
when (item.type) {
is HomeMenuAdapter.HomeMenuBean.Type.Brightness -> {}
is HomeMenuAdapter.HomeMenuBean.Type.Sn ->
findNavController().navigate(R.id.home_to_sn)
is HomeMenuAdapter.HomeMenuBean.Type.Volume -> {}
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerView.adapter = adapter
binding.recyclerView.itemAnimator = null
lifecycleScope.launch {
viewModel.homeMenuBeans.collect {
Timber.d(it.toString())
adapter.submitList(it)
}
}
}
}

View File

@ -0,0 +1,123 @@
package com.yuanxuan.rokid.ui
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.yuanxuan.rokid.databinding.ItemHomeBinding
class HomeMenuAdapter(val itemClick: (HomeMenuBean) -> Unit) :
ListAdapter<HomeMenuAdapter.HomeMenuBean, HomeMenuAdapter.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
val binding = ItemHomeBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(
binding = binding,
itemClick = itemClick,
)
}
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
val item = getItem(position)
bindTitle(
holder = holder,
item = item,
)
holder.bindClick(item)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: List<Any?>) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
payloads.forEach {
val payload = it as Int
if ((DiffCallback.FLAG_TYPE_VALUE_CHANGED and payload) != 0) {
bindTitle(holder, getItem(position))
}
}
}
}
private fun bindTitle(holder: ViewHolder, item: HomeMenuBean) {
val displayTitle = when (val type = item.type) {
is HomeMenuBean.Type.Sn -> item.title
is HomeMenuBean.Type.Brightness,
is HomeMenuBean.Type.Volume -> "${item.title}: ${type.value}"
}
holder.binding.title.text = displayTitle
}
class ViewHolder(val binding: ItemHomeBinding, val itemClick: (HomeMenuBean) -> Unit) :
RecyclerView.ViewHolder(binding.root) {
fun bindClick(item: HomeMenuBean) {
binding.parent.setOnClickListener {
itemClick(item)
}
}
}
data class HomeMenuBean(
val title: String,
@param:DrawableRes val icon: Int,
val type: Type,
) {
sealed interface Type {
val value: String
data class Brightness(override val value: String) : Type
data class Volume(override val value: String) : Type
data class Sn(override val value: String) : Type
}
}
class DiffCallback : DiffUtil.ItemCallback<HomeMenuBean>() {
companion object {
const val FLAG_TYPE_VALUE_CHANGED = 1
}
override fun areItemsTheSame(
oldItem: HomeMenuBean,
newItem: HomeMenuBean
): Boolean {
return oldItem.title == newItem.title
}
override fun areContentsTheSame(
oldItem: HomeMenuBean,
newItem: HomeMenuBean
): Boolean {
return oldItem == newItem
}
override fun getChangePayload(oldItem: HomeMenuBean, newItem: HomeMenuBean): Any? {
var payload = 0
val oldType = oldItem.type
val newType = newItem.type
// 不会出现类型变化 只有value 变更
when (oldType) {
is HomeMenuBean.Type.Brightness,
is HomeMenuBean.Type.Volume -> oldType.value == newType.value
is HomeMenuBean.Type.Sn -> true
}.let {
if (it.not()) {
payload = 0 or FLAG_TYPE_VALUE_CHANGED
}
}
return if (payload == 0) null else payload
}
}
}

View File

@ -0,0 +1,72 @@
package com.yuanxuan.rokid.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.yuanxuan.rokid.R
import com.yuanxuan.rokid.dependencies.AppDependencies
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class HomeViewModel : ViewModel() {
private val _homeMenuBeans = MutableStateFlow(
listOf(
HomeMenuAdapter.HomeMenuBean(
title = "音量",
icon = R.drawable.icon_battery_100,
type = HomeMenuAdapter.HomeMenuBean.Type.Volume("0"),
),
HomeMenuAdapter.HomeMenuBean(
title = "亮度",
icon = R.drawable.icon_battery_100,
type = HomeMenuAdapter.HomeMenuBean.Type.Brightness("0"),
),
HomeMenuAdapter.HomeMenuBean(
title = "SN",
icon = R.drawable.icon_battery_100,
type = HomeMenuAdapter.HomeMenuBean.Type.Sn("0"),
),
)
)
val homeMenuBeans = _homeMenuBeans.asStateFlow()
init {
viewModelScope.launch {
AppDependencies.deviceServiceManager.brightness.collect { brightness ->
_homeMenuBeans.update { oldBeans ->
oldBeans.map { data ->
when (data.type) {
is HomeMenuAdapter.HomeMenuBean.Type.Brightness -> data.copy(
type = data.type.copy(
value = "$brightness"
)
)
else -> data
}
}
}
}
}
viewModelScope.launch {
AppDependencies.deviceServiceManager.volume.collect { volume ->
_homeMenuBeans.update { oldBeans ->
oldBeans.map { data ->
when (data.type) {
is HomeMenuAdapter.HomeMenuBean.Type.Volume -> data.copy(
type = data.type.copy(
value = "$volume"
)
)
else -> data
}
}
}
}
}
}
}

View File

@ -0,0 +1,6 @@
package com.yuanxuan.rokid.ui
import androidx.fragment.app.Fragment
class SettingFragment: Fragment() {
}

View File

@ -0,0 +1,39 @@
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 com.yuanxuan.rokid.databinding.FragmentSnBinding
import com.yuanxuan.rokid.dependencies.AppDependencies
import timber.log.Timber
class SnFragment : Fragment() {
private lateinit var binding: FragmentSnBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentSnBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
try {
binding.sn.text = "SN:${AppDependencies.deviceServiceManager.sn}"
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onDestroy() {
super.onDestroy()
Timber.d("onDestroy")
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true">
<set>
<objectAnimator
android:duration="200"
android:propertyName="translationY"
android:valueTo="-10dp"
android:valueType="floatType" />
</set>
</item>
<item android:state_focused="false">
<set>
<objectAnimator
android:duration="200"
android:propertyName="translationY"
android:valueTo="0dp"
android:valueType="floatType" />
</set>
</item>
</selector>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="10.dp" />
<stroke
android:width="1.dp"
android:color="@color/white" />
</shape>

View File

@ -7,6 +7,14 @@
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/app_navigation" />
<TextView
android:id="@+id/battery_level"
android:layout_width="wrap_content"
@ -47,6 +55,18 @@
app:layout_constraintBottom_toTopOf="@id/bottom"
app:layout_constraintStart_toStartOf="parent" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_voice_input"
android:layout_width="200.dp"
android:layout_height="100.dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/bottom"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:lottie_loop="true"
app:lottie_rawRes="@raw/lottie_input_voice"
tools:visibility="visible" />
<Space
android:id="@+id/bottom"
android:layout_width="match_parent"

View File

@ -0,0 +1,19 @@
<?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">
<androidx.leanback.widget.HorizontalGridView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="85.dp"
android:clipToPadding="false"
android:paddingStart="10.dp"
android:paddingTop="10.dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/item_home" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,13 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="setting"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,34 @@
<?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:focusable="true"
android:layout_height="match_parent">
<Button
android:id="@+id/sn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusedByDefault="true"
android:nextFocusLeft="@id/sn2"
android:stateListAnimator="@animator/focus_animator"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="askjdhkjashdkjhsad" />
<Button
android:id="@+id/sn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stateListAnimator="@animator/focus_animator"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@id/sn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="askjdhkjashdkjhsad" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,25 @@
<?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:id="@+id/parent"
android:layout_width="50.dp"
android:layout_height="75.dp"
android:layout_marginEnd="10.dp"
android:background="@drawable/bg_home_menu"
android:focusable="true"
android:stateListAnimator="@animator/focus_animator">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="10.sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/home_menu_setting" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/app_navigation"
app:startDestination="@id/home_fragment">
<fragment
android:id="@+id/home_fragment"
android:name="com.yuanxuan.rokid.ui.HomeFragment"
android:label="home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/home_to_sn"
app:destination="@id/sn_fragment" />
</fragment>
<fragment
android:id="@+id/setting_fragment"
android:name="com.yuanxuan.rokid.ui.SettingFragment"
android:label="setting"
tools:layout="@layout/fragment_setting" />
<fragment
android:id="@+id/sn_fragment"
android:name="com.yuanxuan.rokid.ui.SnFragment"
android:label="sn"
tools:layout="@layout/fragment_sn" />
</navigation>

Binary file not shown.

View File

@ -3,5 +3,6 @@
<style name="Base.Theme.Rokid" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
<item name="android:windowBackground">@android:color/transparent</item>
</style>
</resources>

View File

@ -1,3 +1,4 @@
<resources>
<string name="app_name">Rokid</string>
<string name="home_menu_setting">设置</string>
</resources>

View File

@ -14,6 +14,9 @@ okhttp = "5.3.0"
worker = "2.11.0"
retrofit = "3.0.0"
gson = "2.13.2"
lottie = "6.6.6"
navigation = "2.9.6"
leanback = "1.2.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -31,7 +34,10 @@ androidx-work-ktx = { group = "androidx.work", name = "work-runtime-ktx", versio
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
retrofit-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
lottie = { group = "com.airbnb.android", name = "lottie", version.ref = "lottie" }
androidx-navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigation" }
androidx-navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigation" }
androidx-leanback = { group = "androidx.leanback", name = "leanback", version.ref = "leanback" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }