From 00f1c1b8b5530465970f34673acc0e42fcc30227 Mon Sep 17 00:00:00 2001 From: Sanmer Bot Date: Tue, 23 Jul 2024 18:27:24 +0800 Subject: [PATCH 01/34] Translated using Weblate (Hebrew) (#123) Currently translated at 100.0% (44 of 44 strings) Translation: PI/App Translate-URL: https://weblate.sanmer.app/projects/pi/app/he/ Co-authored-by: eyalm2000 --- app/src/main/res/values-iw/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 71994977..d0301628 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -40,4 +40,8 @@ מורשה מבצע מבקש + הרשאות + דחה + קבל + אפליקציה \ No newline at end of file From ce516d08cc6711fa480967592bd20754f7760135 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Tue, 23 Jul 2024 22:05:04 +0800 Subject: [PATCH 02/34] Enable V4 Signing - Remove V2 Signing --- .github/workflows/android.yml | 2 +- app/build.gradle.kts | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index bcb83283..f3588bb5 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -63,7 +63,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ steps.release-name.outputs.name }} - path: app/build/outputs/apk/release/*.apk + path: app/build/outputs/apk/release/*.apk* - name: Upload mappings if: success() && github.ref == 'refs/heads/main' diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 54dfcc37..cf67be50 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -42,8 +42,8 @@ android { storePassword = project.releaseKeyStorePassword keyAlias = project.releaseKeyAlias keyPassword = project.releaseKeyPassword - enableV2Signing = true enableV3Signing = true + enableV4Signing = true } } else { signingConfigs.getByName("debug") @@ -74,10 +74,8 @@ android { "META-INF/**", "okhttp3/**", "kotlin/**", - "org/**", - "**.properties", "**.bin", - "**/*.proto" + "**.properties" ) dependenciesInfo.includeInApk = false From 9f8a066224da992943f62e049c651a800d52fdf8 Mon Sep 17 00:00:00 2001 From: Sanmer Date: Wed, 24 Jul 2024 16:45:58 +0800 Subject: [PATCH 03/34] Update android.yml --- .github/workflows/android.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index f3588bb5..bce892d0 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -58,16 +58,16 @@ jobs: run: | name=`ls app/build/outputs/apk/release/*.apk | awk -F '(/|.apk)' '{print $6}'` && echo "name=${name}" >> $GITHUB_OUTPUT - - name: Upload built apk + - name: Upload apk if: success() && github.ref == 'refs/heads/main' uses: actions/upload-artifact@v4 with: name: ${{ steps.release-name.outputs.name }} path: app/build/outputs/apk/release/*.apk* - - name: Upload mappings + - name: Upload mapping if: success() && github.ref == 'refs/heads/main' uses: actions/upload-artifact@v4 with: - name: mappings + name: ${{ steps.release-name.outputs.name }}-mapping path: app/build/outputs/mapping/release From 7861490279e33c4ad778353898da8bca5555fb30 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Wed, 24 Jul 2024 19:29:46 +0800 Subject: [PATCH 04/34] Bump targetSdk to 35 - Bump minSdk to 30 --- build-logic/src/main/kotlin/ApplicationConventionPlugin.kt | 4 ++-- build-logic/src/main/kotlin/LibraryConventionPlugin.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build-logic/src/main/kotlin/ApplicationConventionPlugin.kt b/build-logic/src/main/kotlin/ApplicationConventionPlugin.kt index 0f9eef52..991ada2b 100644 --- a/build-logic/src/main/kotlin/ApplicationConventionPlugin.kt +++ b/build-logic/src/main/kotlin/ApplicationConventionPlugin.kt @@ -18,8 +18,8 @@ class ApplicationConventionPlugin : Plugin { buildToolsVersion = "35.0.0" defaultConfig { - minSdk = 29 - targetSdk = 34 + minSdk = 30 + targetSdk = compileSdk } compileOptions { diff --git a/build-logic/src/main/kotlin/LibraryConventionPlugin.kt b/build-logic/src/main/kotlin/LibraryConventionPlugin.kt index bdecd35a..d3621dcb 100644 --- a/build-logic/src/main/kotlin/LibraryConventionPlugin.kt +++ b/build-logic/src/main/kotlin/LibraryConventionPlugin.kt @@ -18,7 +18,7 @@ class LibraryConventionPlugin : Plugin { buildToolsVersion = "35.0.0" defaultConfig { - minSdk = 29 + minSdk = 30 } compileOptions { From 033cb773b18fadd21e7edb4fd79a61eba5a14a06 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Wed, 24 Jul 2024 19:45:51 +0800 Subject: [PATCH 05/34] Optimize `PermissionManagerDelegate` --- .../pi/delegate/PermissionManagerDelegate.kt | 116 +++++++++--------- .../virtual/VirtualDeviceManagerHidden.java | 8 ++ .../android/content/pm/IPackageManager.java | 4 - .../permission/IPermissionManager.java | 19 +-- 4 files changed, 80 insertions(+), 67 deletions(-) create mode 100644 stub/src/main/java/android/companion/virtual/VirtualDeviceManagerHidden.java diff --git a/core/src/main/kotlin/dev/sanmer/pi/delegate/PermissionManagerDelegate.kt b/core/src/main/kotlin/dev/sanmer/pi/delegate/PermissionManagerDelegate.kt index d5f71b80..a84d4e95 100644 --- a/core/src/main/kotlin/dev/sanmer/pi/delegate/PermissionManagerDelegate.kt +++ b/core/src/main/kotlin/dev/sanmer/pi/delegate/PermissionManagerDelegate.kt @@ -1,5 +1,7 @@ package dev.sanmer.pi.delegate +import android.companion.virtual.VirtualDeviceManagerHidden +import android.content.Context import android.content.pm.IPackageManager import android.permission.IPermissionManager import dev.sanmer.pi.BuildCompat @@ -23,36 +25,28 @@ class PermissionManagerDelegate( fun grantRuntimePermission(packageName: String, permissionName: String, userId: Int) { when { - BuildCompat.atLeastU -> try { - permissionManager.grantRuntimePermission( - packageName, - permissionName, - "default:0", - userId - ) - } catch (e: NoSuchMethodError) { - try { + BuildCompat.atLeastU -> + rollback { permissionManager.grantRuntimePermission( packageName, permissionName, - 0, - userId) - } catch (e: NoSuchMethodError) { + VirtualDeviceManagerHidden.PERSISTENT_DEVICE_ID_DEFAULT, + userId + ) + } ?: rollback { permissionManager.grantRuntimePermission( packageName, permissionName, + Context.DEVICE_ID_DEFAULT, userId ) - } - } - - BuildCompat.atLeastR -> permissionManager.grantRuntimePermission( - packageName, - permissionName, - userId - ) + } ?: permissionManager.grantRuntimePermission( + packageName, + permissionName, + userId + ) - else -> packageManager.grantRuntimePermission( + else -> permissionManager.grantRuntimePermission( packageName, permissionName, userId @@ -62,59 +56,61 @@ class PermissionManagerDelegate( fun revokeRuntimePermission(packageName: String, permissionName: String, userId: Int) { when { - BuildCompat.atLeastU -> try { - permissionManager.revokeRuntimePermission( - packageName, - permissionName, - "default:0", - userId, - null - ) - } catch (e: NoSuchMethodError) { - try { + BuildCompat.atLeastU -> + rollback { permissionManager.revokeRuntimePermission( packageName, permissionName, - 0, + VirtualDeviceManagerHidden.PERSISTENT_DEVICE_ID_DEFAULT, userId, null ) - } catch (e: NoSuchMethodError) { + } ?: rollback { permissionManager.revokeRuntimePermission( packageName, permissionName, - userId + Context.DEVICE_ID_DEFAULT, + userId, + null ) - } - } - - BuildCompat.atLeastR -> permissionManager.revokeRuntimePermission( - packageName, - permissionName, - userId - ) + } ?: permissionManager.revokeRuntimePermission( + packageName, + permissionName, + userId, + null + ) - else -> packageManager.revokeRuntimePermission( + else -> permissionManager.revokeRuntimePermission( packageName, permissionName, - userId + userId, + null ) } } fun checkPermission(packageName: String, permissionName: String, userId: Int): Int { return when { - BuildCompat.atLeastS -> packageManager.checkPermission( - permissionName, - packageName, - userId - ) - - BuildCompat.atLeastR -> permissionManager.checkPermission( - permissionName, - packageName, - userId - ) + BuildCompat.atLeastU -> + rollback { + permissionManager.checkPermission( + packageName, + permissionName, + VirtualDeviceManagerHidden.PERSISTENT_DEVICE_ID_DEFAULT, + userId + ) + } ?: rollback { + permissionManager.checkPermission( + packageName, + permissionName, + Context.DEVICE_ID_DEFAULT, + userId + ) + } ?: packageManager.checkPermission( + packageName, + permissionName, + userId + ) else -> packageManager.checkPermission( permissionName, @@ -123,4 +119,14 @@ class PermissionManagerDelegate( ) } } + + private inline fun rollback(block: () -> T): T? { + return try { + block() + } catch (_: NoSuchMethodError) { + null + } catch (_: NoSuchFieldError) { + null + } + } } \ No newline at end of file diff --git a/stub/src/main/java/android/companion/virtual/VirtualDeviceManagerHidden.java b/stub/src/main/java/android/companion/virtual/VirtualDeviceManagerHidden.java new file mode 100644 index 00000000..3e726d22 --- /dev/null +++ b/stub/src/main/java/android/companion/virtual/VirtualDeviceManagerHidden.java @@ -0,0 +1,8 @@ +package android.companion.virtual; + +import dev.rikka.tools.refine.RefineAs; + +@RefineAs(VirtualDeviceManager.class) +public class VirtualDeviceManagerHidden { + public static String PERSISTENT_DEVICE_ID_DEFAULT; +} \ No newline at end of file diff --git a/stub/src/main/java/android/content/pm/IPackageManager.java b/stub/src/main/java/android/content/pm/IPackageManager.java index b0378e10..16575177 100644 --- a/stub/src/main/java/android/content/pm/IPackageManager.java +++ b/stub/src/main/java/android/content/pm/IPackageManager.java @@ -44,10 +44,6 @@ public interface IPackageManager extends IInterface { String[] getPackagesForUid(int uid) throws RemoteException; - void grantRuntimePermission(String packageName, String permissionName, int userId) throws RemoteException; - - void revokeRuntimePermission(String packageName, String permissionName, int userId) throws RemoteException; - int checkPermission(String permName, String pkgName, int userId) throws RemoteException; abstract class Stub extends Binder implements IPackageManager { diff --git a/stub/src/main/java/android/permission/IPermissionManager.java b/stub/src/main/java/android/permission/IPermissionManager.java index 72a248cb..f0bf65f7 100644 --- a/stub/src/main/java/android/permission/IPermissionManager.java +++ b/stub/src/main/java/android/permission/IPermissionManager.java @@ -5,28 +5,31 @@ import android.os.IInterface; import android.os.RemoteException; +import androidx.annotation.RequiresApi; + public interface IPermissionManager extends IInterface { void grantRuntimePermission(String packageName, String permissionName, int userId) throws RemoteException; - // Android 14 QPR2 + @RequiresApi(34) // QPR2 void grantRuntimePermission(String packageName, String permissionName, int deviceId, int userId) throws RemoteException; - // Android 14 QPR3 + @RequiresApi(34) // 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 + @RequiresApi(34) // QPR2 void revokeRuntimePermission(String packageName, String permissionName, int deviceId, int userId, String reason) throws RemoteException; - // Android 14 QPR3 + @RequiresApi(34) // 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; + @RequiresApi(34) // QPR2 + int checkPermission(String packageName, String permissionName, int deviceId, int userId) throws RemoteException; + + @RequiresApi(34) // QPR3 + int checkPermission(String packageName, String permissionName, String persistentDeviceId, int userId) throws RemoteException; abstract class Stub extends Binder implements IPermissionManager { public static IPermissionManager asInterface(IBinder obj) { From 800ce55e8b693ad0c0175a1fd0f0eaad58ea4ffe Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Wed, 24 Jul 2024 19:46:02 +0800 Subject: [PATCH 06/34] Optimize `MediaStoreCompat` --- .../dev/sanmer/pi/compat/MediaStoreCompat.kt | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/compat/MediaStoreCompat.kt b/app/src/main/kotlin/dev/sanmer/pi/compat/MediaStoreCompat.kt index c0ee0d4e..0ccbef8c 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/compat/MediaStoreCompat.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/compat/MediaStoreCompat.kt @@ -4,12 +4,10 @@ import android.content.ContentResolver import android.content.ContentValues import android.content.Context import android.net.Uri -import android.os.Build import android.os.Environment import android.provider.DocumentsContract import android.provider.MediaStore import android.system.Os -import androidx.annotation.RequiresApi import androidx.core.net.toFile import androidx.core.net.toUri import androidx.documentfile.provider.DocumentFile @@ -17,7 +15,6 @@ import java.io.File import java.io.IOException object MediaStoreCompat { - @RequiresApi(Build.VERSION_CODES.R) fun Context.createMediaStoreUri( file: File, collection: Uri = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL), @@ -45,17 +42,13 @@ object MediaStoreCompat { fun Context.createDownloadUri( path: String, mimeType: String - ) = when { - BuildCompat.atLeastR -> runCatching { - createMediaStoreUri( - file = File(Environment.DIRECTORY_DOWNLOADS, path), - mimeType = mimeType - ) - }.getOrElse { - createDownloadUri(path) - } - - else -> createDownloadUri(path) + ) = runCatching { + createMediaStoreUri( + file = File(Environment.DIRECTORY_DOWNLOADS, path), + mimeType = mimeType + ) + }.getOrElse { + createDownloadUri(path) } private fun ContentResolver.queryString(uri: Uri, columnName: String): String? { From 2874b46cf3163731ba412de11c80d360d3c4d601 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Wed, 24 Jul 2024 19:46:23 +0800 Subject: [PATCH 07/34] Optimize `BuildCompat` --- app/src/main/kotlin/dev/sanmer/pi/compat/BuildCompat.kt | 9 +++------ core/src/main/kotlin/dev/sanmer/pi/BuildCompat.kt | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/compat/BuildCompat.kt b/app/src/main/kotlin/dev/sanmer/pi/compat/BuildCompat.kt index 8036a118..46004540 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/compat/BuildCompat.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/compat/BuildCompat.kt @@ -5,14 +5,11 @@ import androidx.annotation.ChecksSdkIntAtLeast object BuildCompat { @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - val atLeastU get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE + val atLeastU inline get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) - val atLeastT get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + val atLeastT inline get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) - val atLeastS get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S - - @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) - val atLeastR get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R + val atLeastS inline get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S } \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/pi/BuildCompat.kt b/core/src/main/kotlin/dev/sanmer/pi/BuildCompat.kt index 19a4fa40..4f95322e 100644 --- a/core/src/main/kotlin/dev/sanmer/pi/BuildCompat.kt +++ b/core/src/main/kotlin/dev/sanmer/pi/BuildCompat.kt @@ -5,14 +5,11 @@ import androidx.annotation.ChecksSdkIntAtLeast internal object BuildCompat { @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - val atLeastU get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE + val atLeastU inline get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) - val atLeastT get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + val atLeastT inline get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) - val atLeastS get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S - - @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) - val atLeastR get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R + val atLeastS inline get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S } \ No newline at end of file From 8b91244f50531c7cfc6b81353c945fd82fa0e1d0 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Wed, 24 Jul 2024 23:19:46 +0800 Subject: [PATCH 08/34] Optimize `AppList` --- .../main/kotlin/dev/sanmer/pi/ui/screens/apps/AppsScreen.kt | 4 ++++ .../dev/sanmer/pi/ui/screens/apps/component/AppList.kt | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) 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 f1a9419d..699e4451 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,6 +2,7 @@ package dev.sanmer.pi.ui.screens.apps import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState @@ -17,6 +18,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.painterResource @@ -68,6 +70,8 @@ fun AppsScreen( modifier = Modifier .imePadding() .nestedScroll(scrollBehavior.nestedScrollConnection) + .fillMaxSize(), + contentAlignment = Alignment.TopCenter ) { if (viewModel.isLoading) { Loading( 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 72e0ed31..5e09a9c8 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 @@ -9,6 +9,7 @@ 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.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -63,7 +64,9 @@ internal fun AppList( } LazyColumn( - modifier = Modifier.animateContentSize(), + modifier = Modifier + .fillMaxWidth() + .animateContentSize(), state = state, contentPadding = contentPadding ) { From 24440fd73835858dd5869dfedcb52d5e629a6f99 Mon Sep 17 00:00:00 2001 From: Sanmer Date: Thu, 25 Jul 2024 02:41:59 +0800 Subject: [PATCH 09/34] [skip ci] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8562c746..1b00f3b6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ PI is short for `Package Installer`. ## Supported Versions -Android 10 ~ 14 +Android 11 ~ 15 ## Credits - [tabler/tabler-icons](https://github.com/tabler/tabler-icons.git) From c2a75528d8909320ed795bdffc56e4d59f706205 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:33:46 +0800 Subject: [PATCH 10/34] Bump androidx.activity:activity-compose from 1.9.0 to 1.9.1 (#127) --- 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 75f612b5..46051224 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] androidGradlePlugin = "8.5.1" -androidxActivity = "1.9.0" +androidxActivity = "1.9.1" androidxAnnotation = "1.8.0" androidxAppCompat = "1.7.0" androidxCompose = "1.7.0-beta05" From 03eddd730c3cbf4e1460eac8251eddafb373f709 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:34:22 +0800 Subject: [PATCH 11/34] Bump androidxLifecycle from 2.8.3 to 2.8.4 (#125) --- 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 46051224..2f5c5db1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ androidxCoreSplashscreen = "1.0.1" androidxDataStore = "1.1.1" androidxDocumentFile = "1.0.1" androidxHiltNavigationCompose = "1.2.0" -androidxLifecycle = "2.8.3" +androidxLifecycle = "2.8.4" androidxNavigation = "2.7.7" androidxRoom = "2.6.1" appiconloader = "1.5.0" From e4cfdc5658362e651bcb1b5b61e506ad8796927a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:34:49 +0800 Subject: [PATCH 12/34] Bump androidxCompose from 1.7.0-beta05 to 1.7.0-beta06 (#124) --- 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 2f5c5db1..2370bd32 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ androidGradlePlugin = "8.5.1" androidxActivity = "1.9.1" androidxAnnotation = "1.8.0" androidxAppCompat = "1.7.0" -androidxCompose = "1.7.0-beta05" +androidxCompose = "1.7.0-beta06" androidxComposeMaterial3 = "1.2.1" androidxCore = "1.13.1" androidxCoreSplashscreen = "1.0.1" From 55e0e7a1ca018af4ea99313a3b256f0a34d7e448 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:39:11 +0800 Subject: [PATCH 13/34] Bump androidx.annotation:annotation from 1.8.0 to 1.8.1 (#126) --- 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 2370bd32..5d792f31 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] androidGradlePlugin = "8.5.1" androidxActivity = "1.9.1" -androidxAnnotation = "1.8.0" +androidxAnnotation = "1.8.1" androidxAppCompat = "1.7.0" androidxCompose = "1.7.0-beta06" androidxComposeMaterial3 = "1.2.1" From d6fe5b4e799b8754bad020f2e63b038ca1cd2621 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sat, 27 Jul 2024 22:46:17 +0800 Subject: [PATCH 14/34] Optimize `PageIndicator` --- .../sanmer/pi/ui/component/PageIndicator.kt | 28 +++++++++---------- .../dev/sanmer/pi/ui/main/InstallScreen.kt | 6 ++-- .../dev/sanmer/pi/ui/main/PermissionScreen.kt | 2 +- 3 files changed, 18 insertions(+), 18 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 d2899559..5672e486 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 @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -30,6 +29,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.isSpecified import androidx.compose.ui.unit.sp import dev.sanmer.pi.R @@ -38,11 +38,11 @@ fun PageIndicator( icon: @Composable ColumnScope.() -> Unit, text: @Composable ColumnScope.() -> Unit, modifier: Modifier = Modifier, - minHeight: Dp? = null + height: Dp = Dp.Unspecified ) = Column( - modifier = modifier then (if (minHeight != null) { + modifier = modifier then (if (height.isSpecified) { Modifier - .defaultMinSize(minHeight = minHeight) + .height(height = height) .fillMaxWidth() } else { Modifier.fillMaxSize() @@ -62,7 +62,7 @@ fun PageIndicator( @DrawableRes icon: Int, text: String, modifier: Modifier = Modifier, - minHeight: Dp? = null + height: Dp = Dp.Unspecified ) = PageIndicator( modifier = modifier, icon = { @@ -81,7 +81,7 @@ fun PageIndicator( overflow = TextOverflow.Ellipsis ) }, - minHeight = minHeight + height = height ) @Composable @@ -89,22 +89,22 @@ fun PageIndicator( @DrawableRes icon: Int, @StringRes text: Int, modifier: Modifier = Modifier, - minHeight: Dp? = null + height: Dp = Dp.Unspecified ) = PageIndicator( modifier = modifier, icon = icon, text = stringResource(id = text), - minHeight = minHeight + height = height ) @Composable fun Loading( modifier: Modifier = Modifier, - minHeight: Dp? = null + height: Dp = Dp.Unspecified ) = PageIndicator( icon = { CircularProgressIndicator( - modifier = Modifier.size(50.dp), + modifier = Modifier.size(PageIndicatorDefaults.IconSize * (3f/4f)), strokeWidth = 5.dp, strokeCap = StrokeCap.Round ) @@ -117,23 +117,23 @@ fun Loading( ) }, modifier = modifier, - minHeight = minHeight + height = height ) @Composable fun Failed( message: String?, modifier: Modifier = Modifier, - minHeight: Dp? = null + height: Dp = Dp.Unspecified ) = PageIndicator( icon = R.drawable.alert_triangle, text = message ?: stringResource(id = R.string.unknown_error), modifier = modifier, - minHeight = minHeight + height = height ) object PageIndicatorDefaults { - val IconSize = 80.dp + val IconSize = 64.dp val IconColor @Composable get() = MaterialTheme.colorScheme.outline.copy(0.5f) val TextStyle @Composable get() = TextStyle( 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 183df1b8..90338e98 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 @@ -93,17 +93,17 @@ fun InstallScreen( ) { state -> when (state) { State.None -> Loading( - minHeight = 240.dp + height = 240.dp ) State.InvalidProvider -> Failed( message = stringResource(id = R.string.install_invalid_provider), - minHeight = 240.dp + height = 240.dp ) State.InvalidPackage -> Failed( message = stringResource(id = R.string.install_invalid_package), - minHeight = 240.dp + height = 240.dp ) else -> InstallContent() 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 833d449b..43ebb25e 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 @@ -76,7 +76,7 @@ fun PermissionScreen( ) { isLoading -> when { isLoading -> Loading( - minHeight = 240.dp + height = 240.dp ) else -> PermissionContent() From 1d06e37f5be9fa9aad9ade6372f960f6759be925 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sat, 27 Jul 2024 23:03:31 +0800 Subject: [PATCH 15/34] Remove `UserPreferences.dynamicColor` --- .../pi/datastore/UserPreferencesDataSource.kt | 8 ----- .../pi/datastore/model/UserPreferences.kt | 2 -- .../repository/UserPreferencesRepository.kt | 2 -- .../dev/sanmer/pi/ui/InstallActivity.kt | 6 ++-- .../kotlin/dev/sanmer/pi/ui/MainActivity.kt | 6 ++-- .../dev/sanmer/pi/ui/PermissionActivity.kt | 6 ++-- .../pi/ui/screens/settings/SettingsScreen.kt | 32 ------------------- .../kotlin/dev/sanmer/pi/ui/theme/Theme.kt | 7 ++-- .../sanmer/pi/viewmodel/SettingsViewModel.kt | 6 ---- 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 | 4 --- app/src/main/res/values/strings.xml | 4 --- 20 files changed, 9 insertions(+), 108 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/datastore/UserPreferencesDataSource.kt b/app/src/main/kotlin/dev/sanmer/pi/datastore/UserPreferencesDataSource.kt index 90854a3e..5d832bb9 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/datastore/UserPreferencesDataSource.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/datastore/UserPreferencesDataSource.kt @@ -20,14 +20,6 @@ class UserPreferencesDataSource @Inject constructor( } } - suspend fun setDynamicColor(value: Boolean) = withContext(Dispatchers.IO) { - userPreferences.updateData { - it.copy( - dynamicColor = value - ) - } - } - suspend fun setRequester(value: String) = withContext(Dispatchers.IO) { userPreferences.updateData { it.copy( diff --git a/app/src/main/kotlin/dev/sanmer/pi/datastore/model/UserPreferences.kt b/app/src/main/kotlin/dev/sanmer/pi/datastore/model/UserPreferences.kt index 6a677263..f8fe6ab4 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/datastore/model/UserPreferences.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/datastore/model/UserPreferences.kt @@ -1,7 +1,6 @@ package dev.sanmer.pi.datastore.model import dev.sanmer.pi.BuildConfig -import dev.sanmer.pi.compat.BuildCompat import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray @@ -12,7 +11,6 @@ import java.io.OutputStream @Serializable data class UserPreferences( val provider: Provider = Provider.None, - val dynamicColor: Boolean = BuildCompat.atLeastS, val requester: String = BuildConfig.APPLICATION_ID, val executor: String = BuildConfig.APPLICATION_ID ) { diff --git a/app/src/main/kotlin/dev/sanmer/pi/repository/UserPreferencesRepository.kt b/app/src/main/kotlin/dev/sanmer/pi/repository/UserPreferencesRepository.kt index 314fedf3..d0eb5d0d 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/repository/UserPreferencesRepository.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/repository/UserPreferencesRepository.kt @@ -13,8 +13,6 @@ class UserPreferencesRepository @Inject constructor( suspend fun setProvider(value: Provider) = userPreferencesDataSource.setProvider(value) - suspend fun setDynamicColor(value: Boolean) = userPreferencesDataSource.setDynamicColor(value) - suspend fun setRequester(value: String) = userPreferencesDataSource.setRequester(value) suspend fun setExecutor(value: String) = userPreferencesDataSource.setExecutor(value) 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 d7d90a5b..936c4a04 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/InstallActivity.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/InstallActivity.kt @@ -55,15 +55,13 @@ class InstallActivity : ComponentActivity() { val preferences = if (userPreferences == null) { return@setContent } else { - checkNotNull(userPreferences) + requireNotNull(userPreferences) } CompositionLocalProvider( LocalUserPreferences provides preferences ) { - AppTheme( - dynamicColor = preferences.dynamicColor - ) { + AppTheme { InstallScreen() } } 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 e420b9f5..fe6f6c54 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/MainActivity.kt @@ -46,7 +46,7 @@ class MainActivity : ComponentActivity() { return@setContent } else { isLoading = false - checkNotNull(userPreferences) + requireNotNull(userPreferences) } LaunchedEffect(userPreferences) { @@ -56,9 +56,7 @@ class MainActivity : ComponentActivity() { CompositionLocalProvider( LocalUserPreferences provides preferences ) { - AppTheme( - dynamicColor = preferences.dynamicColor - ) { + AppTheme { Crossfade( targetState = preferences.provider != Provider.None, label = "MainActivity" 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 6cac5762..7290be2c 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/PermissionActivity.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/PermissionActivity.kt @@ -44,15 +44,13 @@ class PermissionActivity : ComponentActivity() { val preferences = if (userPreferences == null) { return@setContent } else { - checkNotNull(userPreferences) + requireNotNull(userPreferences) } CompositionLocalProvider( LocalUserPreferences provides preferences ) { - AppTheme( - dynamicColor = preferences.dynamicColor - ) { + AppTheme { PermissionScreen() } } 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 16b06831..16edd10e 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 @@ -8,9 +8,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable @@ -18,7 +16,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import dev.sanmer.pi.BuildConfig @@ -31,7 +28,6 @@ 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 -import dev.sanmer.pi.ui.component.SettingSwitchItem import dev.sanmer.pi.ui.ktx.navigateSingleTopTo import dev.sanmer.pi.ui.main.Screen import dev.sanmer.pi.ui.provider.LocalUserPreferences @@ -60,10 +56,6 @@ fun SettingsScreen( .verticalScroll(rememberScrollState()) .padding(contentPadding) ) { - TittleItem( - text = stringResource(id = R.string.settings_behavior) - ) - ServiceItem( isAlive = viewModel.isProviderAlive, platform = viewModel.providerPlatform, @@ -85,23 +77,10 @@ fun SettingsScreen( } ) - TittleItem( - text = stringResource(id = R.string.settings_interface) - ) - LanguageItem( context = context ) - SettingSwitchItem( - icon = R.drawable.color_swatch, - title = stringResource(id = R.string.settings_dynamic_color), - desc = stringResource(id = R.string.settings_dynamic_color_desc), - checked = userPreferences.dynamicColor, - onChange = viewModel::setDynamicColor, - enabled = BuildCompat.atLeastS - ) - SettingNormalItem( icon = R.drawable.language, title = stringResource(id = R.string.settings_translation), @@ -123,17 +102,6 @@ fun SettingsScreen( } } -@Composable -private fun TittleItem( - text: String, - modifier: Modifier = Modifier -) = Text( - modifier = modifier.padding(all = 16.dp), - text = text, - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.primary -) - @Composable private fun ServiceItem( isAlive: Boolean, diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/theme/Theme.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/theme/Theme.kt index 5f3402c1..37a9926d 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/theme/Theme.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/theme/Theme.kt @@ -17,11 +17,10 @@ import dev.sanmer.pi.compat.BuildCompat @Composable fun AppTheme( - dynamicColor: Boolean, darkMode: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { - val colorScheme = getColorScheme(dynamicColor, darkMode) + val colorScheme = getColorScheme(darkMode) SystemBarStyle( darkMode = darkMode @@ -36,9 +35,9 @@ fun AppTheme( } @Composable -private fun getColorScheme(dynamicColor: Boolean, darkMode: Boolean): ColorScheme { +private fun getColorScheme(darkMode: Boolean): ColorScheme { val context = LocalContext.current - return if (BuildCompat.atLeastS && dynamicColor) { + return if (BuildCompat.atLeastS) { when { darkMode -> dynamicDarkColorScheme(context) else -> dynamicLightColorScheme(context) diff --git a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/SettingsViewModel.kt b/app/src/main/kotlin/dev/sanmer/pi/viewmodel/SettingsViewModel.kt index 9a2a241f..c7f5f362 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/SettingsViewModel.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/viewmodel/SettingsViewModel.kt @@ -22,12 +22,6 @@ class SettingsViewModel @Inject constructor( Timber.d("SettingsViewModel init") } - fun setDynamicColor(value: Boolean) { - viewModelScope.launch { - userPreferencesRepository.setDynamicColor(value) - } - } - fun setProvider(value: Provider) { viewModelScope.launch { userPreferencesRepository.setProvider(value) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index a93ea234..02f466ed 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -9,7 +9,6 @@ مقدم طلب التثبيت قائمة فارغة الإعدادات - لون ديناميكي المشاركة في الترجمة ساعدنا في ترجمة PI إلى لغتك حزمة التثبيت @@ -25,11 +24,8 @@ تثبيت الخدمة خطأ غير معروف 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 bddf7cfd..3f70c927 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1,6 +1,5 @@ - Comportamiento Instalar Configuración Modo de trabajo @@ -10,11 +9,8 @@ Versión %1$d, %2$s El servicio no está en ejecución Haga clic para intentar iniciar - Interfaz Idioma Sistema por defecto - Color dinámico - Utilizar el color del tema del sistema Participar en la traducción Ayúdenos a traducir PI a su idioma Código fuente diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 84ecc4c2..031fa2d7 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -9,17 +9,13 @@ Version %1$d, %2$s Service d\'installation Erreur inconnue - Comportement Cliquer pour essayer de démarrer - Interface Language Système par défaut Code source Échec de l\'analyse du paquet Paquet d\'installation Le service n\'est pas disponible - Couleur dynamique - Utiliser la couleur du thème système Participer à la traduction Aidez nous a traduire PI dans vôtre langue OK diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 2e980ad5..e5522ea6 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -4,15 +4,11 @@ Mode kerja Diotorisasi Eksekutor - Tindakan Layanan telah berjalan Versi %1$d, %2$s Klik untuk mencoba memulai - Antarmuka Bahasa Bawaan sistem - Warna dinamis - Menggunakan warna tema sistem Berpartisipasi dalam penerjemahan Kode sumber Paket instalasi diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index d0301628..b4c46874 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -6,7 +6,6 @@ דורש הרשאות המסופקות על ידי Sui או Shizuku ארכיטקטורה פיצ\'ר דינמי - תפעול חבילת התקנה מבקש ההתקנה צפיפות תצוגה @@ -16,11 +15,8 @@ השירות פועל גרסה %1$d, %2$s השירות לא פועל - ממשק שפה ברירת מחדל של המערכת - צבע דינמי - שימוש בצבע ערכת הנושא של המערכת עזור לנו לתרגם את PI לשפה שלך קוד מקור שגיאה לא ידועה diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index bdbd770a..12fd0e81 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -10,8 +10,6 @@ Solicitante de instalação Lista vazia Configurações - Cor dinâmica - Use a cor do tema do sistema Pacote de instalação Instalar Instalação bem-sucedida @@ -27,9 +25,7 @@ Não especificado Participe da tradução Ajude-nos a traduzir o PI para o seu idioma - Comportamento Clique para tentar começar - Interface Idioma Código fonte OK diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index bf5837c4..d7c8e7ea 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -8,8 +8,6 @@ Versão %1$d, %2$s Solicitante de instalação Lista vazia - Cor dinâmica - Use a cor do tema do sistema Pacote de instalação Instalar Falha na instalação @@ -27,9 +25,7 @@ Não especificado Participe da tradução Ajude-nos a traduzir o PI para o seu idioma - Comportamento Clique para tentar começar - Interface Idioma Padrão do sistema Código fonte diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 846d6c97..59ce2a52 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -17,13 +17,9 @@ Неопределённый Установка завершена Сервис установки - Поведение Нажмите, чтобы попытаться начать - Интерфейс Язык Системный по умолчанию - Динамический цвет - Использует системные цвета Участие в переводе Помогите нам перевести PI на ваш язык Исходный код diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 2e47ea47..000b6d3c 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -8,8 +8,6 @@ Trình yêu cầu cài đặt Danh sách trống Thiết đặt - Màu sắc linh động - Sử dụng bảng màu Material You Tham gia dịch thuật Hãy giúp chúng tôi dịch PI sang ngôn ngữ của bạn Gói cài đặt diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 1b874cfe..0b0625e1 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -15,16 +15,12 @@ 设置 - 行为 服务正在运行 版本 %1$d,%2$s 服务未运行 点击尝试启动 - 界面 语言 系统默认 - 动态颜色 - 使用系统主题颜色 参与翻译 帮助我们将 PI 翻译至您的语言 源码 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 72a6f712..00fd6774 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,16 +15,12 @@ Settings - Behavior Service is running Version %1$d, %2$s Service is not running Click to try to start - Interface Language System default - Dynamic color - Use system theme color Participate in translation Help us translate PI into your language Source code From 0f3348cc698167522bce180a8fc6546625d29d4c Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 28 Jul 2024 00:09:44 +0800 Subject: [PATCH 16/34] Fix `UserPreferences` --- .../kotlin/dev/sanmer/pi/datastore/model/UserPreferences.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/kotlin/dev/sanmer/pi/datastore/model/UserPreferences.kt b/app/src/main/kotlin/dev/sanmer/pi/datastore/model/UserPreferences.kt index f8fe6ab4..00d1449f 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/datastore/model/UserPreferences.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/datastore/model/UserPreferences.kt @@ -5,13 +5,17 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.protobuf.ProtoBuf +import kotlinx.serialization.protobuf.ProtoNumber import java.io.InputStream import java.io.OutputStream @Serializable data class UserPreferences( + @ProtoNumber(1) val provider: Provider = Provider.None, + @ProtoNumber(3) val requester: String = BuildConfig.APPLICATION_ID, + @ProtoNumber(4) val executor: String = BuildConfig.APPLICATION_ID ) { fun encodeTo(output: OutputStream) = output.write( From 918dea36a5b868ac2e87a54ee1c36b595ea658c9 Mon Sep 17 00:00:00 2001 From: Sanmer Bot Date: Sun, 28 Jul 2024 10:19:46 +0800 Subject: [PATCH 17/34] Translations update from Weblate - SanmerApps (#128) * [skip ci] Added translation using Weblate (Sundanese) * Translated using Weblate (Sundanese) Currently translated at 2.2% (1 of 44 strings) Translation: PI/App Translate-URL: https://weblate.sanmer.app/projects/pi/app/su/ * Translated using Weblate (Sundanese) Currently translated at 100.0% (40 of 40 strings) Translation: PI/App Translate-URL: https://weblate.sanmer.app/projects/pi/app/su/ --------- Co-authored-by: Ian Perdiansah --- app/src/main/res/values-su/strings.xml | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/src/main/res/values-su/strings.xml diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml new file mode 100644 index 00000000..6e41ec17 --- /dev/null +++ b/app/src/main/res/values-su/strings.xml @@ -0,0 +1,43 @@ + + + Pasang + Kapadetan layar + Modeu gawé + Ngabutuhkeun idin nu disadiakeun ku Sui atawa Shizuku + Ngabutuhkeun idin Root nu disadiakeun ku Magisk, KernelSU, atawa APatch + Diijinkeun + Paménta + Éksékutor + Pangaturan + Layanan geus jalan + Vérsi %1$d, %2$s + Layanan teu jalan + Klik keur ngajalankeun + Basa + Ngamuat… + Téang… + Parsing pakét gagal + Aplikasi + Eusi kosong + Idin + Tolak + Idinkeun + Heug + Bolay + Pamasangan réngsé + Pamasangan gagal + Bawaan ti sistem + Babantu pikeun tarjamahan + Ngabantu ka urang pikeun tarjamahan PI kana basa anjeun + Sumber kodeu + Pakét pamasangan + Paménta pamasangan + Fitur dinamis + ABI + Basa + Teu puguh rupana + Pasang + Layanan teu sadia + Pasang layanan + Error teu dipikanyaho + \ No newline at end of file From f0369e038513994ac31ae8a9dc824f3516c9de75 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 28 Jul 2024 12:28:54 +0800 Subject: [PATCH 18/34] Remove `PermissionActivity` --- app/src/main/AndroidManifest.xml | 13 - .../dev/sanmer/pi/ui/PermissionActivity.kt | 99 -------- .../dev/sanmer/pi/ui/main/PermissionScreen.kt | 226 ------------------ .../pi/viewmodel/PermissionViewModel.kt | 91 ------- 4 files changed, 429 deletions(-) delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/PermissionActivity.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt delete mode 100644 app/src/main/kotlin/dev/sanmer/pi/viewmodel/PermissionViewModel.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 05dbbf17..ba08b28b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,19 +53,6 @@ - - - - - - - init() - else -> finish() - } - - setContent { - val userPreferences by userPreferencesRepository.data - .collectAsStateWithLifecycle(initialValue = null) - - val preferences = if (userPreferences == null) { - return@setContent - } else { - requireNotNull(userPreferences) - } - - CompositionLocalProvider( - LocalUserPreferences provides preferences - ) { - AppTheme { - PermissionScreen() - } - } - } - } - - override fun finish() { - Intent().apply { - putExtra(EXTRA_PERMISSIONS, viewModel.permissions.toTypedArray()) - putExtra(EXTRA_PERMISSION_GRANT_RESULTS, viewModel.permissionResults()) - setResult(RESULT_OK, this) - } - - super.finish() - } - - override fun onDestroy() { - Timber.d("onDestroy") - super.onDestroy() - } - - private fun init() { - val packageName = checkNotNull(callingPackage) - val permissions = intent.permissions.toList() - - lifecycleScope.launch { - if (!viewModel.load(packageName, permissions)) { - finish() - } - } - } - - companion object { - const val ACTION_REQUEST_PERMISSIONS = "dev.sanmer.pi.action.REQUEST_PERMISSIONS" - const val EXTRA_PERMISSIONS = "dev.sanmer.pi.extra.PERMISSIONS" - const val EXTRA_PERMISSION_GRANT_RESULTS = "dev.sanmer.pi.extra.PERMISSION_GRANT_RESULTS" - - private val Intent.permissions: Array - get() = getStringArrayExtra(EXTRA_PERMISSIONS) ?: emptyArray() - - private val Intent.isOk: Boolean - get() = action == ACTION_REQUEST_PERMISSIONS - || permissions.isNotEmpty() - - } -} \ No newline at end of file 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 deleted file mode 100644 index 43ebb25e..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/main/PermissionScreen.kt +++ /dev/null @@ -1,226 +0,0 @@ -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 -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -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.material3.Button -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton -import androidx.compose.material3.OutlinedCard -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import dev.sanmer.pi.R -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.bottom -import dev.sanmer.pi.ui.screens.apps.component.AppItem -import dev.sanmer.pi.viewmodel.PermissionViewModel - -@Composable -fun PermissionScreen( - viewModel: PermissionViewModel = hiltViewModel() -) { - val context = LocalContext.current - - BackHandler { - context.finishActivity() - } - - BottomSheetLayout( - bottomBar = { bottomPadding -> - if (!viewModel.isLoading) { - BottomBar( - modifier = Modifier - .padding(bottomPadding) - .padding(horizontal = 20.dp) - .padding(bottom = 20.dp), - onGrant = viewModel::grantPermissions - ) - } - }, - shape = MaterialTheme.shapes.large.bottom(0.dp) - ) { contentPadding -> - Crossfade( - modifier = Modifier - .animateContentSize() - .padding(contentPadding) - .padding(all = 20.dp), - targetState = viewModel.isLoading, - label = "PermissionScreen" - ) { isLoading -> - when { - isLoading -> Loading( - height = 240.dp - ) - - else -> PermissionContent() - } - } - } -} - -@Composable -private fun BottomBar( - modifier: Modifier = Modifier, - onGrant: () -> Unit -) = Row( - modifier = modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(20.dp) -) { - Spacer(modifier = Modifier.weight(1f)) - - val context = LocalContext.current - OutlinedButton( - onClick = { - context.finishActivity() - } - ) { - Text(text = stringResource(id = R.string.permission_button_deny)) - } - - Button( - onClick = { - onGrant() - context.finishActivity() - } - ) { - Text(text = stringResource(id = R.string.permission_button_grant)) - } -} - -@Composable -private fun PermissionContent( - modifier: Modifier = Modifier, - viewModel: PermissionViewModel = hiltViewModel() -) = Column( - modifier = modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(16.dp), - horizontalAlignment = Alignment.CenterHorizontally -) { - AppItem( - packageInfo = viewModel.packageInfo - ) - - PermissionsItem( - permissions = viewModel.permissions, - isRequiredPermission = viewModel::isRequiredPermission, - togglePermission = viewModel::togglePermission - ) -} - -@Composable -private fun AppItem( - packageInfo: IPackageInfo, -) = TittleItem( - text = stringResource(id = R.string.permission_app_title) -) { - Surface( - shape = MaterialTheme.shapes.large, - tonalElevation = 6.dp, - border = CardDefaults.outlinedCardBorder() - ) { - AppItem( - pi = packageInfo, - enabled = false, - iconSize = 45.dp, - iconEnd = 15.dp, - contentPaddingValues = PaddingValues(15.dp), - verticalAlignment = Alignment.Top - ) - } -} - -@Composable -private fun PermissionsItem( - permissions: List, - isRequiredPermission: (String) -> Boolean, - togglePermission: (String) -> Unit -) { - val state = rememberLazyListState() - LazyColumn( - state = state, - verticalArrangement = Arrangement.spacedBy(10.dp) - ) { - item { - TittleItem(text = stringResource(id = R.string.permission_permissions_title)) - } - items( - items = permissions - ) { - PermissionItem( - permission = it, - isRequiredPermission = isRequiredPermission, - togglePermission = togglePermission - ) - } - } -} - -@Composable -private fun PermissionItem( - permission: String, - isRequiredPermission: (String) -> Boolean, - togglePermission: (String) -> Unit, -) { - val required by remember { - derivedStateOf { isRequiredPermission(permission) } - } - - OutlinedCard( - shape = MaterialTheme.shapes.medium, - onClick = { togglePermission(permission) } - ) { - Row( - modifier = Modifier - .padding(vertical = 10.dp, horizontal = 15.dp) - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - painter = painterResource(id = R.drawable.code), - contentDescription = null, - tint = MaterialTheme.colorScheme.onSurfaceVariant - ) - - Column( - modifier = Modifier.padding(start = 10.dp) - ) { - Text( - text = permission, - style = MaterialTheme.typography.bodyMedium, - textDecoration = when { - !required -> TextDecoration.LineThrough - else -> TextDecoration.None - }, - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/PermissionViewModel.kt b/app/src/main/kotlin/dev/sanmer/pi/viewmodel/PermissionViewModel.kt deleted file mode 100644 index 83911f35..00000000 --- a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/PermissionViewModel.kt +++ /dev/null @@ -1,91 +0,0 @@ -package dev.sanmer.pi.viewmodel - -import android.content.pm.PackageInfo -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import dev.sanmer.pi.Compat -import dev.sanmer.pi.UserHandleCompat -import dev.sanmer.pi.model.IPackageInfo -import dev.sanmer.pi.model.IPackageInfo.Companion.toIPackageInfo -import dev.sanmer.pi.repository.UserPreferencesRepository -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.withContext -import javax.inject.Inject - -@HiltViewModel -class PermissionViewModel @Inject constructor( - private val userPreferencesRepository: UserPreferencesRepository -) : ViewModel() { - private val pm by lazy { Compat.getPackageManager() } - private val perm by lazy { Compat.getPermissionManager() } - - var packageInfo by mutableStateOf(IPackageInfo.empty()) - private set - - var permissions by mutableStateOf(emptyList()) - private set - private val requiredPermissions = mutableStateListOf() - - var isLoading by mutableStateOf(true) - private set - - suspend fun load(packageName: String, permissions: List) = withContext(Dispatchers.IO) { - val userPreferences = userPreferencesRepository.data.first() - - if (!Compat.init(userPreferences.provider)) { - return@withContext false - } - - this@PermissionViewModel.packageInfo = getPackageInfo(packageName).toIPackageInfo() - this@PermissionViewModel.permissions = permissions - - requiredPermissions.addAll(permissions) - isLoading = false - - true - } - - fun isRequiredPermission(permission: String): Boolean { - return permission in requiredPermissions - } - - fun togglePermission(permission: String) { - if (isRequiredPermission(permission)) { - requiredPermissions.remove(permission) - } else { - requiredPermissions.add(permission) - } - } - - fun grantPermissions() { - requiredPermissions.forEach { - perm.grantRuntimePermission( - packageName = packageInfo.packageName, - permissionName = it, - userId = UserHandleCompat.myUserId() - ) - } - } - - fun permissionResults() = permissions.map { - perm.checkPermission( - packageName = packageInfo.packageName, - permissionName = it, - userId = UserHandleCompat.myUserId() - ) - }.toIntArray() - - private fun getPackageInfo(packageName: String?): PackageInfo { - if (packageName == null) return PackageInfo() - return runCatching { - pm.getPackageInfo( - packageName, 0, UserHandleCompat.myUserId() - ) - }.getOrNull() ?: PackageInfo() - } -} \ No newline at end of file From 14ee81be8c9a974311bf0ad153a1b079c23b96cc Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 28 Jul 2024 13:23:05 +0800 Subject: [PATCH 19/34] Optimize `AppList` --- .../pi/ui/screens/apps/component/AppItem.kt | 32 +-- .../pi/ui/screens/apps/component/AppList.kt | 240 +++++++----------- .../dev/sanmer/pi/viewmodel/AppsViewModel.kt | 65 ++--- app/src/main/res/drawable/chevron_right.xml | 12 + app/src/main/res/values-zh-rCN/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 6 files changed, 140 insertions(+), 211 deletions(-) create mode 100644 app/src/main/res/drawable/chevron_right.xml 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 d2d6f0d0..7e338aa9 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 @@ -4,7 +4,6 @@ import androidx.annotation.DrawableRes import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -16,9 +15,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest @@ -28,28 +25,21 @@ import dev.sanmer.pi.model.IPackageInfo import dev.sanmer.pi.ui.component.Logo @Composable -internal fun AppItem( +fun AppItem( pi: IPackageInfo, - iconSize: Dp = 45.dp, - iconEnd: Dp = 12.dp, - contentPaddingValues: PaddingValues = PaddingValues(12.dp), - verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, - onClick: () -> Unit = {}, - enabled: Boolean = true + onClick: () -> Unit, + trailing: @Composable (() -> Unit)? = null ) = Row( modifier = Modifier - .clip(shape = MaterialTheme.shapes.medium) - .clickable( - enabled = enabled, - onClick = onClick - ) - .padding(contentPaddingValues) + .clickable(onClick = onClick) + .padding(all = 10.dp) .fillMaxWidth(), - verticalAlignment = verticalAlignment + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp) ) { val context = LocalContext.current AsyncImage( - modifier = Modifier.size(iconSize), + modifier = Modifier.size(40.dp), model = ImageRequest.Builder(context) .data(pi) .crossfade(true) @@ -58,9 +48,7 @@ internal fun AppItem( ) Column( - modifier = Modifier - .padding(start = iconEnd) - .weight(1f) + modifier = Modifier.weight(1f), ) { Text( text = pi.appLabel, @@ -94,6 +82,8 @@ internal fun AppItem( if (pi.isAuthorized) Icon(R.drawable.package_import) } } + + trailing?.invoke() } @Composable 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 5e09a9c8..3f8e1f51 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 @@ -1,209 +1,159 @@ package dev.sanmer.pi.ui.screens.apps.component import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Box 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.asPaddingValues 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.FilledTonalIconButton +import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp 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.ktx.bottom import dev.sanmer.pi.viewmodel.AppsViewModel import kotlinx.coroutines.launch @Composable -internal fun AppList( +fun AppList( list: List, state: LazyListState, buildSettings: (IPackageInfo) -> AppsViewModel.Settings, contentPadding: PaddingValues = PaddingValues(0.dp) +) = LazyColumn( + modifier = Modifier + .fillMaxWidth() + .animateContentSize(), + state = state, + contentPadding = contentPadding ) { - var packageName by remember { mutableStateOf("") } - val packageInfo by remember(list, packageName) { - derivedStateOf { - list.firstOrNull { it.packageName == packageName } - } - } - - packageInfo?.let { - BottomSheet( + items(list) { + AppItem( pi = it, - onClose = { packageName = "" }, buildSettings = buildSettings ) } - - LazyColumn( - modifier = Modifier - .fillMaxWidth() - .animateContentSize(), - state = state, - contentPadding = contentPadding - ) { - items( - items = list, - key = { it.packageName } - ) { pi -> - AppItem( - pi = pi, - onClick = { packageName = pi.packageName } - ) - } - } } @Composable -private fun BottomSheet( +private fun AppItem( pi: IPackageInfo, - onClose: () -> Unit, buildSettings: (IPackageInfo) -> AppsViewModel.Settings -) = ModalBottomSheet( - onDismissRequest = onClose, - dragHandle = null, - 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(contentPadding) - .padding(all = 20.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - AppItem( - pi = pi.toIPackageInfo(), - enabled = false, - iconSize = 60.dp, - iconEnd = 20.dp, - contentPaddingValues = PaddingValues(0.dp), - verticalAlignment = Alignment.Top - ) - - SettingButtons( - settings = settings - ) - - SettingItem( - pi = pi, - settings = settings - ) - } -} - -@Composable -private fun SettingButtons( - settings: AppsViewModel.Settings -) = Row( - verticalAlignment = Alignment.CenterVertically ) { - val context = LocalContext.current - val scope = rememberCoroutineScope() + var expend by rememberSaveable { mutableStateOf(false) } + val degrees by animateFloatAsState( + targetValue = if (expend) 90f else 0f, + label = "AppItem Icon" + ) - if (settings.isOpenable) { - FilledTonalIconButton( - onClick = { settings.launch(context) } - ) { + AppItem( + pi = pi, + onClick = { expend = !expend }, + trailing = { Icon( - painter = painterResource(id = R.drawable.window_maximize), - contentDescription = null + painter = painterResource(id = R.drawable.chevron_right), + contentDescription = null, + modifier = Modifier.rotate(degrees) ) } - } - - FilledTonalIconButton( - onClick = { settings.view(context) } - ) { - Icon( - painter = painterResource(id = R.drawable.eye), - contentDescription = null - ) - } + ) - FilledTonalIconButton( - onClick = { - scope.launch { settings.export(context) } - } - ) { - Icon( - painter = painterResource(id = R.drawable.package_export), - contentDescription = null - ) - } + if (expend) SettingItem( + pi = pi, + settings = buildSettings(pi), + onClose = { expend = false } + ) } @OptIn(ExperimentalLayoutApi::class) @Composable private fun SettingItem( + onClose: () -> Unit, pi: IPackageInfo, settings: AppsViewModel.Settings -) = FlowRow( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) +) = Box( + modifier = Modifier + .padding(all = 10.dp) + .clip(shape = MaterialTheme.shapes.medium) + .border( + border = CardDefaults.outlinedCardBorder(), + shape = MaterialTheme.shapes.medium + ) ) { - val scope = rememberCoroutineScope() + FlowRow( + modifier = Modifier + .fillMaxWidth() + .padding(all = 10.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + + MenuChip( + selected = pi.isAuthorized, + onClick = { + scope.launch { + settings.setAuthorized() + onClose() + } + }, + label = { Text(text = stringResource(id = R.string.app_authorized)) }, + ) - MenuChip( - selected = pi.isAuthorized, - onClick = { - scope.launch { - settings.setAuthorized() - } - }, - label = { Text(text = stringResource(id = R.string.app_authorized)) }, - ) + MenuChip( + selected = pi.isRequester, + onClick = { + scope.launch { + settings.setRequester() + } + }, + label = { Text(text = stringResource(id = R.string.app_requester)) }, + ) - MenuChip( - selected = pi.isRequester, - onClick = { - scope.launch { - settings.setRequester() - } - }, - label = { Text(text = stringResource(id = R.string.app_requester)) }, - ) + MenuChip( + selected = pi.isExecutor, + onClick = { + scope.launch { + settings.setExecutor() + onClose() + } + }, + label = { Text(text = stringResource(id = R.string.app_executor)) }, + ) - MenuChip( - selected = pi.isExecutor, - onClick = { - scope.launch { - settings.setExecutor() - } - }, - label = { Text(text = stringResource(id = R.string.app_executor)) }, - ) + MenuChip( + selected = false, + onClick = { + scope.launch { + if (settings.export(context)) { + onClose() + } + } + }, + label = { Text(text = stringResource(id = R.string.app_export)) }, + ) + } } \ No newline at end of file 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 0a47f7e4..07ce3c9f 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt @@ -15,7 +15,6 @@ 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.viewPackage import dev.sanmer.pi.model.IPackageInfo import dev.sanmer.pi.model.IPackageInfo.Companion.toIPackageInfo import dev.sanmer.pi.repository.UserPreferencesRepository @@ -30,7 +29,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber import java.io.File -import java.io.InputStream import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream import javax.inject.Inject @@ -75,7 +73,9 @@ class AppsViewModel @Inject constructor( .onEach { isAlive -> if (!isAlive) return@onEach - packagesFlow.value = getPackages() + packagesFlow.update { + getPackages() + } aom.startWatchingMode( op = AppOpsManagerDelegate.OP_REQUEST_INSTALL_PACKAGES, @@ -161,26 +161,10 @@ class AppsViewModel @Inject constructor( fun closeSearch() { isSearch = false - keyFlow.value = "" + keyFlow.update { "" } } fun buildSettings(packageInfo: IPackageInfo) = object : Settings { - private val launchIntent by lazy { - pm.getLaunchIntentForPackage( - packageInfo.packageName, UserHandleCompat.myUserId() - ) - } - - override val isOpenable by lazy { launchIntent != null } - - override fun launch(context: Context) { - context.startActivity(launchIntent) - } - - override fun view(context: Context) { - context.viewPackage(packageInfo.packageName) - } - override suspend fun export(context: Context): Boolean { val sourceDir = packageInfo.applicationInfo?.let { File(it.sourceDir) } if (sourceDir == null) return false @@ -192,25 +176,19 @@ class AppsViewModel @Inject constructor( file.name.endsWith(".apk") } ?: return false - val streams = files.map { it to it.inputStream().buffered() } - when { - streams.size == 1 -> { - context.exportApk( - input = streams.first().second, - path = path - ) - } + return when { + files.size == 1 -> context.exportApk( + file = files.first(), + path = path + ) - streams.size > 1 -> { - context.exportApks( - inputs = streams, - path = path + 's' - ) - } - } + files.size > 1 -> context.exportApks( + files = files.toList(), + path = path + 's' + ) - streams.forEach { it.second.close() } - return true + else -> false + } } override suspend fun setAuthorized() { @@ -244,7 +222,7 @@ class AppsViewModel @Inject constructor( } private suspend fun Context.exportApk( - input: InputStream, + file: File, path: String, ) = withContext(Dispatchers.IO) { val uri = createDownloadUri( @@ -253,7 +231,7 @@ class AppsViewModel @Inject constructor( ) contentResolver.openOutputStream(uri)?.use { output -> - input.copyTo(output) + file.inputStream().buffered().copyTo(output) return@withContext true } @@ -261,7 +239,7 @@ class AppsViewModel @Inject constructor( } private suspend fun Context.exportApks( - inputs: List>, + files: List, path: String, ) = withContext(Dispatchers.IO) { val uri = createDownloadUri( @@ -270,9 +248,9 @@ class AppsViewModel @Inject constructor( ) contentResolver.openOutputStream(uri)?.let(::ZipOutputStream)?.use { output -> - inputs.forEach { (file, input) -> + files.forEach { file -> output.putNextEntry(ZipEntry(file.name)) - input.copyTo(output) + file.inputStream().buffered().copyTo(output) output.closeEntry() } @@ -288,9 +266,6 @@ class AppsViewModel @Inject constructor( ).isAllowed() interface Settings { - val isOpenable: Boolean - fun launch(context: Context) - fun view(context: Context) suspend fun export(context: Context): Boolean suspend fun setAuthorized() suspend fun setRequester() diff --git a/app/src/main/res/drawable/chevron_right.xml b/app/src/main/res/drawable/chevron_right.xml new file mode 100644 index 00000000..11239d1b --- /dev/null +++ b/app/src/main/res/drawable/chevron_right.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 0b0625e1..27e28c86 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -12,6 +12,7 @@ 已授权 请求者 执行者 + 导出 设置 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 00fd6774..22ce254a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Authorized Requester Executor + Export Settings From 729a48d91671ae522ae211037cd809fa28bb21ee Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 28 Jul 2024 13:28:31 +0800 Subject: [PATCH 20/34] Remove unused res --- .../workingmode/component/WorkingModeItem.kt | 2 +- app/src/main/res/drawable/color_swatch.xml | 30 ------------- app/src/main/res/drawable/eye.xml | 18 -------- app/src/main/res/drawable/package_export.xml | 42 ------------------- app/src/main/res/drawable/window_maximize.xml | 30 ------------- app/src/main/res/values-es/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-su/strings.xml | 4 -- app/src/main/res/values-zh-rCN/strings.xml | 6 --- app/src/main/res/values/strings.xml | 6 --- 13 files changed, 1 insertion(+), 157 deletions(-) delete mode 100644 app/src/main/res/drawable/color_swatch.xml delete mode 100644 app/src/main/res/drawable/eye.xml delete mode 100644 app/src/main/res/drawable/package_export.xml delete mode 100644 app/src/main/res/drawable/window_maximize.xml diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/component/WorkingModeItem.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/component/WorkingModeItem.kt index 5923d289..777dcd37 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/component/WorkingModeItem.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/screens/workingmode/component/WorkingModeItem.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp import dev.sanmer.pi.R @Composable -internal fun WorkingModeItem( +fun WorkingModeItem( title: String, desc: String, modifier: Modifier = Modifier, diff --git a/app/src/main/res/drawable/color_swatch.xml b/app/src/main/res/drawable/color_swatch.xml deleted file mode 100644 index ebd72a23..00000000 --- a/app/src/main/res/drawable/color_swatch.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/eye.xml b/app/src/main/res/drawable/eye.xml deleted file mode 100644 index 719a32b0..00000000 --- a/app/src/main/res/drawable/eye.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/package_export.xml b/app/src/main/res/drawable/package_export.xml deleted file mode 100644 index 42063035..00000000 --- a/app/src/main/res/drawable/package_export.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/window_maximize.xml b/app/src/main/res/drawable/window_maximize.xml deleted file mode 100644 index 9f235eac..00000000 --- a/app/src/main/res/drawable/window_maximize.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3f70c927..25f57b1d 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -36,8 +36,4 @@ Autorizado Solicitante Ejecutor - Permisos - Denegar - Aplicación - Conceder \ 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 e5522ea6..83b4afb9 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -36,8 +36,4 @@ 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 diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index b4c46874..a11dc040 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -36,8 +36,4 @@ מורשה מבצע מבקש - הרשאות - דחה - קבל - אפליקציה \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 12fd0e81..a3f8a479 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -36,8 +36,4 @@ Autorizado Solicitante Executor - Permissões - Negar - Permitir - App \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index d7c8e7ea..e32823aa 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -36,8 +36,4 @@ Autorizado Solicitante Executor - Permissões - Negar - Conceder - Aplicação \ No newline at end of file diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml index 6e41ec17..2278482b 100644 --- a/app/src/main/res/values-su/strings.xml +++ b/app/src/main/res/values-su/strings.xml @@ -17,11 +17,7 @@ Ngamuat… Téang… Parsing pakét gagal - Aplikasi Eusi kosong - Idin - Tolak - Idinkeun Heug Bolay Pamasangan réngsé diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 27e28c86..58feaad5 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -38,12 +38,6 @@ 服务不可用 安装包解析失败 - - 应用 - 权限 - 拒绝 - 允许 - 确定 取消 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 22ce254a..00efd3df 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,12 +38,6 @@ Service is not available Package parsing failed - - Application - Permissions - Deny - Grant - OK Cancel From 54af1805a70a9659ef50f162f6e09fd6701da305 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 28 Jul 2024 13:31:34 +0800 Subject: [PATCH 21/34] 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 cf67be50..da6af087 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -31,6 +31,7 @@ android { "pt", "pt-rBR", "ru", + "su", "vi", "zh-rCN" ) diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml index feb9ec9f..46db803b 100644 --- a/app/src/main/res/xml/locales_config.xml +++ b/app/src/main/res/xml/locales_config.xml @@ -9,6 +9,7 @@ + \ No newline at end of file From c7f817c2425ffb8a46a15f5f2fffce3477d8d376 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 28 Jul 2024 14:30:15 +0800 Subject: [PATCH 22/34] Fix `AppItem` --- .../pi/ui/screens/apps/component/AppItem.kt | 2 +- .../pi/ui/screens/apps/component/AppList.kt | 20 +++++++++---------- 2 files changed, 10 insertions(+), 12 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 7e338aa9..f39a34a7 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 @@ -32,7 +32,7 @@ fun AppItem( ) = Row( modifier = Modifier .clickable(onClick = onClick) - .padding(all = 10.dp) + .padding(all = 15.dp) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp) 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 3f8e1f51..8bb1b1a8 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 @@ -30,6 +30,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import dev.sanmer.pi.BuildConfig import dev.sanmer.pi.R import dev.sanmer.pi.model.IPackageInfo import dev.sanmer.pi.ui.component.MenuChip @@ -62,7 +63,7 @@ private fun AppItem( pi: IPackageInfo, buildSettings: (IPackageInfo) -> AppsViewModel.Settings ) { - var expend by rememberSaveable { mutableStateOf(false) } + var expend by rememberSaveable(pi) { mutableStateOf(false) } val degrees by animateFloatAsState( targetValue = if (expend) 90f else 0f, label = "AppItem Icon" @@ -82,15 +83,13 @@ private fun AppItem( if (expend) SettingItem( pi = pi, - settings = buildSettings(pi), - onClose = { expend = false } + settings = buildSettings(pi) ) } @OptIn(ExperimentalLayoutApi::class) @Composable private fun SettingItem( - onClose: () -> Unit, pi: IPackageInfo, settings: AppsViewModel.Settings ) = Box( @@ -105,7 +104,7 @@ private fun SettingItem( FlowRow( modifier = Modifier .fillMaxWidth() - .padding(all = 10.dp), + .padding(all = 15.dp), horizontalArrangement = Arrangement.spacedBy(10.dp), verticalArrangement = Arrangement.spacedBy(10.dp) ) { @@ -114,10 +113,10 @@ private fun SettingItem( MenuChip( selected = pi.isAuthorized, + enabled = pi.packageName != BuildConfig.APPLICATION_ID, onClick = { scope.launch { settings.setAuthorized() - onClose() } }, label = { Text(text = stringResource(id = R.string.app_authorized)) }, @@ -125,6 +124,7 @@ private fun SettingItem( MenuChip( selected = pi.isRequester, + enabled = !pi.isRequester, onClick = { scope.launch { settings.setRequester() @@ -135,22 +135,20 @@ private fun SettingItem( MenuChip( selected = pi.isExecutor, + enabled = !pi.isExecutor, onClick = { scope.launch { settings.setExecutor() - onClose() } }, - label = { Text(text = stringResource(id = R.string.app_executor)) }, + label = { Text(text = stringResource(id = R.string.app_executor)) } ) MenuChip( selected = false, onClick = { scope.launch { - if (settings.export(context)) { - onClose() - } + settings.export(context) } }, label = { Text(text = stringResource(id = R.string.app_export)) }, From 2e4fba77103dc7f34a6a3b38698208c6273a8f42 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 28 Jul 2024 14:47:40 +0800 Subject: [PATCH 23/34] Add exported message --- .../sanmer/pi/ui/provider/LocalSnackbarHostState.kt | 6 ++++++ .../dev/sanmer/pi/ui/screens/apps/AppsScreen.kt | 12 ++++++++++++ .../sanmer/pi/ui/screens/apps/component/AppList.kt | 11 ++++++++++- app/src/main/res/values-zh-rCN/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 5 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 app/src/main/kotlin/dev/sanmer/pi/ui/provider/LocalSnackbarHostState.kt diff --git a/app/src/main/kotlin/dev/sanmer/pi/ui/provider/LocalSnackbarHostState.kt b/app/src/main/kotlin/dev/sanmer/pi/ui/provider/LocalSnackbarHostState.kt new file mode 100644 index 00000000..e6e62a05 --- /dev/null +++ b/app/src/main/kotlin/dev/sanmer/pi/ui/provider/LocalSnackbarHostState.kt @@ -0,0 +1,6 @@ +package dev.sanmer.pi.ui.provider + +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.staticCompositionLocalOf + +val LocalSnackbarHostState = staticCompositionLocalOf { SnackbarHostState() } \ No newline at end of file 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 699e4451..8be1a84f 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 @@ -8,7 +8,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.Snackbar +import androidx.compose.material3.SnackbarHost import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior @@ -32,6 +35,7 @@ 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.main.Screen +import dev.sanmer.pi.ui.provider.LocalSnackbarHostState import dev.sanmer.pi.ui.screens.apps.component.AppList import dev.sanmer.pi.viewmodel.AppsViewModel @@ -64,6 +68,14 @@ fun AppsScreen( navController = navController, scrollBehavior = scrollBehavior ) + }, + snackbarHost = { + SnackbarHost(hostState = LocalSnackbarHostState.current) { + Snackbar( + snackbarData = it, + shape = MaterialTheme.shapes.medium + ) + } } ) { contentPadding -> Box( 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 8bb1b1a8..de905216 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 @@ -34,6 +34,7 @@ import dev.sanmer.pi.BuildConfig import dev.sanmer.pi.R import dev.sanmer.pi.model.IPackageInfo import dev.sanmer.pi.ui.component.MenuChip +import dev.sanmer.pi.ui.provider.LocalSnackbarHostState import dev.sanmer.pi.viewmodel.AppsViewModel import kotlinx.coroutines.launch @@ -101,6 +102,8 @@ private fun SettingItem( shape = MaterialTheme.shapes.medium ) ) { + val snackbarHostState = LocalSnackbarHostState.current + FlowRow( modifier = Modifier .fillMaxWidth() @@ -148,7 +151,13 @@ private fun SettingItem( selected = false, onClick = { scope.launch { - settings.export(context) + if (settings.export(context)) { + snackbarHostState.showSnackbar( + message = context.getString( + R.string.app_export_msg, "Download/PI" + ) + ) + } } }, label = { Text(text = stringResource(id = R.string.app_export)) }, diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 58feaad5..956330d3 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -13,6 +13,7 @@ 请求者 执行者 导出 + 已导出至 %1$s 设置 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 00efd3df..7f6f605a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ Requester Executor Export + Exported to %1$s Settings From ec5853e5bb84e286b7269651202d8c62ead9ff41 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 28 Jul 2024 18:45:12 +0800 Subject: [PATCH 24/34] Tidy up dependencies --- app/build.gradle.kts | 7 ++-- gradle/libs.versions.toml | 73 ++++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 39 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index da6af087..d084316b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -97,14 +97,15 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.androidx.appcompat) implementation(libs.androidx.compose.ui.util) - implementation(libs.androidx.core.ktx) + implementation(libs.androidx.core) implementation(libs.androidx.core.splashscreen) implementation(libs.androidx.datastore.core) implementation(libs.androidx.documentfile) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.lifecycle.service) - implementation(libs.androidx.lifecycle.viewModel.compose) + implementation(libs.androidx.lifecycle.viewmodel) + implementation(libs.androidx.lifecycle.viewmodel.savedstate) implementation(libs.androidx.navigation.compose) implementation(libs.appiconloader) implementation(libs.appiconloader.coil) @@ -114,4 +115,4 @@ dependencies { implementation(libs.kotlinx.datetime) implementation(libs.kotlinx.serialization.protobuf) implementation(libs.timber) -} \ No newline at end of file +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5d792f31..0891a988 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,43 +25,44 @@ ksp = "2.0.0-1.0.23" timber = "5.0.1" [libraries] -android-gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } -compose-gradle = { group = "org.jetbrains.kotlin", name = "compose-compiler-gradle-plugin", version.ref = "kotlin" } -kotlin-gradle = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } -ksp-gradle = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } +android-gradle = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" } +compose-gradle = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" } +kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +ksp-gradle = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } -androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } -androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidxAnnotation" } -androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } -androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidxComposeMaterial3" } -androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "androidxCompose" } -androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "androidxCompose" } -androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "androidxCompose" } -androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util", version.ref = "androidxCompose" } -androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" } -androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" } -androidx-datastore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" } -androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "androidxDocumentFile" } -androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } -androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } -androidx-lifecycle-service = { group = "androidx.lifecycle", name = "lifecycle-service", version.ref = "androidxLifecycle" } -androidx-lifecycle-viewModel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } -androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" } -androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "androidxRoom" } -androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "androidxRoom" } -androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "androidxRoom" } -appiconloader = { group = "me.zhanghai.android.appiconloader", name = "appiconloader", version.ref = "appiconloader" } -appiconloader-coil = { group = "me.zhanghai.android.appiconloader", name = "appiconloader-coil", version.ref = "appiconloader" } -coil-kt = { group = "io.coil-kt", name = "coil", version.ref = "coil" } -coil-kt-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" } -hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } -hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } -kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } -kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } -kotlinx-serialization-protobuf = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-protobuf", version.ref = "kotlinxSerialization" } -rikka-refine-annotation = { group = "dev.rikka.tools.refine", name = "annotation", version.ref = "hiddenApiRefine" } -rikka-refine-compiler = { group = "dev.rikka.tools.refine", name = "annotation-processor", version.ref = "hiddenApiRefine" } -rikka-refine-runtime = { group = "dev.rikka.tools.refine", name = "runtime", version.ref = "hiddenApiRefine" } +androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivity" } +androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidxAnnotation" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidxAppCompat" } +androidx-compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "androidxComposeMaterial3" } +androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidxCompose" } +androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "androidxCompose" } +androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "androidxCompose" } +androidx-compose-ui-util = { module = "androidx.compose.ui:ui-util", version.ref = "androidxCompose" } +androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidxCore" } +androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidxCoreSplashscreen" } +androidx-datastore-core = { module = "androidx.datastore:datastore", version.ref = "androidxDataStore" } +androidx-documentfile = { module = "androidx.documentfile:documentfile", version.ref = "androidxDocumentFile" } +androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } +androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxLifecycle" } +androidx-lifecycle-service = { module = "androidx.lifecycle:lifecycle-service", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "androidxLifecycle" } +androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidxNavigation" } +androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "androidxRoom" } +androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidxRoom" } +androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "androidxRoom" } +appiconloader = { module = "me.zhanghai.android.appiconloader:appiconloader", version.ref = "appiconloader" } +appiconloader-coil = { module = "me.zhanghai.android.appiconloader:appiconloader-coil", version.ref = "appiconloader" } +coil-kt = { module = "io.coil-kt:coil", version.ref = "coil" } +coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } +hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } +hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } +kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } +kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" } +kotlinx-serialization-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "kotlinxSerialization" } +rikka-refine-annotation = { module = "dev.rikka.tools.refine:annotation", version.ref = "hiddenApiRefine" } +rikka-refine-compiler = { module = "dev.rikka.tools.refine:annotation-processor", version.ref = "hiddenApiRefine" } +rikka-refine-runtime = { module = "dev.rikka.tools.refine:runtime", version.ref = "hiddenApiRefine" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } sanmer-su = { module = "dev.sanmer.su:core", version = "+" } From e49b1eb41bb8fe0c1ae2a9e7326e02491d0fd011 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Mon, 29 Jul 2024 00:27:54 +0800 Subject: [PATCH 25/34] Optimize `Session.writeApk` --- .../dev/sanmer/pi/delegate/PackageInstallerDelegate.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/dev/sanmer/pi/delegate/PackageInstallerDelegate.kt b/core/src/main/kotlin/dev/sanmer/pi/delegate/PackageInstallerDelegate.kt index 898bd2c6..2b36a567 100644 --- a/core/src/main/kotlin/dev/sanmer/pi/delegate/PackageInstallerDelegate.kt +++ b/core/src/main/kotlin/dev/sanmer/pi/delegate/PackageInstallerDelegate.kt @@ -18,6 +18,8 @@ import dev.sanmer.pi.IntentReceiverCompat import dev.sanmer.su.IServiceManager import dev.sanmer.su.ServiceManagerCompat.getSystemService import dev.sanmer.su.ServiceManagerCompat.proxyBy +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import java.io.File class PackageInstallerDelegate( @@ -183,7 +185,7 @@ class PackageInstallerDelegate( commit(sender) } - fun PackageInstaller.Session.writeApk(path: File) { + suspend fun PackageInstaller.Session.writeApk(path: File) = withContext(Dispatchers.IO) { openWrite(path.name, 0, path.length()).use { output -> path.inputStream().buffered().use { input -> input.copyTo(output) @@ -192,7 +194,7 @@ class PackageInstallerDelegate( } } - fun PackageInstaller.Session.writeApks(path: File, filenames: List) { + suspend fun PackageInstaller.Session.writeApks(path: File, filenames: List) { filenames.forEach { name -> val file = File(path, name) if (file.exists()) writeApk(file) From b5966763133f7e8ddb2cf4151516f5551eee288f Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Mon, 29 Jul 2024 00:28:17 +0800 Subject: [PATCH 26/34] Remove unused code --- .../dev/sanmer/pi/compat/MediaStoreCompat.kt | 30 ++----------------- .../dev/sanmer/pi/viewmodel/AppsViewModel.kt | 11 +++---- 2 files changed, 8 insertions(+), 33 deletions(-) diff --git a/app/src/main/kotlin/dev/sanmer/pi/compat/MediaStoreCompat.kt b/app/src/main/kotlin/dev/sanmer/pi/compat/MediaStoreCompat.kt index 0ccbef8c..e4dd2119 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/compat/MediaStoreCompat.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/compat/MediaStoreCompat.kt @@ -4,15 +4,12 @@ import android.content.ContentResolver import android.content.ContentValues import android.content.Context import android.net.Uri -import android.os.Environment import android.provider.DocumentsContract import android.provider.MediaStore import android.system.Os import androidx.core.net.toFile -import androidx.core.net.toUri import androidx.documentfile.provider.DocumentFile import java.io.File -import java.io.IOException object MediaStoreCompat { fun Context.createMediaStoreUri( @@ -26,29 +23,7 @@ object MediaStoreCompat { put(MediaStore.MediaColumns.MIME_TYPE, mimeType) } - return contentResolver.insert(collection, entry) ?: throw IOException("Cannot insert $file") - } - - private fun createDownloadUri( - path: String - ) = Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DOWNLOADS - ).let { - val file = File(it, path) - file.parentFile?.apply { if (!exists()) mkdirs() } - file.toUri() - } - - fun Context.createDownloadUri( - path: String, - mimeType: String - ) = runCatching { - createMediaStoreUri( - file = File(Environment.DIRECTORY_DOWNLOADS, path), - mimeType = mimeType - ) - }.getOrElse { - createDownloadUri(path) + return requireNotNull(contentResolver.insert(collection, entry)) } private fun ContentResolver.queryString(uri: Uri, columnName: String): String? { @@ -111,8 +86,7 @@ object MediaStoreCompat { require(uri.scheme == "content") { "Uri lacks 'content' scheme: $uri" } contentResolver.openFileDescriptor( - getDocumentUri(this, uri), - "r" + getDocumentUri(this, uri), "r" )?.use { return Os.readlink("/proc/self/fd/${it.fd}") } 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 07ce3c9f..7f85d0e7 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/viewmodel/AppsViewModel.kt @@ -3,6 +3,7 @@ package dev.sanmer.pi.viewmodel import android.content.Context import android.content.pm.PackageInfo import android.content.pm.PackageManager +import android.os.Environment import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -12,7 +13,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import dev.sanmer.pi.Compat import dev.sanmer.pi.PackageInfoCompat.isOverlayPackage import dev.sanmer.pi.UserHandleCompat -import dev.sanmer.pi.compat.MediaStoreCompat.createDownloadUri +import dev.sanmer.pi.compat.MediaStoreCompat.createMediaStoreUri import dev.sanmer.pi.delegate.AppOpsManagerDelegate import dev.sanmer.pi.delegate.AppOpsManagerDelegate.Mode.Companion.isAllowed import dev.sanmer.pi.model.IPackageInfo @@ -225,8 +226,8 @@ class AppsViewModel @Inject constructor( file: File, path: String, ) = withContext(Dispatchers.IO) { - val uri = createDownloadUri( - path = path, + val uri = createMediaStoreUri( + file = File(Environment.DIRECTORY_DOWNLOADS, path), mimeType = "android/vnd.android.package-archive" ) @@ -242,8 +243,8 @@ class AppsViewModel @Inject constructor( files: List, path: String, ) = withContext(Dispatchers.IO) { - val uri = createDownloadUri( - path = path, + val uri = createMediaStoreUri( + file = File(Environment.DIRECTORY_DOWNLOADS, path), mimeType = "android/zip" ) From d4cba6fce42d73ac786644d86a4d4c25b057b5ef Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Mon, 29 Jul 2024 00:29:47 +0800 Subject: [PATCH 27/34] Optimize `InstallService` --- app/src/main/AndroidManifest.xml | 4 +- app/src/main/kotlin/dev/sanmer/pi/Const.kt | 3 - .../dev/sanmer/pi/service/InstallService.kt | 210 ++++++++++-------- 3 files changed, 117 insertions(+), 100 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ba08b28b..906719a4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ - + diff --git a/app/src/main/kotlin/dev/sanmer/pi/Const.kt b/app/src/main/kotlin/dev/sanmer/pi/Const.kt index 7bcedac6..d6c9db8d 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/Const.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/Const.kt @@ -1,11 +1,8 @@ package dev.sanmer.pi object Const { - // 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/service/InstallService.kt b/app/src/main/kotlin/dev/sanmer/pi/service/InstallService.kt index 21b26e44..4dad7fd5 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/service/InstallService.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/service/InstallService.kt @@ -9,6 +9,7 @@ import android.content.Intent import android.content.pm.PackageInfo import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller.SessionInfo +import android.content.pm.ServiceInfo import android.graphics.Bitmap import android.os.Process import androidx.core.app.NotificationCompat @@ -32,33 +33,43 @@ import dev.sanmer.pi.ktx.parcelable import dev.sanmer.pi.ktx.tmpDir import dev.sanmer.pi.repository.UserPreferencesRepository import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import me.zhanghai.android.appiconloader.AppIconLoader import timber.log.Timber import java.io.File import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds @AndroidEntryPoint class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallback { @Inject lateinit var userPreferencesRepository: UserPreferencesRepository - private val appIconLoader by lazy { - AppIconLoader(45.dp, true, this) - } + private val appIconLoader by lazy { AppIconLoader(45.dp, true, this) } + private val notificationManager by lazy { NotificationManagerCompat.from(this) } private val pm by lazy { Compat.getPackageManager() } private val pi by lazy { Compat.getPackageInstaller() } - private val tasks = mutableListOf() + init { + lifecycleScope.launch { + while (currentCoroutineContext().isActive) { + delay(5.seconds) + if (pendingTask.isEmpty()) stopSelf() + } + } + } override fun onCreated(sessionId: Int) { val session = pi.getSessionInfo(sessionId) Timber.i("onCreated<$sessionId>: ${session?.appPackageName}") - tasks.add(sessionId) - onProgressChanged( + notifyProgress( id = sessionId, appLabel = session?.label ?: sessionId.toString(), appIcon = session?.appIcon, @@ -69,7 +80,7 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb override fun onProgressChanged(sessionId: Int, progress: Float) { val session = pi.getSessionInfo(sessionId) - onProgressChanged( + notifyProgress( id = sessionId, appLabel = session?.label ?: sessionId.toString(), appIcon = session?.appIcon, @@ -77,15 +88,6 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb ) } - override fun onFinished(sessionId: Int, success: Boolean) { - Timber.i("onFinished<$sessionId>: success = $success") - tasks.remove(sessionId) - - if (tasks.isEmpty()) { - stopSelf() - } - } - private val SessionInfo.label get() = appLabel ?: appPackageName ?: sessionId.toString() @@ -107,69 +109,79 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + val sticky = super.onStartCommand(intent, flags, startId) + val archivePath = intent?.archivePathOrNull ?: return sticky + val archiveInfo = intent.archiveInfoOrNull ?: return sticky + val filenames = intent.filenames + lifecycleScope.launch(Dispatchers.IO) { - val archivePath = intent?.archivePathOrNull ?: return@launch - val archiveInfo = intent.archiveInfoOrNull ?: return@launch - val filenames = intent.filenames - - val appIcon = archiveInfo.applicationInfo?.let(appIconLoader::loadIcon) - val appLabel = archiveInfo.applicationInfo?.loadLabel(packageManager) - ?: archiveInfo.packageName - - val userPreferences = userPreferencesRepository.data.first() - val originatingUid = getPackageUid(userPreferences.requester) - pi.setInstallerPackageName(userPreferences.executor) - - val params = createSessionParams() - params.setAppIcon(appIcon) - params.setAppLabel(appLabel) - params.setAppPackageName(archiveInfo.packageName) - if (originatingUid != Process.INVALID_UID) { - params.setOriginatingUid(originatingUid) - } + install(archivePath, archiveInfo, filenames) + pendingTask.removeAt(0) + } - val sessionId = pi.createSession(params) - val session = pi.openSession(sessionId) + return sticky + } - when { - archivePath.isDirectory -> { - session.writeApks(archivePath, filenames) - } + private suspend fun install( + archivePath: File, + archiveInfo: PackageInfo, + filenames: List + ) = withContext(Dispatchers.IO) { + val appIcon = archiveInfo.applicationInfo?.let(appIconLoader::loadIcon) + val appLabel = archiveInfo.applicationInfo?.loadLabel(packageManager) + ?: archiveInfo.packageName + + val userPreferences = userPreferencesRepository.data.first() + val originatingUid = getPackageUid(userPreferences.requester) + pi.setInstallerPackageName(userPreferences.executor) + + val params = createSessionParams() + params.setAppIcon(appIcon) + params.setAppLabel(appLabel) + params.setAppPackageName(archiveInfo.packageName) + if (originatingUid != Process.INVALID_UID) { + params.setOriginatingUid(originatingUid) + } - archivePath.isFile -> { - session.writeApk(archivePath) - } - } + val sessionId = pi.createSession(params) + val session = pi.openSession(sessionId) - val result = session.commit() - val status = result.getIntExtra( - PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE - ) + when { + archivePath.isDirectory -> { + session.writeApks(archivePath, filenames) + } - when (status) { - PackageInstaller.STATUS_SUCCESS -> { - onInstallSucceeded( - id = sessionId, - appLabel = appLabel, - appIcon = appIcon, - packageName = archiveInfo.packageName - ) - } - - else -> { - val msg = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) - Timber.e("onFailed<${archiveInfo.packageName}>: $msg") - onInstallFailed( - id = sessionId, - appLabel = appLabel, - appIcon = appIcon, - ) - } + archivePath.isFile -> { + session.writeApk(archivePath) } } - return super.onStartCommand(intent, flags, startId) + val result = session.commit() + val status = result.getIntExtra( + PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE + ) + + when (status) { + PackageInstaller.STATUS_SUCCESS -> { + notifySuccess( + id = sessionId, + appLabel = appLabel, + appIcon = appIcon, + packageName = archiveInfo.packageName + ) + } + + else -> { + val msg = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + Timber.e("onFailed<${archiveInfo.packageName}>: $msg") + notifyFailure( + id = sessionId, + appLabel = appLabel, + appIcon = appIcon, + ) + } + } } private fun createSessionParams(): PackageInstaller.SessionParams { @@ -200,13 +212,30 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb Process.INVALID_UID ) - private fun onProgressChanged( + private fun setForeground() { + val notification = newNotificationBuilder() + .setContentTitle(getText(R.string.notification_name_install)) + .setSilent(true) + .setOngoing(true) + .setGroup(GROUP_KEY) + .setGroupSummary(true) + .build() + + ServiceCompat.startForeground( + this, + notification.hashCode(), + notification, + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + ) + } + + private fun notifyProgress( id: Int, appLabel: CharSequence, appIcon: Bitmap?, progress: Float ) { - val notification = baseNotificationBuilder() + val notification = newNotificationBuilder() .setLargeIcon(appIcon) .setContentTitle(appLabel) .setProgress(100, (100 * progress).toInt(), false) @@ -218,7 +247,7 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb notify(id, notification) } - private fun onInstallSucceeded( + private fun notifySuccess( id: Int, appLabel: CharSequence, appIcon: Bitmap?, @@ -230,10 +259,10 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb ) } - val notification = baseNotificationBuilder() + val notification = newNotificationBuilder() .setLargeIcon(appIcon) .setContentTitle(appLabel) - .setContentText(getString(R.string.message_install_success)) + .setContentText(getText(R.string.message_install_success)) .setContentIntent(intent) .setSilent(true) .setAutoCancel(true) @@ -242,34 +271,22 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb notify(id, notification) } - private fun onInstallFailed( + private fun notifyFailure( id: Int, appLabel: CharSequence, appIcon: Bitmap? ) { - val notification = baseNotificationBuilder() + val notification = newNotificationBuilder() .setLargeIcon(appIcon) .setContentTitle(appLabel) - .setContentText(getString(R.string.message_install_fail)) + .setContentText(getText(R.string.message_install_fail)) .build() notify(id, notification) } - private fun setForeground() { - val notification = baseNotificationBuilder() - .setContentTitle(getString(R.string.notification_name_install)) - .setSilent(true) - .setOngoing(true) - .setGroup(GROUP_KEY) - .setGroupSummary(true) - .build() - - startForeground(Const.NOTIFICATION_ID_INSTALL, notification) - } - - private fun baseNotificationBuilder() = - NotificationCompat.Builder(this, Const.CHANNEL_ID_INSTALL) + private fun newNotificationBuilder() = + NotificationCompat.Builder(applicationContext, Const.CHANNEL_ID_INSTALL) .setSmallIcon(R.drawable.launcher_outline) @SuppressLint("MissingPermission") @@ -280,13 +297,13 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb true } - NotificationManagerCompat.from(this).apply { - if (granted) notify(id, notification) + if (granted) { + notificationManager.notify(id, notification) } } companion object { - private const val GROUP_KEY = "INSTALL_SERVICE_GROUP_KEY" + private const val GROUP_KEY = "dev.sanmer.pi.INSTALL_SERVICE_GROUP_KEY" private const val EXTRA_ARCHIVE_PATH = "dev.sanmer.pi.extra.ARCHIVE_PATH" private val Intent.archivePathOrNull: File? @@ -300,6 +317,8 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb private val Intent.filenames: List get() = getStringArrayExtra(EXTRA_ARCHIVE_FILENAMES)?.toList() ?: emptyList() + private val pendingTask = mutableListOf() + fun start( context: Context, archivePath: File, @@ -311,6 +330,7 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb intent.putExtra(EXTRA_ARCHIVE_INFO, archiveInfo) intent.putExtra(EXTRA_ARCHIVE_FILENAMES, filenames.toTypedArray()) + pendingTask.add(archiveInfo.packageName) context.startService(intent) } } From 8166e53bd7ea2aefadebcb632aaa9a6926d7ee8b Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Mon, 29 Jul 2024 21:08:20 +0800 Subject: [PATCH 28/34] Optimize `Screen` --- .../dev/sanmer/pi/ui/main/MainScreen.kt | 88 +++++++++---------- .../sanmer/pi/ui/screens/apps/AppsScreen.kt | 2 +- .../pi/ui/screens/settings/SettingsScreen.kt | 2 +- 3 files changed, 44 insertions(+), 48 deletions(-) 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 20dc2fb2..c924ed6a 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,18 +1,21 @@ package dev.sanmer.pi.ui.main +import androidx.compose.animation.AnimatedContentScope +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.navigation.NamedNavArgument +import androidx.navigation.NavBackStackEntry 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.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 @@ -26,56 +29,49 @@ fun MainScreen() { ) { NavHost( navController = navController, - startDestination = Screen.Apps.route + startDestination = Screen.Apps() ) { - apps(navController) - settings(navController) - workingMode(navController) + Screen.Apps(navController).addTo(this) + Screen.Settings(navController).addTo(this) + Screen.WorkingMode(navController).addTo(this) } } } -enum class Screen(val route: String) { - Apps("Apps"), - Settings("Settings"), - WorkingMode("WorkingMode"); +sealed class Screen( + private val route: String, + private val content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit, + private val arguments: List = emptyList(), + private val enterTransition: (AnimatedContentTransitionScope.() -> EnterTransition) = { fadeIn() }, + private val exitTransition: (AnimatedContentTransitionScope.() -> ExitTransition) = { fadeOut() }, +) { + fun addTo(builder: NavGraphBuilder) = builder.composable( + route = this@Screen.route, + arguments = this@Screen.arguments, + enterTransition = this@Screen.enterTransition, + exitTransition = this@Screen.exitTransition, + content = this@Screen.content + ) - companion object { + @Suppress("FunctionName") + companion object Routes { + fun Apps() = "Apps" + fun Settings() = "Settings" + fun WorkingMode() = "WorkingMode" + } - fun NavGraphBuilder.apps( - navController: NavController - ) = composable( - route = Apps.route, - enterTransition = { fadeIn() }, - exitTransition = { fadeOut() } - ) { - AppsScreen( - navController = navController - ) - } + class Apps(navController: NavController) : Screen( + route = Apps(), + content = { AppsScreen(navController = navController) } + ) - fun NavGraphBuilder.settings( - navController: NavController - ) = composable( - route = Settings.route, - enterTransition = { fadeIn() }, - exitTransition = { fadeOut() } - ) { - SettingsScreen( - navController = navController - ) - } + class Settings(navController: NavController) : Screen( + route = Settings(), + content = { SettingsScreen(navController = navController) } + ) - fun NavGraphBuilder.workingMode( - navController: NavController - ) = composable( - route = WorkingMode.route, - enterTransition = { fadeIn() }, - exitTransition = { fadeOut() } - ) { - WorkingModeScreen( - navController = navController - ) - } - } + class WorkingMode(navController: NavController) : Screen( + route = WorkingMode(), + content = { WorkingModeScreen(navController = navController) } + ) } \ No newline at end of file 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 8be1a84f..5ec41176 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 @@ -146,7 +146,7 @@ private fun TopBar( } IconButton( - onClick = { navController.navigateSingleTopTo(Screen.Settings.route) } + onClick = { navController.navigateSingleTopTo(Screen.Settings()) } ) { Icon( painter = painterResource(id = R.drawable.settings), 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 16edd10e..618c82da 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 @@ -73,7 +73,7 @@ fun SettingsScreen( } ), onClick = { - navController.navigateSingleTopTo(Screen.WorkingMode.route) + navController.navigateSingleTopTo(Screen.WorkingMode()) } ) From f446da288324c11d3a354c8a35befae58174499b Mon Sep 17 00:00:00 2001 From: Sanmer Bot Date: Wed, 31 Jul 2024 01:17:29 +0800 Subject: [PATCH 29/34] Translations update from Weblate - SanmerApps (#129) * Translated using Weblate (Portuguese) Currently translated at 100.0% (38 of 38 strings) Translation: PI/App Translate-URL: https://weblate.sanmer.app/projects/pi/app/pt/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (38 of 38 strings) Translation: PI/App Translate-URL: https://weblate.sanmer.app/projects/pi/app/pt_BR/ * Translated using Weblate (French) Currently translated at 89.4% (34 of 38 strings) Translation: PI/App Translate-URL: https://weblate.sanmer.app/projects/pi/app/fr/ --------- Co-authored-by: igor Co-authored-by: Matheotaku --- app/src/main/res/values-fr/strings.xml | 3 ++- app/src/main/res/values-pt-rBR/strings.xml | 2 ++ app/src/main/res/values-pt/strings.xml | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 031fa2d7..402d806d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -3,7 +3,7 @@ Installer Mode de travail Nécessite l\'autorisation fournie par Sui ou Shizuku - Nécessite les permissions Root fournies par Magisk ou KernelSU + Nécessite les permissions Root fournies par Magisk, KernelSU ou APatch Service est en cours d\'exécution Le service n\'est pas en cours d\'exécution Version %1$d, %2$s @@ -33,4 +33,5 @@ Non précisé Installer Paramètres + Exporter \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index a3f8a479..1632ad43 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -36,4 +36,6 @@ Autorizado Solicitante Executor + Exportar + Exportado para %1$s \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index e32823aa..eb332a4d 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -36,4 +36,6 @@ Autorizado Solicitante Executor + Exportar + Exportado para %1$s \ No newline at end of file From 122bb2d69a6682e88f0edc299c7cfd0783372465 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 10:54:01 +0800 Subject: [PATCH 30/34] Bump the kotlin-ksp group with 2 updates (#130) --- 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 0891a988..d71a00bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ kotlin = "2.0.0" kotlinxCoroutines = "1.8.1" kotlinxDatetime = "0.6.0" kotlinxSerialization = "1.7.1" -ksp = "2.0.0-1.0.23" +ksp = "2.0.0-1.0.24" timber = "5.0.1" [libraries] From 04a3e66575e4cca9931e13aca7d25813aeed0df1 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Wed, 31 Jul 2024 22:36:41 +0800 Subject: [PATCH 31/34] Optimize `BottomSheetLayout` --- .../dev/sanmer/pi/ui/component/BottomSheetLayout.kt | 8 ++++++++ 1 file changed, 8 insertions(+) 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 99d510d9..c616ac4b 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 @@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.widthIn import androidx.compose.material3.BottomSheetDefaults import androidx.compose.material3.LocalContentColor import androidx.compose.material3.contentColorFor @@ -17,11 +19,13 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.layout.SubcomposeLayout +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastMap @@ -32,6 +36,7 @@ fun BottomSheetLayout( modifier: Modifier = Modifier, containerColor: Color = BottomSheetDefaults.ContainerColor, contentColor: Color = contentColorFor(containerColor), + sheetMaxWidth: Dp = BottomSheetDefaults.SheetMaxWidth, shape: Shape = BottomSheetDefaults.ExpandedShape, scrimColor: Color = BottomSheetDefaults.ScrimColor, contentWindowInsets: WindowInsets = BottomSheetDefaults.windowInsets, @@ -41,12 +46,15 @@ fun BottomSheetLayout( modifier = modifier .background(scrimColor) .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Bottom ) { var edgeToTop by remember { mutableStateOf(false) } SubcomposeLayout( modifier = Modifier + .widthIn(max = sheetMaxWidth) + .fillMaxWidth() .background( color = containerColor, shape = shape From 9f2c42641ea1782975137bd97dc75428663d9d6f Mon Sep 17 00:00:00 2001 From: Sanmer Date: Thu, 1 Aug 2024 23:15:29 +0800 Subject: [PATCH 32/34] [skip ci] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 1b00f3b6..b6554457 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # PI [![release](https://img.shields.io/github/v/release/SanmerApps/PI?label=release&color=red)](https://github.com/SanmerApps/PI/releases) [![download](https://shields.io/github/downloads/SanmerApps/PI/total?label=download)](https://github.com/SanmerApps/PI/releases/latest) [![translated](https://weblate.sanmer.app/widgets/pi/-/app/svg-badge.svg)](https://weblate.sanmer.app/engage/pi/) -PI is short for `Package Installer`. - ## Supported Versions Android 11 ~ 15 From b5ea6e2910aed2836fe90ffcc5308798a1a290c7 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Fri, 2 Aug 2024 14:10:18 +0800 Subject: [PATCH 33/34] Tidy up res --- app/src/main/kotlin/dev/sanmer/pi/App.kt | 2 +- .../dev/sanmer/pi/service/InstallService.kt | 2 +- .../sanmer/pi/ui/screens/apps/AppsScreen.kt | 2 +- .../pi/ui/screens/apps/component/AppItem.kt | 6 +-- .../pi/ui/screens/apps/component/AppList.kt | 22 +++++----- app/src/main/res/drawable/cube_plus.xml | 42 ------------------- app/src/main/res/drawable/file_import.xml | 18 ++++++++ app/src/main/res/drawable/package_import.xml | 42 ------------------- app/src/main/res/drawable/player_play.xml | 12 ++++++ app/src/main/res/drawable/settings.xml | 18 -------- app/src/main/res/drawable/settings_2.xml | 18 ++++++++ app/src/main/res/drawable/shield.xml | 12 ++++++ app/src/main/res/values-ar/strings.xml | 7 ++-- app/src/main/res/values-es/strings.xml | 7 ++-- app/src/main/res/values-fr/strings.xml | 5 +-- app/src/main/res/values-in/strings.xml | 7 ++-- app/src/main/res/values-iw/strings.xml | 7 ++-- app/src/main/res/values-pt-rBR/strings.xml | 7 ++-- app/src/main/res/values-pt/strings.xml | 7 ++-- app/src/main/res/values-ru/strings.xml | 5 +-- app/src/main/res/values-su/strings.xml | 7 ++-- app/src/main/res/values-vi/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 14 ++----- app/src/main/res/values/strings.xml | 14 ++----- .../res/values/strings_untranslatable.xml | 3 -- 25 files changed, 111 insertions(+), 177 deletions(-) delete mode 100644 app/src/main/res/drawable/cube_plus.xml create mode 100644 app/src/main/res/drawable/file_import.xml delete mode 100644 app/src/main/res/drawable/package_import.xml create mode 100644 app/src/main/res/drawable/player_play.xml delete mode 100644 app/src/main/res/drawable/settings.xml create mode 100644 app/src/main/res/drawable/settings_2.xml create mode 100644 app/src/main/res/drawable/shield.xml diff --git a/app/src/main/kotlin/dev/sanmer/pi/App.kt b/app/src/main/kotlin/dev/sanmer/pi/App.kt index 1c7be41a..f03203da 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/App.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/App.kt @@ -39,7 +39,7 @@ class App : Application(), ImageLoaderFactory { val channels = listOf( NotificationChannel( Const.CHANNEL_ID_INSTALL, - context.getString(R.string.notification_name_install), + context.getString(R.string.install_service), NotificationManager.IMPORTANCE_HIGH ) ) 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 4dad7fd5..deaca905 100644 --- a/app/src/main/kotlin/dev/sanmer/pi/service/InstallService.kt +++ b/app/src/main/kotlin/dev/sanmer/pi/service/InstallService.kt @@ -214,7 +214,7 @@ class InstallService : LifecycleService(), PackageInstallerDelegate.SessionCallb private fun setForeground() { val notification = newNotificationBuilder() - .setContentTitle(getText(R.string.notification_name_install)) + .setContentTitle(getText(R.string.install_service)) .setSilent(true) .setOngoing(true) .setGroup(GROUP_KEY) 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 5ec41176..0a8e55cb 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 @@ -149,7 +149,7 @@ private fun TopBar( onClick = { navController.navigateSingleTopTo(Screen.Settings()) } ) { Icon( - painter = painterResource(id = R.drawable.settings), + painter = painterResource(id = R.drawable.settings_2), contentDescription = null ) } 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 f39a34a7..b95926c2 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 @@ -77,9 +77,9 @@ fun AppItem( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp) ) { - if (pi.isRequester) Icon(R.drawable.cube_plus) - if (pi.isExecutor) Icon(R.drawable.code) - if (pi.isAuthorized) Icon(R.drawable.package_import) + if (pi.isRequester) Icon(R.drawable.file_import) + if (pi.isExecutor) Icon(R.drawable.player_play) + if (pi.isAuthorized) Icon(R.drawable.shield) } } 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 de905216..dc988cb3 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 @@ -114,17 +114,6 @@ private fun SettingItem( val context = LocalContext.current val scope = rememberCoroutineScope() - MenuChip( - selected = pi.isAuthorized, - enabled = pi.packageName != BuildConfig.APPLICATION_ID, - onClick = { - scope.launch { - settings.setAuthorized() - } - }, - label = { Text(text = stringResource(id = R.string.app_authorized)) }, - ) - MenuChip( selected = pi.isRequester, enabled = !pi.isRequester, @@ -147,6 +136,17 @@ private fun SettingItem( label = { Text(text = stringResource(id = R.string.app_executor)) } ) + MenuChip( + selected = pi.isAuthorized, + enabled = pi.packageName != BuildConfig.APPLICATION_ID, + onClick = { + scope.launch { + settings.setAuthorized() + } + }, + label = { Text(text = stringResource(id = R.string.app_authorize)) }, + ) + MenuChip( selected = false, onClick = { diff --git a/app/src/main/res/drawable/cube_plus.xml b/app/src/main/res/drawable/cube_plus.xml deleted file mode 100644 index 88e6957d..00000000 --- a/app/src/main/res/drawable/cube_plus.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/file_import.xml b/app/src/main/res/drawable/file_import.xml new file mode 100644 index 00000000..1daefc48 --- /dev/null +++ b/app/src/main/res/drawable/file_import.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/package_import.xml b/app/src/main/res/drawable/package_import.xml deleted file mode 100644 index bf79916e..00000000 --- a/app/src/main/res/drawable/package_import.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/player_play.xml b/app/src/main/res/drawable/player_play.xml new file mode 100644 index 00000000..1788d56e --- /dev/null +++ b/app/src/main/res/drawable/player_play.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/settings.xml b/app/src/main/res/drawable/settings.xml deleted file mode 100644 index e66e7c0b..00000000 --- a/app/src/main/res/drawable/settings.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/settings_2.xml b/app/src/main/res/drawable/settings_2.xml new file mode 100644 index 00000000..3d3d11f7 --- /dev/null +++ b/app/src/main/res/drawable/settings_2.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/shield.xml b/app/src/main/res/drawable/shield.xml new file mode 100644 index 00000000..c6f03fca --- /dev/null +++ b/app/src/main/res/drawable/shield.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 02f466ed..1980f9c3 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -21,7 +21,7 @@ فشل التثبيت جاري التحميل… بحث… - تثبيت الخدمة + تثبيت الخدمة خطأ غير معروف ABI يتطلب صلاحيات الروت التي يوفرها Magisk أو KernelSU أو APatch @@ -29,11 +29,10 @@ اللغة إفتراضي تبع النظام رمز المصدر - ‍ حسناً - إلغاء + إلغاء فشل تحليل الحزمة الحزمة غير متاحة - مخول + مخول الطالب المنفذ \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 25f57b1d..c252ab9e 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -25,15 +25,14 @@ Error al analizar el paquete ABI Lista vacía - Instalar servicio + Instalar servicio Error desconocido - Ok - Cancelar + Cancelar Instalación correcta Instalación fallida Cargando… Buscar… - Autorizado + Autorizado Solicitante Ejecutor \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 402d806d..f52460f1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -7,7 +7,7 @@ Service est en cours d\'exécution Le service n\'est pas en cours d\'exécution Version %1$d, %2$s - Service d\'installation + Service d\'installation Erreur inconnue Cliquer pour essayer de démarrer Language @@ -18,8 +18,7 @@ Le service n\'est pas disponible Participer à la traduction Aidez nous a traduire PI dans vôtre langue - OK - Annuler + Annuler Installation réussie Échec de l\'installation Chargement… diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 83b4afb9..93502f2c 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -2,7 +2,7 @@ Setelan Mode kerja - Diotorisasi + Diotorisasi Eksekutor Layanan telah berjalan Versi %1$d, %2$s @@ -23,14 +23,13 @@ Pasang Layanan tidak tersedia Parsing paket gagal - Oke - Batal + Batal Pemasangan berhasil Pemasangan gagal Memuat… Cari… Daftar kosong - Layanan pemasangan + Layanan pemasangan Kesalahan tidak diketahui Pasang Memerlukan izin Root dari Magisk, KernelSU, atau APatch diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index a11dc040..3f6919e4 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -20,20 +20,19 @@ עזור לנו לתרגם את PI לשפה שלך קוד מקור שגיאה לא ידועה - התקנת שירות + התקנת שירות רשימה ריקה חיפוש… נטען… ההתקנה נכשלה ההתקנה בוצעה בהצלחה - ביטול - אישור + ביטול התקנה לא מוגדר שפה השירות אינו זמין ניתוח החבילה כשל - מורשה + מורשה מבצע מבקש \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 1632ad43..07ff587d 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -16,7 +16,7 @@ Falha na instalação Carregando… Pesquisar… - Instalar serviço + Instalar serviço Erro desconhecido Densidade da tela Recurso dinâmico @@ -28,12 +28,11 @@ Clique para tentar começar Idioma Código fonte - OK - Cancelar + Cancelar Padrão do sistema Falha na análise do pacote O serviço não está disponível - Autorizado + Autorizado Solicitante Executor Exportar diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index eb332a4d..22ac9381 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -16,7 +16,7 @@ Necessita de permissão concedida por Sui ou Shizuku Definições Instalação bem-sucedida - Instalar serviço + Instalar serviço Erro desconhecido Recurso dinâmico ABI @@ -29,11 +29,10 @@ Idioma Padrão do sistema Código fonte - OK - Cancelar + Cancelar O serviço não está disponível Falha na análise do pacote - Autorizado + Autorizado Solicitante Executor Exportar diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 59ce2a52..98d92bd5 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -9,14 +9,13 @@ Требуется разрешение Sui или Shizuku Плотность экрана Язык - ОК Установка прервана Пустой список Неизвестная ошибка Динамическая функция Неопределённый Установка завершена - Сервис установки + Сервис установки Нажмите, чтобы попытаться начать Язык Системный по умолчанию @@ -31,6 +30,6 @@ Настройки Сервис не доступен Сбой синтаксического анализа пакета - Отмена + Отмена Поиск… \ No newline at end of file diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml index 2278482b..ef0a6628 100644 --- a/app/src/main/res/values-su/strings.xml +++ b/app/src/main/res/values-su/strings.xml @@ -5,7 +5,7 @@ Modeu gawé Ngabutuhkeun idin nu disadiakeun ku Sui atawa Shizuku Ngabutuhkeun idin Root nu disadiakeun ku Magisk, KernelSU, atawa APatch - Diijinkeun + Diijinkeun Paménta Éksékutor Pangaturan @@ -18,8 +18,7 @@ Téang… Parsing pakét gagal Eusi kosong - Heug - Bolay + Bolay Pamasangan réngsé Pamasangan gagal Bawaan ti sistem @@ -34,6 +33,6 @@ Teu puguh rupana Pasang Layanan teu sadia - Pasang layanan + Pasang layanan Error teu dipikanyaho \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 000b6d3c..ec39e7c6 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -20,7 +20,7 @@ Cài đặt thất bại Đang tải… Tìm… - Cài đặt dịch vụ + Cài đặt dịch vụ Lỗi không rõ ABI Chế độ làm việc diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 956330d3..a27adb09 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -9,9 +9,9 @@ 需要由 Magisk, KernelSU 或 APatch 提供 Root 权限 - 已授权 请求者 执行者 + 授权 导出 已导出至 %1$s @@ -36,25 +36,19 @@ 语言 未知 安装 + 取消 服务不可用 安装包解析失败 - - 确定 - 取消 - + 安装服务 安装成功 安装失败 加载中… + 未知错误 搜索… 空列表 - - 安装服务 - - - 未知错误 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7f6f605a..3466aae6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,9 +9,9 @@ Requires Root permissions provided by Magisk, KernelSU or APatch - Authorized Requester Executor + Authorize Export Exported to %1$s @@ -36,25 +36,19 @@ Language Unspecified Install + Cancel Service is not available Package parsing failed - - OK - Cancel - + Install service Install successful Install failed Loading… + Unknown error Search… Empty list - - Install service - - - Unknown error \ No newline at end of file diff --git a/app/src/main/res/values/strings_untranslatable.xml b/app/src/main/res/values/strings_untranslatable.xml index e205809d..df64da2b 100644 --- a/app/src/main/res/values/strings_untranslatable.xml +++ b/app/src/main/res/values/strings_untranslatable.xml @@ -6,7 +6,4 @@ Shizuku Root - - @string/dialog_cancel - \ No newline at end of file From 010a05685cee7c8470c35176725b6063dc95ddc0 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Fri, 2 Aug 2024 14:16:38 +0800 Subject: [PATCH 34/34] Bump version to 1.1.4 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 31e81432..f119da7d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ task("clean") { } subprojects { - val baseVersionName by extra("1.1.3") + val baseVersionName by extra("1.1.4") apply(plugin = "maven-publish") configure {