feat: version checking using github api
This commit is contained in:
@@ -67,6 +67,7 @@ android {
|
|||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
prefab = true
|
prefab = true
|
||||||
|
buildConfig = true
|
||||||
}
|
}
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
@@ -108,4 +109,4 @@ dependencies {
|
|||||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,12 +42,14 @@ import androidx.navigation3.runtime.NavKey
|
|||||||
import androidx.navigation3.runtime.entryProvider
|
import androidx.navigation3.runtime.entryProvider
|
||||||
import androidx.navigation3.runtime.rememberDecoratedNavEntries
|
import androidx.navigation3.runtime.rememberDecoratedNavEntries
|
||||||
import androidx.navigation3.ui.NavDisplay
|
import androidx.navigation3.ui.NavDisplay
|
||||||
|
import io.github.miuzarte.scrcpyforandroid.BuildConfig
|
||||||
import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade
|
import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.ThemeModes
|
import io.github.miuzarte.scrcpyforandroid.constants.ThemeModes
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.UiMotion
|
import io.github.miuzarte.scrcpyforandroid.constants.UiMotion
|
||||||
import io.github.miuzarte.scrcpyforandroid.models.DeviceShortcuts
|
import io.github.miuzarte.scrcpyforandroid.models.DeviceShortcuts
|
||||||
import io.github.miuzarte.scrcpyforandroid.nativecore.NativeAdbService
|
import io.github.miuzarte.scrcpyforandroid.nativecore.NativeAdbService
|
||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
||||||
|
import io.github.miuzarte.scrcpyforandroid.services.AppUpdateChecker
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
||||||
@@ -217,6 +219,15 @@ fun MainScreen() {
|
|||||||
}
|
}
|
||||||
val currentSession by scrcpy.currentSessionState.collectAsState()
|
val currentSession by scrcpy.currentSessionState.collectAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(asBundle.lastUpdateCheckAt) {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
if (now - asBundle.lastUpdateCheckAt < AppUpdateChecker.CHECK_INTERVAL_MS) return@LaunchedEffect
|
||||||
|
taskScope.launch {
|
||||||
|
appSettings.updateBundle { it.copy(lastUpdateCheckAt = now) }
|
||||||
|
AppUpdateChecker.ensureChecked(BuildConfig.VERSION_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun handleBackNavigation() {
|
fun handleBackNavigation() {
|
||||||
if (rootBackStack.size > 1) {
|
if (rootBackStack.size > 1) {
|
||||||
popRoot()
|
popRoot()
|
||||||
|
|||||||
@@ -29,12 +29,14 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import io.github.miuzarte.scrcpyforandroid.BuildConfig
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.ThemeModes
|
import io.github.miuzarte.scrcpyforandroid.constants.ThemeModes
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
||||||
import io.github.miuzarte.scrcpyforandroid.scaffolds.LazyColumn
|
import io.github.miuzarte.scrcpyforandroid.scaffolds.LazyColumn
|
||||||
import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperSlider
|
import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperSlider
|
||||||
import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperTextField
|
import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperTextField
|
||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
||||||
|
import io.github.miuzarte.scrcpyforandroid.services.AppUpdateChecker
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.PreferenceMigration
|
import io.github.miuzarte.scrcpyforandroid.storage.PreferenceMigration
|
||||||
@@ -57,7 +59,6 @@ import top.yukonga.miuix.kmp.basic.TopAppBar
|
|||||||
import top.yukonga.miuix.kmp.extra.SuperArrow
|
import top.yukonga.miuix.kmp.extra.SuperArrow
|
||||||
import top.yukonga.miuix.kmp.extra.SuperDropdown
|
import top.yukonga.miuix.kmp.extra.SuperDropdown
|
||||||
import top.yukonga.miuix.kmp.extra.SuperSwitch
|
import top.yukonga.miuix.kmp.extra.SuperSwitch
|
||||||
import top.yukonga.miuix.kmp.theme.ColorSchemeMode
|
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@@ -100,6 +101,7 @@ fun SettingsPage(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val appContext = context.applicationContext
|
val appContext = context.applicationContext
|
||||||
var needMigration by remember { mutableStateOf(false) }
|
var needMigration by remember { mutableStateOf(false) }
|
||||||
|
val updateState by AppUpdateChecker.state.collectAsState()
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
needMigration = PreferenceMigration(appContext).needsMigration()
|
needMigration = PreferenceMigration(appContext).needsMigration()
|
||||||
}
|
}
|
||||||
@@ -152,6 +154,37 @@ fun SettingsPage(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val updateSummary = remember(updateState) {
|
||||||
|
when (val state = updateState) {
|
||||||
|
AppUpdateChecker.State.Idle ->
|
||||||
|
"当前版本 ${BuildConfig.VERSION_NAME}"
|
||||||
|
|
||||||
|
AppUpdateChecker.State.Checking ->
|
||||||
|
"当前版本 ${BuildConfig.VERSION_NAME},正在检查更新"
|
||||||
|
|
||||||
|
is AppUpdateChecker.State.Ready -> {
|
||||||
|
when {
|
||||||
|
state.release.hasUpdate ->
|
||||||
|
"当前版本 ${state.release.currentVersion},发现新版本 ${state.release.latestVersion}"
|
||||||
|
|
||||||
|
state.release.currentVersion == state.release.latestVersion.removePrefix("v")
|
||||||
|
|| state.release.currentVersion == state.release.latestVersion ->
|
||||||
|
"当前版本 ${state.release.currentVersion},已是最新版本"
|
||||||
|
|
||||||
|
else ->
|
||||||
|
"当前版本 ${state.release.currentVersion},高于最新发布版本 ${state.release.latestVersion}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val updateUrl = remember(updateState) {
|
||||||
|
when (val state = updateState) {
|
||||||
|
is AppUpdateChecker.State.Ready -> state.release.htmlUrl
|
||||||
|
else -> "https://github.com/Miuzarte/ScrcpyForAndroid/releases"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 设置
|
// 设置
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
@@ -163,11 +196,19 @@ fun SettingsPage(
|
|||||||
SuperDropdown(
|
SuperDropdown(
|
||||||
title = "外观模式",
|
title = "外观模式",
|
||||||
summary = ThemeModes.baseOptions
|
summary = ThemeModes.baseOptions
|
||||||
.getOrNull(asBundle.themeBaseIndex.coerceIn(0, ThemeModes.baseOptions.lastIndex))
|
.getOrNull(
|
||||||
|
asBundle.themeBaseIndex.coerceIn(
|
||||||
|
0,
|
||||||
|
ThemeModes.baseOptions.lastIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
?.label
|
?.label
|
||||||
?: "跟随系统",
|
?: "跟随系统",
|
||||||
items = themeItems,
|
items = themeItems,
|
||||||
selectedIndex = asBundle.themeBaseIndex.coerceIn(0, ThemeModes.baseOptions.lastIndex),
|
selectedIndex = asBundle.themeBaseIndex.coerceIn(
|
||||||
|
0,
|
||||||
|
ThemeModes.baseOptions.lastIndex
|
||||||
|
),
|
||||||
onSelectedIndexChange = {
|
onSelectedIndexChange = {
|
||||||
asBundle = asBundle.copy(themeBaseIndex = it)
|
asBundle = asBundle.copy(themeBaseIndex = it)
|
||||||
},
|
},
|
||||||
@@ -441,13 +482,13 @@ fun SettingsPage(
|
|||||||
SectionSmallTitle("关于", showLeadingSpacer = false)
|
SectionSmallTitle("关于", showLeadingSpacer = false)
|
||||||
Card {
|
Card {
|
||||||
SuperArrow(
|
SuperArrow(
|
||||||
title = "前往仓库",
|
title = "仓库发布页",
|
||||||
summary = "github.com/Miuzarte/ScrcpyForAndroid",
|
summary = "$updateSummary\n${AppUpdateChecker.REPO_URL.removePrefix("https://")}",
|
||||||
onClick = {
|
onClick = {
|
||||||
context.startActivity(
|
context.startActivity(
|
||||||
Intent(
|
Intent(
|
||||||
Intent.ACTION_VIEW,
|
Intent.ACTION_VIEW,
|
||||||
"https://github.com/Miuzarte/ScrcpyForAndroid".toUri(),
|
updateUrl.toUri(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package io.github.miuzarte.scrcpyforandroid.services
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import io.github.miuzarte.scrcpyforandroid.BuildConfig
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.json.JSONArray
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
object AppUpdateChecker {
|
||||||
|
private const val TAG = "AppUpdateChecker"
|
||||||
|
const val RELEASES_API_URL =
|
||||||
|
"https://api.github.com/repos/Miuzarte/ScrcpyForAndroid/releases?per_page=1"
|
||||||
|
const val RELEASES_URL =
|
||||||
|
"https://github.com/Miuzarte/ScrcpyForAndroid/releases"
|
||||||
|
const val REPO_URL =
|
||||||
|
"https://github.com/Miuzarte/ScrcpyForAndroid"
|
||||||
|
const val CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000L
|
||||||
|
|
||||||
|
data class ReleaseInfo(
|
||||||
|
val currentVersion: String,
|
||||||
|
val latestVersion: String,
|
||||||
|
val hasUpdate: Boolean,
|
||||||
|
val htmlUrl: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed interface State {
|
||||||
|
data object Idle : State
|
||||||
|
data object Checking : State
|
||||||
|
data class Ready(val release: ReleaseInfo) : State
|
||||||
|
}
|
||||||
|
|
||||||
|
private val checkMutex = Mutex()
|
||||||
|
private val _state = MutableStateFlow<State>(State.Idle)
|
||||||
|
val state: StateFlow<State> = _state.asStateFlow()
|
||||||
|
|
||||||
|
suspend fun ensureChecked(currentVersion: String = BuildConfig.VERSION_NAME) {
|
||||||
|
checkMutex.withLock {
|
||||||
|
if (_state.value is State.Ready || _state.value is State.Checking) return
|
||||||
|
_state.value = State.Checking
|
||||||
|
runCatching { fetchLatestRelease(currentVersion) }
|
||||||
|
.onSuccess { _state.value = State.Ready(it) }
|
||||||
|
.onFailure { error ->
|
||||||
|
EventLogger.logEvent(
|
||||||
|
"检查更新失败: ${error.message ?: error.javaClass.simpleName}",
|
||||||
|
Log.WARN,
|
||||||
|
error
|
||||||
|
)
|
||||||
|
_state.value = State.Idle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun fetchLatestRelease(currentVersion: String): ReleaseInfo =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val connection = (URL(RELEASES_API_URL).openConnection() as HttpURLConnection).apply {
|
||||||
|
requestMethod = "GET"
|
||||||
|
connectTimeout = 10_000
|
||||||
|
readTimeout = 10_000
|
||||||
|
setRequestProperty("Accept", "application/vnd.github+json")
|
||||||
|
setRequestProperty("X-GitHub-Api-Version", "2022-11-28")
|
||||||
|
setRequestProperty("User-Agent", "ScrcpyForAndroid/$currentVersion")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val body = connection.inputStream.bufferedReader().use { it.readText() }
|
||||||
|
val release = JSONArray(body).optJSONObject(0)
|
||||||
|
?: error("GitHub releases response is empty")
|
||||||
|
val latestVersion = release.optString("tag_name")
|
||||||
|
.ifBlank { release.optString("name") }
|
||||||
|
.ifBlank { error("GitHub release has no tag name") }
|
||||||
|
ReleaseInfo(
|
||||||
|
currentVersion = currentVersion,
|
||||||
|
latestVersion = latestVersion.removePrefix("v").removePrefix("V"),
|
||||||
|
hasUpdate = compareVersions(currentVersion, latestVersion) < 0,
|
||||||
|
htmlUrl = release.optString("html_url").ifBlank { RELEASES_URL },
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
connection.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compareVersions(current: String, latest: String): Int {
|
||||||
|
val currentParts = normalizeVersion(current)
|
||||||
|
val latestParts = normalizeVersion(latest)
|
||||||
|
val maxSize = maxOf(currentParts.size, latestParts.size)
|
||||||
|
for (i in 0 until maxSize) {
|
||||||
|
val currentPart = currentParts.getOrElse(i) { 0 }
|
||||||
|
val latestPart = latestParts.getOrElse(i) { 0 }
|
||||||
|
if (currentPart != latestPart) {
|
||||||
|
return currentPart.compareTo(latestPart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun normalizeVersion(value: String) =
|
||||||
|
value
|
||||||
|
.trim()
|
||||||
|
.removePrefix("v")
|
||||||
|
.removePrefix("V")
|
||||||
|
.split(Regex("[^0-9]+"))
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
|
.mapNotNull { it.toIntOrNull() }
|
||||||
|
.ifEmpty { listOf(0) }
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import android.os.Parcelable
|
|||||||
import androidx.datastore.preferences.core.Preferences
|
import androidx.datastore.preferences.core.Preferences
|
||||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
import androidx.datastore.preferences.core.intPreferencesKey
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
|
import androidx.datastore.preferences.core.longPreferencesKey
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@@ -68,6 +69,10 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
|
|||||||
booleanPreferencesKey("adb_mdns_lan_discovery"),
|
booleanPreferencesKey("adb_mdns_lan_discovery"),
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
val LAST_UPDATE_CHECK_AT = Pair(
|
||||||
|
longPreferencesKey("last_update_check_at"),
|
||||||
|
0L
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme Settings
|
// Theme Settings
|
||||||
@@ -91,6 +96,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
|
|||||||
val adbPairingAutoDiscoverOnDialogOpen by setting(ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN)
|
val adbPairingAutoDiscoverOnDialogOpen by setting(ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN)
|
||||||
val adbAutoReconnectPairedDevice by setting(ADB_AUTO_RECONNECT_PAIRED_DEVICE)
|
val adbAutoReconnectPairedDevice by setting(ADB_AUTO_RECONNECT_PAIRED_DEVICE)
|
||||||
val adbMdnsLanDiscovery by setting(ADB_MDNS_LAN_DISCOVERY)
|
val adbMdnsLanDiscovery by setting(ADB_MDNS_LAN_DISCOVERY)
|
||||||
|
val lastUpdateCheckAt by setting(LAST_UPDATE_CHECK_AT)
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Bundle(
|
data class Bundle(
|
||||||
@@ -108,6 +114,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
|
|||||||
val adbPairingAutoDiscoverOnDialogOpen: Boolean,
|
val adbPairingAutoDiscoverOnDialogOpen: Boolean,
|
||||||
val adbAutoReconnectPairedDevice: Boolean,
|
val adbAutoReconnectPairedDevice: Boolean,
|
||||||
val adbMdnsLanDiscovery: Boolean,
|
val adbMdnsLanDiscovery: Boolean,
|
||||||
|
val lastUpdateCheckAt: Long,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +133,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
|
|||||||
bundleField(ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN) { bundle: Bundle -> bundle.adbPairingAutoDiscoverOnDialogOpen },
|
bundleField(ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN) { bundle: Bundle -> bundle.adbPairingAutoDiscoverOnDialogOpen },
|
||||||
bundleField(ADB_AUTO_RECONNECT_PAIRED_DEVICE) { bundle: Bundle -> bundle.adbAutoReconnectPairedDevice },
|
bundleField(ADB_AUTO_RECONNECT_PAIRED_DEVICE) { bundle: Bundle -> bundle.adbAutoReconnectPairedDevice },
|
||||||
bundleField(ADB_MDNS_LAN_DISCOVERY) { bundle: Bundle -> bundle.adbMdnsLanDiscovery },
|
bundleField(ADB_MDNS_LAN_DISCOVERY) { bundle: Bundle -> bundle.adbMdnsLanDiscovery },
|
||||||
|
bundleField(LAST_UPDATE_CHECK_AT) { bundle: Bundle -> bundle.lastUpdateCheckAt },
|
||||||
)
|
)
|
||||||
|
|
||||||
val bundleState: StateFlow<Bundle> = createBundleState(::bundleFromPreferences)
|
val bundleState: StateFlow<Bundle> = createBundleState(::bundleFromPreferences)
|
||||||
@@ -147,6 +155,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
|
|||||||
),
|
),
|
||||||
adbAutoReconnectPairedDevice = preferences.read(ADB_AUTO_RECONNECT_PAIRED_DEVICE),
|
adbAutoReconnectPairedDevice = preferences.read(ADB_AUTO_RECONNECT_PAIRED_DEVICE),
|
||||||
adbMdnsLanDiscovery = preferences.read(ADB_MDNS_LAN_DISCOVERY),
|
adbMdnsLanDiscovery = preferences.read(ADB_MDNS_LAN_DISCOVERY),
|
||||||
|
lastUpdateCheckAt = preferences.read(LAST_UPDATE_CHECK_AT),
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun loadBundle() = loadBundle(::bundleFromPreferences)
|
suspend fun loadBundle() = loadBundle(::bundleFromPreferences)
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
|||||||
import top.yukonga.miuix.kmp.basic.SmallTitle
|
import top.yukonga.miuix.kmp.basic.SmallTitle
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SectionSmallTitle(text: String, showLeadingSpacer: Boolean = true) {
|
fun SectionSmallTitle(
|
||||||
|
text: String,
|
||||||
|
showLeadingSpacer: Boolean = true,
|
||||||
|
insideMargin: PaddingValues = PaddingValues(16.dp, 8.dp),
|
||||||
|
) {
|
||||||
if (showLeadingSpacer) {
|
if (showLeadingSpacer) {
|
||||||
Spacer(Modifier.height(UiSpacing.SectionTitleLeadingGap))
|
Spacer(Modifier.height(UiSpacing.SectionTitleLeadingGap))
|
||||||
}
|
}
|
||||||
SmallTitle(
|
SmallTitle(
|
||||||
text = text,
|
text = text,
|
||||||
insideMargin = PaddingValues(16.dp, 8.dp)
|
insideMargin = insideMargin,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user