diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e5f50af..2062a41 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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) diff --git a/app/src/main/java/com/yuanxuan/rokid/MainActivity.kt b/app/src/main/java/com/yuanxuan/rokid/MainActivity.kt index 436b409..1044ec4 100644 --- a/app/src/main/java/com/yuanxuan/rokid/MainActivity.kt +++ b/app/src/main/java/com/yuanxuan/rokid/MainActivity.kt @@ -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) + } + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/MainViewModel.kt b/app/src/main/java/com/yuanxuan/rokid/MainViewModel.kt new file mode 100644 index 0000000..e0467e6 --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/MainViewModel.kt @@ -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() + 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 //前滑 + } + +} \ No newline at end of file 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 40bb5c6..fbee719 100644 --- a/app/src/main/java/com/yuanxuan/rokid/device/DeviceServiceManager.kt +++ b/app/src/main/java/com/yuanxuan/rokid/device/DeviceServiceManager.kt @@ -67,6 +67,19 @@ class DeviceServiceManager(val context: Application) : ConnectivityManager.Netwo context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager } + /** + * 音量 + */ + private val _volume: MutableStateFlow = MutableStateFlow(0) + val volume = _volume.asStateFlow() + + + /** + * 亮度 + */ + private val _brightness: MutableStateFlow = 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 diff --git a/app/src/main/java/com/yuanxuan/rokid/extension/DensityExtensions.kt b/app/src/main/java/com/yuanxuan/rokid/extension/DensityExtensions.kt new file mode 100644 index 0000000..ac7f0cf --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/extension/DensityExtensions.kt @@ -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 + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/extension/ViewExtensions.kt b/app/src/main/java/com/yuanxuan/rokid/extension/ViewExtensions.kt new file mode 100644 index 0000000..a37a1da --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/extension/ViewExtensions.kt @@ -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 + } + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/network/http/ApiResponse.kt b/app/src/main/java/com/yuanxuan/rokid/network/bean/ApiResponse.kt similarity index 82% rename from app/src/main/java/com/yuanxuan/rokid/network/http/ApiResponse.kt rename to app/src/main/java/com/yuanxuan/rokid/network/bean/ApiResponse.kt index 24d4d58..69df65f 100644 --- a/app/src/main/java/com/yuanxuan/rokid/network/http/ApiResponse.kt +++ b/app/src/main/java/com/yuanxuan/rokid/network/bean/ApiResponse.kt @@ -1,4 +1,4 @@ -package com.yuanxuan.rokid.network.http +package com.yuanxuan.rokid.network.bean data class ApiResponse( val code: String, diff --git a/app/src/main/java/com/yuanxuan/rokid/network/http/ApiRepository.kt b/app/src/main/java/com/yuanxuan/rokid/network/http/ApiRepository.kt index 7ff169f..459d570 100644 --- a/app/src/main/java/com/yuanxuan/rokid/network/http/ApiRepository.kt +++ b/app/src/main/java/com/yuanxuan/rokid/network/http/ApiRepository.kt @@ -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? { diff --git a/app/src/main/java/com/yuanxuan/rokid/network/http/ApiService.kt b/app/src/main/java/com/yuanxuan/rokid/network/http/ApiService.kt index dcec3a1..616b9bb 100644 --- a/app/src/main/java/com/yuanxuan/rokid/network/http/ApiService.kt +++ b/app/src/main/java/com/yuanxuan/rokid/network/http/ApiService.kt @@ -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 diff --git a/app/src/main/java/com/yuanxuan/rokid/network/websocket/WebSocketManager.kt b/app/src/main/java/com/yuanxuan/rokid/network/websocket/WebSocketManager.kt index 259fa5e..1427966 100644 --- a/app/src/main/java/com/yuanxuan/rokid/network/websocket/WebSocketManager.kt +++ b/app/src/main/java/com/yuanxuan/rokid/network/websocket/WebSocketManager.kt @@ -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) { diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/HomeFragment.kt b/app/src/main/java/com/yuanxuan/rokid/ui/HomeFragment.kt new file mode 100644 index 0000000..671a88f --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/ui/HomeFragment.kt @@ -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() + + 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) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/HomeMenuAdapter.kt b/app/src/main/java/com/yuanxuan/rokid/ui/HomeMenuAdapter.kt new file mode 100644 index 0000000..42c3518 --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/ui/HomeMenuAdapter.kt @@ -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(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) { + 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() { + + 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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/yuanxuan/rokid/ui/HomeViewModel.kt b/app/src/main/java/com/yuanxuan/rokid/ui/HomeViewModel.kt new file mode 100644 index 0000000..7e02fb5 --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/ui/HomeViewModel.kt @@ -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 + } + } + } + } + } + } +} \ 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 new file mode 100644 index 0000000..9082b58 --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/ui/SettingFragment.kt @@ -0,0 +1,6 @@ +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/SnFragment.kt b/app/src/main/java/com/yuanxuan/rokid/ui/SnFragment.kt new file mode 100644 index 0000000..7958bd1 --- /dev/null +++ b/app/src/main/java/com/yuanxuan/rokid/ui/SnFragment.kt @@ -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") + } + +} \ No newline at end of file diff --git a/app/src/main/res/animator/focus_animator.xml b/app/src/main/res/animator/focus_animator.xml new file mode 100644 index 0000000..0b72bd4 --- /dev/null +++ b/app/src/main/res/animator/focus_animator.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/bg_home_menu.xml b/app/src/main/res/drawable/bg_home_menu.xml new file mode 100644 index 0000000..90a8d8a --- /dev/null +++ b/app/src/main/res/drawable/bg_home_menu.xml @@ -0,0 +1,7 @@ + + + + + \ 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 244e535..3c4d3b4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,6 +7,14 @@ android:layout_height="match_parent" tools:context=".MainActivity"> + + + + + + + + + \ 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 new file mode 100644 index 0000000..bdb24a7 --- /dev/null +++ b/app/src/main/res/layout/fragment_setting.xml @@ -0,0 +1,13 @@ + + + + + \ 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 new file mode 100644 index 0000000..60a7030 --- /dev/null +++ b/app/src/main/res/layout/fragment_sn.xml @@ -0,0 +1,34 @@ + + + +