diff --git a/app/src/main/java/com/yuanxuan/rokid/MainActivity.kt b/app/src/main/java/com/yuanxuan/rokid/MainActivity.kt index 1044ec4..ba1f598 100644 --- a/app/src/main/java/com/yuanxuan/rokid/MainActivity.kt +++ b/app/src/main/java/com/yuanxuan/rokid/MainActivity.kt @@ -1,9 +1,9 @@ package com.yuanxuan.rokid 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 @@ -38,38 +38,44 @@ class MainActivity : AppCompatActivity() { * 拦截返回键事件,防止返回到桌面 */ 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) - } - } +// /** +// * 触摸板事件 +// * [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 +// } +// +// KeyEvent.KEYCODE_DPAD_LEFT -> { +// viewModel.onKeyEventDispatched(MainViewModel.KeyEvent.DpadLeft) +// true +// } +// +// KeyEvent.KEYCODE_DPAD_RIGHT -> { +// viewModel.onKeyEventDispatched(MainViewModel.KeyEvent.DpadRight) +// true +// } +// +// else -> super.dispatchKeyEvent(event) +// } +// } else { +// super.dispatchKeyEvent(event) +// } +// } private fun observer() { lifecycleScope.launch { diff --git a/app/src/main/java/com/yuanxuan/rokid/device/DeviceServiceManager.kt b/app/src/main/java/com/yuanxuan/rokid/device/DeviceServiceManager.kt index fbee719..085d8fd 100644 --- a/app/src/main/java/com/yuanxuan/rokid/device/DeviceServiceManager.kt +++ b/app/src/main/java/com/yuanxuan/rokid/device/DeviceServiceManager.kt @@ -42,6 +42,8 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo * 语音指令等待超时 */ private const val INSTRUCT_WAITE_TIME = 15000L + const val MAX_VOLUME = 15 + const val MAX_BRIGHTNESS = 15 } private var instructService: IInstructService? = null diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/BrightnessFragment.kt b/app/src/main/java/com/yuanxuan/rokid/ui/BrightnessFragment.kt new file mode 100644 index 0000000..948aedb --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/ui/BrightnessFragment.kt @@ -0,0 +1,70 @@ +package com.yuanxuan.rokid.ui + +import android.os.Bundle +import android.view.KeyEvent +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 com.yuanxuan.rokid.R +import com.yuanxuan.rokid.databinding.FragmentSettingBinding +import com.yuanxuan.rokid.dependencies.AppDependencies +import kotlinx.coroutines.launch + +class BrightnessFragment : Fragment() { + + private lateinit var binding: FragmentSettingBinding + private val viewModel by viewModels() + + private val adapter by lazy { + SeekBarAdapter() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentSettingBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.title.text = resources.getString(R.string.setting_brightness_title) + binding.tip.text = resources.getString(R.string.setting_brightness_tip) + + binding.recyclerView.adapter = adapter + + viewLifecycleOwner.lifecycleScope.launch { + viewModel.brightnessFlow.collect { data -> + adapter.submitList(data) + } + } + + binding.root.requestFocus() + binding.root.setOnKeyListener { _, _, event -> + return@setOnKeyListener if (event.action == KeyEvent.ACTION_UP) { + when (event.keyCode) { + KeyEvent.KEYCODE_DPAD_RIGHT -> { + AppDependencies.deviceServiceManager.brightnessAdd() + true + } + + KeyEvent.KEYCODE_DPAD_LEFT -> { + AppDependencies.deviceServiceManager.brightnessSubtract() + true + } + + else -> { + false + } + } + } else { + false + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/HomeFragment.kt b/app/src/main/java/com/yuanxuan/rokid/ui/HomeFragment.kt index 671a88f..144c12a 100644 --- a/app/src/main/java/com/yuanxuan/rokid/ui/HomeFragment.kt +++ b/app/src/main/java/com/yuanxuan/rokid/ui/HomeFragment.kt @@ -11,7 +11,6 @@ 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() { @@ -21,10 +20,9 @@ class HomeFragment : Fragment() { 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 -> {} + is HomeMenuAdapter.HomeMenuBean.Type.Brightness -> findNavController().navigate(R.id.home_to_brightness) + is HomeMenuAdapter.HomeMenuBean.Type.Sn -> findNavController().navigate(R.id.home_to_sn) + is HomeMenuAdapter.HomeMenuBean.Type.Volume -> findNavController().navigate(R.id.home_to_volume) } } } @@ -47,7 +45,6 @@ class HomeFragment : Fragment() { lifecycleScope.launch { viewModel.homeMenuBeans.collect { - Timber.d(it.toString()) adapter.submitList(it) } } diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/HomeMenuAdapter.kt b/app/src/main/java/com/yuanxuan/rokid/ui/HomeMenuAdapter.kt index 42c3518..7305450 100644 --- a/app/src/main/java/com/yuanxuan/rokid/ui/HomeMenuAdapter.kt +++ b/app/src/main/java/com/yuanxuan/rokid/ui/HomeMenuAdapter.kt @@ -3,6 +3,7 @@ package com.yuanxuan.rokid.ui import android.view.LayoutInflater import android.view.ViewGroup import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView @@ -49,10 +50,11 @@ class HomeMenuAdapter(val itemClick: (HomeMenuBean) -> Unit) : } private fun bindTitle(holder: ViewHolder, item: HomeMenuBean) { + val title = holder.binding.root.context.getString(item.title) val displayTitle = when (val type = item.type) { - is HomeMenuBean.Type.Sn -> item.title + is HomeMenuBean.Type.Sn -> title is HomeMenuBean.Type.Brightness, - is HomeMenuBean.Type.Volume -> "${item.title}: ${type.value}" + is HomeMenuBean.Type.Volume -> "${title}: ${type.value}" } holder.binding.title.text = displayTitle } @@ -68,7 +70,7 @@ class HomeMenuAdapter(val itemClick: (HomeMenuBean) -> Unit) : } data class HomeMenuBean( - val title: String, + @param:StringRes val title: Int, @param:DrawableRes val icon: Int, val type: Type, ) { @@ -83,6 +85,9 @@ class HomeMenuAdapter(val itemClick: (HomeMenuBean) -> Unit) : class DiffCallback : DiffUtil.ItemCallback() { + /** + * 其实没有必要 因为只有一个变化 直接返回一个payload就行了 不需要计算 + */ companion object { const val FLAG_TYPE_VALUE_CHANGED = 1 } diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/HomeViewModel.kt b/app/src/main/java/com/yuanxuan/rokid/ui/HomeViewModel.kt index 7e02fb5..14b9736 100644 --- a/app/src/main/java/com/yuanxuan/rokid/ui/HomeViewModel.kt +++ b/app/src/main/java/com/yuanxuan/rokid/ui/HomeViewModel.kt @@ -4,69 +4,36 @@ 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 +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.shareIn 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 = + combine( + AppDependencies.deviceServiceManager.brightness, + AppDependencies.deviceServiceManager.volume, + ) { brightness, volume -> + listOf( + HomeMenuAdapter.HomeMenuBean( + title = R.string.home_item_volume, + icon = R.drawable.icon_battery_100, + type = HomeMenuAdapter.HomeMenuBean.Type.Volume("$volume"), + ), + HomeMenuAdapter.HomeMenuBean( + title = R.string.home_item_brightness, + icon = R.drawable.icon_battery_100, + type = HomeMenuAdapter.HomeMenuBean.Type.Brightness("$brightness"), + ), + HomeMenuAdapter.HomeMenuBean( + title = R.string.home_item_sn, + icon = R.drawable.icon_battery_100, + type = HomeMenuAdapter.HomeMenuBean.Type.Sn("0"), // 这里用不上 随便填一个 + ), + ) + }.shareIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + replay = 1 ) - ) - 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 - } - } - } - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/SeekBarAdapter.kt b/app/src/main/java/com/yuanxuan/rokid/ui/SeekBarAdapter.kt new file mode 100644 index 0000000..6fa6032 --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/ui/SeekBarAdapter.kt @@ -0,0 +1,67 @@ +package com.yuanxuan.rokid.ui + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.yuanxuan.rokid.databinding.ItemSeekBarBinding + +class SeekBarAdapter : + ListAdapter(DiffCallback()) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ViewHolder { + val binding = ItemSeekBarBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return ViewHolder(binding) + } + + override fun onBindViewHolder( + holder: ViewHolder, + position: Int + ) { + bindSeekBar(item = getItem(position), holder = holder) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: List) { + if (payloads.isEmpty()) { + super.onBindViewHolder(holder, position, payloads) + } else { + bindSeekBar(item = getItem(position), holder = holder) + } + } + + private fun bindSeekBar(item: SeekBarBean, holder: ViewHolder) { + holder.binding.root.isEnabled = item.checked + } + + + class ViewHolder(val binding: ItemSeekBarBinding) : RecyclerView.ViewHolder(binding.root) + + + data class SeekBarBean( + val value: Int, + val checked: Boolean, + ) + + class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: SeekBarBean, + newItem: SeekBarBean + ): Boolean { + return oldItem.value == newItem.value + } + + override fun areContentsTheSame( + oldItem: SeekBarBean, + newItem: SeekBarBean + ): Boolean { + return oldItem == newItem + } + + override fun getChangePayload(oldItem: SeekBarBean, newItem: SeekBarBean): Any { + return true + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/SettingFragment.kt b/app/src/main/java/com/yuanxuan/rokid/ui/SettingFragment.kt deleted file mode 100644 index 9082b58..0000000 --- a/app/src/main/java/com/yuanxuan/rokid/ui/SettingFragment.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.yuanxuan.rokid.ui - -import androidx.fragment.app.Fragment - -class SettingFragment: Fragment() { -} \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/SettingViewModel.kt b/app/src/main/java/com/yuanxuan/rokid/ui/SettingViewModel.kt new file mode 100644 index 0000000..9544241 --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/ui/SettingViewModel.kt @@ -0,0 +1,50 @@ +package com.yuanxuan.rokid.ui + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.yuanxuan.rokid.dependencies.AppDependencies +import com.yuanxuan.rokid.device.DeviceServiceManager +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.shareIn + +class SettingViewModel : ViewModel() { + + val volumeFlow: SharedFlow> = + AppDependencies.deviceServiceManager.volume.map { volume -> + buildList { + (1..DeviceServiceManager.MAX_VOLUME).map { i -> + add( + SeekBarAdapter.SeekBarBean( + value = i, + checked = i <= volume + ) + ) + } + } + }.shareIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + replay = 1 + ) + + val brightnessFlow: SharedFlow> = + AppDependencies.deviceServiceManager.brightness.map { volume -> + buildList { + (1..DeviceServiceManager.MAX_BRIGHTNESS).map { i -> + add( + SeekBarAdapter.SeekBarBean( + value = i, + checked = i <= volume + ) + ) + } + } + }.shareIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + replay = 1 + ) + +} \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/VolumeFragment.kt b/app/src/main/java/com/yuanxuan/rokid/ui/VolumeFragment.kt new file mode 100644 index 0000000..da9f64d --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/ui/VolumeFragment.kt @@ -0,0 +1,70 @@ +package com.yuanxuan.rokid.ui + +import android.os.Bundle +import android.view.KeyEvent +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 com.yuanxuan.rokid.R +import com.yuanxuan.rokid.databinding.FragmentSettingBinding +import com.yuanxuan.rokid.dependencies.AppDependencies +import kotlinx.coroutines.launch + +class VolumeFragment : Fragment() { + + private lateinit var binding: FragmentSettingBinding + private val viewModel by viewModels() + + private val adapter by lazy { + SeekBarAdapter() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentSettingBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.title.text = resources.getString(R.string.setting_volume_title) + binding.tip.text = resources.getString(R.string.setting_volume_tip) + + binding.recyclerView.adapter = adapter + + viewLifecycleOwner.lifecycleScope.launch { + viewModel.volumeFlow.collect { data -> + adapter.submitList(data) + } + } + + binding.root.requestFocus() + binding.root.setOnKeyListener { _, _, event -> + return@setOnKeyListener if (event.action == KeyEvent.ACTION_DOWN) { + when (event.keyCode) { + KeyEvent.KEYCODE_DPAD_RIGHT -> { + AppDependencies.deviceServiceManager.volumeAdd() + true + } + + KeyEvent.KEYCODE_DPAD_LEFT -> { + AppDependencies.deviceServiceManager.volumeSubtract() + true + } + + else -> { + false + } + } + } else { + false + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_home_menu.xml b/app/src/main/res/drawable/bg_home_menu.xml index 90a8d8a..5f1e3bf 100644 --- a/app/src/main/res/drawable/bg_home_menu.xml +++ b/app/src/main/res/drawable/bg_home_menu.xml @@ -1,7 +1,7 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_seek_bar.xml b/app/src/main/res/drawable/bg_seek_bar.xml new file mode 100644 index 0000000..35a6ef5 --- /dev/null +++ b/app/src/main/res/drawable/bg_seek_bar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3c4d3b4..6fb9f21 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -29,7 +29,7 @@ android:id="@+id/wifi_iv" android:layout_width="@dimen/status_bar_icon_size" android:layout_height="@dimen/status_bar_icon_size" - android:layout_marginEnd="5.dp" + android:layout_marginEnd="5dp" android:src="@drawable/wifi_level_list" android:tint="@color/white" app:layout_constraintBottom_toBottomOf="@id/battery_level" @@ -40,7 +40,7 @@ android:id="@+id/battery_level_iv" android:layout_width="@dimen/status_bar_icon_size" android:layout_height="@dimen/status_bar_icon_size" - android:layout_marginEnd="5.dp" + android:layout_marginEnd="5dp" android:src="@drawable/battery_level_list" android:tint="@color/white" app:layout_constraintBottom_toBottomOf="@id/battery_level" @@ -57,8 +57,8 @@ \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 5556166..f766b9e 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -8,12 +8,13 @@ \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_setting.xml b/app/src/main/res/layout/fragment_setting.xml index bdb24a7..3089b30 100644 --- a/app/src/main/res/layout/fragment_setting.xml +++ b/app/src/main/res/layout/fragment_setting.xml @@ -1,13 +1,47 @@ + android:layout_height="match_parent" + android:focusable="true" + android:descendantFocusability="blocksDescendants"> + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + tools:text="@string/setting_volume_tip" /> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_sn.xml b/app/src/main/res/layout/fragment_sn.xml index 60a7030..c8432f5 100644 --- a/app/src/main/res/layout/fragment_sn.xml +++ b/app/src/main/res/layout/fragment_sn.xml @@ -3,32 +3,18 @@ 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"> -