From f9d0692bdf1f95ad72ca18357a8d5633c66feb5a Mon Sep 17 00:00:00 2001 From: lisonge Date: Tue, 27 Feb 2024 22:05:25 +0800 Subject: [PATCH] perf: toast --- app/build.gradle.kts | 2 + app/proguard-rules.pro | 1 + app/src/main/kotlin/li/songe/gkd/App.kt | 4 +- .../main/kotlin/li/songe/gkd/MainActivity.kt | 16 +++++ .../main/kotlin/li/songe/gkd/notif/Notif.kt | 3 +- .../main/kotlin/li/songe/gkd/ui/AboutPage.kt | 67 ++++++++++++------- .../li/songe/gkd/ui/home/ControlPage.kt | 29 ++++---- .../kotlin/li/songe/gkd/ui/home/HomePage.kt | 2 +- .../li/songe/gkd/ui/home/SettingsPage.kt | 26 +++++-- .../li/songe/gkd/ui/home/SubsManagePage.kt | 34 +++++++--- .../kotlin/li/songe/gkd/ui/theme/Theme.kt | 2 + .../kotlin/li/songe/gkd/util/CoroutineExt.kt | 7 +- .../main/kotlin/li/songe/gkd/util/Toast.kt | 48 ++++++++++++- settings.gradle.kts | 6 +- 14 files changed, 182 insertions(+), 65 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 81b9e892df..1668c4f907 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -222,4 +222,6 @@ dependencies { implementation(libs.coil.gif) implementation(libs.exp4j) + + implementation(libs.toaster) } \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 1c2c724db4..68dc4da796 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -21,6 +21,7 @@ # self -keep class li.songe.**{*;} -keep interface li.songe.**{*;} +-keep class com.hjq.toast.** {*;} -keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod diff --git a/app/src/main/kotlin/li/songe/gkd/App.kt b/app/src/main/kotlin/li/songe/gkd/App.kt index 3a9ac28cc9..fbba704b83 100644 --- a/app/src/main/kotlin/li/songe/gkd/App.kt +++ b/app/src/main/kotlin/li/songe/gkd/App.kt @@ -4,6 +4,7 @@ import android.app.Application import android.content.Context import android.os.Build import com.blankj.utilcode.util.LogUtils +import com.hjq.toast.Toaster import com.tencent.mmkv.MMKV import dagger.hilt.android.HiltAndroidApp import kotlinx.coroutines.Dispatchers @@ -18,6 +19,7 @@ import li.songe.gkd.util.initSubsState import li.songe.gkd.util.launchTry import org.lsposed.hiddenapibypass.HiddenApiBypass + val appScope by lazy { MainScope() } private lateinit var innerApp: Application @@ -43,7 +45,7 @@ class App : Application() { LogUtils.d("UncaughtExceptionHandler", t, e) errorHandler?.uncaughtException(t, e) } - + Toaster.init(this) MMKV.initialize(this) LogUtils.getConfig().apply { setConsoleSwitch(BuildConfig.DEBUG) diff --git a/app/src/main/kotlin/li/songe/gkd/MainActivity.kt b/app/src/main/kotlin/li/songe/gkd/MainActivity.kt index b85b979b61..f759535e32 100644 --- a/app/src/main/kotlin/li/songe/gkd/MainActivity.kt +++ b/app/src/main/kotlin/li/songe/gkd/MainActivity.kt @@ -7,11 +7,14 @@ import androidx.activity.viewModels import androidx.compose.runtime.CompositionLocalProvider import androidx.lifecycle.lifecycleScope import androidx.navigation.compose.rememberNavController +import com.blankj.utilcode.util.LogUtils import com.dylanc.activityresult.launcher.PickContentLauncher import com.dylanc.activityresult.launcher.RequestPermissionLauncher import com.dylanc.activityresult.launcher.StartActivityLauncher import com.ramcosta.composedestinations.DestinationsNavHost import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import li.songe.gkd.composition.CompositionActivity import li.songe.gkd.composition.CompositionExt.useLifeCycleLog @@ -71,8 +74,21 @@ class MainActivity : CompositionActivity({ } }) { val mainVm by viewModels() + + override fun onStart() { + super.onStart() + activityVisibleFlow.update { it + 1 } + } + + override fun onStop() { + super.onStop() + activityVisibleFlow.update { it - 1 } + LogUtils.d(activityVisibleFlow.value) + } } +val activityVisibleFlow = MutableStateFlow(0) + diff --git a/app/src/main/kotlin/li/songe/gkd/notif/Notif.kt b/app/src/main/kotlin/li/songe/gkd/notif/Notif.kt index 2476f8269c..13c9d19708 100644 --- a/app/src/main/kotlin/li/songe/gkd/notif/Notif.kt +++ b/app/src/main/kotlin/li/songe/gkd/notif/Notif.kt @@ -5,6 +5,7 @@ import li.songe.gkd.util.APP_NAME data class Notif( val id: Int, + @Suppress("UNRESOLVED_REFERENCE") val icon: Int = R.drawable.ic_launcher, val title: String = APP_NAME, val text: String, @@ -12,7 +13,6 @@ data class Notif( val autoCancel: Boolean, ) - val abNotif by lazy { Notif( id = 100, @@ -21,6 +21,7 @@ val abNotif by lazy { autoCancel = false, ) } + val screenshotNotif by lazy { Notif( id = 101, diff --git a/app/src/main/kotlin/li/songe/gkd/ui/AboutPage.kt b/app/src/main/kotlin/li/songe/gkd/ui/AboutPage.kt index cd43b1502b..174382e979 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/AboutPage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/AboutPage.kt @@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.HorizontalDivider @@ -17,8 +19,10 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -39,37 +43,48 @@ import li.songe.gkd.util.launchTry fun AboutPage() { val navController = LocalNavController.current val context = LocalContext.current - Scaffold(topBar = { - TopAppBar(navigationIcon = { - IconButton(onClick = { - navController.popBackStack() - }) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = null, - ) - } - }, title = { Text(text = "关于") }, actions = {}) - }, content = { contentPadding -> + + val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() + Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + TopAppBar( + scrollBehavior = scrollBehavior, + navigationIcon = { + IconButton(onClick = { + navController.popBackStack() + }) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = null, + ) + } + }, + title = { Text(text = "关于") } + ) + } + ) { contentPadding -> Column( modifier = Modifier .fillMaxWidth() + .verticalScroll(rememberScrollState()) .padding(contentPadding), ) { - Column(modifier = Modifier - .clickable { - appScope.launchTry { - // ActivityNotFoundException - // https://bugly.qq.com/v2/crash-reporting/crashes/d0ce46b353/117002?pid=1 - context.startActivity( - Intent( - Intent.ACTION_VIEW, Uri.parse(REPOSITORY_URL) + Column( + modifier = Modifier + .clickable { + appScope.launchTry { + // ActivityNotFoundException + // https://bugly.qq.com/v2/crash-reporting/crashes/d0ce46b353/117002?pid=1 + context.startActivity( + Intent( + Intent.ACTION_VIEW, Uri.parse(REPOSITORY_URL) + ) ) - ) + } } - } - .fillMaxWidth() - .padding(10.dp)) { + .fillMaxWidth() + .padding(10.dp)) { Text( text = "开源地址", fontSize = 18.sp ) @@ -166,7 +181,7 @@ fun AboutPage() { fontSize = 14.sp, ) } + Spacer(modifier = Modifier.height(40.dp)) } - }) - + } } \ No newline at end of file diff --git a/app/src/main/kotlin/li/songe/gkd/ui/home/ControlPage.kt b/app/src/main/kotlin/li/songe/gkd/ui/home/ControlPage.kt index 55a5fc2508..5cdd0808b2 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/home/ControlPage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/home/ControlPage.kt @@ -22,11 +22,13 @@ import androidx.compose.material3.Icon 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.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -67,20 +69,22 @@ fun useControlPage(): ScaffoldExt { val canNotif by usePollState { NotificationManagerCompat.from(context).areNotificationsEnabled() } - - return ScaffoldExt(navItem = controlNav, topBar = { - TopAppBar(title = { - Text( - text = controlNav.label, - ) - }) - }, content = { padding -> + val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() + val scrollState = rememberScrollState() + return ScaffoldExt(navItem = controlNav, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + TopAppBar(scrollBehavior = scrollBehavior, title = { + Text( + text = controlNav.label, + ) + }) + } + ) { padding -> Column( modifier = Modifier + .verticalScroll(scrollState) .padding(padding) - .verticalScroll( - state = rememberScrollState() - ) ) { if (!gkdAccessRunning) { AuthCard( @@ -253,6 +257,7 @@ fun useControlPage(): ScaffoldExt { } } + Spacer(modifier = Modifier.height(40.dp)) } - }) + } } diff --git a/app/src/main/kotlin/li/songe/gkd/ui/home/HomePage.kt b/app/src/main/kotlin/li/songe/gkd/ui/home/HomePage.kt index c817970802..ee979ca2be 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/home/HomePage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/home/HomePage.kt @@ -50,7 +50,7 @@ fun HomePage() { val controlPage = useControlPage() val settingsPage = useSettingsPage() - val pages = arrayOf(appListPage, subsPage, controlPage, settingsPage) + val pages = arrayOf(controlPage, subsPage, appListPage, settingsPage) val currentPage = pages.find { p -> p.navItem === tab } ?: controlPage diff --git a/app/src/main/kotlin/li/songe/gkd/ui/home/SettingsPage.kt b/app/src/main/kotlin/li/songe/gkd/ui/home/SettingsPage.kt index 57de6ad964..dcad85e86e 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/home/SettingsPage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/home/SettingsPage.kt @@ -25,6 +25,8 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.RadioButton 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,6 +35,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -319,15 +322,28 @@ fun useSettingsPage(): ScaffoldExt { else -> {} } - return ScaffoldExt(navItem = settingsNav) { padding -> + val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() + val scrollState = rememberScrollState() + return ScaffoldExt( + navItem = settingsNav, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + TopAppBar(scrollBehavior = scrollBehavior, + title = { + Text( + text = settingsNav.label, + ) + } + ) + }, + ) { padding -> Column( modifier = Modifier + .verticalScroll(scrollState) .padding(padding) - .verticalScroll( - state = rememberScrollState() - ) ) { - TextSwitch(name = "后台隐藏", + TextSwitch( + name = "后台隐藏", desc = "在[最近任务]界面中隐藏本应用", checked = store.excludeFromRecents, onCheckedChange = { diff --git a/app/src/main/kotlin/li/songe/gkd/ui/home/SubsManagePage.kt b/app/src/main/kotlin/li/songe/gkd/ui/home/SubsManagePage.kt index d1959edd8b..0eeee175ec 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/home/SubsManagePage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/home/SubsManagePage.kt @@ -31,6 +31,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.pullrefresh.PullRefreshIndicator import androidx.compose.material3.pullrefresh.pullRefresh import androidx.compose.material3.pullrefresh.rememberPullRefreshState @@ -44,6 +46,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.unit.dp import androidx.compose.ui.unit.sp @@ -181,15 +184,13 @@ fun useSubsManagePage(): ScaffoldExt { HorizontalDivider() } 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) } } } @@ -256,8 +257,20 @@ fun useSubsManagePage(): ScaffoldExt { }) } + val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() return ScaffoldExt( navItem = subsNav, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + TopAppBar( + scrollBehavior = scrollBehavior, + title = { + Text( + text = subsNav.label, + ) + } + ) + }, floatingActionButton = { FloatingActionButton(onClick = { if (!vm.refreshingFlow.value) { @@ -319,8 +332,7 @@ fun useSubsManagePage(): ScaffoldExt { elevation = CardDefaults.cardElevation(draggedElevation = 10.dp), shape = RoundedCornerShape(8.dp), border = if (isDragging) BorderStroke( - width, - MaterialTheme.colorScheme.primary + width, MaterialTheme.colorScheme.primary ) else null, interactionSource = interactionSource, ) { diff --git a/app/src/main/kotlin/li/songe/gkd/ui/theme/Theme.kt b/app/src/main/kotlin/li/songe/gkd/ui/theme/Theme.kt index fc669cf520..e5bcb25047 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/theme/Theme.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/theme/Theme.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.core.view.WindowInsetsControllerCompat import li.songe.gkd.util.map import li.songe.gkd.util.storeFlow +import li.songe.gkd.util.updateToastStyle val LightColorScheme = lightColorScheme() val DarkColorScheme = darkColorScheme() @@ -42,6 +43,7 @@ fun AppTheme( WindowInsetsControllerCompat(context.window, context.window.decorView).apply { isAppearanceLightStatusBars = !darkTheme } + updateToastStyle(darkTheme) } MaterialTheme( colorScheme = colorScheme, content = content diff --git a/app/src/main/kotlin/li/songe/gkd/util/CoroutineExt.kt b/app/src/main/kotlin/li/songe/gkd/util/CoroutineExt.kt index b551de297c..f41a10d10b 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/CoroutineExt.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/CoroutineExt.kt @@ -1,6 +1,7 @@ package li.songe.gkd.util import com.blankj.utilcode.util.LogUtils +import com.hjq.toast.Toaster import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart @@ -20,7 +21,7 @@ fun CoroutineScope.launchTry( } catch (e: Exception) { e.printStackTrace() LogUtils.d(e) - toast(e.message ?: e.stackTraceToString()) + Toaster.show(e.message ?: e.stackTraceToString()) } } @@ -37,7 +38,7 @@ fun CoroutineScope.launchAsFn( e.printStackTrace() } catch (e: Exception) { e.printStackTrace() - toast(e.message ?: e.toString()) + Toaster.show(e.message ?: e.stackTraceToString()) } } } @@ -54,7 +55,7 @@ fun CoroutineScope.launchAsFn( block(it) } catch (e: Exception) { e.printStackTrace() - toast(e.message ?: e.toString()) + Toaster.show(e.message ?: e.stackTraceToString()) } } } diff --git a/app/src/main/kotlin/li/songe/gkd/util/Toast.kt b/app/src/main/kotlin/li/songe/gkd/util/Toast.kt index b58a9b98c4..aa95b50097 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/Toast.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/Toast.kt @@ -1,23 +1,65 @@ package li.songe.gkd.util +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.graphics.drawable.GradientDrawable +import android.view.Gravity import android.widget.Toast +import com.blankj.utilcode.util.ConvertUtils import com.blankj.utilcode.util.LogUtils +import com.blankj.utilcode.util.ScreenUtils +import com.hjq.toast.Toaster +import com.hjq.toast.style.WhiteToastStyle import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import li.songe.gkd.activityVisibleFlow import li.songe.gkd.app import li.songe.gkd.appScope private var lastToast: Toast? = null -fun toast(text: String) { +fun toast(text: CharSequence) { appScope.launch(Dispatchers.Main) { try { lastToast?.cancel() - lastToast = Toast.makeText(app, text, Toast.LENGTH_SHORT) - lastToast!!.show() + lastToast = null + } catch (e: Exception) { + LogUtils.d("lastToast?.cancel", e) + } + try { + Toaster.cancel() + } catch (e: Exception) { + LogUtils.d("Toaster.cancel", e) + } + try { + if (activityVisibleFlow.value <= 0) { + lastToast = Toast.makeText(app, text, Toast.LENGTH_SHORT).apply { + show() + } + } else { + Toaster.show(text) + } } catch (e: Exception) { LogUtils.d("toast失败", e) } } +} + +fun updateToastStyle(darkTheme: Boolean) { + Toaster.setStyle(object : WhiteToastStyle() { + override fun getGravity() = Gravity.BOTTOM + override fun getYOffset() = (ScreenUtils.getScreenHeight() * 0.12f).toInt() + override fun getTranslationZ(context: Context?) = ConvertUtils.dp2px(1f).toFloat() + override fun getTextColor(context: Context?): Int { + return if (darkTheme) Color.WHITE else Color.BLACK + } + + override fun getBackgroundDrawable(context: Context?): Drawable { + return (super.getBackgroundDrawable(context) as GradientDrawable).apply { + setColor(if (!darkTheme) Color.WHITE else Color.parseColor("#191a1c")) + } + } + }) } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index afd643ff54..22126e4b78 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -66,8 +66,8 @@ dependencyResolutionManagement { // compose 编译器的版本, 需要注意它与 compose 的版本没有关联 // https://developer.android.com/jetpack/androidx/releases/compose-compiler - version("compose.compilerVersion", "1.5.9") - val composeVersion = "1.6.1" + version("compose.compilerVersion", "1.5.10") + val composeVersion = "1.6.2" library("compose.ui", "androidx.compose.ui:ui:$composeVersion") library("compose.preview", "androidx.compose.ui:ui-tooling-preview:$composeVersion") library("compose.tooling", "androidx.compose.ui:ui-tooling:$composeVersion") @@ -212,6 +212,8 @@ dependencyResolutionManagement { // https://www.objecthunter.net/exp4j/ library("exp4j", "net.objecthunter:exp4j:0.4.8") + + library("toaster", "com.github.getActivity:Toaster:12.6") } } }