mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-15 19:22:26 +08:00
feat: 支持显示 exampleUrls + 状态栏优化
This commit is contained in:
parent
05ddbb23ce
commit
7357897ce2
|
@ -184,4 +184,7 @@ dependencies {
|
|||
implementation(libs.others.reorderable)
|
||||
|
||||
implementation(libs.androidx.splashscreen)
|
||||
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.coil.gif)
|
||||
}
|
|
@ -5,7 +5,6 @@ import android.content.Context
|
|||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.dylanc.activityresult.launcher.PickContentLauncher
|
||||
|
@ -29,7 +28,6 @@ import li.songe.gkd.util.storeFlow
|
|||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : CompositionActivity({
|
||||
installSplashScreen()
|
||||
useLifeCycleLog()
|
||||
|
||||
val launcher = StartActivityLauncher(this)
|
||||
|
@ -47,7 +45,8 @@ class MainActivity : CompositionActivity({
|
|||
}
|
||||
|
||||
setContent {
|
||||
val navController = rememberNavController()
|
||||
|
||||
val navController = rememberNavController()
|
||||
AppTheme {
|
||||
UpgradeDialog()
|
||||
CompositionLocalProvider(
|
||||
|
|
|
@ -3,6 +3,8 @@ package li.songe.gkd.composition
|
|||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
|
||||
open class CompositionActivity(
|
||||
private val block: CompositionActivity.(Bundle?) -> Unit,
|
||||
|
@ -26,6 +28,8 @@ open class CompositionActivity(
|
|||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
installSplashScreen()
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
block(savedInstanceState)
|
||||
}
|
||||
|
|
|
@ -82,6 +82,20 @@ data class SubscriptionRaw(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IgnoredOnParcel
|
||||
val allExampleUrls by lazy {
|
||||
mutableListOf<String>().apply {
|
||||
if (exampleUrls != null) {
|
||||
addAll(exampleUrls)
|
||||
}
|
||||
rules.forEach { r ->
|
||||
if (r.exampleUrls != null) {
|
||||
addAll(r.exampleUrls)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
|
|
@ -58,6 +58,7 @@ import kotlinx.serialization.encodeToString
|
|||
import li.songe.gkd.data.SubsConfig
|
||||
import li.songe.gkd.data.SubscriptionRaw
|
||||
import li.songe.gkd.db.DbSet
|
||||
import li.songe.gkd.ui.destinations.GroupItemPageDestination
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.ProfileTransitions
|
||||
import li.songe.gkd.util.Singleton
|
||||
|
@ -66,6 +67,7 @@ import li.songe.gkd.util.launchAsFn
|
|||
import li.songe.gkd.util.launchTry
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.subsIdToRawFlow
|
||||
import li.songe.gkd.util.navigate
|
||||
|
||||
@RootNavGraph
|
||||
@Destination(style = ProfileTransitions::class)
|
||||
|
@ -238,16 +240,32 @@ fun AppItemPage(
|
|||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
val groupAppText = Singleton.json.encodeToString(
|
||||
appRaw?.copy(
|
||||
groups = listOf(showGroupItemVal)
|
||||
Row {
|
||||
if (showGroupItemVal.allExampleUrls.isNotEmpty()) {
|
||||
TextButton(onClick = {
|
||||
setShowGroupItem(null)
|
||||
navController.navigate(
|
||||
GroupItemPageDestination(
|
||||
subsInt = subsItemId,
|
||||
appId = appId,
|
||||
groupKey = showGroupItemVal.key
|
||||
)
|
||||
)
|
||||
}) {
|
||||
Text(text = "查看图片")
|
||||
}
|
||||
}
|
||||
TextButton(onClick = {
|
||||
val groupAppText = Singleton.json.encodeToString(
|
||||
appRaw?.copy(
|
||||
groups = listOf(showGroupItemVal)
|
||||
)
|
||||
)
|
||||
)
|
||||
ClipboardUtils.copyText(groupAppText)
|
||||
ToastUtils.showShort("复制成功")
|
||||
}) {
|
||||
Text(text = "复制规则组")
|
||||
ClipboardUtils.copyText(groupAppText)
|
||||
ToastUtils.showShort("复制成功")
|
||||
}) {
|
||||
Text(text = "复制规则组")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -15,11 +15,8 @@ import androidx.compose.foundation.verticalScroll
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
|
@ -48,7 +45,6 @@ import li.songe.gkd.util.usePollState
|
|||
|
||||
val controlNav = BottomNavItem(label = "主页", icon = SafeR.ic_home, route = "settings")
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ControlPage() {
|
||||
val context = LocalContext.current as MainActivity
|
||||
|
@ -64,116 +60,103 @@ fun ControlPage() {
|
|||
}
|
||||
val canDrawOverlays by usePollState { Settings.canDrawOverlays(context) }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(title = {
|
||||
Text(
|
||||
text = "GKD"
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(
|
||||
state = rememberScrollState()
|
||||
)
|
||||
) {
|
||||
if (!notifEnabled) {
|
||||
AuthCard(title = "通知权限", desc = "用于启动后台服务,展示服务运行状态", onAuthClick = {
|
||||
val intent = Intent()
|
||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, context.applicationInfo.uid)
|
||||
context.startActivity(intent)
|
||||
})
|
||||
},
|
||||
) { padding ->
|
||||
Divider()
|
||||
}
|
||||
|
||||
if (!gkdAccessRunning) {
|
||||
AuthCard(title = "无障碍权限",
|
||||
desc = "用于获取屏幕信息,点击屏幕上的控件",
|
||||
onAuthClick = {
|
||||
if (notifEnabled) {
|
||||
appScope.launchTry(Dispatchers.IO) {
|
||||
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
// android.content.ActivityNotFoundException
|
||||
// https://bugly.qq.com/v2/crash-reporting/crashes/d0ce46b353/113010?pid=1
|
||||
context.startActivity(intent)
|
||||
}
|
||||
} else {
|
||||
ToastUtils.showShort("必须先开启[通知权限]")
|
||||
}
|
||||
})
|
||||
Divider()
|
||||
}
|
||||
|
||||
if (!canDrawOverlays) {
|
||||
AuthCard(title = "悬浮窗权限",
|
||||
desc = "用于后台提示,显示保存快照按钮等功能",
|
||||
onAuthClick = {
|
||||
val intent = Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
context.startActivity(intent)
|
||||
})
|
||||
Divider()
|
||||
}
|
||||
|
||||
if (gkdAccessRunning) {
|
||||
TextSwitch(name = "服务开启",
|
||||
desc = "保持服务开启,根据订阅规则匹配屏幕目标节点",
|
||||
checked = store.enableService,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
enableService = it
|
||||
)
|
||||
)
|
||||
})
|
||||
Divider()
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
navController.navigate(ClickLogPageDestination)
|
||||
}
|
||||
.padding(10.dp, 5.dp),
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = "点击记录", fontSize = 18.sp
|
||||
)
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(
|
||||
text = "如误触可在此快速定位关闭规则", fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
Icon(imageVector = Icons.Default.KeyboardArrowRight, contentDescription = null)
|
||||
}
|
||||
Divider()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(
|
||||
state = rememberScrollState()
|
||||
)
|
||||
.padding(padding)
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp, 5.dp)
|
||||
) {
|
||||
if (!notifEnabled) {
|
||||
AuthCard(title = "通知权限",
|
||||
desc = "用于启动后台服务,展示服务运行状态",
|
||||
onAuthClick = {
|
||||
val intent = Intent()
|
||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, context.applicationInfo.uid)
|
||||
context.startActivity(intent)
|
||||
})
|
||||
Divider()
|
||||
Text(text = subsStatus, fontSize = 18.sp)
|
||||
if (latestRecordDesc != null) {
|
||||
Text(
|
||||
text = "最近点击: $latestRecordDesc", fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
|
||||
if (!gkdAccessRunning) {
|
||||
AuthCard(title = "无障碍权限",
|
||||
desc = "用于获取屏幕信息,点击屏幕上的控件",
|
||||
onAuthClick = {
|
||||
if (notifEnabled) {
|
||||
appScope.launchTry(Dispatchers.IO) {
|
||||
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
// android.content.ActivityNotFoundException
|
||||
// https://bugly.qq.com/v2/crash-reporting/crashes/d0ce46b353/113010?pid=1
|
||||
context.startActivity(intent)
|
||||
}
|
||||
} else {
|
||||
ToastUtils.showShort("必须先开启[通知权限]")
|
||||
}
|
||||
})
|
||||
Divider()
|
||||
}
|
||||
|
||||
if (!canDrawOverlays) {
|
||||
AuthCard(title = "悬浮窗权限",
|
||||
desc = "用于后台提示,显示保存快照按钮等功能",
|
||||
onAuthClick = {
|
||||
val intent = Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
context.startActivity(intent)
|
||||
})
|
||||
Divider()
|
||||
}
|
||||
|
||||
if (gkdAccessRunning) {
|
||||
TextSwitch(name = "服务开启",
|
||||
desc = "保持服务开启,根据订阅规则匹配屏幕目标节点",
|
||||
checked = store.enableService,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
enableService = it
|
||||
)
|
||||
)
|
||||
})
|
||||
Divider()
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
navController.navigate(ClickLogPageDestination)
|
||||
}
|
||||
.padding(10.dp, 5.dp),
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = "点击记录", fontSize = 18.sp
|
||||
)
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(
|
||||
text = "如误触可在此快速定位关闭规则", fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
Icon(imageVector = Icons.Default.KeyboardArrowRight, contentDescription = null)
|
||||
}
|
||||
Divider()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp, 5.dp)
|
||||
) {
|
||||
Text(text = subsStatus, fontSize = 18.sp)
|
||||
if (latestRecordDesc != null) {
|
||||
Text(
|
||||
text = "最近点击: $latestRecordDesc", fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
110
app/src/main/java/li/songe/gkd/ui/GroupItemPage.kt
Normal file
110
app/src/main/java/li/songe/gkd/ui/GroupItemPage.kt
Normal file
|
@ -0,0 +1,110 @@
|
|||
package li.songe.gkd.ui
|
||||
|
||||
import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.Singleton.imageLoader
|
||||
import li.songe.gkd.util.appInfoCacheFlow
|
||||
import li.songe.gkd.util.subsIdToRawFlow
|
||||
|
||||
|
||||
@RootNavGraph
|
||||
@Destination
|
||||
@Composable
|
||||
fun GroupItemPage(subsInt: Long, appId: String, groupKey: Int) {
|
||||
val navController = LocalNavController.current
|
||||
val subsIdToRawState = subsIdToRawFlow.collectAsState()
|
||||
val appRaw = remember {
|
||||
subsIdToRawState.value[subsInt]?.apps?.first { a -> a.id == appId }
|
||||
}
|
||||
val group = remember {
|
||||
appRaw?.groups?.find { g -> g.key == groupKey }
|
||||
}
|
||||
val appInfoCache by appInfoCacheFlow.collectAsState()
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = ((appInfoCache[appId]?.name ?: appRaw?.name
|
||||
?: appId) + "/" + (group?.name ?: "未知规则"))
|
||||
)
|
||||
},
|
||||
actions = {},
|
||||
modifier = Modifier.zIndex(1f),
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background.copy(alpha = 0.5f)
|
||||
)
|
||||
)
|
||||
if (group != null) {
|
||||
val state = rememberPagerState { group.allExampleUrls.size }
|
||||
HorizontalPager(
|
||||
modifier = Modifier.fillMaxSize(), state = state
|
||||
) { p ->
|
||||
val url = group.allExampleUrls.getOrNull(p)
|
||||
if (url != null) {
|
||||
SubcomposeAsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current).data(url)
|
||||
.crossfade(DefaultDurationMillis).build(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
loading = {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
CircularProgressIndicator(modifier = Modifier.size(50.dp))
|
||||
}
|
||||
},
|
||||
error = {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = "加载失败", color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
},
|
||||
imageLoader = imageLoader
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import androidx.compose.material3.NavigationBar
|
|||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
|
@ -35,7 +36,13 @@ fun HomePage() {
|
|||
val vm = hiltViewModel<HomePageVm>()
|
||||
val tab by vm.tabFlow.collectAsState()
|
||||
|
||||
Scaffold(bottomBar = {
|
||||
Scaffold(topBar = {
|
||||
TopAppBar(title = {
|
||||
Text(
|
||||
text = tab.label,
|
||||
)
|
||||
})
|
||||
}, bottomBar = {
|
||||
NavigationBar {
|
||||
BottomNavItems.forEach { navItem ->
|
||||
NavigationBarItem(selected = tab == navItem, modifier = Modifier, onClick = {
|
||||
|
|
|
@ -1,57 +1,70 @@
|
|||
package li.songe.gkd.ui
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
import androidx.compose.ui.zIndex
|
||||
import coil.compose.AsyncImage
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
import li.songe.gkd.util.LaunchedEffectTry
|
||||
import li.songe.gkd.util.LocalNavController
|
||||
import li.songe.gkd.util.ProfileTransitions
|
||||
|
||||
@RootNavGraph
|
||||
@Destination(style = ProfileTransitions::class)
|
||||
@Composable
|
||||
fun ImagePreviewPage(
|
||||
filePath: String?,
|
||||
filePath: String,
|
||||
title: String? = null,
|
||||
) {
|
||||
val (bitmap, setBitmap) = remember {
|
||||
mutableStateOf<ImageBitmap?>(null)
|
||||
}
|
||||
LaunchedEffectTry {
|
||||
if (filePath != null) {
|
||||
setBitmap(withContext(IO) { BitmapFactory.decodeFile(filePath).asImageBitmap() })
|
||||
} else {
|
||||
ToastUtils.showShort("图片加载失败")
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
if (bitmap != null) {
|
||||
Image(
|
||||
bitmap = bitmap,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(0.dp)
|
||||
val navController = LocalNavController.current
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
navController.popBackStack()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
if (title != null) {
|
||||
Text(text = title)
|
||||
}
|
||||
},
|
||||
actions = {},
|
||||
modifier = Modifier.zIndex(1f),
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background.copy(alpha = 0.5f)
|
||||
)
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
AsyncImage(
|
||||
model = filePath, contentDescription = null, modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -18,15 +18,12 @@ import androidx.compose.material.icons.filled.KeyboardArrowRight
|
|||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
|
@ -71,7 +68,6 @@ val settingsNav = BottomNavItem(
|
|||
label = "设置", icon = SafeR.ic_cog, route = "settings"
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsPage() {
|
||||
val context = LocalContext.current as MainActivity
|
||||
|
@ -99,199 +95,189 @@ fun SettingsPage() {
|
|||
|
||||
val checkUpdating by checkUpdatingFlow.collectAsState()
|
||||
|
||||
Scaffold(topBar = {
|
||||
TopAppBar(title = {
|
||||
Text(
|
||||
text = "设置"
|
||||
)
|
||||
})
|
||||
}, content = { contentPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(
|
||||
state = rememberScrollState()
|
||||
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(
|
||||
state = rememberScrollState()
|
||||
)
|
||||
) {
|
||||
TextSwitch(name = "后台隐藏",
|
||||
desc = "在[最近任务]界面中隐藏本应用",
|
||||
checked = store.excludeFromRecents,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
excludeFromRecents = it
|
||||
)
|
||||
)
|
||||
.padding(0.dp, 10.dp)
|
||||
.padding(contentPadding)
|
||||
) {
|
||||
})
|
||||
Divider()
|
||||
|
||||
TextSwitch(name = "后台隐藏",
|
||||
desc = "在[最近任务]界面中隐藏本应用",
|
||||
checked = store.excludeFromRecents,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
excludeFromRecents = it
|
||||
)
|
||||
TextSwitch(name = "无障碍前台",
|
||||
desc = "添加前台透明悬浮窗,关闭可能导致不工作",
|
||||
checked = store.enableAbFloatWindow,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
enableAbFloatWindow = it
|
||||
)
|
||||
})
|
||||
Divider()
|
||||
|
||||
TextSwitch(name = "无障碍前台",
|
||||
desc = "添加前台透明悬浮窗,关闭可能导致不工作",
|
||||
checked = store.enableAbFloatWindow,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
enableAbFloatWindow = it
|
||||
)
|
||||
)
|
||||
})
|
||||
Divider()
|
||||
|
||||
TextSwitch(name = "点击提示",
|
||||
desc = "触发点击时提示:[${store.clickToast}]",
|
||||
checked = store.toastWhenClick,
|
||||
modifier = Modifier.clickable {
|
||||
showToastInputDlg = true
|
||||
},
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
toastWhenClick = it
|
||||
)
|
||||
)
|
||||
if (it && !Settings.canDrawOverlays(context)) {
|
||||
ToastUtils.showShort("需要悬浮窗权限")
|
||||
}
|
||||
})
|
||||
Divider()
|
||||
|
||||
Row(modifier = Modifier
|
||||
.clickable {
|
||||
showSubsIntervalDlg = true
|
||||
}
|
||||
.padding(10.dp, 15.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f), text = "自动更新订阅", fontSize = 18.sp
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = updateTimeRadioOptions.find { it.second == store.updateSubsInterval }?.first
|
||||
?: store.updateSubsInterval.toString(), fontSize = 14.sp
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight, contentDescription = "more"
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
})
|
||||
Divider()
|
||||
|
||||
TextSwitch(name = "自动更新应用",
|
||||
desc = "打开应用时自动检测是否存在新版本",
|
||||
checked = store.autoCheckAppUpdate,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
autoCheckAppUpdate = it
|
||||
)
|
||||
TextSwitch(name = "点击提示",
|
||||
desc = "触发点击时提示:[${store.clickToast}]",
|
||||
checked = store.toastWhenClick,
|
||||
modifier = Modifier.clickable {
|
||||
showToastInputDlg = true
|
||||
},
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
toastWhenClick = it
|
||||
)
|
||||
})
|
||||
Divider()
|
||||
|
||||
SettingItem(title = if (checkUpdating) "检查更新ing" else "检查更新", onClick = {
|
||||
appScope.launchTry {
|
||||
if (checkUpdatingFlow.value) return@launchTry
|
||||
val newVersion = checkUpdate()
|
||||
if (newVersion == null) {
|
||||
ToastUtils.showShort("暂无更新")
|
||||
}
|
||||
)
|
||||
if (it && !Settings.canDrawOverlays(context)) {
|
||||
ToastUtils.showShort("需要悬浮窗权限")
|
||||
}
|
||||
})
|
||||
Divider()
|
||||
Divider()
|
||||
|
||||
Row(modifier = Modifier
|
||||
.clickable {
|
||||
showEnableDarkThemeDlg = true
|
||||
}
|
||||
.padding(10.dp, 15.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(modifier = Modifier
|
||||
.clickable {
|
||||
showSubsIntervalDlg = true
|
||||
}
|
||||
.padding(10.dp, 15.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f), text = "自动更新订阅", fontSize = 18.sp
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f), text = "深色模式", fontSize = 18.sp
|
||||
text = updateTimeRadioOptions.find { it.second == store.updateSubsInterval }?.first
|
||||
?: store.updateSubsInterval.toString(), fontSize = 14.sp
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = darkThemeRadioOptions.find { it.second == store.enableDarkTheme }?.first
|
||||
?: store.enableDarkTheme.toString(), fontSize = 14.sp
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight, contentDescription = "more"
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight, contentDescription = "more"
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
|
||||
TextSwitch(name = "自动更新应用",
|
||||
desc = "打开应用时自动检测是否存在新版本",
|
||||
checked = store.autoCheckAppUpdate,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
autoCheckAppUpdate = it
|
||||
)
|
||||
)
|
||||
})
|
||||
Divider()
|
||||
|
||||
SettingItem(title = if (checkUpdating) "检查更新ing" else "检查更新", onClick = {
|
||||
appScope.launchTry {
|
||||
if (checkUpdatingFlow.value) return@launchTry
|
||||
val newVersion = checkUpdate()
|
||||
if (newVersion == null) {
|
||||
ToastUtils.showShort("暂无更新")
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
Row(modifier = Modifier
|
||||
.clickable {
|
||||
showEnableGroupDlg = true
|
||||
}
|
||||
.padding(10.dp, 15.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f), text = "规则启用", fontSize = 18.sp
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = enableGroupRadioOptions.find { it.second == store.enableGroup }?.first
|
||||
?: store.enableGroup.toString(), fontSize = 14.sp
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight, contentDescription = "more"
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
})
|
||||
Divider()
|
||||
|
||||
TextSwitch(name = "保存日志",
|
||||
desc = "保存最近7天的日志,大概占用您5M的空间",
|
||||
checked = store.log2FileSwitch,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
log2FileSwitch = it
|
||||
)
|
||||
Row(modifier = Modifier
|
||||
.clickable {
|
||||
showEnableDarkThemeDlg = true
|
||||
}
|
||||
.padding(10.dp, 15.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f), text = "深色模式", fontSize = 18.sp
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = darkThemeRadioOptions.find { it.second == store.enableDarkTheme }?.first
|
||||
?: store.enableDarkTheme.toString(), fontSize = 14.sp
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight, contentDescription = "more"
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
Row(modifier = Modifier
|
||||
.clickable {
|
||||
showEnableGroupDlg = true
|
||||
}
|
||||
.padding(10.dp, 15.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f), text = "规则启用", fontSize = 18.sp
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = enableGroupRadioOptions.find { it.second == store.enableGroup }?.first
|
||||
?: store.enableGroup.toString(), fontSize = 14.sp
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight, contentDescription = "more"
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
|
||||
TextSwitch(name = "保存日志",
|
||||
desc = "保存最近7天的日志,大概占用您5M的空间",
|
||||
checked = store.log2FileSwitch,
|
||||
onCheckedChange = {
|
||||
updateStorage(
|
||||
storeFlow, store.copy(
|
||||
log2FileSwitch = it
|
||||
)
|
||||
if (!it) {
|
||||
appScope.launchTry(Dispatchers.IO) {
|
||||
val logFiles = LogUtils.getLogFiles()
|
||||
if (logFiles.isNotEmpty()) {
|
||||
logFiles.forEach { f ->
|
||||
f.delete()
|
||||
}
|
||||
ToastUtils.showShort("已删除全部日志")
|
||||
)
|
||||
if (!it) {
|
||||
appScope.launchTry(Dispatchers.IO) {
|
||||
val logFiles = LogUtils.getLogFiles()
|
||||
if (logFiles.isNotEmpty()) {
|
||||
logFiles.forEach { f ->
|
||||
f.delete()
|
||||
}
|
||||
ToastUtils.showShort("已删除全部日志")
|
||||
}
|
||||
}
|
||||
})
|
||||
Divider()
|
||||
|
||||
SettingItem(title = "分享日志", onClick = {
|
||||
vm.viewModelScope.launchTry(Dispatchers.IO) {
|
||||
val logFiles = LogUtils.getLogFiles()
|
||||
if (logFiles.isNotEmpty()) {
|
||||
showShareLogDlg = true
|
||||
} else {
|
||||
ToastUtils.showShort("暂无日志")
|
||||
}
|
||||
}
|
||||
})
|
||||
Divider()
|
||||
Divider()
|
||||
|
||||
SettingItem(title = "高级模式", onClick = {
|
||||
navController.navigate(DebugPageDestination)
|
||||
})
|
||||
Divider()
|
||||
SettingItem(title = "分享日志", onClick = {
|
||||
vm.viewModelScope.launchTry(Dispatchers.IO) {
|
||||
val logFiles = LogUtils.getLogFiles()
|
||||
if (logFiles.isNotEmpty()) {
|
||||
showShareLogDlg = true
|
||||
} else {
|
||||
ToastUtils.showShort("暂无日志")
|
||||
}
|
||||
}
|
||||
})
|
||||
Divider()
|
||||
|
||||
SettingItem(title = "关于", onClick = {
|
||||
navController.navigate(AboutPageDestination)
|
||||
})
|
||||
SettingItem(title = "高级模式", onClick = {
|
||||
navController.navigate(DebugPageDestination)
|
||||
})
|
||||
Divider()
|
||||
|
||||
SettingItem(title = "关于", onClick = {
|
||||
navController.navigate(AboutPageDestination)
|
||||
})
|
||||
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
}
|
||||
})
|
||||
|
||||
if (showSubsIntervalDlg) {
|
||||
Dialog(onDismissRequest = { showSubsIntervalDlg = false }) {
|
||||
|
@ -348,7 +334,8 @@ fun SettingsPage() {
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(selected = (option.second == store.enableDarkTheme),
|
||||
.selectable(
|
||||
selected = (option.second == store.enableDarkTheme),
|
||||
onClick = {
|
||||
updateStorage(
|
||||
storeFlow,
|
||||
|
@ -388,7 +375,8 @@ fun SettingsPage() {
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(selected = (option.second == store.enableGroup),
|
||||
.selectable(
|
||||
selected = (option.second == store.enableGroup),
|
||||
onClick = {
|
||||
updateStorage(
|
||||
storeFlow,
|
||||
|
|
|
@ -42,6 +42,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
@ -139,10 +140,28 @@ fun SnapshotPage() {
|
|||
fontFamily = FontFamily.Monospace
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Text(text = snapshot.appName ?: "")
|
||||
Text(
|
||||
text = snapshot.appName ?: snapshot.appId ?: snapshot.id.toString(),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
if (snapshot.activityId != null) {
|
||||
val showActivityId =
|
||||
if (snapshot.appId != null && snapshot.activityId.startsWith(
|
||||
snapshot.appId
|
||||
)
|
||||
) {
|
||||
snapshot.activityId.substring(snapshot.appId.length)
|
||||
} else {
|
||||
snapshot.activityId
|
||||
}
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Text(
|
||||
text = showActivityId, overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Text(text = snapshot.activityId ?: "")
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
|
@ -180,7 +199,8 @@ fun SnapshotPage() {
|
|||
.clickable(onClick = scope.launchAsFn {
|
||||
navController.navigate(
|
||||
ImagePreviewPageDestination(
|
||||
filePath = snapshotVal.screenshotFile.absolutePath
|
||||
filePath = snapshotVal.screenshotFile.absolutePath,
|
||||
title = snapshotVal.appName,
|
||||
)
|
||||
)
|
||||
selectedSnapshot = null
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package li.songe.gkd.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.webkit.URLUtil
|
||||
|
@ -28,7 +29,6 @@ import androidx.compose.material3.OutlinedTextField
|
|||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material3.pullrefresh.pullRefresh
|
||||
import androidx.compose.material3.pullrefresh.rememberPullRefreshState
|
||||
|
@ -76,6 +76,7 @@ val subsNav = BottomNavItem(
|
|||
label = "订阅", icon = SafeR.ic_link, route = "subscription"
|
||||
)
|
||||
|
||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||
@Composable
|
||||
fun SubsManagePage() {
|
||||
val context = LocalContext.current
|
||||
|
@ -130,13 +131,6 @@ fun SubsManagePage() {
|
|||
})
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(title = {
|
||||
Text(
|
||||
text = "订阅",
|
||||
)
|
||||
})
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = {
|
||||
if (subItems.any { it.id == 0L }) {
|
||||
|
@ -151,11 +145,10 @@ fun SubsManagePage() {
|
|||
)
|
||||
}
|
||||
},
|
||||
) { padding ->
|
||||
) { _ ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.pullRefresh(pullRefreshState, subItems.isNotEmpty())
|
||||
) {
|
||||
LazyColumn(
|
||||
|
@ -261,15 +254,13 @@ fun SubsManagePage() {
|
|||
Divider()
|
||||
}
|
||||
if (menuSubItemVal.id != -2L) {
|
||||
Text(text = "删除订阅",
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
deleteSubItem = menuSubItemVal
|
||||
menuSubItem = null
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
color = MaterialTheme.colorScheme.error)
|
||||
Text(text = "删除订阅", modifier = Modifier
|
||||
.clickable {
|
||||
deleteSubItem = menuSubItemVal
|
||||
menuSubItem = null
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp), color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,6 @@ fun SubsPage(
|
|||
focusRequester.requestFocus()
|
||||
}
|
||||
})
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
|
@ -137,8 +136,11 @@ fun SubsPage(
|
|||
}, actions = {
|
||||
if (showSearchBar) {
|
||||
IconButton(onClick = {
|
||||
showSearchBar = false
|
||||
vm.searchStrFlow.value = ""
|
||||
if (vm.searchStrFlow.value.isEmpty()) {
|
||||
showSearchBar = false
|
||||
} else {
|
||||
vm.searchStrFlow.value = ""
|
||||
}
|
||||
}) {
|
||||
Icon(Icons.Outlined.Close, contentDescription = null)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import li.songe.gkd.util.appInfoCacheFlow
|
|||
import li.songe.gkd.util.map
|
||||
import li.songe.gkd.util.storeFlow
|
||||
import li.songe.gkd.util.subsIdToRawFlow
|
||||
import li.songe.gkd.util.subsItemsFlow
|
||||
import java.text.Collator
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
@ -23,8 +24,8 @@ import javax.inject.Inject
|
|||
class SubsVm @Inject constructor(stateHandle: SavedStateHandle) : ViewModel() {
|
||||
private val args = SubsPageDestination.argsFrom(stateHandle)
|
||||
|
||||
val subsItemFlow = DbSet.subsItemDao.queryById(args.subsItemId)
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, null)
|
||||
val subsItemFlow =
|
||||
subsItemsFlow.map(viewModelScope) { s -> s.find { v -> v.id == args.subsItemId } }
|
||||
|
||||
private val appSubsConfigsFlow = DbSet.subsConfigDao.queryAppTypeConfig(args.subsItemId)
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
|
||||
|
|
|
@ -4,35 +4,26 @@ import androidx.compose.animation.AnimatedContentTransitionScope
|
|||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import com.blankj.utilcode.util.ScreenUtils
|
||||
import com.ramcosta.composedestinations.spec.DestinationStyle
|
||||
|
||||
object ProfileTransitions : DestinationStyle.Animated {
|
||||
private const val durationMillis = 400
|
||||
override fun AnimatedContentTransitionScope<NavBackStackEntry>.enterTransition(): EnterTransition? {
|
||||
return slideInHorizontally(
|
||||
initialOffsetX = { ScreenUtils.getScreenWidth() }, animationSpec = tween(durationMillis)
|
||||
)
|
||||
return slideInHorizontally(tween()) { it }
|
||||
}
|
||||
|
||||
override fun AnimatedContentTransitionScope<NavBackStackEntry>.exitTransition(): ExitTransition? {
|
||||
return slideOutHorizontally(
|
||||
targetOffsetX = { -ScreenUtils.getScreenWidth()/2 }, animationSpec = tween(durationMillis)
|
||||
)
|
||||
return slideOutHorizontally(tween()) { -it } + fadeOut(tween())
|
||||
}
|
||||
|
||||
override fun AnimatedContentTransitionScope<NavBackStackEntry>.popEnterTransition(): EnterTransition? {
|
||||
return slideInHorizontally(
|
||||
initialOffsetX = { -ScreenUtils.getScreenWidth()/2 }, animationSpec = tween(durationMillis)
|
||||
)
|
||||
return slideInHorizontally(tween()) { -it }
|
||||
}
|
||||
|
||||
override fun AnimatedContentTransitionScope<NavBackStackEntry>.popExitTransition(): ExitTransition? {
|
||||
return slideOutHorizontally(
|
||||
targetOffsetX = { ScreenUtils.getScreenWidth() }, animationSpec = tween(durationMillis)
|
||||
)
|
||||
return slideOutHorizontally(tween()) { it }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
package li.songe.gkd.util
|
||||
|
||||
import android.os.Build
|
||||
import coil.ImageLoader
|
||||
import coil.decode.GifDecoder
|
||||
import coil.decode.ImageDecoderDecoder
|
||||
import coil.disk.DiskCache
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.okhttp.OkHttp
|
||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||
|
@ -7,6 +12,7 @@ import io.ktor.http.ContentType
|
|||
import io.ktor.serialization.kotlinx.json.json
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import li.songe.gkd.app
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
object Singleton {
|
||||
|
@ -39,4 +45,19 @@ object Singleton {
|
|||
}
|
||||
}
|
||||
|
||||
val imageLoader by lazy {
|
||||
ImageLoader.Builder(app).components {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
add(ImageDecoderDecoder.Factory())
|
||||
} else {
|
||||
add(GifDecoder.Factory())
|
||||
}
|
||||
}.diskCache {
|
||||
app.filesDir
|
||||
app.getExternalFilesDir(null)
|
||||
DiskCache.Builder()
|
||||
.directory((app.externalCacheDir ?: app.cacheDir).resolve("imageCache")).build()
|
||||
}.build()
|
||||
}
|
||||
|
||||
}
|
|
@ -75,7 +75,7 @@ dependencyResolutionManagement {
|
|||
library("compose.tooling", "androidx.compose.ui:ui-tooling:$composeVersion")
|
||||
library("compose.junit4", "androidx.compose.ui:ui-test-junit4:$composeVersion")
|
||||
library("compose.material3", "androidx.compose.material3:material3:1.1.2")
|
||||
library("compose.activity", "androidx.activity:activity-compose:1.7.2")
|
||||
library("compose.activity", "androidx.activity:activity-compose:1.8.0-rc01")
|
||||
|
||||
// https://github.com/Tencent/MMKV/blob/master/README_CN.md
|
||||
library("tencent.mmkv", "com.tencent:mmkv:1.3.1")
|
||||
|
@ -186,6 +186,9 @@ dependencyResolutionManagement {
|
|||
)
|
||||
library("destinations.ksp", "io.github.raamcosta.compose-destinations:ksp:1.9.54")
|
||||
|
||||
library("coil.compose", "io.coil-kt:coil-compose:2.5.0")
|
||||
library("coil.gif", "io.coil-kt:coil-gif:2.5.0")
|
||||
|
||||
// https://github.com/aclassen/ComposeReorderable
|
||||
library("others.reorderable", "org.burnoutcrew.composereorderable:reorderable:0.9.6")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user