diff --git a/api/android/morphe-library.api b/api/android/morphe-library.api index fc1cc67..a6e5ce3 100644 --- a/api/android/morphe-library.api +++ b/api/android/morphe-library.api @@ -47,6 +47,7 @@ public final class app/morphe/library/installation/installer/AdbInstaller : app/ public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun install (Lapp/morphe/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun install (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -79,6 +80,7 @@ public abstract class app/morphe/library/installation/installer/Installer { public abstract fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; protected final fun getLogger ()Ljava/util/logging/Logger; public abstract fun install (Lapp/morphe/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun install (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -95,6 +97,7 @@ public final class app/morphe/library/installation/installer/LocalInstaller : ap public fun close ()V public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun install (Lapp/morphe/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun install (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -128,6 +131,7 @@ public abstract class app/morphe/library/installation/installer/RootInstaller : public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; protected final fun getShellCommandRunner ()Lapp/morphe/library/installation/command/ShellCommandRunner; public fun install (Lapp/morphe/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun install (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; protected final fun invoke (Ljava/lang/String;)Lapp/morphe/library/installation/command/RunResult; protected final fun move (Ljava/io/File;Ljava/lang/String;)V public fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/api/jvm/morphe-library.api b/api/jvm/morphe-library.api index d4c4a08..de436fc 100644 --- a/api/jvm/morphe-library.api +++ b/api/jvm/morphe-library.api @@ -23,6 +23,7 @@ public final class app/morphe/library/installation/installer/AdbInstaller : app/ public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun install (Lapp/morphe/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun install (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -55,6 +56,7 @@ public abstract class app/morphe/library/installation/installer/Installer { public abstract fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; protected final fun getLogger ()Ljava/util/logging/Logger; public abstract fun install (Lapp/morphe/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun install (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -74,6 +76,7 @@ public abstract class app/morphe/library/installation/installer/RootInstaller : public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; protected final fun getShellCommandRunner ()Lapp/morphe/library/installation/command/ShellCommandRunner; public fun install (Lapp/morphe/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun install (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; protected final fun invoke (Ljava/lang/String;)Lapp/morphe/library/installation/command/RunResult; protected final fun move (Ljava/io/File;Ljava/lang/String;)V public fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d39dab8..1f43786 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ android = "8.9.3" binary-compatibility-validator = "0.18.1" core-ktx = "1.18.0" guava = "33.5.0-jre" -jadb = "1.2.1" +jadb = "1.2.3" kotlin = "2.2.21" libsu = "5.2.2" diff --git a/src/androidMain/kotlin/app/morphe/library/installation/installer/LocalInstaller.kt b/src/androidMain/kotlin/app/morphe/library/installation/installer/LocalInstaller.kt index d59cdcd..1d2afd7 100644 --- a/src/androidMain/kotlin/app/morphe/library/installation/installer/LocalInstaller.kt +++ b/src/androidMain/kotlin/app/morphe/library/installation/installer/LocalInstaller.kt @@ -9,7 +9,6 @@ import android.content.IntentFilter import android.content.pm.PackageInstaller import android.content.pm.PackageManager import androidx.core.content.ContextCompat -import app.morphe.library.installation.installer.Installer.Apk import java.io.Closeable import java.io.File @@ -66,6 +65,26 @@ class LocalInstaller( } } + override suspend fun install(apks: List) { + logger.info("Installing ${apks.joinToString(", ") { it.file.name }}") + + val packageInstaller = context.packageManager.packageInstaller + + packageInstaller.openSession(packageInstaller.createSession(sessionParams)).use { session -> + for (apk in apks) { + apk.file.inputStream().use { inputStream -> + // openWrite needs a unique name for each call + session.openWrite(apk.file.name, 0, apk.file.length()).use { outputStream -> + inputStream.copyTo(outputStream) + // flush + session.fsync(outputStream) + } + } + } + session.commit(intentSender) + } + } + @SuppressLint("MissingPermission") override suspend fun uninstall(packageName: String) { logger.info("Uninstalling $packageName") diff --git a/src/commonMain/kotlin/app/morphe/library/installation/installer/AdbInstaller.kt b/src/commonMain/kotlin/app/morphe/library/installation/installer/AdbInstaller.kt index a52da9e..6c5d4ab 100644 --- a/src/commonMain/kotlin/app/morphe/library/installation/installer/AdbInstaller.kt +++ b/src/commonMain/kotlin/app/morphe/library/installation/installer/AdbInstaller.kt @@ -30,6 +30,12 @@ class AdbInstaller( return runPackageManager { install(apk.file) } } + override suspend fun install(apks: List): AdbInstallerResult { + logger.info("Installing ${apks.joinToString(", ") { it.file.name }}") + + return runPackageManager { install(apks.map { it.file }) } + } + override suspend fun uninstall(packageName: String): AdbInstallerResult { logger.info("Uninstalling $packageName") diff --git a/src/commonMain/kotlin/app/morphe/library/installation/installer/Installer.kt b/src/commonMain/kotlin/app/morphe/library/installation/installer/Installer.kt index 7386c24..206bf2f 100644 --- a/src/commonMain/kotlin/app/morphe/library/installation/installer/Installer.kt +++ b/src/commonMain/kotlin/app/morphe/library/installation/installer/Installer.kt @@ -1,6 +1,5 @@ package app.morphe.library.installation.installer -import app.morphe.library.installation.installer.Installer.Apk import java.io.File import java.util.logging.Logger @@ -25,6 +24,15 @@ abstract class Installer interna */ abstract suspend fun install(apk: Apk): TInstallerResult + /** + * Installs the [Apk] files. + * + * @param apks The [Apk] files. + * + * @return The result of the installation. + */ + abstract suspend fun install(apks: List): TInstallerResult + /** * Uninstalls the package. * diff --git a/src/commonMain/kotlin/app/morphe/library/installation/installer/RootInstaller.kt b/src/commonMain/kotlin/app/morphe/library/installation/installer/RootInstaller.kt index e8f05c4..e29f5ee 100644 --- a/src/commonMain/kotlin/app/morphe/library/installation/installer/RootInstaller.kt +++ b/src/commonMain/kotlin/app/morphe/library/installation/installer/RootInstaller.kt @@ -16,8 +16,6 @@ import app.morphe.library.installation.installer.Constants.RESTART import app.morphe.library.installation.installer.Constants.TMP_FILE_PATH import app.morphe.library.installation.installer.Constants.UMOUNT import app.morphe.library.installation.installer.Constants.invoke -import app.morphe.library.installation.installer.Installer.Apk -import app.morphe.library.installation.installer.RootInstaller.NoRootPermissionException import java.io.File /** @@ -70,6 +68,10 @@ abstract class RootInstaller internal constructor( return RootInstallerResult.SUCCESS } + override suspend fun install(apks: List): RootInstallerResult { + TODO() + } + override suspend fun uninstall(packageName: String): RootInstallerResult { logger.info("Uninstalling $packageName by unmounting")