feat: debug window
This commit is contained in:
parent
4ab4487197
commit
54bd378bf7
|
@ -73,6 +73,8 @@ dependencies {
|
||||||
implementation(libs.androidx.datastore.preferences)
|
implementation(libs.androidx.datastore.preferences)
|
||||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||||
implementation(libs.accompanist.systemuicontroller)
|
implementation(libs.accompanist.systemuicontroller)
|
||||||
|
implementation(libs.lifecycle.runtime.ktx)
|
||||||
|
implementation(libs.androidx.savedstate.ktx)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package icu.fur93.esp32_car.page
|
package icu.fur93.esp32_car.page
|
||||||
|
|
||||||
|
import DebugWindowManager
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
@ -18,6 +19,7 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
import icu.fur93.esp32_car.BuildConfig
|
import icu.fur93.esp32_car.BuildConfig
|
||||||
import icu.fur93.esp32_car.R
|
import icu.fur93.esp32_car.R
|
||||||
|
@ -45,17 +47,33 @@ fun SettingsPage(viewModel: CarViewModel) {
|
||||||
}
|
}
|
||||||
SettingsList(viewModel)
|
SettingsList(viewModel)
|
||||||
TipText("GitHub: colour93/esp32-car-android")
|
TipText("GitHub: colour93/esp32-car-android")
|
||||||
TipText("Build: ${
|
TipText(
|
||||||
|
"Build: ${
|
||||||
android.text.format.DateFormat.format(
|
android.text.format.DateFormat.format(
|
||||||
"yyyy/MM/dd HH:mm:ss",
|
"yyyy/MM/dd HH:mm:ss",
|
||||||
BuildConfig.BUILD_TIME.toLong()
|
BuildConfig.BUILD_TIME.toLong()
|
||||||
)
|
)
|
||||||
}")
|
}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsList(viewModel: CarViewModel) {
|
fun SettingsList(viewModel: CarViewModel) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
var debugMode by remember { mutableStateOf(DebugWindowManager.isShowing()) }
|
||||||
|
|
||||||
|
val onDebugModeChange = { enable: Boolean ->
|
||||||
|
if (enable) {
|
||||||
|
DebugWindowManager.show(context, viewModel)
|
||||||
|
} else {
|
||||||
|
DebugWindowManager.hide(context)
|
||||||
|
}
|
||||||
|
debugMode = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 LazyColumn 显示设置列表
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
item {
|
item {
|
||||||
SettingConnectDeviceItem(viewModel)
|
SettingConnectDeviceItem(viewModel)
|
||||||
|
@ -65,7 +83,10 @@ fun SettingsList(viewModel: CarViewModel) {
|
||||||
headlineContent = { Text("设备名称") },
|
headlineContent = { Text("设备名称") },
|
||||||
supportingContent = { Text("当前名称: Name") },
|
supportingContent = { Text("当前名称: Name") },
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
Icon(ImageVector.vectorResource(R.drawable.arrow_right_24), "设备名称")
|
Icon(
|
||||||
|
ImageVector.vectorResource(R.drawable.arrow_right_24),
|
||||||
|
contentDescription = "设备名称"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -74,10 +95,8 @@ fun SettingsList(viewModel: CarViewModel) {
|
||||||
headlineContent = { Text("调试模式") },
|
headlineContent = { Text("调试模式") },
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
Switch(
|
Switch(
|
||||||
checked = false,
|
checked = debugMode,
|
||||||
onCheckedChange = {
|
onCheckedChange = onDebugModeChange
|
||||||
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -86,13 +105,17 @@ fun SettingsList(viewModel: CarViewModel) {
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text("PID 参数") },
|
headlineContent = { Text("PID 参数") },
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
Icon(ImageVector.vectorResource(R.drawable.arrow_right_24), "PID 参数")
|
Icon(
|
||||||
|
ImageVector.vectorResource(R.drawable.arrow_right_24),
|
||||||
|
contentDescription = "PID 参数"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingConnectDeviceItem(viewModel: CarViewModel) {
|
fun SettingConnectDeviceItem(viewModel: CarViewModel) {
|
||||||
var showScanDialog by remember { mutableStateOf(false) }
|
var showScanDialog by remember { mutableStateOf(false) }
|
||||||
|
|
|
@ -3,6 +3,7 @@ package icu.fur93.esp32_car.viewmodel
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.bluetooth.BluetoothDevice
|
import android.bluetooth.BluetoothDevice
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import icu.fur93.esp32_car.const.CarCommands
|
import icu.fur93.esp32_car.const.CarCommands
|
||||||
|
@ -19,6 +20,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import android.view.WindowManager
|
||||||
|
|
||||||
|
|
||||||
// 用例
|
// 用例
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
package icu.fur93.esp32_car.window
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.gestures.detectDragGestures
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.FilterChipDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import icu.fur93.esp32_car.R
|
||||||
|
import icu.fur93.esp32_car.entity.LogDirection
|
||||||
|
import icu.fur93.esp32_car.entity.LogEntry
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DebugWindow(
|
||||||
|
onDrag: (Float, Float) -> Unit,
|
||||||
|
onResize: (Float, Float) -> Unit,
|
||||||
|
logs: List<LogEntry>
|
||||||
|
) {
|
||||||
|
var logFilter by remember { mutableStateOf(LogFilter.ALL) }
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(280.dp)
|
||||||
|
.height(200.dp)
|
||||||
|
.background(
|
||||||
|
color = Color.Black.copy(alpha = 0.8f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
// 顶部拖动条
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(32.dp)
|
||||||
|
.background(
|
||||||
|
color = Color.DarkGray,
|
||||||
|
shape = RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp)
|
||||||
|
)
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectDragGestures { change, dragAmount ->
|
||||||
|
change.consume()
|
||||||
|
onDrag(dragAmount.x, dragAmount.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Debug Window",
|
||||||
|
color = Color.White,
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 12.dp, vertical = 6.dp)
|
||||||
|
.align(Alignment.CenterStart)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤选项
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(Color.DarkGray.copy(alpha = 0.3f))
|
||||||
|
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceEvenly
|
||||||
|
) {
|
||||||
|
FilterChip(
|
||||||
|
selected = logFilter == LogFilter.ALL,
|
||||||
|
onClick = { logFilter = LogFilter.ALL },
|
||||||
|
label = { Text("全部", color = Color.White) },
|
||||||
|
colors = FilterChipDefaults.filterChipColors(
|
||||||
|
selectedContainerColor = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
)
|
||||||
|
FilterChip(
|
||||||
|
selected = logFilter == LogFilter.SEND,
|
||||||
|
onClick = { logFilter = LogFilter.SEND },
|
||||||
|
label = { Text("发送", color = Color.White) },
|
||||||
|
colors = FilterChipDefaults.filterChipColors(
|
||||||
|
selectedContainerColor = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
)
|
||||||
|
FilterChip(
|
||||||
|
selected = logFilter == LogFilter.RECEIVE,
|
||||||
|
onClick = { logFilter = LogFilter.RECEIVE },
|
||||||
|
label = { Text("接收", color = Color.White) },
|
||||||
|
colors = FilterChipDefaults.filterChipColors(
|
||||||
|
selectedContainerColor = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日志列表
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
reverseLayout = true // 最新的日志显示在顶部
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
logs.filter {
|
||||||
|
when (logFilter) {
|
||||||
|
LogFilter.ALL -> true
|
||||||
|
LogFilter.SEND -> it.direction == LogDirection.SEND
|
||||||
|
LogFilter.RECEIVE -> it.direction == LogDirection.RECEIVE
|
||||||
|
}
|
||||||
|
}.reversed()
|
||||||
|
) { log ->
|
||||||
|
LogItem(log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右下角调整大小的手柄
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
|
.padding(4.dp)
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectDragGestures { change, dragAmount ->
|
||||||
|
change.consume()
|
||||||
|
onResize(dragAmount.x, dragAmount.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = ImageVector.vectorResource(R.drawable.resize_24),
|
||||||
|
contentDescription = "Resize",
|
||||||
|
tint = Color.White.copy(alpha = 0.6f),
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LogItem(log: LogEntry) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 2.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
// 方向指示
|
||||||
|
Text(
|
||||||
|
text = if (log.direction == LogDirection.SEND) "→" else "←",
|
||||||
|
color = if (log.direction == LogDirection.SEND)
|
||||||
|
Color(0xFF4CAF50) else Color(0xFF2196F3),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
modifier = Modifier.width(16.dp)
|
||||||
|
)
|
||||||
|
// 日志内容
|
||||||
|
Text(
|
||||||
|
text = log.data,
|
||||||
|
color = Color.White,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
// 时间戳
|
||||||
|
Text(
|
||||||
|
text = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
|
||||||
|
.format(log.timestamp),
|
||||||
|
color = Color.Gray,
|
||||||
|
style = MaterialTheme.typography.bodySmall
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class LogFilter {
|
||||||
|
ALL, SEND, RECEIVE
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.PixelFormat
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.platform.ComposeView
|
||||||
|
import androidx.lifecycle.setViewTreeLifecycleOwner
|
||||||
|
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||||
|
import icu.fur93.esp32_car.window.DebugWindow
|
||||||
|
import icu.fur93.esp32_car.viewmodel.CarViewModel
|
||||||
|
|
||||||
|
object DebugWindowManager {
|
||||||
|
private var debugWindow: ComposeView? = null
|
||||||
|
private var layoutParams: WindowManager.LayoutParams? = null
|
||||||
|
|
||||||
|
// 添加最小尺寸限制
|
||||||
|
private const val MIN_WIDTH_DP = 200
|
||||||
|
private const val MIN_HEIGHT_DP = 150
|
||||||
|
|
||||||
|
fun show(context: Context, viewModel: CarViewModel) {
|
||||||
|
if (debugWindow != null) return
|
||||||
|
|
||||||
|
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
val activity = context as? ComponentActivity ?: return
|
||||||
|
|
||||||
|
val density = context.resources.displayMetrics.density
|
||||||
|
val width = (280 * density).toInt()
|
||||||
|
val height = (200 * density).toInt()
|
||||||
|
|
||||||
|
val params = WindowManager.LayoutParams(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
WindowManager.LayoutParams.TYPE_APPLICATION,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||||
|
PixelFormat.TRANSLUCENT
|
||||||
|
).apply {
|
||||||
|
gravity = Gravity.TOP or Gravity.START
|
||||||
|
x = 100
|
||||||
|
y = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
val window = ComposeView(context).apply {
|
||||||
|
setViewTreeLifecycleOwner(activity)
|
||||||
|
setViewTreeSavedStateRegistryOwner(activity)
|
||||||
|
|
||||||
|
setContent {
|
||||||
|
val logs by viewModel.logs.collectAsState()
|
||||||
|
|
||||||
|
DebugWindow(
|
||||||
|
onDrag = { offsetX, offsetY ->
|
||||||
|
params.x += offsetX.toInt()
|
||||||
|
params.y += offsetY.toInt()
|
||||||
|
windowManager.updateViewLayout(this, params)
|
||||||
|
},
|
||||||
|
onResize = { offsetX, offsetY ->
|
||||||
|
val density = context.resources.displayMetrics.density
|
||||||
|
val minWidth = (MIN_WIDTH_DP * density).toInt()
|
||||||
|
val minHeight = (MIN_HEIGHT_DP * density).toInt()
|
||||||
|
params.width = (params.width + offsetX.toInt()).coerceAtLeast(minWidth)
|
||||||
|
params.height = (params.height + offsetY.toInt()).coerceAtLeast(minHeight)
|
||||||
|
windowManager.updateViewLayout(this, params)
|
||||||
|
},
|
||||||
|
logs = logs
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
windowManager.addView(window, params)
|
||||||
|
debugWindow = window
|
||||||
|
layoutParams = params
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hide(context: Context) {
|
||||||
|
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
debugWindow?.let {
|
||||||
|
windowManager.removeView(it)
|
||||||
|
debugWindow = null
|
||||||
|
layoutParams = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isShowing(): Boolean {
|
||||||
|
return debugWindow != null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updatePosition(context: Context, x: Int, y: Int) {
|
||||||
|
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
layoutParams?.let { params ->
|
||||||
|
params.x = x
|
||||||
|
params.y = y
|
||||||
|
debugWindow?.let {
|
||||||
|
windowManager.updateViewLayout(it, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M22,22H20V20H22V22M22,18H20V16H22V18M18,22H16V20H18V22M18,18H16V16H18V18M14,22H12V20H14V22M22,14H20V12H22V14Z"/>
|
||||||
|
</vector>
|
|
@ -15,6 +15,8 @@ datastorePreferencesCoreJvm = "1.1.1"
|
||||||
datastorePreferences = "1.1.1"
|
datastorePreferences = "1.1.1"
|
||||||
lifecycleViewmodelCompose = "2.9.0-alpha08"
|
lifecycleViewmodelCompose = "2.9.0-alpha08"
|
||||||
accompanistSystemuicontroller = "0.36.0"
|
accompanistSystemuicontroller = "0.36.0"
|
||||||
|
lifecycleRuntimeKtxVersion = "2.7.0"
|
||||||
|
savedstateKtx = "1.2.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
|
@ -38,6 +40,8 @@ androidx-datastore-preferences-core-jvm = { group = "androidx.datastore", name =
|
||||||
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastorePreferences" }
|
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastorePreferences" }
|
||||||
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version = "2.7.0" }
|
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version = "2.7.0" }
|
||||||
accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
|
accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
|
||||||
|
lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtxVersion" }
|
||||||
|
androidx-savedstate-ktx = { group = "androidx.savedstate", name = "savedstate-ktx", version.ref = "savedstateKtx" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|
Loading…
Reference in New Issue