feat: home
This commit is contained in:
parent
e5d17bc970
commit
699bc11c25
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 //前滑
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.yuanxuan.rokid.network.http
|
||||
package com.yuanxuan.rokid.network.bean
|
||||
|
||||
data class ApiResponse<T>(
|
||||
val code: String,
|
||||
|
|
@ -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? {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.yuanxuan.rokid.ui
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
class SettingFragment: Fragment() {
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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.
|
|
@ -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>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
<resources>
|
||||
<string name="app_name">Rokid</string>
|
||||
<string name="home_menu_setting">设置</string>
|
||||
</resources>
|
||||
|
|
@ -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" }
|
||||
|
|
|
|||
Loading…
Reference in New Issue