From 751d411676a2559e3634b2cfd712e0a21ee01723 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 11:10:47 +0800 Subject: [PATCH 01/17] Bump coil from 2.6.0 to 2.7.0 (#121) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1a948674..75f612b5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ androidxLifecycle = "2.8.3" androidxNavigation = "2.7.7" androidxRoom = "2.6.1" appiconloader = "1.5.0" -coil = "2.6.0" +coil = "2.7.0" hiddenApiRefine = "4.4.0" hilt = "2.51.1" kotlin = "2.0.0" From 6416281c11e4b4c0a292fbae9ed51874a9a9c0d1 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Thu, 18 Jul 2024 13:46:56 +0800 Subject: [PATCH 02/17] Tidy up code --- app/src/main/kotlin/dev/sanmer/pi/App.kt | 22 ++++++++++++-- .../kotlin/dev/sanmer/pi/{app => }/Const.kt | 8 +++-- .../sanmer/pi/app/utils/NotificationUtils.kt | 27 ----------------- .../pi/datastore/UserPreferencesSerializer.kt | 26 ++++++++++++++++ .../sanmer/pi/datastore/di/DataStoreModule.kt | 30 ------------------- .../dev/sanmer/pi/service/InstallService.kt | 6 ++-- .../pi/ui/screens/settings/SettingsScreen.kt | 2 +- 7 files changed, 56 insertions(+), 65 deletions(-) rename app/src/main/kotlin/dev/sanmer/pi/{app => }/Const.kt (50%) delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/app/utils/NotificationUtils.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/datastore/di/DataStoreModule.kt diff --git a/app/src/main/kotlin/dev/sanmer/pi/App.kt b/app/src/main/kotlin/dev/sanmer/pi/App.kt index ccb08444..4902cc98 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/App.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/App.kt @@ -1,10 +1,13 @@ package dev.sanmer.pi import android.app.Application +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat import coil.ImageLoader import coil.ImageLoaderFactory import dagger.hilt.android.HiltAndroidApp -import dev.sanmer.pi.app.utils.NotificationUtils import dev.sanmer.pi.ktx.dp import dev.sanmer.su.ServiceManagerCompat import me.zhanghai.android.appiconloader.coil.AppIconFetcher @@ -24,8 +27,8 @@ class App : Application(), ImageLoaderFactory { override fun onCreate() { super.onCreate() + createNotificationChannels(this) ServiceManagerCompat.setHiddenApiExemptions("") - NotificationUtils.init(this) } override fun newImageLoader() = @@ -36,6 +39,21 @@ class App : Application(), ImageLoaderFactory { } .build() + private fun createNotificationChannels(context: Context) { + val channels = listOf( + NotificationChannel( + Const.CHANNEL_ID_INSTALL, + context.getString(R.string.notification_name_install), + NotificationManager.IMPORTANCE_HIGH + ) + ) + + NotificationManagerCompat.from(context).apply { + createNotificationChannels(channels) + deleteUnlistedNotificationChannels(channels.map { it.id }) + } + } + class DebugTree : Timber.DebugTree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { super.log(priority, "$tag", message, t) diff --git a/app/src/main/kotlin/dev/sanmer/pi/app/Const.kt b/app/src/main/kotlin/dev/sanmer/pi/Const.kt similarity index 50% rename from app/src/main/kotlin/dev/sanmer/pi/app/Const.kt rename to app/src/main/kotlin/dev/sanmer/pi/Const.kt index 814adf82..7bcedac6 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/app/Const.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/Const.kt @@ -1,7 +1,11 @@ -package dev.sanmer.pi.app +package dev.sanmer.pi object Const { - // URL + // Url const val GITHUB_URL = "https://github.com/SanmerApps/PI" const val TRANSLATE_URL = "https://weblate.sanmer.app/engage/pi" + + // Notification + const val CHANNEL_ID_INSTALL = "INSTALL" + const val NOTIFICATION_ID_INSTALL = 1024 } \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/app/utils/NotificationUtils.kt b/app/src/main/kotlin/dev/sanmer/pi/app/utils/NotificationUtils.kt deleted file mode 100644 index e59f6930..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/app/utils/NotificationUtils.kt +++ /dev/null @@ -1,27 +0,0 @@ -package dev.sanmer.pi.app.utils - -import android.app.NotificationChannel -import android.app.NotificationManager -import android.content.Context -import androidx.core.app.NotificationManagerCompat -import dev.sanmer.pi.R - -object NotificationUtils { - const val CHANNEL_ID_INSTALL = "INSTALL" - const val NOTIFICATION_ID_INSTALL = 1024 - - fun init(context: Context) { - val channels = listOf( - NotificationChannel( - CHANNEL_ID_INSTALL, - context.getString(R.string.notification_name_install), - NotificationManager.IMPORTANCE_HIGH - ) - ) - - NotificationManagerCompat.from(context).apply { - createNotificationChannels(channels) - deleteUnlistedNotificationChannels(channels.map { it.id }) - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/datastore/UserPreferencesSerializer.kt b/app/src/main/kotlin/dev/sanmer/pi/datastore/UserPreferencesSerializer.kt index aac9bb71..3cc2e751 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/datastore/UserPreferencesSerializer.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/datastore/UserPreferencesSerializer.kt @@ -1,12 +1,22 @@ package dev.sanmer.pi.datastore +import android.content.Context import androidx.datastore.core.CorruptionException +import androidx.datastore.core.DataStore +import androidx.datastore.core.DataStoreFactory import androidx.datastore.core.Serializer +import androidx.datastore.dataStoreFile +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import dev.sanmer.pi.datastore.model.UserPreferences import kotlinx.serialization.SerializationException import java.io.InputStream import java.io.OutputStream import javax.inject.Inject +import javax.inject.Singleton class UserPreferencesSerializer @Inject constructor() : Serializer { override val defaultValue = UserPreferences() @@ -21,4 +31,20 @@ class UserPreferencesSerializer @Inject constructor() : Serializer = + DataStoreFactory.create( + serializer = userPreferencesSerializer + ) { + context.dataStoreFile("user_preferences.pb") + } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/datastore/di/DataStoreModule.kt b/app/src/main/kotlin/dev/sanmer/pi/datastore/di/DataStoreModule.kt deleted file mode 100644 index 00a50065..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/datastore/di/DataStoreModule.kt +++ /dev/null @@ -1,30 +0,0 @@ -package dev.sanmer.pi.datastore.di - -import android.content.Context -import androidx.datastore.core.DataStore -import androidx.datastore.core.DataStoreFactory -import androidx.datastore.dataStoreFile -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import dev.sanmer.pi.datastore.UserPreferencesSerializer -import dev.sanmer.pi.datastore.model.UserPreferences -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -object DataStoreModule { - @Provides - @Singleton - fun providesUserPreferencesDataStore( - @ApplicationContext context: Context, - userPreferencesSerializer: UserPreferencesSerializer - ): DataStore = - DataStoreFactory.create( - serializer = userPreferencesSerializer - ) { - context.dataStoreFile("user_preferences.pb") - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/service/InstallService.kt b/app/src/main/kotlin/dev/sanmer/pi/service/InstallService.kt index 9b683fef..21b26e44 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/service/InstallService.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/service/InstallService.kt @@ -18,9 +18,9 @@ import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import dev.sanmer.pi.Compat +import dev.sanmer.pi.Const import dev.sanmer.pi.ContextCompat.userId import dev.sanmer.pi.R -import dev.sanmer.pi.app.utils.NotificationUtils import dev.sanmer.pi.compat.BuildCompat import dev.sanmer.pi.compat.PermissionCompat import dev.sanmer.pi.delegate.PackageInstallerDelegate @@ -265,11 +265,11 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb .setGroupSummary(true) .build() - startForeground(NotificationUtils.NOTIFICATION_ID_INSTALL, notification) + startForeground(Const.NOTIFICATION_ID_INSTALL, notification) } private fun baseNotificationBuilder() = - NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_INSTALL) + NotificationCompat.Builder(this, Const.CHANNEL_ID_INSTALL) .setSmallIcon(R.drawable.launcher_outline) @SuppressLint("MissingPermission") diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt index f05bc1af..54e42a65 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt @@ -25,8 +25,8 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import dev.sanmer.pi.BuildConfig +import dev.sanmer.pi.Const import dev.sanmer.pi.R -import dev.sanmer.pi.app.Const import dev.sanmer.pi.compat.BuildCompat import dev.sanmer.pi.datastore.model.Provider import dev.sanmer.pi.ktx.applicationLocale From ca2887d361d01d42bd8fa44b7a0f0b9d4b9f79da Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Thu, 18 Jul 2024 14:05:24 +0800 Subject: [PATCH 03/17] Fix `AppOpsCallback` for Android 14 QPR3 --- .../kotlin/dev/sanmer/pi/delegate/AppOpsManagerDelegate.kt | 4 ++++ .../src/main/java/android/permission/IPermissionManager.java | 5 +++++ .../main/java/com/android/internal/app/IAppOpsCallback.java | 3 +++ 3 files changed, 12 insertions(+) diff --git a/core/src/main/kotlin/dev/sanmer/pi/delegate/AppOpsManagerDelegate.kt b/core/src/main/kotlin/dev/sanmer/pi/delegate/AppOpsManagerDelegate.kt index 43bbc892..c1af2380 100644 --- a/core/src/main/kotlin/dev/sanmer/pi/delegate/AppOpsManagerDelegate.kt +++ b/core/src/main/kotlin/dev/sanmer/pi/delegate/AppOpsManagerDelegate.kt @@ -112,6 +112,10 @@ class AppOpsManagerDelegate( callback.opChanged(op, uid, packageName) } + override fun opChanged(op: Int, uid: Int, packageName: String, persistentDeviceId: String?) { + callback.opChanged(op, uid, packageName) + } + } enum class Mode(val code: Int) { diff --git a/stub/src/main/java/android/permission/IPermissionManager.java b/stub/src/main/java/android/permission/IPermissionManager.java index 31b9770f..72a248cb 100644 --- a/stub/src/main/java/android/permission/IPermissionManager.java +++ b/stub/src/main/java/android/permission/IPermissionManager.java @@ -9,16 +9,21 @@ public interface IPermissionManager extends IInterface { void grantRuntimePermission(String packageName, String permissionName, int userId) throws RemoteException; + // Android 14 QPR2 void grantRuntimePermission(String packageName, String permissionName, int deviceId, int userId) throws RemoteException; + // Android 14 QPR3 void grantRuntimePermission(String packageName, String permissionName, String persistentDeviceId, int userId) throws RemoteException; void revokeRuntimePermission(String packageName, String permissionName, int userId) throws RemoteException; + // Android 14 QPR1 void revokeRuntimePermission(String packageName, String permissionName, int userId, String reason) throws RemoteException; + // Android 14 QPR2 void revokeRuntimePermission(String packageName, String permissionName, int deviceId, int userId, String reason) throws RemoteException; + // Android 14 QPR3 void revokeRuntimePermission(String packageName, String permissionName, String persistentDeviceId, int userId, String reason) throws RemoteException; int checkPermission(String permName, String pkgName, int userId) throws RemoteException; diff --git a/stub/src/main/java/com/android/internal/app/IAppOpsCallback.java b/stub/src/main/java/com/android/internal/app/IAppOpsCallback.java index 3b7e813a..df83e6e9 100644 --- a/stub/src/main/java/com/android/internal/app/IAppOpsCallback.java +++ b/stub/src/main/java/com/android/internal/app/IAppOpsCallback.java @@ -9,6 +9,9 @@ public interface IAppOpsCallback extends IInterface { void opChanged(int op, int uid, String packageName) throws RemoteException; + // Android 14 QPR3 + void opChanged(int op, int uid, String packageName, String persistentDeviceId) throws RemoteException; + abstract class Stub extends Binder implements IAppOpsCallback { public static IAppOpsCallback asInterface(IBinder binder) { From b80c0121762fbc304a2caaaa9676d83dfd7a0198 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Thu, 18 Jul 2024 14:57:51 +0800 Subject: [PATCH 04/17] Optimize UI - Remove `FastScrollbar` - Remove setting & uninstall --- .../dev/sanmer/pi/ui/InstallActivity.kt | 2 +- .../kotlin/dev/sanmer/pi/ui/MainActivity.kt | 2 +- .../dev/sanmer/pi/ui/PermissionActivity.kt | 2 +- .../pi/ui/component/NavigateUpTopBar.kt | 2 +- .../sanmer/pi/ui/component/PageIndicator.kt | 12 +- .../sanmer/pi/ui/component/SearchTopBar.kt | 7 +- .../dev/sanmer/pi/ui/component/SettingItem.kt | 38 +- .../ui/component/scrollbar/FastScrollbar.kt | 253 ---------- .../component/scrollbar/LazyScrollbarExt.kt | 155 ------ .../pi/ui/component/scrollbar/Scrollbar.kt | 448 ------------------ .../pi/ui/component/scrollbar/ScrollbarExt.kt | 101 ---- .../pi/ui/component/scrollbar/ThumbExt.kt | 75 --- .../kotlin/dev/sanmer/pi/ui/ktx/ShapExt.kt | 8 + .../dev/sanmer/pi/ui/main/InstallScreen.kt | 12 +- .../dev/sanmer/pi/ui/main/MainScreen.kt | 62 ++- .../dev/sanmer/pi/ui/main/PermissionScreen.kt | 12 +- .../dev/sanmer/pi/ui/main/SetupScreen.kt | 2 +- .../dev/sanmer/pi/ui/navigation/Main.kt | 6 - .../sanmer/pi/ui/navigation/graphs/Apps.kt | 31 -- .../pi/ui/navigation/graphs/Settings.kt | 43 -- .../LocalUserPreferences.kt | 2 +- .../sanmer/pi/ui/screens/apps/AppsScreen.kt | 5 +- .../screens/apps/{ => component}/AppItem.kt | 5 +- .../screens/apps/{ => component}/AppList.kt | 95 +--- .../pi/ui/screens/settings/SettingsScreen.kt | 6 +- .../workingmode/WorkingModeScreen.kt | 5 +- .../component}/WorkingModeItem.kt | 5 +- .../dev/sanmer/pi/viewmodel/AppsViewModel.kt | 60 +-- 28 files changed, 143 insertions(+), 1313 deletions(-) delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/FastScrollbar.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/LazyScrollbarExt.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/Scrollbar.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/ScrollbarExt.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/ThumbExt.kt create mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/ktx/ShapExt.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/navigation/Main.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/navigation/graphs/Apps.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/navigation/graphs/Settings.kt rename app/src/main/kotlin/dev/sanmer/pi/ui/{providable => provider}/LocalUserPreferences.kt (83%) rename app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/{ => component}/AppItem.kt (95%) rename app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/{ => component}/AppList.kt (70%) rename app/src/main/kotlin/dev/sanmer/pi/ui/screens/{settings => }/workingmode/WorkingModeScreen.kt (94%) rename app/src/main/kotlin/dev/sanmer/pi/ui/screens/{settings/workingmode => workingmode/component}/WorkingModeItem.kt (93%) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/InstallActivity.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/InstallActivity.kt index 73492bc5..d7d90a5b 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/InstallActivity.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/InstallActivity.kt @@ -16,7 +16,7 @@ import dev.sanmer.pi.compat.BuildCompat import dev.sanmer.pi.compat.PermissionCompat import dev.sanmer.pi.repository.UserPreferencesRepository import dev.sanmer.pi.ui.main.InstallScreen -import dev.sanmer.pi.ui.providable.LocalUserPreferences +import dev.sanmer.pi.ui.provider.LocalUserPreferences import dev.sanmer.pi.ui.theme.AppTheme import dev.sanmer.pi.viewmodel.InstallViewModel import kotlinx.coroutines.launch diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt index 640b91ba..6ac948e6 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt @@ -20,7 +20,7 @@ import dev.sanmer.pi.receiver.PackageReceiver import dev.sanmer.pi.repository.UserPreferencesRepository import dev.sanmer.pi.ui.main.MainScreen import dev.sanmer.pi.ui.main.SetupScreen -import dev.sanmer.pi.ui.providable.LocalUserPreferences +import dev.sanmer.pi.ui.provider.LocalUserPreferences import dev.sanmer.pi.ui.theme.AppTheme import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/PermissionActivity.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/PermissionActivity.kt index ea199387..6cac5762 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/PermissionActivity.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/PermissionActivity.kt @@ -13,7 +13,7 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import dev.sanmer.pi.repository.UserPreferencesRepository import dev.sanmer.pi.ui.main.PermissionScreen -import dev.sanmer.pi.ui.providable.LocalUserPreferences +import dev.sanmer.pi.ui.provider.LocalUserPreferences import dev.sanmer.pi.ui.theme.AppTheme import dev.sanmer.pi.viewmodel.PermissionViewModel import kotlinx.coroutines.launch diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/NavigateUpTopBar.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/NavigateUpTopBar.kt index 5d0c87b1..4704425a 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/NavigateUpTopBar.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/component/NavigateUpTopBar.kt @@ -37,7 +37,7 @@ fun NavigateUpTopBar( modifier = modifier, title = title, subtitle = subtitle, - onBack = { navController.popBackStack() }, + onBack = { navController.navigateUp() }, actions = actions, windowInsets = windowInsets, colors = colors, diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/PageIndicator.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/PageIndicator.kt index f8fc988b..b25f33a5 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/PageIndicator.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/component/PageIndicator.kt @@ -52,7 +52,7 @@ fun PageIndicator( ) { icon() Spacer(modifier = Modifier.height(20.dp)) - ProvideTextStyle(value = PageIndicatorDefaults.textStyle) { + ProvideTextStyle(value = PageIndicatorDefaults.TextStyle) { text() } } @@ -69,8 +69,8 @@ fun PageIndicator( Icon( painter = painterResource(id = icon), contentDescription = null, - tint = PageIndicatorDefaults.iconColor, - modifier = Modifier.size(PageIndicatorDefaults.iconSize) + tint = PageIndicatorDefaults.IconColor, + modifier = Modifier.size(PageIndicatorDefaults.IconSize) ) }, text = { @@ -129,10 +129,10 @@ fun Failed( ) object PageIndicatorDefaults { - val iconSize = 80.dp - val iconColor @Composable get() = MaterialTheme.colorScheme.outline.copy(0.5f) + val IconSize = 80.dp + val IconColor @Composable get() = MaterialTheme.colorScheme.outline.copy(0.5f) - val textStyle @Composable get() = TextStyle( + val TextStyle @Composable get() = TextStyle( color = MaterialTheme.colorScheme.outline.copy(0.5f), fontSize = 20.sp, fontFamily = FontFamily.SansSerif, diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/SearchTopBar.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/SearchTopBar.kt index 2cc1db81..e4cf337c 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/SearchTopBar.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/component/SearchTopBar.kt @@ -2,7 +2,6 @@ package dev.sanmer.pi.ui.component import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Icon @@ -27,7 +26,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.unit.dp import dev.sanmer.pi.R @Composable @@ -80,9 +78,10 @@ fun SearchTopBar( imeAction = ImeAction.Search ), keyboardActions = KeyboardActions { - defaultKeyboardAction(ImeAction.Search) + if (query.isNotBlank()) { + defaultKeyboardAction(ImeAction.Done) + } }, - shape = RoundedCornerShape(15.dp), colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = Color.Transparent, unfocusedBorderColor = Color.Transparent diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/SettingItem.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/SettingItem.kt index 404b74df..76b35cd6 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/SettingItem.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/component/SettingItem.kt @@ -42,13 +42,13 @@ fun SettingNormalItem( onClick: () -> Unit, modifier: Modifier = Modifier, contentPaddingValues: PaddingValues = PaddingValues(vertical = 16.dp, horizontal = 24.dp), - itemTextStyle: SettingItemTextStyle = SettingItemDefaults.itemStyle(), + textStyle: SettingTextStyle = SettingItemDefaults.textStyle(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, @DrawableRes icon: Int? = null, enabled: Boolean = true, ) { val layoutDirection = LocalLayoutDirection.current - val start by remember { + val startPadding by remember { derivedStateOf { contentPaddingValues.calculateStartPadding(layoutDirection) } } @@ -73,7 +73,7 @@ fun SettingNormalItem( tint = LocalContentColor.current ) - Spacer(modifier = Modifier.width(start)) + Spacer(modifier = Modifier.width(startPadding)) } Column( @@ -82,14 +82,14 @@ fun SettingNormalItem( ) { Text( text = title, - style = itemTextStyle.titleTextStyle, - color = itemTextStyle.titleTextColor + style = textStyle.titleTextStyle, + color = textStyle.titleTextColor ) Text( text = desc, - style = itemTextStyle.descTextStyle, - color = itemTextStyle.descTextColor + style = textStyle.descTextStyle, + color = textStyle.descTextColor ) } } @@ -103,13 +103,13 @@ fun SettingSwitchItem( onChange: (Boolean) -> Unit, modifier: Modifier = Modifier, contentPaddingValues: PaddingValues = PaddingValues(vertical = 16.dp, horizontal = 24.dp), - itemTextStyle: SettingItemTextStyle = SettingItemDefaults.itemStyle(), + textStyle: SettingTextStyle = SettingItemDefaults.textStyle(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, @DrawableRes icon: Int? = null, enabled: Boolean = true ) { val layoutDirection = LocalLayoutDirection.current - val start by remember { + val startPadding by remember { derivedStateOf { contentPaddingValues.calculateStartPadding(layoutDirection) } } @@ -135,7 +135,7 @@ fun SettingSwitchItem( contentDescription = null ) - Spacer(modifier = Modifier.width(start)) + Spacer(modifier = Modifier.width(startPadding)) } Column( @@ -146,14 +146,14 @@ fun SettingSwitchItem( ) { Text( text = title, - style = itemTextStyle.titleTextStyle, - color = itemTextStyle.titleTextColor + style = textStyle.titleTextStyle, + color = textStyle.titleTextColor ) Text( text = desc, - style = itemTextStyle.descTextStyle, - color = itemTextStyle.descTextColor + style = textStyle.descTextStyle, + color = textStyle.descTextColor ) } @@ -165,7 +165,7 @@ fun SettingSwitchItem( } @Immutable -class SettingItemTextStyle internal constructor( +class SettingTextStyle internal constructor( val titleTextColor: Color, val descTextColor: Color, val titleTextStyle: TextStyle, @@ -173,7 +173,7 @@ class SettingItemTextStyle internal constructor( ) { override fun equals(other: Any?): Boolean { if (this === other) return true - if (other == null || other !is SettingItemTextStyle) return false + if (other == null || other !is SettingTextStyle) return false if (titleTextColor != other.titleTextColor) return false if (descTextColor != other.descTextColor) return false @@ -197,12 +197,12 @@ object SettingItemDefaults { val TextSwitchPadding = 16.dp @Composable - fun itemStyle( + fun textStyle( titleTextColor: Color = LocalContentColor.current, descTextColor: Color = MaterialTheme.colorScheme.outline, - titleTextStyle: TextStyle = MaterialTheme.typography.bodyLarge, + titleTextStyle: TextStyle = MaterialTheme.typography.titleMedium, descTextStyle: TextStyle = MaterialTheme.typography.bodyMedium - ) = SettingItemTextStyle( + ) = SettingTextStyle( titleTextColor = titleTextColor, descTextColor = descTextColor, titleTextStyle = titleTextStyle, diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/FastScrollbar.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/FastScrollbar.kt deleted file mode 100644 index fcb51df6..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/FastScrollbar.kt +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2023 Sanmer - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.sanmer.pi.ui.component.scrollbar - -import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.SpringSpec -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.ScrollableState -import androidx.compose.foundation.interaction.InteractionSource -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.collectIsDraggedAsState -import androidx.compose.foundation.interaction.collectIsHoveredAsState -import androidx.compose.foundation.interaction.collectIsPressedAsState -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.surfaceColorAtElevation -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import kotlinx.coroutines.delay - -@Composable -fun VerticalFastScrollbar( - state: LazyListState, - modifier: Modifier = Modifier, - contentPadding: PaddingValues = PaddingValues(horizontal = 2.dp), - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - colors: ScrollbarColors = ScrollbarDefaults.colors(), - thumb: @Composable () -> Unit = { - ScrollbarDefaults.Thumb( - color = colors.thumbColor( - canScrollForward = state.canScrollForward, - isScrollInProgress = state.isScrollInProgress, - interactionSource = interactionSource - ), - orientation = Orientation.Vertical, - ) - } -) { - val scrollbarState = state.scrollbarState() - val reverseLayout by remember(state) { - derivedStateOf { - state.layoutInfo.reverseLayout - } - } - - state.FastScrollbar( - modifier = Modifier - .fillMaxHeight() - .padding(contentPadding) - .then(modifier), - state = scrollbarState, - orientation = Orientation.Vertical, - onThumbMoved = state.rememberDraggableScroller(), - reverseLayout = reverseLayout, - colors = colors, - thumb = thumb, - interactionSource = interactionSource - ) -} - -@Composable -private fun ScrollableState.FastScrollbar( - state: ScrollbarState, - orientation: Orientation, - reverseLayout: Boolean, - onThumbMoved: (Float) -> Unit, - modifier: Modifier, - colors: ScrollbarColors, - thumb: @Composable () -> Unit, - interactionSource: MutableInteractionSource -) = Scrollbar( - modifier = modifier, - orientation = orientation, - interactionSource = interactionSource, - state = state, - thumb = thumb, - backgroundColor = colors.trackColor( - canScrollForward = canScrollForward, - isScrollInProgress = isScrollInProgress, - interactionSource = interactionSource - ), - onThumbMoved = onThumbMoved, - reverseLayout = reverseLayout -) - -@Immutable -class ScrollbarColors internal constructor( - private val contentColor: Color, - private val activeContentColor: Color, - private val containerColor: Color, - private val activeContainerColor: Color -) { - @Composable - private fun scrollbarColor( - color1: Color, - color2: Color, - canScrollForward: Boolean, - isScrollInProgress: Boolean, - interactionSource: InteractionSource, - ): Color { - var state by remember { mutableStateOf(ThumbState.Dormant) } - val pressed by interactionSource.collectIsPressedAsState() - val hovered by interactionSource.collectIsHoveredAsState() - val dragged by interactionSource.collectIsDraggedAsState() - val active = canScrollForward && (pressed || hovered || dragged || isScrollInProgress) - - val color by animateColorAsState( - targetValue = when (state) { - ThumbState.Active -> color1 - ThumbState.Inactive -> color2 - ThumbState.Dormant -> color2.copy(0f) - }, - animationSpec = SpringSpec(stiffness = Spring.StiffnessLow), - label = "scrollbarColor" - ) - LaunchedEffect(active) { - when (active) { - true -> state = ThumbState.Active - false -> if (state == ThumbState.Active) { - state = ThumbState.Inactive - delay(SCROLLBAR_INACTIVE_TO_DORMANT_TIME_IN_MS) - state = ThumbState.Dormant - } - } - } - - return color - } - - @Composable - fun thumbColor( - canScrollForward: Boolean, - isScrollInProgress: Boolean, - interactionSource: InteractionSource - ): Color = scrollbarColor( - color1 = activeContentColor, - color2 = contentColor, - canScrollForward = canScrollForward, - isScrollInProgress = isScrollInProgress, - interactionSource = interactionSource - ) - - @Composable - fun trackColor( - canScrollForward: Boolean, - isScrollInProgress: Boolean, - interactionSource: InteractionSource - ): Color = scrollbarColor( - color1 = activeContainerColor, - color2 = containerColor, - canScrollForward = canScrollForward, - isScrollInProgress = isScrollInProgress, - interactionSource = interactionSource - ) - - @Suppress("RedundantIf") - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || other !is ScrollbarColors) return false - - if (contentColor != other.contentColor) return false - if (activeContentColor != other.activeContentColor) return false - if (containerColor != other.containerColor) return false - if (activeContainerColor != other.activeContainerColor) return false - - return true - } - - override fun hashCode(): Int { - var result = contentColor.hashCode() - result = 31 * result + activeContentColor.hashCode() - result = 31 * result + containerColor.hashCode() - result = 31 * result + activeContainerColor.hashCode() - - return result - } - - companion object { - private const val SCROLLBAR_INACTIVE_TO_DORMANT_TIME_IN_MS = 2_000L - - private enum class ThumbState { - Active, Inactive, Dormant - } - } -} - -object ScrollbarDefaults { - @Composable - fun colors( - contentColor: Color = MaterialTheme.colorScheme.primary, - scrolledContentColor: Color = contentColor, - containerColor: Color = MaterialTheme.colorScheme.surfaceColorAtElevation(10.dp), - scrolledContainerColor: Color = containerColor - ) = ScrollbarColors( - contentColor, - scrolledContentColor, - containerColor, - scrolledContainerColor - ) - - @Composable - fun Thumb( - color: Color, - orientation: Orientation, - size: Dp = 8.dp - ) = Box( - modifier = Modifier - .run { - when (orientation) { - Orientation.Vertical -> width(size).fillMaxHeight() - Orientation.Horizontal -> height(size).fillMaxWidth() - } - } - .background( - color = color, - shape = CircleShape - ) - ) -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/LazyScrollbarExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/LazyScrollbarExt.kt deleted file mode 100644 index 96f5f4b1..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/LazyScrollbarExt.kt +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2023 Sanmer - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.sanmer.pi.ui.component.scrollbar - -import androidx.compose.foundation.gestures.ScrollableState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filterNotNull -import kotlin.math.abs -import kotlin.math.min - -/** - * Calculates the [ScrollbarState] for lazy layouts. - * @param itemsAvailable the total amount of items available to scroll in the layout. - * @param visibleItems a list of items currently visible in the layout. - * @param firstVisibleItemIndex a function for interpolating the first visible index in the lazy layout - * as scrolling progresses for smooth and linear scrollbar thumb progression. - * */ -@Composable -internal inline fun LazyState.scrollbarState( - itemsAvailable: Int, - crossinline visibleItems: LazyState.() -> List, - crossinline firstVisibleItemIndex: LazyState.(List) -> Float, - crossinline itemPercentVisible: LazyState.(LazyStateItem) -> Float -): ScrollbarState { - var state by remember { mutableStateOf(ScrollbarState.FULL) } - - LaunchedEffect( - key1 = this, - key2 = itemsAvailable, - ) { - snapshotFlow { - if (itemsAvailable == 0) return@snapshotFlow null - - val visibleItemsInfo = visibleItems(this@scrollbarState) - if (visibleItemsInfo.isEmpty()) return@snapshotFlow null - - val firstIndex = min( - a = firstVisibleItemIndex(visibleItemsInfo), - b = itemsAvailable.toFloat(), - ) - if (firstIndex.isNaN()) return@snapshotFlow null - - val itemsVisible = visibleItemsInfo.sumOf { - itemPercentVisible(it).toDouble() - }.toFloat() - - val thumbTravelPercent = min( - a = firstIndex / itemsAvailable, - b = 1f, - ) - val thumbSizePercent = min( - a = itemsVisible / itemsAvailable, - b = 1f, - ) - ScrollbarState( - thumbSizePercent = thumbSizePercent, - thumbMovedPercent = thumbTravelPercent - ) - } - .filterNotNull() - .distinctUntilChanged() - .collect { state = it } - } - return state -} - -/** - * Linearly interpolates the index for the first item in [visibleItems] for smooth scrollbar - * progression. - * @param visibleItems a list of items currently visible in the layout. - * @param itemSize a lookup function for the size of an item in the layout. - * @param offset a lookup function for the offset of an item relative to the start of the view port. - * @param nextItemOnMainAxis a lookup function for the next item on the main axis in the direction - * of the scroll. - * @param itemIndex a lookup function for index of an item in the layout relative to - * the total amount of items available. - * - * @return a [Float] in the range [firstItemPosition..nextItemPosition) where nextItemPosition - * is the index of the consecutive item along the major axis. - * */ -internal inline fun LazyState.interpolateFirstItemIndex( - visibleItems: List, - crossinline itemSize: LazyState.(LazyStateItem) -> Int, - crossinline offset: LazyState.(LazyStateItem) -> Int, - crossinline nextItemOnMainAxis: LazyState.(LazyStateItem) -> LazyStateItem?, - crossinline itemIndex: (LazyStateItem) -> Int, -): Float { - if (visibleItems.isEmpty()) return 0f - - val firstItem = visibleItems.first() - val firstItemIndex = itemIndex(firstItem) - - if (firstItemIndex < 0) return Float.NaN - - val firstItemSize = itemSize(firstItem) - if (firstItemSize == 0) return Float.NaN - - val itemOffset = offset(firstItem).toFloat() - val offsetPercentage = abs(itemOffset) / firstItemSize - - val nextItem = nextItemOnMainAxis(firstItem) ?: return firstItemIndex + offsetPercentage - - val nextItemIndex = itemIndex(nextItem) - - return firstItemIndex + ((nextItemIndex - firstItemIndex) * offsetPercentage) -} - -/** - * Returns the percentage of an item that is currently visible in the view port. - * @param itemSize the size of the item - * @param itemStartOffset the start offset of the item relative to the view port start - * @param viewportStartOffset the start offset of the view port - * @param viewportEndOffset the end offset of the view port - */ -internal fun itemVisibilityPercentage( - itemSize: Int, - itemStartOffset: Int, - viewportStartOffset: Int, - viewportEndOffset: Int, -): Float { - if (itemSize == 0) return 0f - val itemEnd = itemStartOffset + itemSize - val startOffset = when { - itemStartOffset > viewportStartOffset -> 0 - else -> abs(abs(viewportStartOffset) - abs(itemStartOffset)) - } - val endOffset = when { - itemEnd < viewportEndOffset -> 0 - else -> abs(abs(itemEnd) - abs(viewportEndOffset)) - } - val size = itemSize.toFloat() - return (size - startOffset - endOffset) / size -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/Scrollbar.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/Scrollbar.kt deleted file mode 100644 index 10a3bfe0..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/Scrollbar.kt +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright 2023 Sanmer - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.sanmer.pi.ui.component.scrollbar - -import androidx.compose.animation.core.animateDpAsState -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.detectHorizontalDragGestures -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.gestures.detectVerticalDragGestures -import androidx.compose.foundation.hoverable -import androidx.compose.foundation.interaction.DragInteraction -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.PressInteraction -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.PointerInputChange -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.layout.positionInRoot -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntSize -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.max -import androidx.compose.ui.util.packFloats -import androidx.compose.ui.util.unpackFloat1 -import androidx.compose.ui.util.unpackFloat2 -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.delay -import kotlinx.coroutines.withTimeout -import kotlin.math.max -import kotlin.math.min - -/** - * The delay between scrolls when a user long presses on the scrollbar track to initiate a scroll - * instead of dragging the scrollbar thumb. - */ -private const val SCROLLBAR_PRESS_DELAY_MS = 10L - -/** - * The percentage displacement of the scrollbar when scrolled by long presses on the scrollbar - * track. - */ -private const val SCROLLBAR_PRESS_DELTA_PCT = 0.02f - -/** - * Class definition for the core properties of a scroll bar - */ -@Immutable -@JvmInline -value class ScrollbarState internal constructor( - internal val packedValue: Long -) { - companion object { - val FULL = ScrollbarState( - thumbSizePercent = 1f, - thumbMovedPercent = 0f - ) - } -} - -/** - * Class definition for the core properties of a scroll bar track - */ -@Immutable -@JvmInline -private value class ScrollbarTrack( - val packedValue: Long -) { - constructor( - max: Float, - min: Float - ) : this(packFloats(max, min)) -} - -/** - * Creates a [ScrollbarState] with the listed properties - * @param thumbSizePercent the thumb size of the scrollbar as a percentage of the total track size. - * Refers to either the thumb width (for horizontal scrollbars) - * or height (for vertical scrollbars). - * @param thumbMovedPercent the distance the thumb has traveled as a percentage of total - * track size. - */ -fun ScrollbarState( - thumbSizePercent: Float, - thumbMovedPercent: Float -) = ScrollbarState( - packedValue = packFloats( - val1 = thumbSizePercent, - val2 = thumbMovedPercent - ) -) - -/** - * Returns the thumb size of the scrollbar as a percentage of the total track size - */ -val ScrollbarState.thumbSizePercent - get() = unpackFloat1(packedValue) - -/** - * Returns the distance the thumb has traveled as a percentage of total track size - */ -val ScrollbarState.thumbMovedPercent - get() = unpackFloat2(packedValue) - -/** - * Returns the size of the scrollbar track in pixels - */ -private val ScrollbarTrack.size - get() = unpackFloat2(packedValue) - unpackFloat1(packedValue) - -/** - * Returns the position of the scrollbar thumb on the track as a percentage - */ -private fun ScrollbarTrack.thumbPosition( - dimension: Float -): Float = max( - a = min( - a = dimension / size, - b = 1f, - ), - b = 0f, -) - -/** - * Returns the value of [offset] along the axis specified by [this] - */ -internal fun Orientation.valueOf(offset: Offset) = when (this) { - Orientation.Horizontal -> offset.x - Orientation.Vertical -> offset.y -} - -/** - * Returns the value of [intSize] along the axis specified by [this] - */ -internal fun Orientation.valueOf(intSize: IntSize) = when (this) { - Orientation.Horizontal -> intSize.width - Orientation.Vertical -> intSize.height -} - -/** - * Returns the value of [intOffset] along the axis specified by [this] - */ -internal fun Orientation.valueOf(intOffset: IntOffset) = when (this) { - Orientation.Horizontal -> intOffset.x - Orientation.Vertical -> intOffset.y -} - -/** - * A Composable for drawing a scrollbar - * @param orientation the scroll direction of the scrollbar - * @param state the state describing the position of the scrollbar - * @param backgroundColor the color of the background for scrollbar - * @param thumbSize the size of the scrollbar thumb - * @param interactionSource allows for observing the state of the scroll bar - * @param thumb a composable for drawing the scrollbar thumb - * @param onThumbMoved an function for reacting to scroll bar displacements caused by direct - * interactions on the scrollbar thumb by the user, for example implementing a fast scroll - * @param reverseLayout reverse the direction of scrolling and layout - */ -@Composable -fun Scrollbar( - modifier: Modifier, - orientation: Orientation, - state: ScrollbarState, - backgroundColor: Color, - thumbSize: Dp = 50.dp, - interactionSource: MutableInteractionSource?, - thumb: @Composable () -> Unit, - onThumbMoved: ((Float) -> Unit)?, - reverseLayout: Boolean -) { - // Only displayed when thumb size > 0.50 - if (state.thumbSizePercent > 0.50) return - - // Using Offset.Unspecified and Float.NaN instead of null - // to prevent unnecessary boxing of primitives - var pressedOffset by remember { mutableStateOf(Offset.Unspecified) } - var draggedOffset by remember { mutableStateOf(Offset.Unspecified) } - - // Used to immediately show drag feedback in the UI while the scrolling implementation - // catches up - var interactionThumbTravelPercent by remember { mutableFloatStateOf(Float.NaN) } - - var track by remember { mutableStateOf(ScrollbarTrack(packedValue = 0)) } - - val thumbSizePx = with(LocalDensity.current) { thumbSize.toPx() } - val oldThumbSizePx = state.thumbSizePercent * track.size - - val thumbTravelPercent = when { - interactionThumbTravelPercent.isNaN() -> { - // Calculate the resize percentage - val p = (track.size - thumbSizePx) / (track.size - oldThumbSizePx) - val new = state.thumbMovedPercent * p - - val thumbMovedPercent = when { - new.isNaN() -> 0f - new.isInfinite() -> 1f - else -> new - } - - when { - reverseLayout -> 1f - thumbMovedPercent - else -> thumbMovedPercent - } - } - - else -> interactionThumbTravelPercent - } - - val thumbSizeDp by animateDpAsState( - targetValue = with(LocalDensity.current) { thumbSizePx.toDp() }, - label = "thumbSizeDp", - ) - - val thumbMovedPx = min( - a = when { - reverseLayout -> track.size * thumbTravelPercent - thumbSizePx - else -> track.size * thumbTravelPercent - }, - b = track.size - thumbSizePx - ) - - // scrollbar track container - Box( - modifier = modifier - .run { - val withHover = interactionSource?.let(::hoverable) ?: this - when (orientation) { - Orientation.Vertical -> withHover.fillMaxHeight() - Orientation.Horizontal -> withHover.fillMaxWidth() - } - } - .onGloballyPositioned { coordinates -> - val scrollbarStartCoordinate = orientation.valueOf(coordinates.positionInRoot()) - track = ScrollbarTrack( - max = scrollbarStartCoordinate, - min = scrollbarStartCoordinate + orientation.valueOf(coordinates.size), - ) - } - // Process scrollbar presses - .pointerInput(Unit) { - detectTapGestures( - onPress = { offset -> - try { - // Wait for a long press before scrolling - withTimeout(viewConfiguration.longPressTimeoutMillis) { - tryAwaitRelease() - } - } catch (e: TimeoutCancellationException) { - // Start the press triggered scroll - val initialPress = PressInteraction.Press(offset) - interactionSource?.tryEmit(initialPress) - - pressedOffset = offset - interactionSource?.tryEmit( - when { - tryAwaitRelease() -> PressInteraction.Release(initialPress) - else -> PressInteraction.Cancel(initialPress) - }, - ) - - // End the press - pressedOffset = Offset.Unspecified - } - }, - ) - } - // Process scrollbar drags - .pointerInput(Unit) { - var dragInteraction: DragInteraction.Start? = null - val onDragStart: (Offset) -> Unit = { offset -> - val start = DragInteraction.Start() - dragInteraction = start - interactionSource?.tryEmit(start) - draggedOffset = offset - } - val onDragEnd: () -> Unit = { - dragInteraction?.let { interactionSource?.tryEmit(DragInteraction.Stop(it)) } - draggedOffset = Offset.Unspecified - } - val onDragCancel: () -> Unit = { - dragInteraction?.let { interactionSource?.tryEmit(DragInteraction.Cancel(it)) } - draggedOffset = Offset.Unspecified - } - val onDrag: (change: PointerInputChange, dragAmount: Float) -> Unit = - onDrag@{ _, delta -> - if (draggedOffset == Offset.Unspecified) return@onDrag - draggedOffset = when (orientation) { - Orientation.Vertical -> draggedOffset.copy( - y = draggedOffset.y + delta, - ) - - Orientation.Horizontal -> draggedOffset.copy( - x = draggedOffset.x + delta, - ) - } - } - - when (orientation) { - Orientation.Horizontal -> detectHorizontalDragGestures( - onDragStart = onDragStart, - onDragEnd = onDragEnd, - onDragCancel = onDragCancel, - onHorizontalDrag = onDrag, - ) - - Orientation.Vertical -> detectVerticalDragGestures( - onDragStart = onDragStart, - onDragEnd = onDragEnd, - onDragCancel = onDragCancel, - onVerticalDrag = onDrag, - ) - } - } - .background( - color = backgroundColor, - shape = CircleShape - ), - ) { - val scrollbarThumbMovedDp = max( - a = with(LocalDensity.current) { thumbMovedPx.toDp() }, - b = 0.dp, - ) - // scrollbar thumb container - Box( - modifier = Modifier - .align(Alignment.TopStart) - .run { - when (orientation) { - Orientation.Horizontal -> width(thumbSizeDp) - Orientation.Vertical -> height(thumbSizeDp) - } - } - .offset( - y = when (orientation) { - Orientation.Horizontal -> 0.dp - Orientation.Vertical -> scrollbarThumbMovedDp - }, - x = when (orientation) { - Orientation.Horizontal -> scrollbarThumbMovedDp - Orientation.Vertical -> 0.dp - }, - ), - ) { - thumb() - } - } - - if (onThumbMoved == null) return - val thumbMoved: (Float) -> Unit = { - onThumbMoved( - when { - reverseLayout -> 1f - it - else -> it - } - ) - } - - // State that will be read inside the effects that follow - // but will not cause re-triggering of them - val updatedState by rememberUpdatedState(state) - - // Process presses - LaunchedEffect(pressedOffset) { - // Press ended, reset interactionThumbTravelPercent - if (pressedOffset == Offset.Unspecified) { - interactionThumbTravelPercent = Float.NaN - return@LaunchedEffect - } - - var currentThumbMovedPercent = updatedState.thumbMovedPercent - val destinationThumbMovedPercent = track.thumbPosition( - dimension = orientation.valueOf(pressedOffset), - ) - val isPositive = currentThumbMovedPercent < destinationThumbMovedPercent - val delta = SCROLLBAR_PRESS_DELTA_PCT * if (isPositive) 1f else -1f - - while (currentThumbMovedPercent != destinationThumbMovedPercent) { - currentThumbMovedPercent = when { - isPositive -> min( - a = currentThumbMovedPercent + delta, - b = destinationThumbMovedPercent, - ) - - else -> max( - a = currentThumbMovedPercent + delta, - b = destinationThumbMovedPercent, - ) - } - - thumbMoved(currentThumbMovedPercent) - - interactionThumbTravelPercent = currentThumbMovedPercent - delay(SCROLLBAR_PRESS_DELAY_MS) - } - } - - // Process drags - LaunchedEffect(draggedOffset) { - if (draggedOffset == Offset.Unspecified) { - interactionThumbTravelPercent = Float.NaN - return@LaunchedEffect - } - - val currentTravel = track.thumbPosition( - dimension = orientation.valueOf(draggedOffset), - ) - - thumbMoved(currentTravel) - interactionThumbTravelPercent = currentTravel - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/ScrollbarExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/ScrollbarExt.kt deleted file mode 100644 index 020edbf4..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/ScrollbarExt.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2023 Sanmer - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.sanmer.pi.ui.component.scrollbar - -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.lazy.LazyListItemInfo -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.grid.LazyGridItemInfo -import androidx.compose.foundation.lazy.grid.LazyGridState -import androidx.compose.runtime.Composable - -/** - * Calculates a [ScrollbarState] driven by the changes in a [LazyListState]. - * - * @param itemsAvailable the total amount of items available to scroll in the lazy list. - * @param itemIndex a lookup function for index of an item in the list relative to [itemsAvailable]. - */ -@Composable -fun LazyListState.scrollbarState( - itemsAvailable: Int = layoutInfo.totalItemsCount, - itemIndex: (LazyListItemInfo) -> Int = LazyListItemInfo::index -): ScrollbarState = scrollbarState( - itemsAvailable = itemsAvailable, - visibleItems = { layoutInfo.visibleItemsInfo }, - firstVisibleItemIndex = { visibleItems -> - interpolateFirstItemIndex( - visibleItems = visibleItems, - itemSize = { it.size }, - offset = { it.offset }, - nextItemOnMainAxis = { first -> visibleItems.find { it != first } }, - itemIndex = itemIndex, - ) - }, - itemPercentVisible = itemPercentVisible@{ itemInfo -> - itemVisibilityPercentage( - itemSize = itemInfo.size, - itemStartOffset = itemInfo.offset, - viewportStartOffset = layoutInfo.viewportStartOffset, - viewportEndOffset = layoutInfo.viewportEndOffset, - ) - } -) - -/** - * Calculates a [ScrollbarState] driven by the changes in a [LazyGridState] - * - * @param itemsAvailable the total amount of items available to scroll in the grid. - * @param itemIndex a lookup function for index of an item in the grid relative to [itemsAvailable]. - */ -@Composable -fun LazyGridState.scrollbarState( - itemsAvailable: Int, - itemIndex: (LazyGridItemInfo) -> Int = LazyGridItemInfo::index -): ScrollbarState = scrollbarState( - itemsAvailable = itemsAvailable, - visibleItems = { layoutInfo.visibleItemsInfo }, - firstVisibleItemIndex = { visibleItems -> - interpolateFirstItemIndex( - visibleItems = visibleItems, - itemSize = { - layoutInfo.orientation.valueOf(it.size) - }, - offset = { layoutInfo.orientation.valueOf(it.offset) }, - nextItemOnMainAxis = { first -> - when (layoutInfo.orientation) { - Orientation.Vertical -> visibleItems.find { - it != first && it.row != first.row - } - - Orientation.Horizontal -> visibleItems.find { - it != first && it.column != first.column - } - } - }, - itemIndex = itemIndex, - ) - }, - itemPercentVisible = itemPercentVisible@{ itemInfo -> - itemVisibilityPercentage( - itemSize = layoutInfo.orientation.valueOf(itemInfo.size), - itemStartOffset = layoutInfo.orientation.valueOf(itemInfo.offset), - viewportStartOffset = layoutInfo.viewportStartOffset, - viewportEndOffset = layoutInfo.viewportEndOffset, - ) - } -) \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/ThumbExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/ThumbExt.kt deleted file mode 100644 index bd4ef453..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/scrollbar/ThumbExt.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2023 Sanmer - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.sanmer.pi.ui.component.scrollbar - -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.grid.LazyGridState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.runtime.setValue - -/** - * Remembers a function to react to [Scrollbar] thumb position displacements for a [LazyListState] - * @param itemsAvailable the amount of items in the list. - */ -@Composable -fun LazyListState.rememberDraggableScroller( - itemsAvailable: Int = layoutInfo.totalItemsCount -): (Float) -> Unit = rememberDraggableScroller( - itemsAvailable = itemsAvailable, - scroll = ::scrollToItem -) - -/** - * Remembers a function to react to [Scrollbar] thumb position displacements for a [LazyGridState] - * @param itemsAvailable the amount of items in the grid. - */ -@Composable -fun LazyGridState.rememberDraggableScroller( - itemsAvailable: Int = layoutInfo.totalItemsCount -): (Float) -> Unit = rememberDraggableScroller( - itemsAvailable = itemsAvailable, - scroll = ::scrollToItem -) - -/** - * Generic function to react to [Scrollbar] thumb displacements in a lazy layout. - * @param itemsAvailable the total amount of items available to scroll in the layout. - * @param scroll a function to be invoked when an index has been identified to scroll to. - */ -@Composable -private inline fun rememberDraggableScroller( - itemsAvailable: Int, - crossinline scroll: suspend (index: Int) -> Unit -): (Float) -> Unit { - var percentage by remember { mutableFloatStateOf(Float.NaN) } - val itemCount by rememberUpdatedState(itemsAvailable) - - LaunchedEffect(percentage) { - if (percentage.isNaN()) return@LaunchedEffect - val indexToFind = (itemCount * percentage).toInt() - scroll(indexToFind) - } - return remember { - { newPercentage -> percentage = newPercentage } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/ShapExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/ShapExt.kt new file mode 100644 index 00000000..084cb31d --- /dev/null +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/ShapExt.kt @@ -0,0 +1,8 @@ +package dev.sanmer.pi.ui.ktx + +import androidx.compose.foundation.shape.CornerBasedShape +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.ui.unit.Dp + +fun CornerBasedShape.bottom(size: Dp) = + copy(bottomStart = CornerSize(size), bottomEnd = CornerSize(size)) \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt index 0dd3acd4..98000149 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt @@ -15,8 +15,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.BottomSheetDefaults import androidx.compose.material3.Button import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon @@ -54,7 +52,7 @@ import dev.sanmer.pi.model.IPackageInfo import dev.sanmer.pi.ui.component.BottomSheetLayout import dev.sanmer.pi.ui.component.Failed import dev.sanmer.pi.ui.component.Loading -import dev.sanmer.pi.ui.ktx.expandedShape +import dev.sanmer.pi.ui.ktx.bottom import dev.sanmer.pi.viewmodel.InstallViewModel import dev.sanmer.pi.viewmodel.InstallViewModel.State import dev.sanmer.pi.viewmodel.InstallViewModel.State.Companion.isReady @@ -83,7 +81,7 @@ fun InstallScreen( ) } }, - shape = BottomSheetDefaults.expandedShape(20.dp) + shape = MaterialTheme.shapes.large.bottom(0.dp) ) { Crossfade( modifier = Modifier @@ -190,7 +188,7 @@ private fun PackageItem( text = stringResource(id = R.string.install_package_title) ) { Surface( - shape = RoundedCornerShape(15.dp), + shape = MaterialTheme.shapes.large, tonalElevation = 6.dp, border = CardDefaults.outlinedCardBorder() ) { @@ -245,7 +243,7 @@ private fun RequesterItem( text = stringResource(id = R.string.install_requester_title) ) { OutlinedCard( - shape = RoundedCornerShape(15.dp) + shape = MaterialTheme.shapes.large ) { Row( modifier = Modifier @@ -412,7 +410,7 @@ private fun SplitConfigItem( } OutlinedCard( - shape = RoundedCornerShape(15.dp), + shape = MaterialTheme.shapes.medium, onClick = { toggleSplitConfig(config) }, enabled = !config.isDisabled ) { diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/main/MainScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/main/MainScreen.kt index aba0e993..a7eeff3e 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/MainScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/main/MainScreen.kt @@ -1,16 +1,24 @@ package dev.sanmer.pi.ui.main +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import dev.sanmer.pi.ui.navigation.MainScreen -import dev.sanmer.pi.ui.navigation.graphs.appsScreen -import dev.sanmer.pi.ui.navigation.graphs.settingsScreen +import dev.sanmer.pi.ui.main.Screen.Companion.apps +import dev.sanmer.pi.ui.main.Screen.Companion.settings +import dev.sanmer.pi.ui.main.Screen.Companion.workingMode +import dev.sanmer.pi.ui.screens.apps.AppsScreen +import dev.sanmer.pi.ui.screens.settings.SettingsScreen +import dev.sanmer.pi.ui.screens.workingmode.WorkingModeScreen @Composable fun MainScreen() { @@ -22,12 +30,54 @@ fun MainScreen() { NavHost( modifier = Modifier.padding(it), navController = navController, - startDestination = MainScreen.Apps.route + startDestination = Screen.Apps.route ) { - appsScreen( + apps(navController) + settings(navController) + workingMode(navController) + } + } +} + +enum class Screen(val route: String) { + Apps("Apps"), + Settings("Settings"), + WorkingMode("WorkingMode"); + + companion object { + + fun NavGraphBuilder.apps( + navController: NavController + ) = composable( + route = Apps.route, + enterTransition = { fadeIn() }, + exitTransition = { fadeOut() } + ) { + AppsScreen( + navController = navController + ) + } + + fun NavGraphBuilder.settings( + navController: NavController + ) = composable( + route = Settings.route, + enterTransition = { fadeIn() }, + exitTransition = { fadeOut() } + ) { + SettingsScreen( navController = navController ) - settingsScreen( + } + + fun NavGraphBuilder.workingMode( + navController: NavController + ) = composable( + route = WorkingMode.route, + enterTransition = { fadeIn() }, + exitTransition = { fadeOut() } + ) { + WorkingModeScreen( navController = navController ) } diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt index c7e39096..2427789d 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt @@ -13,8 +13,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.BottomSheetDefaults import androidx.compose.material3.Button import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon @@ -40,8 +38,8 @@ import dev.sanmer.pi.ktx.finishActivity import dev.sanmer.pi.model.IPackageInfo import dev.sanmer.pi.ui.component.BottomSheetLayout import dev.sanmer.pi.ui.component.Loading -import dev.sanmer.pi.ui.ktx.expandedShape -import dev.sanmer.pi.ui.screens.apps.AppItem +import dev.sanmer.pi.ui.ktx.bottom +import dev.sanmer.pi.ui.screens.apps.component.AppItem import dev.sanmer.pi.viewmodel.PermissionViewModel @Composable @@ -66,7 +64,7 @@ fun PermissionScreen( ) } }, - shape = BottomSheetDefaults.expandedShape(20.dp) + shape = MaterialTheme.shapes.large.bottom(0.dp) ) { Crossfade( modifier = Modifier @@ -144,7 +142,7 @@ private fun AppItem( text = stringResource(id = R.string.permission_app_title) ) { Surface( - shape = RoundedCornerShape(15.dp), + shape = MaterialTheme.shapes.large, tonalElevation = 6.dp, border = CardDefaults.outlinedCardBorder() ) { @@ -196,7 +194,7 @@ private fun PermissionItem( } OutlinedCard( - shape = RoundedCornerShape(15.dp), + shape = MaterialTheme.shapes.medium, onClick = { togglePermission(permission) } ) { Row( diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/main/SetupScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/main/SetupScreen.kt index 250e53ad..c6c7105e 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/SetupScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/main/SetupScreen.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import dev.sanmer.pi.R import dev.sanmer.pi.datastore.model.Provider -import dev.sanmer.pi.ui.screens.settings.workingmode.WorkingModeItem +import dev.sanmer.pi.ui.screens.workingmode.component.WorkingModeItem @Composable fun SetupScreen( diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/navigation/Main.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/navigation/Main.kt deleted file mode 100644 index d114e12c..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/navigation/Main.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.sanmer.pi.ui.navigation - -enum class MainScreen(val route: String) { - Apps("AppsScreen"), - Settings("SettingsScreen") -} diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/navigation/graphs/Apps.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/navigation/graphs/Apps.kt deleted file mode 100644 index 5c3f1cf7..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/navigation/graphs/Apps.kt +++ /dev/null @@ -1,31 +0,0 @@ -package dev.sanmer.pi.ui.navigation.graphs - -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import androidx.navigation.navigation -import dev.sanmer.pi.ui.navigation.MainScreen -import dev.sanmer.pi.ui.screens.apps.AppsScreen - -enum class AppsScreen(val route: String) { - Home("Apps") -} - -fun NavGraphBuilder.appsScreen( - navController: NavController -) = navigation( - startDestination = AppsScreen.Home.route, - route = MainScreen.Apps.route -) { - composable( - route = AppsScreen.Home.route, - enterTransition = { fadeIn() }, - exitTransition = { fadeOut() } - ) { - AppsScreen( - navController = navController - ) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/navigation/graphs/Settings.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/navigation/graphs/Settings.kt deleted file mode 100644 index fa71f5a8..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/navigation/graphs/Settings.kt +++ /dev/null @@ -1,43 +0,0 @@ -package dev.sanmer.pi.ui.navigation.graphs - -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import androidx.navigation.navigation -import dev.sanmer.pi.ui.navigation.MainScreen -import dev.sanmer.pi.ui.screens.settings.SettingsScreen -import dev.sanmer.pi.ui.screens.settings.workingmode.WorkingModeScreen - -enum class SettingsScreen(val route: String) { - Home("Settings"), - WorkingMode("WorkingMode") -} - -fun NavGraphBuilder.settingsScreen( - navController: NavController -) = navigation( - startDestination = SettingsScreen.Home.route, - route = MainScreen.Settings.route -) { - composable( - route = SettingsScreen.Home.route, - enterTransition = { fadeIn() }, - exitTransition = { fadeOut() } - ) { - SettingsScreen( - navController = navController - ) - } - - composable( - route = SettingsScreen.WorkingMode.route, - enterTransition = { fadeIn() }, - exitTransition = { fadeOut() } - ) { - WorkingModeScreen( - navController = navController - ) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/providable/LocalUserPreferences.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/provider/LocalUserPreferences.kt similarity index 83% rename from app/src/main/kotlin/dev/sanmer/pi/ui/providable/LocalUserPreferences.kt rename to app/src/main/kotlin/dev/sanmer/pi/ui/provider/LocalUserPreferences.kt index cea8968c..e41ec3cc 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/providable/LocalUserPreferences.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/provider/LocalUserPreferences.kt @@ -1,4 +1,4 @@ -package dev.sanmer.pi.ui.providable +package dev.sanmer.pi.ui.provider import androidx.compose.runtime.staticCompositionLocalOf import dev.sanmer.pi.datastore.model.UserPreferences diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt index f5dfbd74..06bfd23c 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt @@ -30,7 +30,8 @@ import dev.sanmer.pi.ui.component.Loading import dev.sanmer.pi.ui.component.PageIndicator import dev.sanmer.pi.ui.component.SearchTopBar import dev.sanmer.pi.ui.ktx.navigateSingleTopTo -import dev.sanmer.pi.ui.navigation.MainScreen +import dev.sanmer.pi.ui.main.Screen +import dev.sanmer.pi.ui.screens.apps.component.AppList import dev.sanmer.pi.viewmodel.AppsViewModel @Composable @@ -128,7 +129,7 @@ private fun TopBar( } IconButton( - onClick = { navController.navigateSingleTopTo(MainScreen.Settings.route) } + onClick = { navController.navigateSingleTopTo(Screen.Settings.route) } ) { Icon( painter = painterResource(id = R.drawable.settings), diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppItem.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppItem.kt similarity index 95% rename from app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppItem.kt rename to app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppItem.kt index bd47ee7a..19ecf1e9 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppItem.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppItem.kt @@ -1,4 +1,4 @@ -package dev.sanmer.pi.ui.screens.apps +package dev.sanmer.pi.ui.screens.apps.component import androidx.annotation.DrawableRes import androidx.compose.foundation.clickable @@ -11,7 +11,6 @@ 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.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -39,7 +38,7 @@ internal fun AppItem( enabled: Boolean = true ) = Row( modifier = Modifier - .clip(RoundedCornerShape(15.dp)) + .clip(shape = MaterialTheme.shapes.medium) .clickable( enabled = enabled, onClick = onClick diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppList.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt similarity index 70% rename from app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppList.kt rename to app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt index c8089f6e..a6c6f81c 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppList.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt @@ -1,28 +1,20 @@ -package dev.sanmer.pi.ui.screens.apps +package dev.sanmer.pi.ui.screens.apps.component -import androidx.compose.animation.core.RepeatMode -import androidx.compose.animation.core.animateFloat -import androidx.compose.animation.core.infiniteRepeatable -import androidx.compose.animation.core.rememberInfiniteTransition -import androidx.compose.animation.core.tween +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.AlertDialog -import androidx.compose.material3.BottomSheetDefaults import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButtonDefaults @@ -39,7 +31,6 @@ 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.graphicsLayer import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -48,8 +39,7 @@ import dev.sanmer.pi.R import dev.sanmer.pi.model.IPackageInfo import dev.sanmer.pi.model.IPackageInfo.Companion.toIPackageInfo import dev.sanmer.pi.ui.component.MenuChip -import dev.sanmer.pi.ui.component.scrollbar.VerticalFastScrollbar -import dev.sanmer.pi.ui.ktx.expandedShape +import dev.sanmer.pi.ui.ktx.bottom import dev.sanmer.pi.viewmodel.AppsViewModel import kotlinx.coroutines.launch @@ -58,8 +48,6 @@ internal fun AppList( list: List, state: LazyListState, buildSettings: (IPackageInfo) -> AppsViewModel.Settings -) = Box( - modifier = Modifier.fillMaxSize() ) { var packageName by remember { mutableStateOf("") } val packageInfo by remember(list, packageName) { @@ -77,6 +65,7 @@ internal fun AppList( } LazyColumn( + modifier = Modifier.animateContentSize(), state = state ) { items( @@ -89,11 +78,6 @@ internal fun AppList( ) } } - - VerticalFastScrollbar( - state = state, - modifier = Modifier.align(Alignment.CenterEnd) - ) } @Composable @@ -105,7 +89,7 @@ private fun BottomSheet( onDismissRequest = onClose, dragHandle = null, windowInsets = WindowInsets.navigationBars, - shape = BottomSheetDefaults.expandedShape(20.dp), + shape = MaterialTheme.shapes.large.bottom(0.dp), containerColor = MaterialTheme.colorScheme.surface, tonalElevation = 0.dp ) { @@ -127,8 +111,6 @@ private fun BottomSheet( ) SettingButtons( - pi = pi, - onClose = onClose, settings = settings ) @@ -141,8 +123,6 @@ private fun BottomSheet( @Composable private fun SettingButtons( - pi: IPackageInfo, - onClose: () -> Unit, settings: AppsViewModel.Settings ) = Row( verticalAlignment = Alignment.CenterVertically @@ -150,17 +130,6 @@ private fun SettingButtons( val context = LocalContext.current val scope = rememberCoroutineScope() - val infiniteTransition = rememberInfiniteTransition(label = "BottomSheet") - val animateZ by infiniteTransition.animateFloat( - initialValue = 0f, - targetValue = 360f, - animationSpec = infiniteRepeatable( - animation = tween(1000), - repeatMode = RepeatMode.Reverse - ), - label = "animateZ" - ) - if (settings.isOpenable) { ButtonItem( onClick = { settings.launch(context) } @@ -181,62 +150,14 @@ private fun SettingButtons( ) } - ButtonItem( - onClick = { settings.setting(context) } - ) { - Icon( - painter = painterResource(id = R.drawable.settings), - contentDescription = null - ) - } - - if (settings.isUninstallable) { - var animateUninstall by remember { mutableStateOf(false) } - - var uninstall by remember { mutableStateOf(false) } - if (uninstall) UninstallDialog( - appLabel = pi.appLabel, - onClose = { uninstall = false }, - onDelete = { - scope.launch { - animateUninstall = true - if (settings.uninstall()) onClose() - animateUninstall = false - } - } - ) - - ButtonItem( - onClick = { uninstall = true } - ) { - Icon( - painter = painterResource(id = R.drawable.trash), - contentDescription = null, - modifier = Modifier - .graphicsLayer { - rotationZ = if (animateUninstall) animateZ else 0f - } - ) - } - } - - var animateExport by remember { mutableStateOf(false) } ButtonItem( onClick = { - scope.launch { - animateExport = true - settings.export(context) - animateExport = false - } + scope.launch { settings.export(context) } } ) { Icon( painter = painterResource(id = R.drawable.package_export), - contentDescription = null, - modifier = Modifier - .graphicsLayer { - rotationZ = if (animateExport) animateZ else 0f - } + contentDescription = null ) } } @@ -302,7 +223,7 @@ private fun UninstallDialog( onDelete: () -> Unit ) = AlertDialog( onDismissRequest = onClose, - shape = RoundedCornerShape(20.dp), + shape = MaterialTheme.shapes.large, title = { Text(text = appLabel) }, text = { Column( diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt index 54e42a65..9293e402 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt @@ -35,8 +35,8 @@ import dev.sanmer.pi.ui.component.NavigateUpTopBar import dev.sanmer.pi.ui.component.SettingNormalItem import dev.sanmer.pi.ui.component.SettingSwitchItem import dev.sanmer.pi.ui.ktx.navigateSingleTopTo -import dev.sanmer.pi.ui.navigation.graphs.SettingsScreen -import dev.sanmer.pi.ui.providable.LocalUserPreferences +import dev.sanmer.pi.ui.main.Screen +import dev.sanmer.pi.ui.provider.LocalUserPreferences import dev.sanmer.pi.viewmodel.SettingsViewModel import java.util.Locale @@ -86,7 +86,7 @@ fun SettingsScreen( } ), onClick = { - navController.navigateSingleTopTo(SettingsScreen.WorkingMode.route) + navController.navigateSingleTopTo(Screen.WorkingMode.route) } ) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/workingmode/WorkingModeScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt similarity index 94% rename from app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/workingmode/WorkingModeScreen.kt rename to app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt index c89bfd5f..8e2e132f 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/workingmode/WorkingModeScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt @@ -1,4 +1,4 @@ -package dev.sanmer.pi.ui.screens.settings.workingmode +package dev.sanmer.pi.ui.screens.workingmode import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -21,7 +21,8 @@ import androidx.navigation.NavController import dev.sanmer.pi.R import dev.sanmer.pi.datastore.model.Provider import dev.sanmer.pi.ui.component.NavigateUpTopBar -import dev.sanmer.pi.ui.providable.LocalUserPreferences +import dev.sanmer.pi.ui.provider.LocalUserPreferences +import dev.sanmer.pi.ui.screens.workingmode.component.WorkingModeItem import dev.sanmer.pi.viewmodel.SettingsViewModel @Composable diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/workingmode/WorkingModeItem.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/component/WorkingModeItem.kt similarity index 93% rename from app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/workingmode/WorkingModeItem.kt rename to app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/component/WorkingModeItem.kt index 9394d9b3..5923d289 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/workingmode/WorkingModeItem.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/component/WorkingModeItem.kt @@ -1,4 +1,4 @@ -package dev.sanmer.pi.ui.screens.settings.workingmode +package dev.sanmer.pi.ui.screens.workingmode.component import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Box @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredHeightIn import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -38,7 +37,7 @@ internal fun WorkingModeItem( onClick = onClick, tonalElevation = if (selected) 4.dp else 0.dp, border = BorderStroke(1.dp, color = MaterialTheme.colorScheme.outline), - shape = RoundedCornerShape(15.dp) + shape = MaterialTheme.shapes.large ) { Column( modifier = Modifier diff --git a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt b/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt index fdb9bfee..ad6c8ffc 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt @@ -1,9 +1,7 @@ package dev.sanmer.pi.viewmodel import android.content.Context -import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo -import android.content.pm.PackageInstaller import android.content.pm.PackageManager import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -13,12 +11,10 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import dev.sanmer.pi.Compat import dev.sanmer.pi.PackageInfoCompat.isOverlayPackage -import dev.sanmer.pi.PackageInfoCompat.isSystemApp import dev.sanmer.pi.UserHandleCompat import dev.sanmer.pi.compat.MediaStoreCompat.createDownloadUri import dev.sanmer.pi.delegate.AppOpsManagerDelegate import dev.sanmer.pi.delegate.AppOpsManagerDelegate.Mode.Companion.isAllowed -import dev.sanmer.pi.ktx.appSetting import dev.sanmer.pi.ktx.viewPackage import dev.sanmer.pi.model.IPackageInfo import dev.sanmer.pi.model.IPackageInfo.Companion.toIPackageInfo @@ -30,6 +26,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber @@ -45,7 +42,6 @@ class AppsViewModel @Inject constructor( ) : ViewModel(), AppOpsManagerDelegate.AppOpsCallback { private val isProviderAlive get() = Compat.isAlive private val pm by lazy { Compat.getPackageManager() } - private val pi by lazy { Compat.getPackageInstaller() } private val aom by lazy { Compat.getAppOpsService() } var isSearch by mutableStateOf(false) @@ -112,14 +108,16 @@ class AppsViewModel @Inject constructor( ) { preferences, source -> if (source.isEmpty()) return@combine - cacheFlow.value = source.map { pi -> - pi.copy( - isRequester = preferences.requester == pi.packageName, - isExecutor = preferences.executor == pi.packageName - ) - }.sortedByDescending { it.lastUpdateTime } - .sortedByDescending { it.isAuthorized } - .sortedByDescending { it.isExecutor || it.isRequester } + cacheFlow.update { + source.map { pi -> + pi.copy( + isRequester = preferences.requester == pi.packageName, + isExecutor = preferences.executor == pi.packageName + ) + }.sortedByDescending { it.lastUpdateTime } + .sortedByDescending { it.isAuthorized } + .sortedByDescending { it.isExecutor || it.isRequester } + } isLoading = false @@ -132,8 +130,8 @@ class AppsViewModel @Inject constructor( cacheFlow ) { key, source -> - appsFlow.value = source - .filter { + appsFlow.update { + source.filter { if (key.isNotBlank()) { it.appLabel.contains(key, ignoreCase = true) || it.packageName.contains(key, ignoreCase = true) @@ -141,6 +139,7 @@ class AppsViewModel @Inject constructor( true } } + } }.launchIn(viewModelScope) } @@ -183,17 +182,6 @@ class AppsViewModel @Inject constructor( override val isOpenable by lazy { launchIntent != null } - override val isUninstallable by lazy { - val isUpdatedSystemApp = packageInfo.applicationInfo?.let { - it.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP != 0 - } ?: false - - when { - isUpdatedSystemApp -> true - else -> !packageInfo.isSystemApp - } - } - override fun launch(context: Context) { context.startActivity(launchIntent) } @@ -202,23 +190,6 @@ class AppsViewModel @Inject constructor( context.viewPackage(packageInfo.packageName) } - override fun setting(context: Context) { - context.appSetting(packageInfo.packageName) - } - - override suspend fun uninstall() = withContext(Dispatchers.IO) { - val result = pi.uninstall(packageInfo.packageName) - val status = result.getIntExtra( - PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE - ) - - when (status) { - PackageInstaller.STATUS_SUCCESS -> !packageInfo.isSystemApp - else -> false - } - } - override suspend fun export(context: Context): Boolean { val sourceDir = packageInfo.applicationInfo?.let { File(it.sourceDir) } if (sourceDir == null) return false @@ -327,11 +298,8 @@ class AppsViewModel @Inject constructor( interface Settings { val isOpenable: Boolean - val isUninstallable: Boolean fun launch(context: Context) fun view(context: Context) - fun setting(context: Context) - suspend fun uninstall(): Boolean suspend fun export(context: Context): Boolean suspend fun setAuthorized() suspend fun setRequester() From 5b66b9193b65127afb7972e365c43428840b135e Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Thu, 18 Jul 2024 15:11:25 +0800 Subject: [PATCH 05/17] Remove unused code & res --- .../main/kotlin/dev/sanmer/pi/ktx/DpExt.kt | 2 +- .../kotlin/dev/sanmer/pi/ktx/LocaleExt.kt | 13 +++++ .../kotlin/dev/sanmer/pi/ktx/ParcelableExt.kt | 10 ++-- .../dev/sanmer/pi/ui/ktx/BottomSheetExt.kt | 9 ---- .../kotlin/dev/sanmer/pi/ui/ktx/MenuExt.kt | 16 ------- .../dev/sanmer/pi/ui/ktx/NavControllerExt.kt | 14 ------ .../pi/ui/screens/apps/component/AppList.kt | 48 ------------------- .../pi/ui/screens/settings/SettingsScreen.kt | 17 ++----- app/src/main/res/drawable/trash.xml | 36 -------------- app/src/main/res/values-ar/strings.xml | 4 +- app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values-fr/strings.xml | 4 +- app/src/main/res/values-in/strings.xml | 4 +- app/src/main/res/values-iw/strings.xml | 4 +- app/src/main/res/values-pt-rBR/strings.xml | 4 +- app/src/main/res/values-pt/strings.xml | 4 +- app/src/main/res/values-ru/strings.xml | 4 +- app/src/main/res/values-vi/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 6 +-- app/src/main/res/values/strings.xml | 6 +-- 20 files changed, 33 insertions(+), 178 deletions(-) create mode 100644 app/src/main/kotlin/dev/sanmer/pi/ktx/LocaleExt.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/ktx/BottomSheetExt.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/ktx/MenuExt.kt delete mode 100644 app/src/main/res/drawable/trash.xml diff --git a/app/src/main/kotlin/dev/sanmer/pi/ktx/DpExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ktx/DpExt.kt index d07e8fcd..f257407e 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ktx/DpExt.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ktx/DpExt.kt @@ -2,4 +2,4 @@ package dev.sanmer.pi.ktx import android.content.res.Resources -val Int.dp: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt() \ No newline at end of file +val Int.dp get() = times(Resources.getSystem().displayMetrics.density).toInt() \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ktx/LocaleExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ktx/LocaleExt.kt new file mode 100644 index 00000000..5678cd61 --- /dev/null +++ b/app/src/main/kotlin/dev/sanmer/pi/ktx/LocaleExt.kt @@ -0,0 +1,13 @@ +package dev.sanmer.pi.ktx + +import java.util.Locale + +val Locale.localizedDisplayName: String + inline get() = getDisplayName(this) + .replaceFirstChar { + if (it.isLowerCase()) { + it.titlecase(this) + } else { + it.toString() + } + } \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ktx/ParcelableExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ktx/ParcelableExt.kt index d78f1f53..3777d9a9 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ktx/ParcelableExt.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ktx/ParcelableExt.kt @@ -6,10 +6,8 @@ import android.os.Parcelable import androidx.core.content.IntentCompat import androidx.core.os.BundleCompat -inline fun Intent.parcelable( - key: String -): T? = IntentCompat.getParcelableExtra(this, key, T::class.java) +inline fun Intent.parcelable(key: String): T? = + IntentCompat.getParcelableExtra(this, key, T::class.java) -inline fun Bundle.parcelable( - key: String -): T? = BundleCompat.getParcelable(this, key, T::class.java) \ No newline at end of file +inline fun Bundle.parcelable(key: String): T? = + BundleCompat.getParcelable(this, key, T::class.java) \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/BottomSheetExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/BottomSheetExt.kt deleted file mode 100644 index 98ea32bd..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/BottomSheetExt.kt +++ /dev/null @@ -1,9 +0,0 @@ -package dev.sanmer.pi.ui.ktx - -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.BottomSheetDefaults -import androidx.compose.ui.unit.Dp - -@Suppress("UnusedReceiverParameter") -fun BottomSheetDefaults.expandedShape(size: Dp) = - RoundedCornerShape(topStart = size, topEnd = size) \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/MenuExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/MenuExt.kt deleted file mode 100644 index 2fd921d1..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/MenuExt.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.sanmer.pi.ui.ktx - -import androidx.compose.foundation.shape.CornerBasedShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.unit.dp - -@Composable -fun ProvideMenuShape( - value: CornerBasedShape = RoundedCornerShape(8.dp), - content: @Composable () -> Unit -) = MaterialTheme( - shapes = MaterialTheme.shapes.copy(extraSmall = value), - content = content -) \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/NavControllerExt.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/NavControllerExt.kt index 0db2f43f..09dfde89 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/NavControllerExt.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/ktx/NavControllerExt.kt @@ -1,7 +1,6 @@ package dev.sanmer.pi.ui.ktx import androidx.navigation.NavController -import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavOptionsBuilder fun NavController.navigateSingleTopTo( @@ -13,17 +12,4 @@ fun NavController.navigateSingleTopTo( launchSingleTop = true restoreState = true builder() -} - -fun NavController.navigatePopUpTo( - route: String -) = navigateSingleTopTo( - route = route -) { - popUpTo( - id = currentDestination?.parent?.id ?: graph.findStartDestination().id - ) { - saveState = true - inclusive = true - } } \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt index a6c6f81c..e303fa8d 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt @@ -8,20 +8,17 @@ import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items -import androidx.compose.material3.AlertDialog import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -214,49 +211,4 @@ private fun ButtonItem( containerColor = MaterialTheme.colorScheme.surfaceVariant ), content = content -) - -@Composable -private fun UninstallDialog( - appLabel: String, - onClose: () -> Unit, - onDelete: () -> Unit -) = AlertDialog( - onDismissRequest = onClose, - shape = MaterialTheme.shapes.large, - title = { Text(text = appLabel) }, - text = { - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - Text( - text = stringResource(id = R.string.app_dialog_desc1), - style = MaterialTheme.typography.bodyMedium - ) - - Text( - text = stringResource(id = R.string.app_dialog_desc2), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.outline - ) - } - }, - confirmButton = { - TextButton( - onClick = { - onDelete() - onClose() - } - ) { - Text(text = stringResource(id = R.string.dialog_ok)) - } - }, - dismissButton = { - TextButton( - onClick = onClose - ) { - Text(text = stringResource(id = R.string.dialog_cancel)) - } - }, ) \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt index 9293e402..ccfee4dd 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt @@ -30,6 +30,7 @@ import dev.sanmer.pi.R import dev.sanmer.pi.compat.BuildCompat import dev.sanmer.pi.datastore.model.Provider import dev.sanmer.pi.ktx.applicationLocale +import dev.sanmer.pi.ktx.localizedDisplayName import dev.sanmer.pi.ktx.viewUrl import dev.sanmer.pi.ui.component.NavigateUpTopBar import dev.sanmer.pi.ui.component.SettingNormalItem @@ -38,7 +39,6 @@ import dev.sanmer.pi.ui.ktx.navigateSingleTopTo import dev.sanmer.pi.ui.main.Screen import dev.sanmer.pi.ui.provider.LocalUserPreferences import dev.sanmer.pi.viewmodel.SettingsViewModel -import java.util.Locale @Composable fun SettingsScreen( @@ -172,7 +172,8 @@ private fun LanguageItem( ) = SettingNormalItem( icon = R.drawable.world, title = stringResource(id = R.string.settings_language), - desc = context.applicationLocale?.localizedDisplayName ?: stringResource(id = R.string.settings_language_system), + desc = context.applicationLocale?.localizedDisplayName + ?: stringResource(id = R.string.settings_language_system), onClick = { // noinspection InlinedApi context.startActivity( @@ -190,17 +191,7 @@ private fun TopBar( navController: NavController, scrollBehavior: TopAppBarScrollBehavior ) = NavigateUpTopBar( - title = stringResource(id = R.string.page_settings), + title = stringResource(id = R.string.settings_title), navController = navController, scrollBehavior = scrollBehavior ) - -private val Locale.localizedDisplayName: String - get() = getDisplayName(this) - .replaceFirstChar { - if (it.isLowerCase()) { - it.titlecase(this) - } else { - it.toString() - } - } diff --git a/app/src/main/res/drawable/trash.xml b/app/src/main/res/drawable/trash.xml deleted file mode 100644 index f3c6affa..00000000 --- a/app/src/main/res/drawable/trash.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index d58c29c7..a93ea234 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -8,7 +8,7 @@ يتطلب إذنًا مقدمًا من Sui أو Shizuku مقدم طلب التثبيت قائمة فارغة - الإعدادات + الإعدادات لون ديناميكي المشاركة في الترجمة ساعدنا في ترجمة PI إلى لغتك @@ -27,8 +27,6 @@ ABI إستخدام لون مظهر النظام يتطلب صلاحيات الروت التي يوفرها Magisk أو KernelSU أو APatch - هل تريد إلغاء تثبيت هذا التطبيق؟ - معلومة: سيؤدي هذا إلى إلغاء تثبيت التحديثات فقط إذا كان تطبيق نظام. السلوك إضغط لمحاولة البدء الواجهة diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index e133a839..bddf7cfd 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1,13 +1,11 @@ - ¿Quieres desinstalar esta aplicación? Comportamiento Instalar - Configuración + Configuración Modo de trabajo Requiere permiso de Sui o Shizuku Requiere permisos Root proporcionados por APatch, KernelSU o Magisk - Consejo: Esto sólo desinstalará las actualizaciones si se trata de una aplicación del sistema. El servicio está en ejecución Versión %1$d, %2$s El servicio no está en ejecución diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index bdf27e3e..84ecc4c2 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -36,7 +36,5 @@ Language Non précisé Installer - Paramètres - Voulez vous désinstaller l\'application ? - Astuce: Cela va uniquement désinstaller les mises à jours si c\'est une application système. + Paramètres \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 281e0992..68ec43f0 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1,9 +1,7 @@ - Setelan + Setelan Mode kerja - Apakah Anda ingin meng-uninstall aplikasi ini? - Tips: Ini hanya akan uninstall pembaruan jika itu aplikasi sistem. Diotorisasi Eksekutor Tindakan diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 33fc0d5c..71994977 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -2,10 +2,8 @@ שיטת העבודה התקנה - הגדרות + הגדרות דורש הרשאות המסופקות על ידי Sui או Shizuku - רוצה להסיר את התקנת האפליקציה? - טיפ: פעולה זו תסיר את התקנת העדכונים רק אם מדובר באפליקציית מערכת. ארכיטקטורה פיצ\'ר דינמי תפעול diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 222ac9a3..bdbd770a 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -9,7 +9,7 @@ Versão %1$d, %2$s Solicitante de instalação Lista vazia - Configurações + Configurações Cor dinâmica Use a cor do tema do sistema Pacote de instalação @@ -27,8 +27,6 @@ Não especificado Participe da tradução Ajude-nos a traduzir o PI para o seu idioma - Deseja desinstalar este app? - Dica: Isso só desinstalará as atualizações se for um app do sistema. Comportamento Clique para tentar começar Interface diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index a12f1d25..bf5837c4 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -16,7 +16,7 @@ Carregando… Pesquisar… Necessita de permissão concedida por Sui ou Shizuku - Definições + Definições Instalação bem-sucedida Instalar serviço Erro desconhecido @@ -27,7 +27,6 @@ Não especificado Participe da tradução Ajude-nos a traduzir o PI para o seu idioma - Deseja desinstalar esta aplicação? Comportamento Clique para tentar começar Interface @@ -36,7 +35,6 @@ Código fonte OK Cancelar - Nota: Isto apenas desinstalará atualizações se for uma aplicação do sistema. O serviço não está disponível Falha na análise do pacote Autorizado diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index abbae25b..846d6c97 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -32,9 +32,7 @@ Установить Загрузка… ABI - Настройки - Вы хотите установить это приложение? - Подсказка: Это удалит обновления, только если это системное приложение. + Настройки Сервис не доступен Сбой синтаксического анализа пакета Отмена diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 547e2309..2e47ea47 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -7,7 +7,7 @@ Phiên bản %1$d, %2$s Trình yêu cầu cài đặt Danh sách trống - Thiết đặt + Thiết đặt Màu sắc linh động Sử dụng bảng màu Material You Tham gia dịch thuật diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 420b8f6e..1b874cfe 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -3,22 +3,18 @@ 安装 - - 设置 - 工作模式 需要由 Sui 或 Shizuku 提供权限 需要由 Magisk, KernelSU 或 APatch 提供 Root 权限 - 你想要卸载这个应用吗? - 提示: 对于系统应用, 这只会卸载更新. 已授权 请求者 执行者 + 设置 行为 服务正在运行 版本 %1$d,%2$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 35740313..72a6f712 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,22 +3,18 @@ Install - - Settings - Working mode Requires permission provided by Sui or Shizuku Requires Root permissions provided by Magisk, KernelSU or APatch - Do you want to uninstall this app? - Tip: This will only uninstall updates if it is a system app. Authorized Requester Executor + Settings Behavior Service is running Version %1$d, %2$s From ea01bb2ec9c0ee023db8f237067c57d4de073106 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Thu, 18 Jul 2024 15:12:24 +0800 Subject: [PATCH 06/17] Update locales_config.xml --- app/build.gradle.kts | 1 + app/src/main/res/xml/locales_config.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 72bc7bc8..54dfcc37 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -26,6 +26,7 @@ android { "ar", "es", "fr", + "in", "iw", "pt", "pt-rBR", diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml index f35d7c56..feb9ec9f 100644 --- a/app/src/main/res/xml/locales_config.xml +++ b/app/src/main/res/xml/locales_config.xml @@ -4,6 +4,7 @@ + From 3ec56cc2ec90606102bd557a15b19bc164eba10d Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Thu, 18 Jul 2024 15:23:44 +0800 Subject: [PATCH 07/17] Remove `PackageReceiver` --- app/src/main/AndroidManifest.xml | 5 +- .../dev/sanmer/pi/receiver/PackageReceiver.kt | 63 ------------------- .../kotlin/dev/sanmer/pi/ui/MainActivity.kt | 7 --- .../dev/sanmer/pi/viewmodel/AppsViewModel.kt | 13 +--- 4 files changed, 3 insertions(+), 85 deletions(-) delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/receiver/PackageReceiver.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 13af696d..05dbbf17 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,13 +1,10 @@ + xmlns:tools="http://schemas.android.com/tools"> - - : $packageName") - } - } - - data class Event( - val action: Action = Action.None, - val packageName: String = "" - ) - - enum class Action(val str: String) { - None("None"), - Added(Intent.ACTION_PACKAGE_ADDED), - Replaced(Intent.ACTION_PACKAGE_REPLACED), - Removed(Intent.ACTION_PACKAGE_REMOVED); - - companion object { - fun from(str: String) = entries.first { it.str == str } - } - } - - companion object { - private val receiver by lazy { PackageReceiver() } - private val filter by lazy { - IntentFilter().apply { - addAction(Intent.ACTION_PACKAGE_ADDED) - addAction(Intent.ACTION_PACKAGE_REPLACED) - addAction(Intent.ACTION_PACKAGE_REMOVED) - addDataScheme("package") - } - } - - private val _eventFlow = MutableStateFlow(Event()) - val eventFlow get() = _eventFlow.asStateFlow() - - fun register(context: Context) { - // noinspection InlinedApi - context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED) - } - - fun unregister(context: Context) { - context.unregisterReceiver(receiver) - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt index 6ac948e6..e420b9f5 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt @@ -16,7 +16,6 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import dev.sanmer.pi.Compat import dev.sanmer.pi.datastore.model.Provider -import dev.sanmer.pi.receiver.PackageReceiver import dev.sanmer.pi.repository.UserPreferencesRepository import dev.sanmer.pi.ui.main.MainScreen import dev.sanmer.pi.ui.main.SetupScreen @@ -38,7 +37,6 @@ class MainActivity : ComponentActivity() { enableEdgeToEdge() splashScreen.setKeepOnScreenCondition { isLoading } - PackageReceiver.register(this) setContent { val userPreferences by userPreferencesRepository.data @@ -78,11 +76,6 @@ class MainActivity : ComponentActivity() { } } - override fun onDestroy() { - PackageReceiver.unregister(this) - super.onDestroy() - } - private fun setProvider(value: Provider) { lifecycleScope.launch { userPreferencesRepository.setProvider(value) diff --git a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt b/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt index ad6c8ffc..0a47f7e4 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt @@ -18,7 +18,6 @@ import dev.sanmer.pi.delegate.AppOpsManagerDelegate.Mode.Companion.isAllowed import dev.sanmer.pi.ktx.viewPackage import dev.sanmer.pi.model.IPackageInfo import dev.sanmer.pi.model.IPackageInfo.Companion.toIPackageInfo -import dev.sanmer.pi.receiver.PackageReceiver import dev.sanmer.pi.repository.UserPreferencesRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -66,12 +65,12 @@ class AppsViewModel @Inject constructor( init { Timber.d("AppsViewModel init") - packagesObserver() + providerObserver() dataObserver() keyObserver() } - private fun packagesObserver() { + private fun providerObserver() { Compat.isAliveFlow .onEach { isAlive -> if (!isAlive) return@onEach @@ -86,14 +85,6 @@ class AppsViewModel @Inject constructor( }.launchIn(viewModelScope) - PackageReceiver.eventFlow - .onEach { - if (!isProviderAlive) return@onEach - - packagesFlow.value = getPackages() - - }.launchIn(viewModelScope) - addCloseable { if (isProviderAlive) { aom.stopWatchingMode(callback = this) From c1b0897e5a8ff8ad34401b5ec901a21cca9ea788 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Thu, 18 Jul 2024 19:11:08 +0800 Subject: [PATCH 08/17] Optimize `nestedScroll` --- app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt | 2 +- .../kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt | 2 +- .../dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt index 06bfd23c..2f463c53 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt @@ -54,7 +54,6 @@ fun AppsScreen( } Scaffold( - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { TopBar( isSearch = viewModel.isSearch, @@ -71,6 +70,7 @@ fun AppsScreen( modifier = Modifier .padding(innerPadding) .imePadding() + .nestedScroll(scrollBehavior.nestedScrollConnection) ) { if (viewModel.isLoading) { Loading() diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt index ccfee4dd..9491a0e7 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt @@ -50,7 +50,6 @@ fun SettingsScreen( val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() Scaffold( - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { TopBar( navController = navController, @@ -63,6 +62,7 @@ fun SettingsScreen( modifier = Modifier .padding(innerPadding) .fillMaxWidth() + .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(rememberScrollState()) ) { TittleItem( diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt index 8e2e132f..3a68fd73 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt @@ -34,7 +34,6 @@ fun WorkingModeScreen( val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() Scaffold( - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { TopBar( navController = navController, @@ -47,6 +46,7 @@ fun WorkingModeScreen( modifier = Modifier .padding(innerPadding) .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(rememberScrollState()) .padding(vertical = 20.dp), verticalArrangement = Arrangement.spacedBy(20.dp), From 2f4f3464a2564f89f5be4880a2ea8fa376101456 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Fri, 19 Jul 2024 13:13:32 +0800 Subject: [PATCH 09/17] Optimize `SettingsScreen` --- .../main/kotlin/dev/sanmer/pi/ui/component/SettingItem.kt | 2 +- .../dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/SettingItem.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/SettingItem.kt index 76b35cd6..b244d7b3 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/SettingItem.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/component/SettingItem.kt @@ -200,7 +200,7 @@ object SettingItemDefaults { fun textStyle( titleTextColor: Color = LocalContentColor.current, descTextColor: Color = MaterialTheme.colorScheme.outline, - titleTextStyle: TextStyle = MaterialTheme.typography.titleMedium, + titleTextStyle: TextStyle = MaterialTheme.typography.bodyLarge, descTextStyle: TextStyle = MaterialTheme.typography.bodyMedium ) = SettingTextStyle( titleTextColor = titleTextColor, diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt index 9491a0e7..4b2d98fb 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt @@ -10,7 +10,6 @@ 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.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -90,8 +89,6 @@ fun SettingsScreen( } ) - HorizontalDivider() - TittleItem( text = stringResource(id = R.string.settings_interface) ) @@ -137,7 +134,8 @@ private fun TittleItem( ) = Text( modifier = modifier.padding(all = 16.dp), text = text, - style = MaterialTheme.typography.titleSmall + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary ) @Composable From 37f161fba894263a51133c069855f058cd9fd6b4 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Fri, 19 Jul 2024 13:48:17 +0800 Subject: [PATCH 10/17] Fix `BottomSheetLayout` --- .../sanmer/pi/ui/component/BottomSheetLayout.kt | 15 +++++++-------- .../kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt | 10 ++++------ .../dev/sanmer/pi/ui/main/PermissionScreen.kt | 10 ++++------ 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/BottomSheetLayout.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/BottomSheetLayout.kt index 14b35105..99d510d9 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/BottomSheetLayout.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/component/BottomSheetLayout.kt @@ -51,7 +51,7 @@ fun BottomSheetLayout( color = containerColor, shape = shape ) - .clip(shape) + .clip(shape = shape) ) { constraints -> val maxWidth = constraints.maxWidth val maxHeight = constraints.maxHeight @@ -65,7 +65,7 @@ fun BottomSheetLayout( val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0) val bottomBarPlaceable = subcompose("BottomBar") { - val innerPadding = PaddingValues( + val bottomBarPadding = PaddingValues( bottom = insetsBottom, start = insetsStart, end = insetsEnd @@ -74,16 +74,15 @@ fun BottomSheetLayout( CompositionLocalProvider( LocalContentColor provides contentColor ) { - bottomBar(innerPadding) + bottomBar(bottomBarPadding) } }.fastMap { it.measure(looseConstraints) } val bottomBarHeight = bottomBarPlaceable.fastMaxBy { it.height }?.height ?: 0 - val bottomHeight = bottomBarHeight + insetsBottom.roundToPx() val bodyContentPlaceable = subcompose("MainContent") { - val innerPadding = PaddingValues( - bottom = insetsTop + bottomHeight.toDp(), + val contentPadding = PaddingValues( + bottom = insetsTop + bottomBarHeight.toDp(), start = insetsStart, end = insetsEnd ) @@ -91,7 +90,7 @@ fun BottomSheetLayout( CompositionLocalProvider( LocalContentColor provides contentColor ) { - content(innerPadding) + content(contentPadding) } }.fastMap { it.measure(looseConstraints) } @@ -109,7 +108,7 @@ fun BottomSheetLayout( bottomBarPlaceable.fastForEach { it.place( x = 0, - y = bodyContentHeight - bottomHeight + y = bodyContentHeight - bottomBarHeight ) } } diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt index 98000149..583c64c6 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt @@ -2,7 +2,6 @@ package dev.sanmer.pi.ui.main import androidx.activity.compose.BackHandler import androidx.compose.animation.Crossfade -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -69,11 +68,11 @@ fun InstallScreen( } BottomSheetLayout( - bottomBar = { + bottomBar = { bottomPadding -> if (viewModel.state.isReady()) { BottomBar( modifier = Modifier - .padding(it) + .padding(bottomPadding) .padding(horizontal = 20.dp) .padding(bottom = 20.dp), onDeny = viewModel::deleteTempDir, @@ -82,11 +81,10 @@ fun InstallScreen( } }, shape = MaterialTheme.shapes.large.bottom(0.dp) - ) { + ) { contentPadding -> Crossfade( modifier = Modifier - .background(MaterialTheme.colorScheme.surface) - .padding(it) + .padding(contentPadding) .padding(all = 20.dp), targetState = viewModel.state, label = "InstallScreen" diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt index 2427789d..ac6c2553 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt @@ -2,7 +2,6 @@ package dev.sanmer.pi.ui.main import androidx.activity.compose.BackHandler import androidx.compose.animation.Crossfade -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -53,11 +52,11 @@ fun PermissionScreen( } BottomSheetLayout( - bottomBar = { + bottomBar = { bottomPadding -> if (!viewModel.isLoading) { BottomBar( modifier = Modifier - .padding(it) + .padding(bottomPadding) .padding(horizontal = 20.dp) .padding(bottom = 20.dp), onGrant = viewModel::grantPermissions @@ -65,11 +64,10 @@ fun PermissionScreen( } }, shape = MaterialTheme.shapes.large.bottom(0.dp) - ) { + ) { contentPadding -> Crossfade( modifier = Modifier - .background(MaterialTheme.colorScheme.surface) - .padding(it) + .padding(contentPadding) .padding(all = 20.dp), targetState = viewModel.isLoading, label = "InstallScreen" From d2c791eea57b0c87dc3b79a9d93d7a48ea1cf202 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Fri, 19 Jul 2024 14:17:45 +0800 Subject: [PATCH 11/17] Optimize `contentPadding` --- .../dev/sanmer/pi/ui/component/PageIndicator.kt | 4 ++++ .../kotlin/dev/sanmer/pi/ui/main/MainScreen.kt | 12 ++++-------- .../dev/sanmer/pi/ui/screens/apps/AppsScreen.kt | 15 ++++++++------- .../pi/ui/screens/apps/component/AppList.kt | 6 ++++-- .../pi/ui/screens/settings/SettingsScreen.kt | 10 +++------- .../ui/screens/workingmode/WorkingModeScreen.kt | 12 +++++------- 6 files changed, 28 insertions(+), 31 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/component/PageIndicator.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/component/PageIndicator.kt index b25f33a5..d2899559 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/component/PageIndicator.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/component/PageIndicator.kt @@ -99,6 +99,7 @@ fun PageIndicator( @Composable fun Loading( + modifier: Modifier = Modifier, minHeight: Dp? = null ) = PageIndicator( icon = { @@ -115,16 +116,19 @@ fun Loading( color = MaterialTheme.colorScheme.outline ) }, + modifier = modifier, minHeight = minHeight ) @Composable fun Failed( message: String?, + modifier: Modifier = Modifier, minHeight: Dp? = null ) = PageIndicator( icon = R.drawable.alert_triangle, text = message ?: stringResource(id = R.string.unknown_error), + modifier = modifier, minHeight = minHeight ) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/main/MainScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/main/MainScreen.kt index a7eeff3e..20dc2fb2 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/MainScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/main/MainScreen.kt @@ -2,12 +2,9 @@ package dev.sanmer.pi.ui.main import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.navigationBars -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Scaffold +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.NavHost @@ -24,11 +21,10 @@ import dev.sanmer.pi.ui.screens.workingmode.WorkingModeScreen fun MainScreen() { val navController = rememberNavController() - Scaffold( - contentWindowInsets = WindowInsets.navigationBars + Surface( + color = MaterialTheme.colorScheme.background ) { NavHost( - modifier = Modifier.padding(it), navController = navController, startDestination = Screen.Apps.route ) { diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt index 2f463c53..f1a9419d 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt @@ -2,7 +2,6 @@ package dev.sanmer.pi.ui.screens.apps import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState @@ -63,30 +62,32 @@ fun AppsScreen( navController = navController, scrollBehavior = scrollBehavior ) - }, - contentWindowInsets = WindowInsets(0) - ) { innerPadding -> + } + ) { contentPadding -> Box( modifier = Modifier - .padding(innerPadding) .imePadding() .nestedScroll(scrollBehavior.nestedScrollConnection) ) { if (viewModel.isLoading) { - Loading() + Loading( + modifier = Modifier.padding(contentPadding) + ) } if (list.isEmpty() && !viewModel.isLoading) { PageIndicator( icon = R.drawable.list_search, text = R.string.empty_list, + modifier = Modifier.padding(contentPadding) ) } AppList( list = list, state = state, - buildSettings = viewModel::buildSettings + buildSettings = viewModel::buildSettings, + contentPadding = contentPadding ) } } diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt index e303fa8d..7a9ee666 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt @@ -44,7 +44,8 @@ import kotlinx.coroutines.launch internal fun AppList( list: List, state: LazyListState, - buildSettings: (IPackageInfo) -> AppsViewModel.Settings + buildSettings: (IPackageInfo) -> AppsViewModel.Settings, + contentPadding: PaddingValues = PaddingValues(0.dp) ) { var packageName by remember { mutableStateOf("") } val packageInfo by remember(list, packageName) { @@ -63,7 +64,8 @@ internal fun AppList( LazyColumn( modifier = Modifier.animateContentSize(), - state = state + state = state, + contentPadding = contentPadding ) { items( items = list, diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt index 4b2d98fb..16b06831 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/settings/SettingsScreen.kt @@ -5,8 +5,6 @@ import android.content.Intent import android.net.Uri import android.provider.Settings import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -54,15 +52,13 @@ fun SettingsScreen( navController = navController, scrollBehavior = scrollBehavior ) - }, - contentWindowInsets = WindowInsets(0) - ) { innerPadding -> + } + ) { contentPadding -> Column( modifier = Modifier - .padding(innerPadding) - .fillMaxWidth() .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(rememberScrollState()) + .padding(contentPadding) ) { TittleItem( text = stringResource(id = R.string.settings_behavior) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt index 3a68fd73..f8d2288f 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/WorkingModeScreen.kt @@ -2,8 +2,7 @@ package dev.sanmer.pi.ui.screens.workingmode import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.WindowInsets -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 @@ -39,15 +38,14 @@ fun WorkingModeScreen( navController = navController, scrollBehavior = scrollBehavior ) - }, - contentWindowInsets = WindowInsets(0) - ) { innerPadding -> + } + ) { contentPadding -> Column( modifier = Modifier - .padding(innerPadding) - .fillMaxSize() + .fillMaxWidth() .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(rememberScrollState()) + .padding(contentPadding) .padding(vertical = 20.dp), verticalArrangement = Arrangement.spacedBy(20.dp), horizontalAlignment = Alignment.CenterHorizontally From 0ab96b3c1f6b978088112a18e5a243e833908792 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Fri, 19 Jul 2024 16:15:44 +0800 Subject: [PATCH 12/17] Optimize `AppList` --- .../pi/ui/screens/apps/component/AppItem.kt | 4 +-- .../pi/ui/screens/apps/component/AppList.kt | 34 +++++++------------ 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppItem.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppItem.kt index 19ecf1e9..d2d6f0d0 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppItem.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppItem.kt @@ -101,7 +101,7 @@ private fun Icon( @DrawableRes icon: Int ) = Logo( icon = icon, - contentColor = MaterialTheme.colorScheme.onSurfaceVariant, - containerColor = MaterialTheme.colorScheme.surfaceVariant, + contentColor = MaterialTheme.colorScheme.onSecondaryContainer, + containerColor = MaterialTheme.colorScheme.secondaryContainer, modifier = Modifier.size(30.dp) ) \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt index 7a9ee666..72e0ed31 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/apps/component/AppList.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -15,7 +16,6 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.Icon -import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text @@ -87,17 +87,19 @@ private fun BottomSheet( ) = ModalBottomSheet( onDismissRequest = onClose, dragHandle = null, - windowInsets = WindowInsets.navigationBars, - shape = MaterialTheme.shapes.large.bottom(0.dp), - containerColor = MaterialTheme.colorScheme.surface, - tonalElevation = 0.dp + windowInsets = WindowInsets(0.dp), + shape = MaterialTheme.shapes.large.bottom(0.dp) ) { + val contentPadding = WindowInsets.navigationBars.asPaddingValues() + val settings by remember(pi) { derivedStateOf { buildSettings(pi) } } Column( - modifier = Modifier.padding(all = 20.dp), + modifier = Modifier + .padding(contentPadding) + .padding(all = 20.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { AppItem( @@ -130,7 +132,7 @@ private fun SettingButtons( val scope = rememberCoroutineScope() if (settings.isOpenable) { - ButtonItem( + FilledTonalIconButton( onClick = { settings.launch(context) } ) { Icon( @@ -140,7 +142,7 @@ private fun SettingButtons( } } - ButtonItem( + FilledTonalIconButton( onClick = { settings.view(context) } ) { Icon( @@ -149,7 +151,7 @@ private fun SettingButtons( ) } - ButtonItem( + FilledTonalIconButton( onClick = { scope.launch { settings.export(context) } } @@ -201,16 +203,4 @@ private fun SettingItem( }, label = { Text(text = stringResource(id = R.string.app_executor)) }, ) -} - -@Composable -private fun ButtonItem( - onClick: () -> Unit, - content: @Composable () -> Unit -) = FilledTonalIconButton( - onClick = onClick, - colors = IconButtonDefaults.filledTonalIconButtonColors( - containerColor = MaterialTheme.colorScheme.surfaceVariant - ), - content = content -) \ No newline at end of file +} \ No newline at end of file From a124e0ae8e6912238c217a20c6c0eb65bcbd82d3 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sat, 20 Jul 2024 13:18:12 +0800 Subject: [PATCH 13/17] Update `Typography` --- app/src/main/kotlin/dev/sanmer/pi/ui/theme/Type.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/theme/Type.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/theme/Type.kt index 387bed73..259a7732 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/theme/Type.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/theme/Type.kt @@ -10,7 +10,7 @@ val Typography = Typography( titleLarge = TextStyle( fontFamily = FontFamily.SansSerif, fontWeight = FontWeight.Medium, - fontSize = 20.sp, + fontSize = 22.sp, lineHeight = 28.sp, letterSpacing = 0.sp, ) From c2effcf084eb707c5e2637f02aa322d7d73f973b Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sat, 20 Jul 2024 13:18:29 +0800 Subject: [PATCH 14/17] Update `Timber.DebugTree` --- app/src/main/kotlin/dev/sanmer/pi/App.kt | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/App.kt b/app/src/main/kotlin/dev/sanmer/pi/App.kt index 4902cc98..1c7be41a 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/App.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/App.kt @@ -17,11 +17,7 @@ import timber.log.Timber @HiltAndroidApp class App : Application(), ImageLoaderFactory { init { - if (BuildConfig.DEBUG) { - Timber.plant(DebugTree()) - } else { - Timber.plant(ReleaseTree()) - } + Timber.plant(Timber.DebugTree()) } override fun onCreate() { @@ -53,20 +49,4 @@ class App : Application(), ImageLoaderFactory { deleteUnlistedNotificationChannels(channels.map { it.id }) } } - - class DebugTree : Timber.DebugTree() { - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - super.log(priority, "$tag", message, t) - } - - override fun createStackElementTag(element: StackTraceElement): String { - return super.createStackElementTag(element) + "(L${element.lineNumber})" - } - } - - class ReleaseTree : Timber.DebugTree() { - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - super.log(priority, "$tag", message, t) - } - } } \ No newline at end of file From 8636609a757f89e9ab1b8b9ff2859824c8d5c808 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 21 Jul 2024 15:10:47 +0800 Subject: [PATCH 15/17] Add `animateContentSize` --- app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt | 2 ++ app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt index 583c64c6..183df1b8 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/main/InstallScreen.kt @@ -2,6 +2,7 @@ package dev.sanmer.pi.ui.main import androidx.activity.compose.BackHandler import androidx.compose.animation.Crossfade +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -84,6 +85,7 @@ fun InstallScreen( ) { contentPadding -> Crossfade( modifier = Modifier + .animateContentSize() .padding(contentPadding) .padding(all = 20.dp), targetState = viewModel.state, diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt index ac6c2553..833d449b 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt @@ -2,6 +2,7 @@ package dev.sanmer.pi.ui.main import androidx.activity.compose.BackHandler import androidx.compose.animation.Crossfade +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -67,10 +68,11 @@ fun PermissionScreen( ) { contentPadding -> Crossfade( modifier = Modifier + .animateContentSize() .padding(contentPadding) .padding(all = 20.dp), targetState = viewModel.isLoading, - label = "InstallScreen" + label = "PermissionScreen" ) { isLoading -> when { isLoading -> Loading( From 1e871656110601b86a0837648e56c6a908e4277b Mon Sep 17 00:00:00 2001 From: Sanmer Bot Date: Mon, 22 Jul 2024 14:12:42 +0800 Subject: [PATCH 16/17] Translated using Weblate (Indonesian) (#122) Currently translated at 100.0% (44 of 44 strings) Translation: PI/App Translate-URL: https://weblate.sanmer.app/projects/pi/app/id/ Co-authored-by: Ian Perdiansah --- app/src/main/res/values-in/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 68ec43f0..2e980ad5 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -40,4 +40,8 @@ Memerlukan izin Root dari Magisk, KernelSU, atau APatch Layanan tidak berjalan Bantu kami menerjemahkan PI ke dalam bahasa Anda + Izin + Tolak + Aplikasi + Izinkan \ No newline at end of file From a363f599b58d3b5ef616e8c8646248e1582f097f Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Mon, 22 Jul 2024 14:21:27 +0800 Subject: [PATCH 17/17] Bump version to 1.1.3 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 61a92115..31e81432 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ task("clean") { } subprojects { - val baseVersionName by extra("1.1.2") + val baseVersionName by extra("1.1.3") apply(plugin = "maven-publish") configure {