Skip to content

Commit b4b64c1

Browse files
Downgrade Support for installer, Dhizuku Mode for whole app and performance improvements (#125)
* Bump version to 1.71.3 and enable BuildConfig in suCore * Update `versionCode` to 1713 and `versionName` to 1.71.3 in `gradle.properties`. * Enable `buildConfig` in the `suCore` module's `buildFeatures`. * Remove a commented-out Kotlin Android plugin alias from `suCore/build.gradle.kts`. * Add Logger utility classes to suCore and app modules * Add `Logger` object to `suCore` module providing debug-only logging for D, I, W, V, and E levels. * Add `Logger` object to `app` module with debug-only logging and a `koinLogLevel` configuration for Koin initialization. * Replace custom logging with specialized Logger utility and refactor shell utilities * Replace `android.util.Log` calls with project-specific `Logger` utility across the application and `suCore` modules. * Update `ThorApplication` to use `Logger.koinLogLevel` for Koin initialization. * Refactor `ShellUtils.isValidOutput` to use Kotlin's `isNullOrEmpty()` extension. * Simplify GCD calculation in `ShellUtils` using augmented assignment (`-=`). * Remove redundant file-written check in `InstallerRepositoryImpl` and improve error logging for bundle installation. * Fix typo in `MainViewModel` documentation ("Logger Dialog" to "Logger DiaLogger"). * Remove unused import in `CallbackList.kt`. * Optimized imports * partial code cleanup * Update `InstallState.ReadyToInstall` with versioning and downgrade support * Enhance `ReadyToInstall` data class to include `isDowngrade` and `oldVersion` properties. * Add helper methods to `ReadyToInstall` for generating dynamic UI content: * `getVersionInfo()`: Provides version comparison strings. * `getActionButtonText()`: Returns appropriate labels for install, update, or downgrade actions. * `getWarningMessage()` and `shouldShowWarning()`: Manage downgrade-specific warnings. * `getActionWord()`: Provides context-specific verbs for the installation process. * Allow app downgrades in SystemGateway * Update `SystemGateway.installApp` to include an optional `canDowngrade` parameter, defaulting to `false`. * Add support for package downgrades in `ShizukuReflector` * Update `installPackage` in `ShizukuReflector.kt` to accept an optional `canDowngrade` parameter. * Include the `-d` flag in the `pm install` command when `canDowngrade` is set to true. * Add support for app downgrades in system gateways * Update `RootSystemGateway` to include the `-d` flag in the `pm install` command when `canDowngrade` is true. * Update `ShizukuSystemGateway` to pass the `canDowngrade` parameter to the package installer. * Update the `installApp` interface signature to support the new `canDowngrade` boolean flag. * Add support for package downgrades and extend application metadata * Update `InstallerRepository` to allow optional package downgrading during installation. * Add `versionCode` and `permissions` fields to the `AppMetadata` domain model. * Enhance APK analysis and add support for downgrade installations * **App Analysis**: Update `AppAnalyzerImpl` to extract `versionCode` and requested permissions. * **Install Logic**: Add a `canDowngrade` parameter to installation methods. * **Downgrade Support**: Implement downgrade requests using `HiddenApiBypass` to invoke `setRequestDowngrade` on `SessionParams`. * **Root Installation**: Pass the downgrade flag to `RootSystemGateway`. * **Compatibility**: Fix `PendingIntent` flag compatibility for Android versions below S (API 31). * **Cleanup**: Remove redundant comments and improve code clarity in `AppAnalyzerImpl`. * Update `PortableInstaller` and `InstallerViewModel` to support app downgrades and enhanced package metadata display. * **Installer UI**: * Display both `versionName` and `versionCode` in the package info header. * Add a warning message when an operation is a downgrade or an update. * Show the number of requested permissions and associated warnings. * Refactor action buttons into a `Row` layout, keeping them aligned with permission info. * Generalize "Install/Update" labels using new helper methods from the install state. * **Installer Logic**: * Enhance package analysis to detect downgrades by comparing `versionCode`. * Track the previously installed version name for context. * Add validation to prevent downgrades using `InstallMode.NORMAL` (restricted to Root or Shizuku). * Pass the `canDowngrade` flag to the repository during the installation process. * Refactor package installation logic and clean up codebase * Add a conditional check to only show the package warning message in `PortableInstaller` if `shouldShowWarning()` is true. * Simplify version code retrieval by using `longVersionCode` directly, removing legacy SDK version checks and deprecated `versionCode` usage in `InstallerViewModel` and `AppAnalyzerImpl`. * Add `@Suppress("unused")` annotations to `getVersionInfo`, `setInstallMode`, and the `ShizukuReflector` file. * Remove an unused `AppMetadata` import in `InstallerViewModel`. * Add Dhizuku API dependency * Add `dhizuku` version 2.5.4 to `gradle/libs.versions.toml`. * Add `dhizuku-api` library definition to `gradle/libs.versions.toml`. * Include `libs.dhizuku.api` as an implementation dependency in `app/build.gradle.kts`. * dhizuku ICON * Add Dhizuku support for system operations * Implement `DhizukuHelper` to handle core Dhizuku API interactions, including service binding via `ShizukuBinderWrapper` and shell command execution. * Add `DhizukuReflector` to provide a high-level interface for force-stopping apps, clearing cache, and managing app enabled/disabled states. * Implement `DhizukuSystemGateway` to integrate Dhizuku as a `SystemGateway` provider, enabling app installation, uninstallation, and package management. * Utilize `HiddenApiBypass` for accessing restricted system methods on Android P and above. * Add Dhizuku support for privileged app operations * Initialize Dhizuku API in `ThorApplication`. * Add `DhizukuSystemGateway` and `DhizukuReflector` to support Dhizuku-based system actions. * Update `SystemRepository` and `SystemGateway` interfaces to include Dhizuku availability checks and integration. * Implement Dhizuku installation mode in `InstallerRepositoryImpl` using `DhizukuAPI` and binder wrapping. * Update `InstallerViewModel` to detect Dhizuku availability and allow it as a privileged install mode (supporting downgrades). * Add Dhizuku UI components, including icons and labels, to the `PortableInstaller` screen. * Fix a compatibility issue in `InstallerViewModel` when retrieving version codes on devices below Android P. * Register Dhizuku-related components in the Koin dependency injection `coreModule`. * Add Dhizuku support for privileged app operations * Initialize Dhizuku API in `ThorApplication`. * Add `DhizukuSystemGateway` and `DhizukuReflector` to support Dhizuku-based system actions. * Update `SystemRepository` and `SystemGateway` interfaces to include Dhizuku availability checks and integration. * Implement Dhizuku installation mode in `InstallerRepositoryImpl` using `DhizukuAPI` and binder wrapping. * Update `InstallerViewModel` to detect Dhizuku availability and allow it as a privileged install mode (supporting downgrades). * Add Dhizuku UI components, including icons and labels, to the `PortableInstaller` screen. * Fix a compatibility issue in `InstallerViewModel` when retrieving version codes on devices below Android P. * Register Dhizuku-related components in the Koin dependency injection `coreModule`. * Update app/src/main/java/com/valhalla/thor/data/source/local/dhizuku/Dhizuku.kt
1 parent a1cb7c9 commit b4b64c1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+963
-350
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@
6464
## Credits
6565

6666
- Portions of this app use code from [`libsu`](https://github.com/topjohnwu/libsu)
67-
by [topjohnwu](https://github.com/topjohnwu/), adapted and integrated as the [`suCore`](https://github.com/trinadhthatakula/Thor/tree/master/suCore) module.
67+
by [topjohnwu](https://github.com/topjohnwu/), adapted and integrated as the [
68+
`suCore`](https://github.com/trinadhthatakula/Thor/tree/master/suCore) module.
6869

6970
### Modifications to libsu
7071

app/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import com.android.build.api.artifact.SingleArtifact
12
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
23
import java.io.FileInputStream
34
import java.util.Properties
4-
import com.android.build.api.artifact.SingleArtifact
55

66
plugins {
77
alias(libs.plugins.android.application)
@@ -208,6 +208,7 @@ dependencies {
208208
implementation(libs.lottie.compose)
209209
implementation(libs.shizuku.api)
210210
implementation(libs.shizuku.provider)
211+
implementation(libs.dhizuku.api)
211212
implementation(libs.hiddenapibypass)
212213
implementation(libs.bundles.coil)
213214
implementation(libs.bundles.koin)

app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,21 @@
5151

5252
<intent-filter>
5353
<action android:name="android.intent.action.VIEW" />
54+
5455
<category android:name="android.intent.category.DEFAULT" />
5556
<category android:name="android.intent.category.BROWSABLE" />
57+
5658
<data android:scheme="content" />
5759
<data android:scheme="file" />
5860
<data android:mimeType="application/vnd.android.package-archive" />
5961
</intent-filter>
6062

6163
<intent-filter>
6264
<action android:name="android.intent.action.VIEW" />
65+
6366
<category android:name="android.intent.category.DEFAULT" />
6467
<category android:name="android.intent.category.BROWSABLE" />
68+
6569
<data android:scheme="content" />
6670
<data android:scheme="file" />
6771
<data android:mimeType="application/octet-stream" />
@@ -74,6 +78,7 @@
7478

7579
<intent-filter>
7680
<action android:name="android.intent.action.VIEW" />
81+
7782
<category android:name="android.intent.category.DEFAULT" />
7883
<category android:name="android.intent.category.BROWSABLE" />
7984

app/src/main/java/com/valhalla/thor/HomeActivity.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.valhalla.thor
22

33
import android.os.Bundle
4-
import android.util.Log
54
import androidx.activity.ComponentActivity
65
import androidx.activity.compose.setContent
76
import androidx.activity.enableEdgeToEdge
@@ -12,6 +11,7 @@ import com.valhalla.thor.presentation.common.ShizukuPermissionHandler
1211
import com.valhalla.thor.presentation.home.HomeViewModel
1312
import com.valhalla.thor.presentation.main.MainScreen
1413
import com.valhalla.thor.presentation.theme.ThorTheme
14+
import com.valhalla.thor.util.Logger
1515
import kotlinx.coroutines.launch
1616
import org.koin.android.ext.android.inject
1717
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -26,15 +26,15 @@ class HomeActivity : ComponentActivity() {
2626

2727
private val shizukuHandler = ShizukuPermissionHandler(
2828
onPermissionGranted = {
29-
Log.d("HomeActivity", "Shizuku Ready")
29+
Logger.d("HomeActivity", "Shizuku Ready")
3030
homeViewModel.loadDashboardData()
3131
},
3232
onPermissionDenied = {
33-
Log.d("HomeActivity", "Shizuku Denied")
33+
Logger.d("HomeActivity", "Shizuku Denied")
3434
// We stop asking automatically. User must click "Refresh" in dashboard manually now.
3535
},
3636
onBinderDead = {
37-
Log.w("HomeActivity", "Shizuku Binder Died")
37+
Logger.w("HomeActivity", "Shizuku Binder Died")
3838
}
3939
)
4040

app/src/main/java/com/valhalla/thor/ThorApplication.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ import com.valhalla.thor.di.coreModule
77
import com.valhalla.thor.di.installerModule
88
import com.valhalla.thor.di.preferenceModule
99
import com.valhalla.thor.di.presentationModule
10+
import com.valhalla.thor.util.Logger
1011
import org.koin.android.ext.koin.androidContext
1112
import org.koin.android.ext.koin.androidLogger
1213
import org.koin.androix.startup.KoinStartup
1314
import org.koin.dsl.koinConfiguration
15+
import com.rosan.dhizuku.api.Dhizuku
1416

1517
class ThorApplication : Application(), KoinStartup {
1618

1719
override fun onKoinStartup() = koinConfiguration {
1820
androidContext(this@ThorApplication)
19-
androidLogger()
21+
androidLogger(Logger.koinLogLevel)
2022
modules(
2123
coreModule,
2224
installerModule,
@@ -29,6 +31,12 @@ class ThorApplication : Application(), KoinStartup {
2931
override fun onCreate() {
3032
super.onCreate()
3133
ThorShellConfig.init()
34+
35+
try {
36+
Dhizuku.init(this)
37+
} catch (e: Exception) {
38+
Logger.e("ThorApp", "Dhizuku init failed", e)
39+
}
3240
}
3341

34-
}
42+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.valhalla.thor.data.gateway
2+
3+
import com.valhalla.thor.domain.gateway.SystemGateway
4+
import com.valhalla.thor.data.source.local.dhizuku.DhizukuReflector
5+
import com.valhalla.thor.data.source.local.dhizuku.DhizukuHelper
6+
7+
class DhizukuSystemGateway(
8+
private val reflector: DhizukuReflector
9+
) : SystemGateway {
10+
11+
override suspend fun isRootAvailable() = false
12+
13+
override fun isShizukuAvailable(): Boolean = false
14+
15+
override fun isDhizukuAvailable(): Boolean {
16+
return DhizukuHelper.isDhizukuAvailable()
17+
}
18+
19+
override suspend fun forceStopApp(packageName: String): Result<Unit> {
20+
return try {
21+
reflector.forceStop(packageName)
22+
Result.success(Unit)
23+
} catch (e: Exception) {
24+
Result.failure(e)
25+
}
26+
}
27+
28+
override suspend fun clearCache(packageName: String): Result<Unit> {
29+
return try {
30+
reflector.clearCache(packageName)
31+
Result.success(Unit)
32+
} catch (e: Exception) {
33+
Result.failure(e)
34+
}
35+
}
36+
37+
override suspend fun setAppDisabled(packageName: String, isDisabled: Boolean): Result<Unit> {
38+
return try {
39+
reflector.setAppEnabled(packageName, !isDisabled)
40+
Result.success(Unit)
41+
} catch (e: Exception) {
42+
Result.failure(e)
43+
}
44+
}
45+
46+
override suspend fun rebootDevice(reason: String): Result<Unit> {
47+
return Result.failure(Exception("Reboot requires Root. Dhizuku cannot perform this action easily."))
48+
}
49+
50+
override suspend fun uninstallApp(packageName: String): Result<Unit> {
51+
return if (reflector.uninstallApp(packageName)) {
52+
Result.success(Unit)
53+
} else {
54+
Result.failure(Exception("Uninstall failed"))
55+
}
56+
}
57+
58+
override suspend fun installApp(apkPath: String, canDowngrade: Boolean): Result<Unit> {
59+
val result = DhizukuHelper.execute("pm install -r -g${if (canDowngrade) " -d" else ""} ${com.valhalla.superuser.ShellUtils.escapedString(apkPath)}")
60+
return if (result.first == 0) {
61+
Result.success(Unit)
62+
} else {
63+
Result.failure(Exception("Dhizuku install failed: ${result.second}"))
64+
}
65+
}
66+
67+
override suspend fun getAppCacheSize(packageName: String): Long {
68+
return 0L
69+
}
70+
}

app/src/main/java/com/valhalla/thor/data/gateway/RootSystemGateway.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class RootSystemGateway(
2020
}
2121

2222
override fun isShizukuAvailable(): Boolean = false
23+
override fun isDhizukuAvailable(): Boolean = false
2324

2425
override suspend fun forceStopApp(packageName: String): Result<Unit> {
2526
return runCommand("am force-stop $packageName")
@@ -44,9 +45,9 @@ class RootSystemGateway(
4445
return runCommand("pm uninstall --user 0 $packageName")
4546
}
4647

47-
override suspend fun installApp(apkPath: String): Result<Unit> {
48-
// Use quotes to prevent path injection/splitting issues
49-
return runCommand("pm install -r -g '$apkPath'")
48+
override suspend fun installApp(apkPath: String, canDowngrade: Boolean): Result<Unit> {
49+
val command = "pm install -r -g${if (canDowngrade) " -d" else ""} ${com.valhalla.superuser.ShellUtils.escapedString(apkPath)}"
50+
return runCommand(command)
5051
}
5152

5253
override suspend fun getAppCacheSize(packageName: String): Long {

app/src/main/java/com/valhalla/thor/data/gateway/ShizukuSystemGateway.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class ShizukuSystemGateway(
2020
}
2121
}
2222

23+
override fun isDhizukuAvailable(): Boolean = false
24+
2325
override suspend fun forceStopApp(packageName: String): Result<Unit> {
2426
return runReflectiveAction { reflector.forceStop(packageName) }
2527
}
@@ -37,15 +39,15 @@ class ShizukuSystemGateway(
3739
}
3840

3941
override suspend fun uninstallApp(packageName: String): Result<Unit> {
40-
return if(reflector.uninstallApp(packageName)){
42+
return if (reflector.uninstallApp(packageName)) {
4143
Result.success(Unit)
4244
} else {
4345
Result.failure(Exception("Uninstall failed"))
4446
}
4547
}
4648

47-
override suspend fun installApp(apkPath: String): Result<Unit> {
48-
return if (reflector.installPackage(apkPath)) {
49+
override suspend fun installApp(apkPath: String, canDowngrade: Boolean): Result<Unit> {
50+
return if (reflector.installPackage(apkPath, canDowngrade)) {
4951
Result.success(Unit)
5052
} else {
5153
Result.failure(Exception("Shizuku install failed. Ensure the file path is readable by Shell/ADB."))
@@ -72,4 +74,4 @@ class ShizukuSystemGateway(
7274
Result.failure(e)
7375
}
7476
}
75-
}
77+
}

app/src/main/java/com/valhalla/thor/data/receivers/InstallReceiver.kt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import android.content.Intent
66
import android.content.pm.PackageInstaller
77
import android.os.Build
88
import com.valhalla.thor.data.ACTION_INSTALL_STATUS
9-
import com.valhalla.thor.domain.InstallerEventBus
109
import com.valhalla.thor.domain.InstallState
10+
import com.valhalla.thor.domain.InstallerEventBus
1111
import kotlinx.coroutines.CoroutineScope
1212
import kotlinx.coroutines.Dispatchers
1313
import kotlinx.coroutines.launch
@@ -33,19 +33,26 @@ class InstallReceiver : BroadcastReceiver(), KoinComponent {
3333
PackageInstaller.STATUS_SUCCESS -> {
3434
eventBus.emit(InstallState.Success)
3535
}
36+
3637
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
37-
val confirmIntent: Intent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
38-
intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT, Intent::class.java)
39-
} else {
40-
@Suppress("DEPRECATION")
41-
intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
42-
}
38+
val confirmIntent: Intent? =
39+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
40+
intent.getParcelableExtra<Intent>(
41+
Intent.EXTRA_INTENT,
42+
Intent::class.java
43+
)
44+
} else {
45+
@Suppress("DEPRECATION")
46+
intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
47+
}
4348
if (confirmIntent != null) {
4449
eventBus.emit(InstallState.UserConfirmationRequired(confirmIntent))
4550
}
4651
}
52+
4753
else -> {
48-
val msg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) ?: "Unknown Error"
54+
val msg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
55+
?: "Unknown Error"
4956
eventBus.emit(InstallState.Error("Install Failed ($status): $msg"))
5057
}
5158
}

app/src/main/java/com/valhalla/thor/data/repository/AppAnalyzerImpl.kt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import android.graphics.drawable.BitmapDrawable
88
import android.graphics.drawable.Drawable
99
import android.net.Uri
1010
import android.os.Build
11+
import androidx.core.graphics.createBitmap
1112
import com.valhalla.thor.domain.model.AppMetadata
1213
import com.valhalla.thor.domain.repository.AppAnalyzer
1314
import kotlinx.coroutines.Dispatchers
1415
import kotlinx.coroutines.withContext
1516
import java.io.File
1617
import java.io.FileOutputStream
1718
import java.util.zip.ZipInputStream
18-
import androidx.core.graphics.createBitmap
1919

2020
class AppAnalyzerImpl(private val context: Context) : AppAnalyzer {
2121

@@ -26,7 +26,6 @@ class AppAnalyzerImpl(private val context: Context) : AppAnalyzer {
2626
val contentResolver = context.contentResolver
2727

2828
// Phase 1: Try to extract a nested APK (for XAPK/APKS)
29-
// We open a fresh stream for the Zip check
3029
var isNestedBundle = false
3130

3231
try {
@@ -35,8 +34,6 @@ class AppAnalyzerImpl(private val context: Context) : AppAnalyzer {
3534
var entry = zipStream.nextEntry
3635
while (entry != null) {
3736
val name = entry.name
38-
// If we find a nested APK, we extract it and stop.
39-
// We prioritize 'base.apk' but accept any .apk if base isn't found first.
4037
if (name.endsWith(".apk", ignoreCase = true)) {
4138
FileOutputStream(tempFile).use { fos ->
4239
zipStream.copyTo(fos)
@@ -49,13 +46,11 @@ class AppAnalyzerImpl(private val context: Context) : AppAnalyzer {
4946
}
5047
}
5148
}
52-
} catch (e: Exception) {
53-
// Not a zip or read error, proceed to fallback
49+
} catch (_: Exception) {
5450
isNestedBundle = false
5551
}
5652

5753
// Phase 2: Fallback (Standard APK)
58-
// If we didn't find any nested APKs inside, the file ITSELF is the APK.
5954
if (!isNestedBundle) {
6055
contentResolver.openInputStream(uri)?.use { input ->
6156
FileOutputStream(tempFile).use { output ->
@@ -65,12 +60,14 @@ class AppAnalyzerImpl(private val context: Context) : AppAnalyzer {
6560
}
6661

6762
// Phase 3: Parsing
68-
// Now tempFile is either the extracted base.apk OR the copy of the original APK.
6963
val pm = context.packageManager
70-
val flags = PackageManager.GET_META_DATA
64+
val flags = PackageManager.GET_META_DATA or PackageManager.GET_PERMISSIONS
7165

7266
val archiveInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
73-
pm.getPackageArchiveInfo(tempFile.absolutePath, PackageManager.PackageInfoFlags.of(flags.toLong()))
67+
pm.getPackageArchiveInfo(
68+
tempFile.absolutePath,
69+
PackageManager.PackageInfoFlags.of(flags.toLong())
70+
)
7471
} else {
7572
@Suppress("DEPRECATION")
7673
pm.getPackageArchiveInfo(tempFile.absolutePath, flags)
@@ -87,21 +84,24 @@ class AppAnalyzerImpl(private val context: Context) : AppAnalyzer {
8784
val label = archiveInfo.applicationInfo?.loadLabel(pm).toString()
8885
val drawable = archiveInfo.applicationInfo?.loadIcon(pm)
8986
val version = archiveInfo.versionName ?: "Unknown"
87+
val versionCode = archiveInfo.longVersionCode
9088
val pkgName = archiveInfo.packageName
89+
val permissions = archiveInfo.requestedPermissions?.toList() ?: emptyList()
9190

9291
Result.success(
9392
AppMetadata(
9493
label = label,
9594
packageName = pkgName,
9695
version = version,
97-
icon = drawable?.toBitmap()
96+
versionCode = versionCode,
97+
icon = drawable?.toBitmap(),
98+
permissions = permissions
9899
)
99100
)
100101

101102
} catch (e: Exception) {
102103
Result.failure(e)
103104
} finally {
104-
// Cleanup: Delete the temp file to save space
105105
if (tempFile.exists()) {
106106
tempFile.delete()
107107
}

0 commit comments

Comments
 (0)