perf: 优化 TopBar

This commit is contained in:
lisonge 2023-10-24 15:30:08 +08:00
parent 9c458e245c
commit 83677ea6cf
6 changed files with 208 additions and 97 deletions

View File

@ -3,15 +3,19 @@ package li.songe.gkd.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
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.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import li.songe.gkd.BuildConfig
import li.songe.gkd.ui.component.SimpleTopAppBar
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.ProfileTransitions
@ -21,7 +25,16 @@ import li.songe.gkd.util.ProfileTransitions
fun AboutPage() {
val navController = LocalNavController.current
Scaffold(topBar = {
SimpleTopAppBar(onClickIcon = { navController.popBackStack() }, title = "关于")
TopAppBar(navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null,
)
}
}, title = { Text(text = "关于") }, actions = {})
}, content = { contentPadding ->
Column(
Modifier

View File

@ -12,13 +12,13 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
@ -31,6 +31,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
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
@ -57,7 +58,6 @@ 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.component.SimpleTopAppBar
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.ProfileTransitions
import li.songe.gkd.util.Singleton
@ -105,18 +105,27 @@ fun AppItemPage(
}
Scaffold(topBar = {
SimpleTopAppBar(
onClickIcon = { navController.popBackStack() },
title = if (subsItem == null) "订阅文件缺失" else (appInfoCache[appRaw?.id]?.name
?: appRaw?.name ?: appRaw?.id ?: "")
)
TopAppBar(navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null,
)
}
}, title = {
Text(
text = if (subsItem == null) "订阅文件缺失" else (appInfoCache[appRaw?.id]?.name
?: appRaw?.name ?: appRaw?.id ?: "")
)
}, actions = {})
}, floatingActionButton = {
if (editable) {
FloatingActionButton(onClick = { showAddDlg = true }) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "add",
modifier = Modifier.size(30.dp)
)
}
}
@ -182,7 +191,6 @@ fun AppItemPage(
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = "more",
modifier = Modifier.size(30.dp)
)
}
Spacer(modifier = Modifier.width(10.dp))

View File

