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"
|
applicationId = "li.songe.gkd"
|
||||||
versionCode = 48
|
versionCode = 48
|
||||||
versionName = "v1.9.1"
|
versionName = "1.9.1"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
@ -153,6 +153,7 @@ android {
|
||||||
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
|
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
|
||||||
freeCompilerArgs += "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api"
|
freeCompilerArgs += "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api"
|
||||||
freeCompilerArgs += "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi"
|
freeCompilerArgs += "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi"
|
||||||
|
freeCompilerArgs += "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi"
|
||||||
}
|
}
|
||||||
dependenciesInfo.includeInApk = false
|
dependenciesInfo.includeInApk = false
|
||||||
packagingOptions.resources.excludes += setOf(
|
packagingOptions.resources.excludes += setOf(
|
||||||
|
@ -196,6 +197,9 @@ dependencies {
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
|
|
||||||
implementation(libs.compose.ui)
|
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.icons)
|
||||||
implementation(libs.compose.preview)
|
implementation(libs.compose.preview)
|
||||||
debugImplementation(libs.compose.tooling)
|
debugImplementation(libs.compose.tooling)
|
||||||
|
|
|
@ -1,42 +1,188 @@
|
||||||
package li.songe.gkd.ui
|
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.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.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
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.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
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.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
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.Destination
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
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.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.EmptyHeight
|
||||||
import li.songe.gkd.ui.style.itemPadding
|
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.LocalNavController
|
||||||
import li.songe.gkd.util.ProfileTransitions
|
import li.songe.gkd.util.ProfileTransitions
|
||||||
import li.songe.gkd.util.REPOSITORY_URL
|
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.format
|
||||||
|
import li.songe.gkd.util.launchAsFn
|
||||||
|
import li.songe.gkd.util.launchTry
|
||||||
import li.songe.gkd.util.openUri
|
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)
|
@Destination<RootGraph>(style = ProfileTransitions::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AboutPage() {
|
fun AboutPage() {
|
||||||
val navController = LocalNavController.current
|
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()
|
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
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 ->
|
) { contentPadding ->
|
||||||
|
@ -63,6 +219,25 @@ fun AboutPage() {
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(contentPadding),
|
.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(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
|
@ -81,86 +256,139 @@ fun AboutPage() {
|
||||||
color = MaterialTheme.colorScheme.primary,
|
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(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
context.openUri(META.commitUrl)
|
context.openUri(ISSUES_URL)
|
||||||
}
|
}
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.itemPadding()
|
.itemPadding()
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "代码记录",
|
text = "问题反馈",
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = META.commitId,
|
text = ISSUES_URL,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column(
|
if (META.updateEnabled) {
|
||||||
modifier = Modifier
|
val checkUpdating by context.mainVm.updateStatus.checkUpdatingFlow.collectAsState()
|
||||||
.fillMaxWidth()
|
|
||||||
.itemPadding()
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = "提交时间",
|
text = "更新",
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
modifier = Modifier.titleItemPadding(),
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
)
|
)
|
||||||
Text(
|
TextSwitch(
|
||||||
text = META.commitTime.format("yyyy-MM-dd HH:mm:ss ZZ"),
|
title = "自动更新",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
subtitle = "自动检查更新",
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Text(
|
||||||
modifier = Modifier
|
text = "日志",
|
||||||
.fillMaxWidth()
|
modifier = Modifier.titleItemPadding(),
|
||||||
.itemPadding()
|
style = MaterialTheme.typography.titleSmall,
|
||||||
) {
|
color = MaterialTheme.colorScheme.primary,
|
||||||
Text(
|
)
|
||||||
text = "构建渠道",
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
TextSwitch(
|
||||||
)
|
title = "保存日志",
|
||||||
Text(
|
subtitle = "保存7天日志便于反馈问题",
|
||||||
text = META.channel,
|
checked = store.log2FileSwitch,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
onCheckedChange = {
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
storeFlow.value = store.copy(
|
||||||
|
log2FileSwitch = it
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (store.log2FileSwitch) {
|
||||||
|
SettingItem(
|
||||||
|
title = "导出日志",
|
||||||
|
imageVector = Icons.Default.Share,
|
||||||
|
onClick = {
|
||||||
|
showShareLogDlg = true
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(EmptyHeight))
|
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.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
import androidx.compose.material.icons.filled.Share
|
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LocalTextStyle
|
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.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.blankj.utilcode.util.LogUtils
|
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.ui.style.titleItemPadding
|
||||||
import li.songe.gkd.util.LocalNavController
|
import li.songe.gkd.util.LocalNavController
|
||||||
import li.songe.gkd.util.ProfileTransitions
|
import li.songe.gkd.util.ProfileTransitions
|
||||||
import li.songe.gkd.util.buildLogFile
|
|
||||||
import li.songe.gkd.util.launchAsFn
|
import li.songe.gkd.util.launchAsFn
|
||||||
import li.songe.gkd.util.launchTry
|
|
||||||
import li.songe.gkd.util.openUri
|
import li.songe.gkd.util.openUri
|
||||||
import li.songe.gkd.util.privacyStoreFlow
|
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.storeFlow
|
||||||
import li.songe.gkd.util.throttle
|
import li.songe.gkd.util.throttle
|
||||||
import li.songe.gkd.util.toast
|
import li.songe.gkd.util.toast
|
||||||
|
@ -104,8 +96,6 @@ fun AdvancedPage() {
|
||||||
val store by storeFlow.collectAsState()
|
val store by storeFlow.collectAsState()
|
||||||
val snapshotCount by vm.snapshotCountFlow.collectAsState()
|
val snapshotCount by vm.snapshotCountFlow.collectAsState()
|
||||||
|
|
||||||
vm.uploadOptions.ShowDialog()
|
|
||||||
|
|
||||||
var showEditPortDlg by remember {
|
var showEditPortDlg by remember {
|
||||||
mutableStateOf(false)
|
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) }
|
var showEditCookieDlg by remember { mutableStateOf(false) }
|
||||||
if (showEditCookieDlg) {
|
if (showEditCookieDlg) {
|
||||||
val privacyStore by privacyStoreFlow.collectAsState()
|
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(
|
||||||
text = "其它",
|
text = "其它",
|
||||||
modifier = Modifier.titleItemPadding(),
|
modifier = Modifier.titleItemPadding(),
|
||||||
|
|
|
@ -5,11 +5,8 @@ import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import li.songe.gkd.db.DbSet
|
import li.songe.gkd.db.DbSet
|
||||||
import li.songe.gkd.ui.component.UploadOptions
|
|
||||||
|
|
||||||
class AdvancedVm : ViewModel() {
|
class AdvancedVm : ViewModel() {
|
||||||
val snapshotCountFlow =
|
val snapshotCountFlow =
|
||||||
DbSet.snapshotDao.count().stateIn(viewModelScope, SharingStarted.Eagerly, 0)
|
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.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.ramcosta.composedestinations.generated.destinations.AboutPageDestination
|
import com.ramcosta.composedestinations.generated.destinations.AboutPageDestination
|
||||||
import com.ramcosta.composedestinations.generated.destinations.AdvancedPageDestination
|
import com.ramcosta.composedestinations.generated.destinations.AdvancedPageDestination
|
||||||
import com.ramcosta.composedestinations.utils.toDestinationsNavigator
|
import com.ramcosta.composedestinations.utils.toDestinationsNavigator
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import li.songe.gkd.META
|
|
||||||
import li.songe.gkd.MainActivity
|
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.SettingItem
|
||||||
import li.songe.gkd.ui.component.TextMenu
|
import li.songe.gkd.ui.component.TextMenu
|
||||||
import li.songe.gkd.ui.component.TextSwitch
|
import li.songe.gkd.ui.component.TextSwitch
|
||||||
import li.songe.gkd.ui.component.updateDialogOptions
|
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.EmptyHeight
|
||||||
import li.songe.gkd.ui.style.itemPadding
|
|
||||||
import li.songe.gkd.ui.style.titleItemPadding
|
import li.songe.gkd.ui.style.titleItemPadding
|
||||||
import li.songe.gkd.ui.theme.supportDynamicColor
|
import li.songe.gkd.ui.theme.supportDynamicColor
|
||||||
import li.songe.gkd.util.DarkThemeOption
|
import li.songe.gkd.util.DarkThemeOption
|
||||||
import li.songe.gkd.util.LocalNavController
|
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.findOption
|
||||||
import li.songe.gkd.util.launchAsFn
|
|
||||||
import li.songe.gkd.util.launchTry
|
|
||||||
import li.songe.gkd.util.storeFlow
|
import li.songe.gkd.util.storeFlow
|
||||||
import li.songe.gkd.util.throttle
|
import li.songe.gkd.util.throttle
|
||||||
import li.songe.gkd.util.toast
|
|
||||||
|
|
||||||
val settingsNav = BottomNavItem(
|
val settingsNav = BottomNavItem(
|
||||||
label = "设置", icon = Icons.Outlined.Settings
|
label = "设置", icon = Icons.Outlined.Settings
|
||||||
|
@ -80,7 +70,6 @@ fun useSettingsPage(): ScaffoldExt {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val checkUpdating by context.mainVm.updateStatus.checkUpdatingFlow.collectAsState()
|
|
||||||
|
|
||||||
if (showToastInputDlg) {
|
if (showToastInputDlg) {
|
||||||
var value by remember {
|
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(
|
||||||
text = "其它",
|
text = "其它",
|
||||||
modifier = Modifier.titleItemPadding(),
|
modifier = Modifier.titleItemPadding(),
|
||||||
|
|
|
@ -9,6 +9,7 @@ const val SERVER_SCRIPT_URL =
|
||||||
"https://registry.npmmirror.com/@gkd-kit/config/latest/files/dist/server.js"
|
"https://registry.npmmirror.com/@gkd-kit/config/latest/files/dist/server.js"
|
||||||
|
|
||||||
const val REPOSITORY_URL = "https://github.com/gkd-kit/gkd"
|
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"
|
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_page_info: Int = R.drawable.ic_page_info
|
||||||
val ic_flash_on: Int = R.drawable.ic_flash_on
|
val ic_flash_on: Int = R.drawable.ic_flash_on
|
||||||
val ic_flash_off: Int = R.drawable.ic_flash_off
|
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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background_tint">#FCFCFC</color>
|
<color name="ic_launcher_background_tint">@color/better_white</color>
|
||||||
<color name="ic_launcher_foreground_tint">#111</color>
|
<color name="ic_launcher_foreground_tint">@color/better_black</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background_tint">#111</color>
|
<color name="better_white">#FCFCFC</color>
|
||||||
<color name="ic_launcher_foreground_tint">#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>
|
</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" }
|
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" }
|
android_gradle = { module = "com.android.tools.build:gradle", version.ref = "android" }
|
||||||
compose_ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
|
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_preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
|
||||||
compose_tooling = { module = "androidx.compose.ui:ui-tooling", 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" }
|
compose_junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user