mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-15 19:22:26 +08:00
feat: about page
This commit is contained in:
parent
3a8b8858c3
commit
39fd831853
|
@ -59,7 +59,7 @@ android {
|
|||
|
||||
applicationId = "li.songe.gkd"
|
||||
versionCode = 48
|
||||
versionName = "v1.9.1"
|
||||
versionName = "1.9.1"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
@ -153,6 +153,7 @@ android {
|
|||
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi"
|
||||
}
|
||||
dependenciesInfo.includeInApk = false
|
||||
packagingOptions.resources.excludes += setOf(
|
||||
|
@ -196,6 +197,9 @@ dependencies {
|
|||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
|
||||
implementation(libs.compose.ui)
|
||||
implementation(libs.compose.ui.graphics)
|
||||
implementation(libs.compose.animation)
|
||||
implementation(libs.compose.animation.graphics)
|
||||
implementation(libs.compose.icons)
|
||||
implementation(libs.compose.preview)
|
||||
debugImplementation(libs.compose.tooling)
|
||||
|
|
|
@ -1,42 +1,188 @@
|
|||
package li.songe.gkd.ui
|
||||
|
||||
import androidx.compose.animation.graphics.res.animatedVectorResource
|
||||
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
||||
import androidx.compose.animation.graphics.vector.AnimatedImageVector
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
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.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.update
|
||||
import li.songe.gkd.META
|
||||
import li.songe.gkd.MainActivity
|
||||
import li.songe.gkd.ui.component.RotatingLoadingIcon
|
||||
import li.songe.gkd.ui.component.SettingItem
|
||||
import li.songe.gkd.ui.component.TextMenu
|
||||
import li.songe.gkd.ui.component.TextSwitch
|
||||
import li.songe.gkd.ui.component.waitResult
|
||||
import li.songe.gkd.ui.style.EmptyHeight
|
||||
import li.songe.gkd.ui.style.itemPadding
|
||||
import li.songe.gkd.ui.style.titleItemPadding
|
||||
import li.songe.gkd.util.ISSUES_URL
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.ProfileTransitions
|
||||
import li.songe.gkd.util.REPOSITORY_URL
|
||||
import li.songe.gkd.util.SafeR
|
||||
import li.songe.gkd.util.UpdateChannelOption
|
||||
import li.songe.gkd.util.buildLogFile
|
||||
import li.songe.gkd.util.checkUpdate
|
||||
import li.songe.gkd.util.findOption
|
||||
import li.songe.gkd.util.format
|
||||
import li.songe.gkd.util.launchAsFn
|
||||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.openUri
|
||||
import li.songe.gkd.util.saveFileToDownloads
|
||||
import li.songe.gkd.util.shareFile
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.throttle
|
||||
import li.songe.gkd.util.toast
|
||||
|
||||
@Destination<RootGraph>(style = ProfileTransitions::class)
|
||||
@Composable
|
||||
fun AboutPage() {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val context = LocalContext.current as MainActivity
|
||||
val vm = viewModel<AboutVm>()
|
||||
val store by storeFlow.collectAsState()
|
||||
|
||||
vm.uploadOptions.ShowDialog()
|
||||
var showInfoDlg by remember { mutableStateOf(false) }
|
||||
if (showInfoDlg) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showInfoDlg = false },
|
||||
title = { Text(text = "版本信息") },
|
||||
text = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Column {
|
||||
Text(text = "构建渠道")
|
||||
Text(text = META.channel)
|
||||
}
|
||||
Column {
|
||||
Text(text = "版本代码")
|
||||
Text(text = META.versionCode.toString())
|
||||
}
|
||||
Column {
|
||||
Text(text = "版本名称")
|
||||
Text(text = META.versionName)
|
||||
}
|
||||
Column {
|
||||
Text(text = "代码记录")
|
||||
Text(
|
||||
modifier = Modifier.clickable { context.openUri(META.commitUrl) },
|
||||
text = META.commitId.substring(0, 16),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
style = LocalTextStyle.current.copy(textDecoration = TextDecoration.Underline),
|
||||
)
|
||||
}
|
||||
Column {
|
||||
Text(text = "提交时间")
|
||||
Text(text = META.commitTime.format("yyyy-MM-dd HH:mm:ss ZZ"))
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showInfoDlg = false
|
||||
}) {
|
||||
Text(text = "关闭")
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
var showShareLogDlg by remember { mutableStateOf(false) }
|
||||
if (showShareLogDlg) {
|
||||
Dialog(onDismissRequest = { showShareLogDlg = false }) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
) {
|
||||
val modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
Text(
|
||||
text = "分享到其他应用", modifier = Modifier
|
||||
.clickable(onClick = throttle {
|
||||
showShareLogDlg = false
|
||||
context.mainVm.viewModelScope.launchTry(Dispatchers.IO) {
|
||||
val logZipFile = buildLogFile()
|
||||
context.shareFile(logZipFile, "分享日志文件")
|
||||
}
|
||||
})
|
||||
.then(modifier)
|
||||
)
|
||||
Text(
|
||||
text = "保存到下载", modifier = Modifier
|
||||
.clickable(onClick = throttle {
|
||||
showShareLogDlg = false
|
||||
context.mainVm.viewModelScope.launchTry(Dispatchers.IO) {
|
||||
val logZipFile = buildLogFile()
|
||||
context.saveFileToDownloads(logZipFile)
|
||||
}
|
||||
})
|
||||
.then(modifier)
|
||||
)
|
||||
Text(
|
||||
text = "生成链接(需科学上网)",
|
||||
modifier = Modifier
|
||||
.clickable(onClick = throttle {
|
||||
showShareLogDlg = false
|
||||
vm.uploadOptions.startTask(getFile = { buildLogFile() })
|
||||
})
|
||||
.then(modifier)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
|
@ -53,7 +199,17 @@ fun AboutPage() {
|
|||
)
|
||||
}
|
||||
},
|
||||
title = { Text(text = "关于") }
|
||||
title = { Text(text = "关于") },
|
||||
actions = {
|
||||
IconButton(onClick = throttle(fn = {
|
||||
showInfoDlg = true
|
||||
})) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Info,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { contentPadding ->
|
||||
|
@ -63,6 +219,25 @@ fun AboutPage() {
|
|||
.verticalScroll(rememberScrollState())
|
||||
.padding(contentPadding),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AnimatedLogoIcon(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = throttle { toast("你干嘛~ 哎呦~") }
|
||||
)
|
||||
.fillMaxWidth(0.33f)
|
||||
.aspectRatio(1f)
|
||||
)
|
||||
Text(text = META.appName, style = MaterialTheme.typography.titleMedium)
|
||||
Text(text = META.versionName, style = MaterialTheme.typography.bodyMedium)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
|
@ -81,86 +256,139 @@ fun AboutPage() {
|
|||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.itemPadding()
|
||||
) {
|
||||
Text(
|
||||
text = "版本代码",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
Text(
|
||||
text = META.versionCode.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.itemPadding()
|
||||
) {
|
||||
Text(
|
||||
text = "版本名称",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
Text(
|
||||
text = META.versionName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
context.openUri(META.commitUrl)
|
||||
context.openUri(ISSUES_URL)
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.itemPadding()
|
||||
) {
|
||||
Text(
|
||||
text = "代码记录",
|
||||
text = "问题反馈",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
Text(
|
||||
text = META.commitId,
|
||||
text = ISSUES_URL,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.itemPadding()
|
||||
) {
|
||||
if (META.updateEnabled) {
|
||||
val checkUpdating by context.mainVm.updateStatus.checkUpdatingFlow.collectAsState()
|
||||
Text(
|
||||
text = "提交时间",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
text = "更新",
|
||||
modifier = Modifier.titleItemPadding(),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Text(
|
||||
text = META.commitTime.format("yyyy-MM-dd HH:mm:ss ZZ"),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
TextSwitch(
|
||||
title = "自动更新",
|
||||
subtitle = "自动检查更新",
|
||||
checked = store.autoCheckAppUpdate,
|
||||
onCheckedChange = {
|
||||
storeFlow.value = store.copy(
|
||||
autoCheckAppUpdate = it
|
||||
)
|
||||
}
|
||||
)
|
||||
TextMenu(
|
||||
title = "更新渠道",
|
||||
option = UpdateChannelOption.allSubObject.findOption(store.updateChannel)
|
||||
) {
|
||||
if (context.mainVm.updateStatus.checkUpdatingFlow.value) return@TextMenu
|
||||
if (it.value == UpdateChannelOption.Beta.value) {
|
||||
context.mainVm.viewModelScope.launchTry {
|
||||
context.mainVm.dialogFlow.waitResult(
|
||||
title = "版本渠道",
|
||||
text = "测试版本渠道更新快\n但不稳定可能存在较多BUG\n请谨慎使用",
|
||||
)
|
||||
storeFlow.update { s -> s.copy(updateChannel = it.value) }
|
||||
}
|
||||
} else {
|
||||
storeFlow.update { s -> s.copy(updateChannel = it.value) }
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
onClick = throttle(fn = context.mainVm.viewModelScope.launchAsFn {
|
||||
if (context.mainVm.updateStatus.checkUpdatingFlow.value) return@launchAsFn
|
||||
val newVersion = context.mainVm.updateStatus.checkUpdate()
|
||||
if (newVersion == null) {
|
||||
toast("暂无更新")
|
||||
}
|
||||
})
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.itemPadding()
|
||||
.itemPadding(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = "构建渠道",
|
||||
text = "检查更新",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
RotatingLoadingIcon(loading = checkUpdating)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = META.channel,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
text = "日志",
|
||||
modifier = Modifier.titleItemPadding(),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
|
||||
TextSwitch(
|
||||
title = "保存日志",
|
||||
subtitle = "保存7天日志便于反馈问题",
|
||||
checked = store.log2FileSwitch,
|
||||
onCheckedChange = {
|
||||
storeFlow.value = store.copy(
|
||||
log2FileSwitch = it
|
||||
)
|
||||
})
|
||||
|
||||
if (store.log2FileSwitch) {
|
||||
SettingItem(
|
||||
title = "导出日志",
|
||||
imageVector = Icons.Default.Share,
|
||||
onClick = {
|
||||
showShareLogDlg = true
|
||||
}
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(EmptyHeight))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AnimatedLogoIcon(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val context = LocalContext.current as MainActivity
|
||||
val enableDarkTheme by context.mainVm.enableDarkThemeFlow.collectAsState()
|
||||
val darkTheme = enableDarkTheme ?: isSystemInDarkTheme()
|
||||
var atEnd by remember { mutableStateOf(false) }
|
||||
val animation = AnimatedImageVector.animatedVectorResource(id = SafeR.ic_logo_animation)
|
||||
val painter = rememberAnimatedVectorPainter(
|
||||
animation,
|
||||
atEnd
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
while (true) {
|
||||
atEnd = !atEnd
|
||||
delay(animation.totalDuration.toLong())
|
||||
}
|
||||
}
|
||||
val colorRid = if (darkTheme) SafeR.better_white else SafeR.better_black
|
||||
Icon(
|
||||
modifier = modifier,
|
||||
painter = painter,
|
||||
contentDescription = null,
|
||||
tint = colorResource(colorRid),
|
||||
)
|
||||
}
|
9
app/src/main/kotlin/li/songe/gkd/ui/AboutVm.kt
Normal file
9
app/src/main/kotlin/li/songe/gkd/ui/AboutVm.kt
Normal file
|
@ -0,0 +1,9 @@
|
|||
package li.songe.gkd.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import li.songe.gkd.ui.component.UploadOptions
|
||||
|
||||
class AboutVm : ViewModel() {
|
||||
val uploadOptions = UploadOptions(viewModelScope)
|
||||
}
|
|
@ -15,16 +15,13 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
|
@ -51,7 +48,6 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.blankj.utilcode.util.LogUtils
|
||||
|
@ -83,13 +79,9 @@ import li.songe.gkd.ui.style.itemPadding
|
|||
import li.songe.gkd.ui.style.titleItemPadding
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.ProfileTransitions
|
||||
import li.songe.gkd.util.buildLogFile
|
||||
import li.songe.gkd.util.launchAsFn
|
||||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.openUri
|
||||
import li.songe.gkd.util.privacyStoreFlow
|
||||
import li.songe.gkd.util.saveFileToDownloads
|
||||
import li.songe.gkd.util.shareFile
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.throttle
|
||||
import li.songe.gkd.util.toast
|
||||
|
@ -104,8 +96,6 @@ fun AdvancedPage() {
|
|||
val store by storeFlow.collectAsState()
|
||||
val snapshotCount by vm.snapshotCountFlow.collectAsState()
|
||||
|
||||
vm.uploadOptions.ShowDialog()
|
||||
|
||||
var showEditPortDlg by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
@ -165,55 +155,6 @@ fun AdvancedPage() {
|
|||
})
|
||||
}
|
||||
|
||||
var showShareLogDlg by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
if (showShareLogDlg) {
|
||||
Dialog(onDismissRequest = { showShareLogDlg = false }) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
) {
|
||||
val modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
Text(
|
||||
text = "分享到其他应用", modifier = Modifier
|
||||
.clickable(onClick = throttle {
|
||||
showShareLogDlg = false
|
||||
vm.viewModelScope.launchTry(Dispatchers.IO) {
|
||||
val logZipFile = buildLogFile()
|
||||
context.shareFile(logZipFile, "分享日志文件")
|
||||
}
|
||||
})
|
||||
.then(modifier)
|
||||
)
|
||||
Text(
|
||||
text = "保存到下载", modifier = Modifier
|
||||
.clickable(onClick = throttle {
|
||||
showShareLogDlg = false
|
||||
vm.viewModelScope.launchTry(Dispatchers.IO) {
|
||||
val logZipFile = buildLogFile()
|
||||
context.saveFileToDownloads(logZipFile)
|
||||
}
|
||||
})
|
||||
.then(modifier)
|
||||
)
|
||||
Text(
|
||||
text = "生成链接(需科学上网)",
|
||||
modifier = Modifier
|
||||
.clickable(onClick = throttle {
|
||||
showShareLogDlg = false
|
||||
vm.uploadOptions.startTask(getFile = { buildLogFile() })
|
||||
})
|
||||
.then(modifier)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var showEditCookieDlg by remember { mutableStateOf(false) }
|
||||
if (showEditCookieDlg) {
|
||||
val privacyStore by privacyStoreFlow.collectAsState()
|
||||
|
@ -540,44 +481,6 @@ fun AdvancedPage() {
|
|||
}
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "日志",
|
||||
modifier = Modifier.titleItemPadding(),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
|
||||
TextSwitch(
|
||||
title = "保存日志",
|
||||
subtitle = "保存7天日志,帮助定位BUG",
|
||||
checked = store.log2FileSwitch,
|
||||
onCheckedChange = {
|
||||
storeFlow.value = store.copy(
|
||||
log2FileSwitch = it
|
||||
)
|
||||
if (!it) {
|
||||
context.mainVm.viewModelScope.launchTry(Dispatchers.IO) {
|
||||
val logFiles = LogUtils.getLogFiles()
|
||||
if (logFiles.isNotEmpty()) {
|
||||
logFiles.forEach { f ->
|
||||
f.delete()
|
||||
}
|
||||
toast("已删除全部日志")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (store.log2FileSwitch) {
|
||||
SettingItem(
|
||||
title = "导出日志",
|
||||
imageVector = Icons.Default.Share,
|
||||
onClick = {
|
||||
showShareLogDlg = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "其它",
|
||||
modifier = Modifier.titleItemPadding(),
|
||||
|
|
|
@ -5,11 +5,8 @@ import androidx.lifecycle.viewModelScope
|
|||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.ui.component.UploadOptions
|
||||
|
||||
class AdvancedVm : ViewModel() {
|
||||
val snapshotCountFlow =
|
||||
DbSet.snapshotDao.count().stateIn(viewModelScope, SharingStarted.Eagerly, 0)
|
||||
|
||||
val uploadOptions = UploadOptions(viewModelScope)
|
||||
}
|
|
@ -33,34 +33,24 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.ramcosta.composedestinations.generated.destinations.AboutPageDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.AdvancedPageDestination
|
||||
import com.ramcosta.composedestinations.utils.toDestinationsNavigator
|
||||
import kotlinx.coroutines.flow.update
|
||||
import li.songe.gkd.META
|
||||
import li.songe.gkd.MainActivity
|
||||
import li.songe.gkd.ui.component.RotatingLoadingIcon
|
||||
import li.songe.gkd.ui.component.SettingItem
|
||||
import li.songe.gkd.ui.component.TextMenu
|
||||
import li.songe.gkd.ui.component.TextSwitch
|
||||
import li.songe.gkd.ui.component.updateDialogOptions
|
||||
import li.songe.gkd.ui.component.waitResult
|
||||
import li.songe.gkd.ui.style.EmptyHeight
|
||||
import li.songe.gkd.ui.style.itemPadding
|
||||
import li.songe.gkd.ui.style.titleItemPadding
|
||||
import li.songe.gkd.ui.theme.supportDynamicColor
|
||||
import li.songe.gkd.util.DarkThemeOption
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.UpdateChannelOption
|
||||
import li.songe.gkd.util.checkUpdate
|
||||
import li.songe.gkd.util.findOption
|
||||
import li.songe.gkd.util.launchAsFn
|
||||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.throttle
|
||||
import li.songe.gkd.util.toast
|
||||
|
||||
val settingsNav = BottomNavItem(
|
||||
label = "设置", icon = Icons.Outlined.Settings
|
||||
|
@ -80,7 +70,6 @@ fun useSettingsPage(): ScaffoldExt {
|
|||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
val checkUpdating by context.mainVm.updateStatus.checkUpdatingFlow.collectAsState()
|
||||
|
||||
if (showToastInputDlg) {
|
||||
var value by remember {
|
||||
|
@ -297,66 +286,6 @@ fun useSettingsPage(): ScaffoldExt {
|
|||
})
|
||||
}
|
||||
|
||||
if (META.updateEnabled) {
|
||||
Text(
|
||||
text = "更新",
|
||||
modifier = Modifier.titleItemPadding(),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
|
||||
TextSwitch(
|
||||
title = "自动更新",
|
||||
subtitle = "打开应用时检测新版本",
|
||||
checked = store.autoCheckAppUpdate,
|
||||
onCheckedChange = {
|
||||
storeFlow.value = store.copy(
|
||||
autoCheckAppUpdate = it
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
TextMenu(
|
||||
title = "更新渠道",
|
||||
option = UpdateChannelOption.allSubObject.findOption(store.updateChannel)
|
||||
) {
|
||||
if (it.value == UpdateChannelOption.Beta.value) {
|
||||
vm.viewModelScope.launchTry {
|
||||
context.mainVm.dialogFlow.waitResult(
|
||||
title = "版本渠道",
|
||||
text = "测试版本渠道更新快\n但不稳定可能存在较多BUG\n请谨慎使用",
|
||||
)
|
||||
storeFlow.update { s -> s.copy(updateChannel = it.value) }
|
||||
}
|
||||
} else {
|
||||
storeFlow.update { s -> s.copy(updateChannel = it.value) }
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
onClick = throttle(fn = context.mainVm.viewModelScope.launchAsFn {
|
||||
if (context.mainVm.updateStatus.checkUpdatingFlow.value) return@launchAsFn
|
||||
val newVersion = context.mainVm.updateStatus.checkUpdate()
|
||||
if (newVersion == null) {
|
||||
toast("暂无更新")
|
||||
}
|
||||
})
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.itemPadding(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = "检查更新",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
RotatingLoadingIcon(loading = checkUpdating)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "其它",
|
||||
modifier = Modifier.titleItemPadding(),
|
||||
|
|
|
@ -9,6 +9,7 @@ const val SERVER_SCRIPT_URL =
|
|||
"https://registry.npmmirror.com/@gkd-kit/config/latest/files/dist/server.js"
|
||||
|
||||
const val REPOSITORY_URL = "https://github.com/gkd-kit/gkd"
|
||||
const val ISSUES_URL = "${REPOSITORY_URL}/issues"
|
||||
|
||||
const val HOME_PAGE_URL = "https://gkd.li"
|
||||
|
||||
|
|
|
@ -12,4 +12,7 @@ object SafeR {
|
|||
val ic_page_info: Int = R.drawable.ic_page_info
|
||||
val ic_flash_on: Int = R.drawable.ic_flash_on
|
||||
val ic_flash_off: Int = R.drawable.ic_flash_off
|
||||
val ic_logo_animation: Int = R.drawable.ic_logo_animation
|
||||
val better_black: Int = R.color.better_black
|
||||
val better_white: Int = R.color.better_white
|
||||
}
|
141
app/src/main/res/drawable/ic_logo_animation.xml
Normal file
141
app/src/main/res/drawable/ic_logo_animation.xml
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt">
|
||||
<aapt:attr name="android:drawable">
|
||||
<vector
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
<group android:name="path1">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M19,17.485l8.485,-8.485l2.816,2.841l-8.485,8.485z" />
|
||||
</group>
|
||||
<group android:name="path2">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M35.816,9l8.485,8.485l-2.816,2.841l-8.485,-8.485z" />
|
||||
</group>
|
||||
<group android:name="path3">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M23.08,21.999l8.466,-8.504l8.466,8.504l-8.466,8.504z" />
|
||||
</group>
|
||||
<group android:name="path4">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M8,55C8.726,50.581 10.104,46.395 12.775,42.977C14.285,41.046 16.133,39.452 17.847,37.729C18.107,37.468 18.465,37.332 19,37C20.272,43.004 20.091,48.871 19.751,54.875C15.872,55 11.994,55 8,55Z" />
|
||||
</group>
|
||||
<path
|
||||
android:name="path5"
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M21.912,55C21.867,52.521 22.113,50.034 21.993,47.565C21.833,44.288 21.429,41.019 21.039,37.758C20.922,36.775 21.013,36.135 22.054,35.748C27.61,33.682 33.218,33.423 38.918,35.103C39.826,35.37 40.103,35.763 39.967,36.755C39.717,38.583 39.642,40.44 39.586,42.288C39.461,46.44 39.392,50.593 39.297,54.873C33.543,55 27.792,55 21.912,55Z" />
|
||||
<group android:name="path6">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M42,55C42.06,48.791 42.24,42.582 42.432,36C50.149,40.036 53.985,46.652 56,54.895C51.379,55 46.75,55 42,55Z" />
|
||||
</group>
|
||||
</vector>
|
||||
</aapt:attr>
|
||||
<target android:name="path1">
|
||||
<aapt:attr name="android:animation">
|
||||
<set>
|
||||
<objectAnimator
|
||||
android:duration="1000"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="translateX"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="-0.7071"
|
||||
android:valueType="floatType" />
|
||||
<objectAnimator
|
||||
android:duration="1000"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="translateY"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="-0.7071"
|
||||
android:valueType="floatType" />
|
||||
</set>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="path2">
|
||||
<aapt:attr name="android:animation">
|
||||
<set>
|
||||
<objectAnimator
|
||||
android:duration="1000"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="translateX"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="0.7071"
|
||||
android:valueType="floatType" />
|
||||
<objectAnimator
|
||||
android:duration="1000"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="translateY"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="-0.7071"
|
||||
android:valueType="floatType" />
|
||||
</set>
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="path3">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator
|
||||
android:duration="1000"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="translateY"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="-1.13137"
|
||||
android:valueType="floatType" />
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="path4">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator
|
||||
android:duration="1000"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="translateX"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="1.5"
|
||||
android:valueType="floatType" />
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="path5">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator
|
||||
android:duration="1000"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="fillAlpha"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueFrom="0.8"
|
||||
android:valueTo="1"
|
||||
android:valueType="floatType" />
|
||||
</aapt:attr>
|
||||
</target>
|
||||
<target android:name="path6">
|
||||
<aapt:attr name="android:animation">
|
||||
<objectAnimator
|
||||
android:duration="1000"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="translateX"
|
||||
android:repeatCount="infinite"
|
||||
android:repeatMode="reverse"
|
||||
android:valueFrom="-0.7"
|
||||
android:valueTo="-2.2"
|
||||
android:valueType="floatType" />
|
||||
</aapt:attr>
|
||||
</target>
|
||||
</animated-vector>
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background_tint">#FCFCFC</color>
|
||||
<color name="ic_launcher_foreground_tint">#111</color>
|
||||
<color name="ic_launcher_background_tint">@color/better_white</color>
|
||||
<color name="ic_launcher_foreground_tint">@color/better_black</color>
|
||||
</resources>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background_tint">#111</color>
|
||||
<color name="ic_launcher_foreground_tint">#FCFCFC</color>
|
||||
<color name="better_white">#FCFCFC</color>
|
||||
<color name="better_black">#111111</color>
|
||||
<color name="ic_launcher_background_tint">@color/better_black</color>
|
||||
<color name="ic_launcher_foreground_tint">@color/better_white</color>
|
||||
</resources>
|
||||
|
|
|
@ -26,6 +26,9 @@ ktor_client_content_negotiation = { module = "io.ktor:ktor-client-content-negoti
|
|||
ktor_serialization_kotlinx_json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
|
||||
android_gradle = { module = "com.android.tools.build:gradle", version.ref = "android" }
|
||||
compose_ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
|
||||
compose_ui_graphics = { module = "androidx.compose.ui:ui-graphics", version.ref = "compose" }
|
||||
compose_animation = { module = "androidx.compose.animation:animation", version.ref = "compose" }
|
||||
compose_animation_graphics = { module = "androidx.compose.animation:animation-graphics", version.ref = "compose" }
|
||||
compose_preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
|
||||
compose_tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
|
||||
compose_junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" }
|
||||
|
|
Loading…
Reference in New Issue
Block a user