@ -8,13 +8,13 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.Divider
@ -24,6 +24,8 @@ 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.collectAsState
import androidx.compose.runtime.getValue
@ -33,16 +35,19 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.hilt.navigation.compose.hiltViewModel
import com.blankj.utilcode.util.LogUtils
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import kotlinx.coroutines.Dispatchers
import li.songe.gkd.data.ClickLog
import li.songe.gkd.db.DbSet
import li.songe.gkd.ui.component.SimpleTopAppBar
import li.songe.gkd.ui.destinations.AppItemPageDestination
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.ProfileTransitions
@ -70,16 +75,26 @@ fun ClickLogPage() {
mutableStateOf(false)
}
Scaffold(topBar = {
SimpleTopAppBar(onClickIcon = { navController.popBackStack() },
title = "点击记录" + if (clickLogCount <= 0) "" else ("-$clickLogCount"),
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
TopAppBar(scrollBehavior = scrollBehavior,
navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null,
)
}
},
title = { Text(text = "点击记录" + if (clickLogCount <= 0) "" else ("-$clickLogCount")) },
actions = {
if (clickLogs.isNotEmpty()) {
IconButton(onClick = { showDeleteDlg = true }) {
Icon(
imageVector = Icons.Default.Delete,
imageVector = Icons.Outlined.Delete,
contentDescription = null,
modifier = Modifier.size(30.dp)
)
}
}
@ -89,9 +104,6 @@ fun ClickLogPage() {
LazyColumn(
modifier = Modifier.padding(contentPadding),
) {
item {
Spacer(modifier = Modifier.height(5.dp))
}
items(clickLogs, { triggerLog -> triggerLog.id }) { triggerLog ->
Column(modifier = Modifier
.clickable {
@ -134,12 +146,13 @@ fun ClickLogPage() {
}
} else {
Column(
modifier = Modifier.fillMaxSize(),
modifier = Modifier
.padding(contentPadding)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.height(30.dp))
Spacer(modifier = Modifier.height(40.dp))
Text(text = "暂无记录")
}
}
})

View File

@ -14,13 +14,17 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
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.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@ -50,7 +54,6 @@ import li.songe.gkd.shizuku.safeGetTasks
import li.songe.gkd.shizuku.shizukuIsSafeOK
import li.songe.gkd.ui.component.AuthCard
import li.songe.gkd.ui.component.SettingItem
import li.songe.gkd.ui.component.SimpleTopAppBar
import li.songe.gkd.ui.component.TextSwitch
import li.songe.gkd.ui.destinations.SnapshotPageDestination
import li.songe.gkd.util.Ext
@ -79,9 +82,16 @@ fun DebugPage() {
}
Scaffold(topBar = {
SimpleTopAppBar(
onClickIcon = { navController.popBackStack() }, title = "高级模式"
)
TopAppBar(navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null,
)
}
}, title = { Text(text = "高级模式") }, actions = {})
}, content = { contentPadding ->
Column(
modifier = Modifier
@ -94,42 +104,41 @@ fun DebugPage() {
val shizukuIsOk by usePollState { shizukuIsSafeOK() }
if (!shizukuIsOk) {
AuthCard(title = "Shizuku授权",
desc = "高级运行模式,能更准确识别界面活动ID",
onAuthClick = {
try {
Shizuku.requestPermission(Activity.RESULT_OK)
} catch (e: Exception) {
ToastUtils.showShort("Shizuku可能没有运行")
}
})
desc = "高级运行模式,能更准确识别界面活动ID",
onAuthClick = {
try {
Shizuku.requestPermission(Activity.RESULT_OK)
} catch (e: Exception) {
ToastUtils.showShort("Shizuku可能没有运行")
}
})
Divider()
} else {
TextSwitch(name = "Shizuku模式",
desc = "高级运行模式,能更准确识别界面活动ID",
checked = store.enableShizuku,
onCheckedChange = { enableShizuku ->
if (enableShizuku) {
appScope.launchTry(Dispatchers.IO) {
// 检验方法是否适配, 再允许使用 shizuku
val tasks =
newActivityTaskManager()?.safeGetTasks()?.firstOrNull()
if (tasks != null) {
updateStorage(
storeFlow, store.copy(
enableShizuku = true
)
)
}
}
} else {
updateStorage(
storeFlow, store.copy(
enableShizuku = false
)
)
}
desc = "高级运行模式,能更准确识别界面活动ID",
checked = store.enableShizuku,
onCheckedChange = { enableShizuku ->
if (enableShizuku) {
appScope.launchTry(Dispatchers.IO) {
// 检验方法是否适配, 再允许使用 shizuku
val tasks = newActivityTaskManager()?.safeGetTasks()?.firstOrNull()
if (tasks != null) {
updateStorage(
storeFlow, store.copy(
enableShizuku = true
)
)
}
}
} else {
updateStorage(
storeFlow, store.copy(
enableShizuku = false
)
)
}
})
})
Divider()
}
@ -178,25 +187,25 @@ fun DebugPage() {
// Build.VERSION.SDK_INT < Build.VERSION_CODES.R
val screenshotRunning by usePollState { ScreenshotService.isRunning() }
TextSwitch(name = "截屏服务",
desc = "生成快照需要获取屏幕截图,Android11无需开启",
checked = screenshotRunning,
onCheckedChange = appScope.launchAsFn<Boolean> {
if (!NotificationManagerCompat.from(context).areNotificationsEnabled()) {
ToastUtils.showShort("需要通知权限")
return@launchAsFn
}
if (it) {
val mediaProjectionManager =
context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
val activityResult =
launcher.launchForResult(mediaProjectionManager.createScreenCaptureIntent())
if (activityResult.resultCode == Activity.RESULT_OK && activityResult.data != null) {
ScreenshotService.start(intent = activityResult.data!!)
}
} else {
ScreenshotService.stop()
}
})
desc = "生成快照需要获取屏幕截图,Android11无需开启",
checked = screenshotRunning,
onCheckedChange = appScope.launchAsFn<Boolean> {
if (!NotificationManagerCompat.from(context).areNotificationsEnabled()) {
ToastUtils.showShort("需要通知权限")
return@launchAsFn
}
if (it) {
val mediaProjectionManager =
context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
val activityResult =
launcher.launchForResult(mediaProjectionManager.createScreenCaptureIntent())
if (activityResult.resultCode == Activity.RESULT_OK && activityResult.data != null) {
ScreenshotService.start(intent = activityResult.data!!)
}
} else {
ScreenshotService.stop()
}
})
Divider()

View File

@ -9,17 +9,16 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.Divider
@ -30,6 +29,8 @@ 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.collectAsState
import androidx.compose.runtime.getValue
@ -39,6 +40,7 @@ import androidx.compose.runtime.rememberCoroutineScope
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.text.font.FontFamily
import androidx.compose.ui.unit.dp
@ -59,7 +61,6 @@ import kotlinx.coroutines.withContext
import li.songe.gkd.data.Snapshot
import li.songe.gkd.db.DbSet
import li.songe.gkd.debug.SnapshotExt
import li.songe.gkd.ui.component.SimpleTopAppBar
import li.songe.gkd.ui.destinations.ImagePreviewPageDestination
import li.songe.gkd.util.LoadStatus
import li.songe.gkd.util.LocalNavController
@ -99,16 +100,26 @@ fun SnapshotPage() {
mutableStateOf(false)
}
Scaffold(topBar = {
SimpleTopAppBar(onClickIcon = { navController.popBackStack() },
title = if (snapshots.isEmpty()) "快照记录" else "快照记录-${snapshots.size}",
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
TopAppBar(scrollBehavior = scrollBehavior,
navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null,
)
}
},
title = { Text(text = if (snapshots.isEmpty()) "快照记录" else "快照记录-${snapshots.size}") },
actions = {
if (snapshots.isNotEmpty()) {
IconButton(onClick = { showDeleteDlg = true }) {
Icon(
imageVector = Icons.Default.Delete,
imageVector = Icons.Outlined.Delete,
contentDescription = null,
modifier = Modifier.size(30.dp)
)
}
}
@ -144,10 +155,12 @@ fun SnapshotPage() {
}
} else {
Column(
modifier = Modifier.fillMaxSize(),
modifier = Modifier
.padding(contentPadding)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.height(30.dp))
Spacer(modifier = Modifier.height(40.dp))
Text(text = "暂无记录")
}
}

View File

@ -1,8 +1,9 @@
package li.songe.gkd.ui
import android.content.Intent
import android.net.Uri
import android.webkit.URLUtil
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@ -11,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
@ -20,7 +20,6 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
@ -44,6 +43,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
@ -54,12 +54,14 @@ import com.blankj.utilcode.util.ToastUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import li.songe.gkd.data.SubsItem
import li.songe.gkd.data.SubscriptionRaw
import li.songe.gkd.db.DbSet
import li.songe.gkd.ui.component.SubsItemCard
import li.songe.gkd.ui.destinations.SubsPageDestination
import li.songe.gkd.util.DEFAULT_SUBS_UPDATE_URL
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.SafeR
import li.songe.gkd.util.formatTimeAgo
import li.songe.gkd.util.launchAsFn
import li.songe.gkd.util.navigate
import li.songe.gkd.util.subsIdToRawFlow
@ -73,9 +75,9 @@ val subsNav = BottomNavItem(
label = "订阅", icon = SafeR.ic_link, route = "subscription"
)
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable
fun SubsManagePage() {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val navController = LocalNavController.current
@ -98,6 +100,10 @@ fun SubsManagePage() {
var showAddLinkDialog by remember { mutableStateOf(false) }
var link by remember { mutableStateOf("") }
val (showSubsRaw, setShowSubsRaw) = remember {
mutableStateOf<SubscriptionRaw?>(null)
}
val refreshing by vm.refreshingFlow.collectAsState()
val pullRefreshState = rememberPullRefreshState(refreshing, vm::refreshSubs)
@ -141,7 +147,6 @@ fun SubsManagePage() {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "info",
modifier = Modifier.size(30.dp)
)
}
},
@ -208,6 +213,17 @@ fun SubsManagePage() {
shape = RoundedCornerShape(16.dp),
) {
Column {
val subsRawVal = subsIdToRaw[menuSubItemVal.id]
if (subsRawVal != null) {
Text(text = "查看详情", modifier = Modifier
.clickable {
menuSubItem = null
setShowSubsRaw(subsRawVal)
}
.fillMaxWidth()
.padding(16.dp))
Divider()
}
if (menuSubItemVal.updateUrl != null) {
Text(text = "复制链接", modifier = Modifier
.clickable {
@ -217,8 +233,8 @@ fun SubsManagePage() {
}
.fillMaxWidth()
.padding(16.dp))
Divider()
}
Divider()
Text(text = "删除订阅", modifier = Modifier
.clickable {
deleteSubItem = menuSubItemVal
@ -314,4 +330,43 @@ fun SubsManagePage() {
}
})
}
if (showSubsRaw != null) {
AlertDialog(onDismissRequest = { setShowSubsRaw(null) }, title = {
Text(text = "订阅详情")
}, text = {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier
) {
Text(text = "名称: " + showSubsRaw.name)
Text(text = "版本: " + showSubsRaw.version)
if (showSubsRaw.author != null) {
Text(text = "作者: " + showSubsRaw.author)
}
val apps = showSubsRaw.apps
val groupsSize = apps.sumOf { it.groups.size }
if (groupsSize > 0) {
Text(text = "规则: ${apps.size}应用/${groupsSize}规则组")
}
Text(
text = "更新: " + formatTimeAgo(
subItems.find { s -> s.id == showSubsRaw.id }?.mtime ?: 0
)
)
}
}, confirmButton = {
if (showSubsRaw.supportUri != null) {
TextButton(onClick = {
context.startActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(showSubsRaw.supportUri)
)
)
}) {
Text(text = "问题反馈")
}
}
})
}
}