From 2d3247e4b88a1ea148fec93071798722ec46d4ad Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Sat, 3 Aug 2024 21:47:08 -0700 Subject: [PATCH] feat(PMInstaller): wait for uninstalls & fix PM session lifecycle bugs - Support waiting for package uninstallation results - Make DowngradeCheckStep wait for uninstall rather than erroring - Relaunch PackageInstaller user action intents infinitely until result received (Workaround of bug on some ROMs where dismissing the PI dialog by clicking outside wouldn't invoke the callback with STATUS_FAILURE_ABORTED) - Make PMIntentReceiver & PMResultReceiver non-exported to keep intents from leaking to other apps - Handle uncaught exceptions thrown in the aforementioned receivers by returning the result back to the app (or logging) to prevent crashes --- app/src/main/AndroidManifest.xml | 4 +- .../aliucord/manager/installers/Installer.kt | 10 +- .../manager/installers/InstallerResult.kt | 14 +-- .../installers/UnknownInstallerError.kt | 14 +++ .../manager/installers/pm/PMInstaller.kt | 111 +++++++++++------- .../manager/installers/pm/PMInstallerError.kt | 28 +++-- .../manager/installers/pm/PMIntentReceiver.kt | 101 ++++++++++------ .../manager/installers/pm/PMResultReceiver.kt | 87 ++++++++++++-- .../patcher/steps/install/InstallStep.kt | 2 +- .../steps/prepare/DowngradeCheckStep.kt | 24 +++- .../manager/patcher/util/PackageInstaller.kt | 15 --- app/src/main/res/values-af-rZA/strings.xml | 4 +- app/src/main/res/values-ar-rSA/strings.xml | 4 +- app/src/main/res/values-ca-rES/strings.xml | 4 +- app/src/main/res/values-cs-rCZ/strings.xml | 4 +- app/src/main/res/values-da-rDK/strings.xml | 4 +- app/src/main/res/values-de-rDE/strings.xml | 4 +- app/src/main/res/values-el-rGR/strings.xml | 4 +- app/src/main/res/values-es-rES/strings.xml | 4 +- app/src/main/res/values-fi-rFI/strings.xml | 4 +- app/src/main/res/values-fr-rFR/strings.xml | 4 +- app/src/main/res/values-hi-rIN/strings.xml | 4 +- app/src/main/res/values-hu-rHU/strings.xml | 4 +- app/src/main/res/values-in-rID/strings.xml | 4 +- app/src/main/res/values-it-rIT/strings.xml | 4 +- app/src/main/res/values-iw-rIL/strings.xml | 4 +- app/src/main/res/values-ja-rJP/strings.xml | 4 +- app/src/main/res/values-kaa/strings.xml | 4 +- app/src/main/res/values-ko-rKR/strings.xml | 4 +- app/src/main/res/values-nl-rNL/strings.xml | 4 +- app/src/main/res/values-no-rNO/strings.xml | 4 +- app/src/main/res/values-pl-rPL/strings.xml | 4 +- app/src/main/res/values-pt-rBR/strings.xml | 4 +- app/src/main/res/values-pt-rPT/strings.xml | 4 +- app/src/main/res/values-ro-rRO/strings.xml | 4 +- app/src/main/res/values-ru-rRU/strings.xml | 4 +- app/src/main/res/values-sr-rSP/strings.xml | 4 +- app/src/main/res/values-sv-rSE/strings.xml | 4 +- app/src/main/res/values-tr-rTR/strings.xml | 4 +- app/src/main/res/values-uk-rUA/strings.xml | 4 +- app/src/main/res/values-vi-rVN/strings.xml | 4 +- app/src/main/res/values-zh-rCN/strings.xml | 4 +- app/src/main/res/values-zh-rTW/strings.xml | 4 +- app/src/main/res/values/strings.xml | 14 ++- 44 files changed, 353 insertions(+), 199 deletions(-) create mode 100644 app/src/main/kotlin/com/aliucord/manager/installers/UnknownInstallerError.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/patcher/util/PackageInstaller.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4773e99..7fe1b13 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,7 +29,9 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"> - + , silent: Boolean = true) /** - * Starts an installation and waits for it to finish with a result. A toast will be shown when the installation was completed. + * Starts an installation and waits for it to finish with a result. A toast will be shown for all result states. * @param apks All APKs (including any splits) to merge into a single install. * @param silent If this is an update, then the update will occur without user interaction. */ suspend fun waitInstall(apks: List, silent: Boolean = true): InstallerResult + + /** + * Triggers an uninstallation and waits for it to complete with a result. A toast will be shown for all result states. + * @param packageName The package name of the target package. + */ + suspend fun waitUninstall(packageName: String): InstallerResult } diff --git a/app/src/main/kotlin/com/aliucord/manager/installers/InstallerResult.kt b/app/src/main/kotlin/com/aliucord/manager/installers/InstallerResult.kt index 174fe07..682609b 100644 --- a/app/src/main/kotlin/com/aliucord/manager/installers/InstallerResult.kt +++ b/app/src/main/kotlin/com/aliucord/manager/installers/InstallerResult.kt @@ -1,7 +1,7 @@ package com.aliucord.manager.installers +import android.content.Context import android.os.Parcelable -import androidx.annotation.StringRes import kotlinx.parcelize.Parcelize /** @@ -26,16 +26,16 @@ sealed interface InstallerResult : Parcelable { * This installation encountered an error and has been aborted. * All implementors should implement [Parcelable]. */ - abstract class Error : InstallerResult { + abstract class Error : InstallerResult, Parcelable { /** - * Loggable error that should not be shown to the user. + * The full internal error representation. */ - abstract val debugReason: String + abstract fun getDebugReason(): String /** - * Simplified + translatable user facing errors. + * Simplified + translatable user facing reason for the failure. + * If null is returned, then the [getDebugReason] will be used instead. */ - @get:StringRes - abstract val localizedReason: Int + open fun getLocalizedReason(context: Context): String? = null } } diff --git a/app/src/main/kotlin/com/aliucord/manager/installers/UnknownInstallerError.kt b/app/src/main/kotlin/com/aliucord/manager/installers/UnknownInstallerError.kt new file mode 100644 index 0000000..576d9ab --- /dev/null +++ b/app/src/main/kotlin/com/aliucord/manager/installers/UnknownInstallerError.kt @@ -0,0 +1,14 @@ +package com.aliucord.manager.installers + +import android.content.Context +import com.aliucord.manager.R +import kotlinx.parcelize.Parcelize + +@Parcelize +data class UnknownInstallerError(val error: Throwable) : InstallerResult.Error() { + override fun getDebugReason() = error.stackTraceToString() + + // No localizations for exceptions, use short message anyway + override fun getLocalizedReason(context: Context) = + error.message ?: context.getString(R.string.install_error_unknown) +} diff --git a/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMInstaller.kt b/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMInstaller.kt index 1af9541..cd69a7d 100644 --- a/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMInstaller.kt +++ b/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMInstaller.kt @@ -1,34 +1,34 @@ package com.aliucord.manager.installers.pm -import android.annotation.SuppressLint +import android.app.Application import android.app.PendingIntent -import android.content.Context import android.content.Intent import android.content.pm.* import android.content.pm.PackageInstaller.SessionParams import android.os.Build import android.os.Process import android.util.Log +import androidx.core.content.ContextCompat import com.aliucord.manager.BuildConfig import com.aliucord.manager.installers.Installer -import com.aliucord.manager.installers.InstallerResult -import kotlinx.coroutines.suspendCancellableCoroutine import com.aliucord.manager.util.isMiui +import kotlinx.coroutines.suspendCancellableCoroutine import java.io.File +import kotlin.coroutines.resume /** * APK installer using the [PackageInstaller] from the system's [PackageManager] service. */ class PMInstaller( - val context: Context, + private val context: Application, ) : Installer { - init { - val pkgInstaller = context.packageManager.packageInstaller + private val _packageInstaller = context.packageManager.packageInstaller - // Destroy all open sessions that may have not been previously cleaned up - for (session in pkgInstaller.mySessions) { - Log.d(BuildConfig.TAG, "Deleting PackageInstaller session ${session.sessionId}") - pkgInstaller.abandonSession(session.sessionId) + init { + // Destroy all open sessions that may have not been previously cleaned up due to fatal errors + for (session in _packageInstaller.mySessions) { + Log.d(BuildConfig.TAG, "Deleting old PackageInstaller session ${session.sessionId}") + _packageInstaller.abandonSession(session.sessionId) } } @@ -36,29 +36,66 @@ class PMInstaller( startInstall(createInstallSession(silent), apks, false) } - override suspend fun waitInstall(apks: List, silent: Boolean): InstallerResult { + override suspend fun waitInstall(apks: List, silent: Boolean) = suspendCancellableCoroutine { continuation -> + // Create a new install session val sessionId = createInstallSession(silent) - return suspendCancellableCoroutine { continuation -> - // This will receive parsed data forwarded by PMIntentReceiver - val relayReceiver = PMResultReceiver(sessionId, continuation) + // This will receive parsed data forwarded by PMIntentReceiver + val relayReceiver = PMResultReceiver( + sessionId = sessionId, + isUninstall = false, + onResult = { continuation.resume(it) }, + ) - // Unregister PMResultReceiver when this coroutine finishes - // additionally, cancel the install session entirely - continuation.invokeOnCancellation { - context.unregisterReceiver(relayReceiver) - context.packageManager.packageInstaller.abandonSession(sessionId) - } + ContextCompat.registerReceiver( + /* context = */ context, + /* receiver = */ relayReceiver, + /* filter = */ PMResultReceiver.intentFilter, + /* flags = */ ContextCompat.RECEIVER_NOT_EXPORTED, + ) - @SuppressLint("UnspecifiedRegisterReceiverFlag") - if (Build.VERSION.SDK_INT >= 33) { - context.registerReceiver(relayReceiver, relayReceiver.filter, Context.RECEIVER_NOT_EXPORTED) - } else { - context.registerReceiver(relayReceiver, relayReceiver.filter) - } - - startInstall(sessionId, apks, relay = true) + // Unregister PMResultReceiver when this coroutine finishes or error occurs + // Additionally, cancel the install session entirely + continuation.invokeOnCancellation { error -> + context.unregisterReceiver(relayReceiver) + context.packageManager.packageInstaller.abandonSession(sessionId) } + + startInstall(sessionId, apks, relay = true) + } + + override suspend fun waitUninstall(packageName: String) = suspendCancellableCoroutine { continuation -> + val callbackIntent = Intent(context, PMIntentReceiver::class.java) + .putExtra(PMIntentReceiver.EXTRA_SESSION_ID, -1) + .putExtra(PMIntentReceiver.EXTRA_RELAY_ENABLED, true) + + val pendingIntent = PendingIntent.getBroadcast( + /* context = */ context, + /* requestCode = */ 0, + /* intent = */ callbackIntent, + /* flags = */ PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, + ) + + // This will receive parsed data forwarded by PMIntentReceiver + val relayReceiver = PMResultReceiver( + sessionId = -1, + isUninstall = true, + onResult = { continuation.resume(it) }, + ) + + ContextCompat.registerReceiver( + /* context = */ context, + /* receiver = */ relayReceiver, + /* filter = */ PMResultReceiver.intentFilter, + /* flags = */ ContextCompat.RECEIVER_NOT_EXPORTED, + ) + + // Unregister PMResultReceiver when this coroutine is cancelled early + continuation.invokeOnCancellation { error -> + context.unregisterReceiver(relayReceiver) + } + + _packageInstaller.uninstall(packageName, pendingIntent.intentSender) } /** @@ -70,13 +107,8 @@ class PMInstaller( val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply { setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO) - if (Build.VERSION.SDK_INT >= 24) { - setOriginatingUid(Process.myUid()) - } - - if (Build.VERSION.SDK_INT >= 26) { - setInstallReason(PackageManager.INSTALL_REASON_USER) - } + if (Build.VERSION.SDK_INT >= 24) setOriginatingUid(Process.myUid()) + if (Build.VERSION.SDK_INT >= 26) setInstallReason(PackageManager.INSTALL_REASON_USER) if (Build.VERSION.SDK_INT >= 31) { setInstallScenario(PackageManager.INSTALL_SCENARIO_FAST) @@ -87,9 +119,7 @@ class PMInstaller( } } - if (Build.VERSION.SDK_INT >= 34) { - setPackageSource(PackageInstaller.PACKAGE_SOURCE_OTHER) - } + if (Build.VERSION.SDK_INT >= 34) setPackageSource(PackageInstaller.PACKAGE_SOURCE_OTHER) } return context.packageManager.packageInstaller.createSession(params) @@ -99,6 +129,7 @@ class PMInstaller( * Start a [PackageInstaller] session for installation. * @param apks The apks to install * @param relay Whether to use the [PMResultReceiver] flow. + * @return The [PendingIntent] that was registered to the [PackageInstaller.Session] */ private fun startInstall(sessionId: Int, apks: List, relay: Boolean) { val callbackIntent = Intent(context, PMIntentReceiver::class.java) @@ -112,7 +143,7 @@ class PMInstaller( /* flags = */ PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, ) - context.packageManager.packageInstaller.openSession(sessionId).use { session -> + _packageInstaller.openSession(sessionId).use { session -> val bufferSize = 1 * 1024 * 1024 // 1MiB for (apk in apks) { diff --git a/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMInstallerError.kt b/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMInstallerError.kt index b7676d1..8bf1411 100644 --- a/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMInstallerError.kt +++ b/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMInstallerError.kt @@ -1,10 +1,10 @@ package com.aliucord.manager.installers.pm +import android.content.Context import android.content.pm.PackageInstaller import android.os.Parcelable import com.aliucord.manager.R import com.aliucord.manager.installers.InstallerResult -import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize /** @@ -13,8 +13,7 @@ import kotlinx.parcelize.Parcelize */ @Parcelize data class PMInstallerError(val status: Int) : InstallerResult.Error(), Parcelable { - @IgnoredOnParcel - override val debugReason = when (status) { + override fun getDebugReason() = when (status) { PackageInstaller.STATUS_FAILURE -> "Unknown failure" PackageInstaller.STATUS_FAILURE_BLOCKED -> "Blocked" PackageInstaller.STATUS_FAILURE_INVALID -> "Invalid package" @@ -25,15 +24,18 @@ data class PMInstallerError(val status: Int) : InstallerResult.Error(), Parcelab else -> "Unknown code ($status)" } - @IgnoredOnParcel - override val localizedReason = when (status) { - PackageInstaller.STATUS_FAILURE -> R.string.install_error_unknown - PackageInstaller.STATUS_FAILURE_BLOCKED -> R.string.install_error_blocked - PackageInstaller.STATUS_FAILURE_INVALID -> R.string.install_error_invalid - PackageInstaller.STATUS_FAILURE_CONFLICT -> R.string.install_error_conflict - PackageInstaller.STATUS_FAILURE_STORAGE -> R.string.install_error_storage - PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> R.string.install_error_incompatible - /* PackageInstaller.STATUS_FAILURE_TIMEOUT */ 8 -> R.string.install_error_timeout - else -> R.string.install_error_unknown + override fun getLocalizedReason(context: Context): String { + val string = when (status) { + PackageInstaller.STATUS_FAILURE -> R.string.install_error_unknown + PackageInstaller.STATUS_FAILURE_BLOCKED -> R.string.install_error_blocked + PackageInstaller.STATUS_FAILURE_INVALID -> R.string.install_error_invalid + PackageInstaller.STATUS_FAILURE_CONFLICT -> R.string.install_error_conflict + PackageInstaller.STATUS_FAILURE_STORAGE -> R.string.install_error_storage + PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> R.string.install_error_incompatible + /* PackageInstaller.STATUS_FAILURE_TIMEOUT */ 8 -> R.string.install_error_timeout + else -> R.string.install_error_unknown + } + + return context.getString(string) } } diff --git a/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMIntentReceiver.kt b/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMIntentReceiver.kt index c464e2f..1b1af97 100644 --- a/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMIntentReceiver.kt +++ b/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMIntentReceiver.kt @@ -1,66 +1,97 @@ package com.aliucord.manager.installers.pm +import android.app.PendingIntent import android.content.* import android.content.pm.PackageInstaller import android.util.Log +import android.widget.Toast import com.aliucord.manager.BuildConfig -import com.aliucord.manager.R import com.aliucord.manager.installers.InstallerResult -import com.aliucord.manager.util.showToast +import com.aliucord.manager.installers.UnknownInstallerError +/** + * This class is used as a callback receiver for [PackageInstaller] events, + * registered as a [PendingIntent]. If [PMIntentReceiver.EXTRA_RELAY_ENABLED] is set to true on + * incoming intents, then the incoming intent will be parsed and relayed as an intent intended for [PMResultReceiver] + * that was registered dynamically with the correct session id, which will handle the relayed result and return + * it as a callback back to the application. + */ class PMIntentReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val realSessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1) - val expectedSessionId = intent.getIntExtra(EXTRA_SESSION_ID, -2) + val expectedSessionId = intent.getIntExtra(EXTRA_SESSION_ID, -1) if (realSessionId != expectedSessionId) return + try { + handleSessionIntent(context, intent, expectedSessionId) + } catch (error: Throwable) { + if (intent.getBooleanExtra(EXTRA_RELAY_ENABLED, false)) { + val relayIntent = Intent(PMResultReceiver.ACTION_RECEIVE_RESULT) + .setPackage(BuildConfig.APPLICATION_ID) + .putExtra(PMResultReceiver.EXTRA_SESSION_ID, expectedSessionId) + .putExtra(PMResultReceiver.EXTRA_RESULT, UnknownInstallerError(error)) + + context.sendBroadcast(relayIntent) + } else { + Log.e(BuildConfig.TAG, "[PMIntentReceiver] Failed to handle intent", error) + } + } + } + + private fun handleSessionIntent(context: Context, intent: Intent, sessionId: Int) { val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999) + + // Launch the user action intent and forward it to PMResultReceiver to keep relaunching it + if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) { + @Suppress("DEPRECATION") + val confirmationIntent = intent + .getParcelableExtra(Intent.EXTRA_INTENT)!! + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + + context.startActivity(confirmationIntent) + + if (intent.getBooleanExtra(EXTRA_RELAY_ENABLED, false)) { + val relayIntent = Intent(PMResultReceiver.ACTION_RECEIVE_INTENT) + .setPackage(BuildConfig.APPLICATION_ID) + .putExtra(PMResultReceiver.EXTRA_SESSION_ID, sessionId) + .putExtra(PMResultReceiver.EXTRA_USER_ACTION_INTENT, confirmationIntent) + + context.sendBroadcast(relayIntent) + } + + return + } + + // Handle install result val installerResult = when (status) { - -999 -> { - // Invalid intent - null - } - - PackageInstaller.STATUS_PENDING_USER_ACTION -> { - @Suppress("DEPRECATION") - val confirmationIntent = intent - .getParcelableExtra(Intent.EXTRA_INTENT)!! - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - context.startActivity(confirmationIntent) - - null // no result yet - } - - PackageInstaller.STATUS_SUCCESS -> { - context.showToast(R.string.installer_success) - InstallerResult.Success - } - - PackageInstaller.STATUS_FAILURE_ABORTED -> { - context.showToast(R.string.installer_aborted) - InstallerResult.Cancelled(systemTriggered = true) - } + PackageInstaller.STATUS_SUCCESS -> InstallerResult.Success + PackageInstaller.STATUS_FAILURE_ABORTED -> InstallerResult.Cancelled(systemTriggered = true) else -> { - Log.w(BuildConfig.TAG, "Install failed with error code $status") + Log.w(BuildConfig.TAG, "PM failed with error code $status") - if (status <= PackageInstaller.STATUS_SUCCESS) - null // Unknown status code (not an error) - else { + if (status <= PackageInstaller.STATUS_SUCCESS) { + // Unknown status code (not an error) + return + } else { PMInstallerError(status).also { - context.showToast(it.localizedReason) + Toast.makeText( + /* context = */ context, + /* text = */ it.getLocalizedReason(context), + /* duration = */ Toast.LENGTH_LONG + ).show() } } } } // Forward result to PMResultReceiver if relaying is enabled and have a real result - if (installerResult != null && intent.getBooleanExtra(EXTRA_RELAY_ENABLED, false)) { + if (intent.getBooleanExtra(EXTRA_RELAY_ENABLED, false)) { val relayIntent = Intent(PMResultReceiver.ACTION_RECEIVE_RESULT) + .setPackage(BuildConfig.APPLICATION_ID) + .putExtra(PMResultReceiver.EXTRA_SESSION_ID, sessionId) .putExtra(PMResultReceiver.EXTRA_RESULT, installerResult) - .putExtra(PMResultReceiver.EXTRA_SESSION_ID, realSessionId) context.sendBroadcast(relayIntent) } diff --git a/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMResultReceiver.kt b/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMResultReceiver.kt index f9f17ec..e7f2a95 100644 --- a/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMResultReceiver.kt +++ b/app/src/main/kotlin/com/aliucord/manager/installers/pm/PMResultReceiver.kt @@ -1,33 +1,102 @@ package com.aliucord.manager.installers.pm import android.content.* +import android.content.pm.PackageInstaller +import com.aliucord.manager.R import com.aliucord.manager.installers.InstallerResult -import kotlin.coroutines.Continuation -import kotlin.coroutines.resume +import com.aliucord.manager.installers.UnknownInstallerError +import com.aliucord.manager.util.showToast +import kotlinx.coroutines.* +import java.util.concurrent.atomic.AtomicBoolean +/** + * This receiver is meant to be registered dynamically in combination with [PMIntentReceiver] in order to + * relay parsed [PackageInstaller] results back to the running application. + * Additionally, it will handle restarting [PackageInstaller] user action intents infinitely until + * we have received a result from the system. + */ class PMResultReceiver( private val sessionId: Int, - private val continuation: Continuation, + private val isUninstall: Boolean, + private val onResult: (InstallerResult) -> Unit, ) : BroadcastReceiver() { /** - * The intent filter this receiver should be registered with to work properly. + * Keep re-launching the user action intent until we have received a result from the PM. + * This is necessary because on some ROMs there is no callback intent sent when the user + * clicks outside of the package installer dialog, forcing us to keep relaunching the dialog + * until the user has clicked one of the buttons, after which the intent has been 'used up' and won't show anymore. */ - val filter = IntentFilter(ACTION_RECEIVE_RESULT) + private val resultReceived = AtomicBoolean(false) - @Suppress("DEPRECATION") override fun onReceive(context: Context, intent: Intent) { - if (intent.action != ACTION_RECEIVE_RESULT) return if (intent.getIntExtra(EXTRA_SESSION_ID, -1) != sessionId) return - val result = intent.getParcelableExtra(EXTRA_RESULT) + try { + when (intent.action) { + ACTION_RECEIVE_INTENT -> handleIntent(context, intent) + ACTION_RECEIVE_RESULT -> handleResult(context, intent) + } + } catch (t: Throwable) { + resultReceived.set(true) + context.unregisterReceiver(this) + onResult(UnknownInstallerError(t)) + } + } + + private fun handleIntent(context: Context, intent: Intent) { + @Suppress("DEPRECATION") + val confirmationIntent = intent.getParcelableExtra(EXTRA_USER_ACTION_INTENT) ?: return - continuation.resume(result) + // No result yet, keep relaunching user action intent + CoroutineScope(Dispatchers.Default).launch { + while (true) { + context.startActivity(confirmationIntent) + delay(1000) + + if (resultReceived.get()) break + } + } + } + + private fun handleResult(context: Context, intent: Intent) { + @Suppress("DEPRECATION") + val result = intent.getParcelableExtra(EXTRA_RESULT) ?: return + + resultReceived.set(true) + + // Show toast for successful and aborted sessions + when (result) { + InstallerResult.Success -> { + context.showToast(if (!isUninstall) R.string.installer_install_success else R.string.installer_uninstall_success) + } + + // The reason we don't do this in PMIntentReceiver is we can't tell whether it was + // an old session that for which `abandonSession(...)` was called + is InstallerResult.Cancelled -> { + context.showToast(if (!isUninstall) R.string.installer_install_aborted else R.string.installer_uninstall_aborted) + } + + else -> {} + } + + context.unregisterReceiver(this) + onResult(result) } companion object { + const val ACTION_RECEIVE_INTENT = "com.aliucord.manager.RELAY_PM_INTENT" const val ACTION_RECEIVE_RESULT = "com.aliucord.manager.RELAY_PM_RESULT" const val EXTRA_RESULT = "installerResult" const val EXTRA_SESSION_ID = "sessionId" + const val EXTRA_USER_ACTION_INTENT = "userActionIntent" + + /** + * The intent filter this receiver should be registered with to work properly. + */ + val intentFilter = IntentFilter().apply { + addAction(ACTION_RECEIVE_INTENT) + addAction(ACTION_RECEIVE_RESULT) + } } } diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/install/InstallStep.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/install/InstallStep.kt index 71a0ac4..e6ce28e 100644 --- a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/install/InstallStep.kt +++ b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/install/InstallStep.kt @@ -75,7 +75,7 @@ class InstallStep(private val options: PatchOptions) : Step(), KoinComponent { ) when (result) { - is InstallerResult.Error -> throw Error("Failed to install APKs: ${result.debugReason}") + is InstallerResult.Error -> throw Error("Failed to install APKs: ${result.getDebugReason()}") is InstallerResult.Cancelled -> { // The install screen is automatically closed immediately once cleanup finishes state = StepState.Skipped diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/prepare/DowngradeCheckStep.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/prepare/DowngradeCheckStep.kt index 622bb34..c3f31c6 100644 --- a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/prepare/DowngradeCheckStep.kt +++ b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/prepare/DowngradeCheckStep.kt @@ -1,12 +1,14 @@ package com.aliucord.manager.patcher.steps.prepare import android.content.Context +import android.content.pm.PackageManager.NameNotFoundException import com.aliucord.manager.R +import com.aliucord.manager.installers.InstallerResult +import com.aliucord.manager.manager.InstallerManager import com.aliucord.manager.patcher.StepRunner import com.aliucord.manager.patcher.steps.StepGroup import com.aliucord.manager.patcher.steps.base.Step import com.aliucord.manager.patcher.steps.base.StepState -import com.aliucord.manager.patcher.util.uninstallApk import com.aliucord.manager.ui.screens.patchopts.PatchOptions import com.aliucord.manager.util.getPackageVersion import com.aliucord.manager.util.showToast @@ -21,6 +23,7 @@ import org.koin.core.component.inject */ class DowngradeCheckStep(private val options: PatchOptions) : Step(), KoinComponent { private val context: Context by inject() + private val installers: InstallerManager by inject() override val group = StepGroup.Prepare override val localizedName = R.string.patch_step_downgrade_check @@ -30,7 +33,7 @@ class DowngradeCheckStep(private val options: PatchOptions) : Step(), KoinCompon context.getPackageVersion(options.packageName) } // Package is not installed - catch (_: Throwable) { + catch (_: NameNotFoundException) { state = StepState.Skipped return } @@ -38,16 +41,25 @@ class DowngradeCheckStep(private val options: PatchOptions) : Step(), KoinCompon val targetVersion = container .getStep() .data.versionCode.toIntOrNull() - ?: throw IllegalArgumentException("Invalid fetched Aliucord target Discord version") + ?: throw IllegalArgumentException("Invalid remote target Discord version") if (currentVersion > targetVersion) { - context.uninstallApk(options.packageName) - withContext(Dispatchers.Main) { context.showToast(R.string.installer_uninstall_new) } - throw Error("Newer version of Aliucord must be uninstalled prior to installing an older version") + when (val result = installers.getActiveInstaller().waitUninstall(options.packageName)) { + is InstallerResult.Error -> throw Error("Failed to uninstall Aliucord: ${result.getDebugReason()}") + is InstallerResult.Cancelled -> { + withContext(Dispatchers.Main) { + context.showToast(R.string.installer_uninstall_new) + } + + throw Error("Newer versions of Aliucord must be uninstalled prior to installing an older version") + } + + else -> {} + } } } } diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/util/PackageInstaller.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/util/PackageInstaller.kt deleted file mode 100644 index 6daf624..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/patcher/util/PackageInstaller.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.aliucord.manager.patcher.util - -import android.content.Context -import android.content.Intent -import android.net.Uri - -// TODO: move this to PMInstaller -fun Context.uninstallApk(packageName: String) { - val packageURI = Uri.parse("package:$packageName") - val uninstallIntent = Intent(Intent.ACTION_DELETE, packageURI).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - - startActivity(uninstallIntent) -} diff --git a/app/src/main/res/values-af-rZA/strings.xml b/app/src/main/res/values-af-rZA/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-af-rZA/strings.xml +++ b/app/src/main/res/values-af-rZA/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-ar-rSA/strings.xml +++ b/app/src/main/res/values-ar-rSA/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-ca-rES/strings.xml b/app/src/main/res/values-ca-rES/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-ca-rES/strings.xml +++ b/app/src/main/res/values-ca-rES/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-da-rDK/strings.xml b/app/src/main/res/values-da-rDK/strings.xml index 03c5655..ed529b5 100644 --- a/app/src/main/res/values-da-rDK/strings.xml +++ b/app/src/main/res/values-da-rDK/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 1cc4b65..4f559b6 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml index fb0a373..6c2f15c 100644 --- a/app/src/main/res/values-el-rGR/strings.xml +++ b/app/src/main/res/values-el-rGR/strings.xml @@ -69,8 +69,8 @@ Αποτυχία ανάκτησης έκδοσης Αποθηκεύτηκε στο %s Απέτυχε η αποθήκευση στο %s - Επιτυχής εγκατάσταση του Aliucord - Εγκατάλειψη εγκατάστασης Aliucord + Επιτυχής εγκατάσταση του Aliucord + Εγκατάλειψη εγκατάστασης Aliucord Αποτυχία επαλήθευσης λήψης Παρακαλώ απεγκαταστήστε την τρέχουσα έκδοση του Aliucord σας για να συνεχίσετε! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index f781f6a..71be6d2 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -69,8 +69,8 @@ Error al recuperar la versión Guardado como %s Error al guardar %s - Aliucord instalado con éxito - Se abortó la instalación de Aliucord + Aliucord instalado con éxito + Se abortó la instalación de Aliucord Error al verificar la descarga ¡Por favor, desinstala tu versión actual de Aliucord para continuar! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index e76859d..b6b1b08 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -69,8 +69,8 @@ Échec de récupération de la version Enregistré dans %s Échec de l\'enregistrement de %s - Aliucord installé avec succès - Installation d\'Aliucord interrompue + Aliucord installé avec succès + Installation d\'Aliucord interrompue Failed to verify download Veuillez désinstaller votre version actuelle d\'Aliucord pour continuer ! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml index c31811b..22f3541 100644 --- a/app/src/main/res/values-hi-rIN/strings.xml +++ b/app/src/main/res/values-hi-rIN/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index 8a27219..5e62f39 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml index 01888b3..20e5f92 100644 --- a/app/src/main/res/values-in-rID/strings.xml +++ b/app/src/main/res/values-in-rID/strings.xml @@ -69,8 +69,8 @@ Gagal untuk mendapatkan versi Disimpan di %s Gagal untuk menyimpan %s - Berhasil memasang Aliucord - Pemasangan Aliucord dibatalkan + Berhasil memasang Aliucord + Pemasangan Aliucord dibatalkan Failed to verify download Silahkan copot versi Aliucord Anda saat ini untuk melanjutkan! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index 19c2d11..fb4b86f 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-iw-rIL/strings.xml b/app/src/main/res/values-iw-rIL/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-iw-rIL/strings.xml +++ b/app/src/main/res/values-iw-rIL/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index b6879dc..46e3bec 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -70,8 +70,8 @@ バージョンの取得に失敗しました %sに保存しました %sの保存に失敗しました - Aliucordのインストールに成功しました - Aliucordのインストールが中断されました + Aliucordのインストールに成功しました + Aliucordのインストールが中断されました Failed to verify download 続行するには現在のバージョンのAliucordをアンインストールしてください! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-kaa/strings.xml b/app/src/main/res/values-kaa/strings.xml index 2fb124c..d1a981e 100644 --- a/app/src/main/res/values-kaa/strings.xml +++ b/app/src/main/res/values-kaa/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Aliucord tabıslı ornatıldı - Aborted Aliucord installation + Aliucord tabıslı ornatıldı + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 33447b9..0565925 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -69,8 +69,8 @@ 버전을 가져오지 못했어요 %s에 저장했어요 %s을(를) 저장하지 못했어요 - Aliucord 설치를 완료했어요 - Aliucord 설치를 중단했어요 + Aliucord 설치를 완료했어요 + Aliucord 설치를 중단했어요 Failed to verify download 계속하려면 Aliucord의 현재 버전을 설치 삭제해 주세요! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml index 2832f4e..6703b1c 100644 --- a/app/src/main/res/values-nl-rNL/strings.xml +++ b/app/src/main/res/values-nl-rNL/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-no-rNO/strings.xml b/app/src/main/res/values-no-rNO/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-no-rNO/strings.xml +++ b/app/src/main/res/values-no-rNO/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index 4bab031..2755933 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -69,8 +69,8 @@ Nie można pobrać wersji Zapisano do %s Nie można zapisać %s - Pomyślnie zainstalowano Aliucord - Przerwano instalację Aliucord + Pomyślnie zainstalowano Aliucord + Przerwano instalację Aliucord Failed to verify download Odinstaluj aktualną wersję Aliucorda, aby kontynuować! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 132bf4e..9361a83 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -69,8 +69,8 @@ Falha ao obter versão Salvo em %s Falha ao salvar %s - Aliucord instalado com sucesso - Instalação do Aliucord abortada + Aliucord instalado com sucesso + Instalação do Aliucord abortada Falha ao verificar o download Por favor, desinstale a versão atual do Aliucord para continuar! Uma vez que as otimizações da bateria não foram desativadas, minimizar esta tela poderá abortar o processo de instalação! diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 4657183..c0540a3 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -69,8 +69,8 @@ Falha ao recuperar versão Guardado em %s Failed to save %s - O Aliucord foi instalado com sucesso - Abortar a instalação de Aliucord + O Aliucord foi instalado com sucesso + Abortar a instalação de Aliucord Failed to verify download Por favor, desinstale a versão atual do Aliucord de modo a continuar o processo! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml index ea96859..0622dc7 100644 --- a/app/src/main/res/values-ro-rRO/strings.xml +++ b/app/src/main/res/values-ro-rRO/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index edd38a7..1174364 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -69,8 +69,8 @@ Не удалось получить версию Сохранено в %s Не удалось сохранить в %s - Aliucord успешно установлен - Установка Aliucord отменена + Aliucord успешно установлен + Установка Aliucord отменена Не удалось верифицировать скачивание Пожалуйста, удалите вашу текущую версию Aliucord, чтобы продолжить! Поскольку оптимизация работы аккумулятора не была отключена, сворачивание этого экрана может прервать процесс установки! diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-sr-rSP/strings.xml +++ b/app/src/main/res/values-sr-rSP/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index d079fbc..a91625b 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -69,8 +69,8 @@ Sürüm alınamadı %s\'a Kaydedildi %s Kaydedilemedi - Aliucord başarıyla indirildi - Aliucord kurulumu iptal edildi + Aliucord başarıyla indirildi + Aliucord kurulumu iptal edildi Failed to verify download Devam etmek için şimdiki Aliucord sürümünüzü lütfen silin! diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index 4f63163..e668cc3 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -69,8 +69,8 @@ Failed to retrieve version Saved to %s Failed to save %s - Successfully installed Aliucord - Aborted Aliucord installation + Successfully installed Aliucord + Aborted Aliucord installation Failed to verify download Please uninstall your current version of Aliucord in order to continue! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml index e076660..e9b74dd 100644 --- a/app/src/main/res/values-vi-rVN/strings.xml +++ b/app/src/main/res/values-vi-rVN/strings.xml @@ -69,8 +69,8 @@ Không truy xuất được phiên bản Đã lưu vào %s Lưu trữ thất bại %s - Aliucord đã được cài đặt thành công - Đã hủy bỏ cài đặt Aliucord + Aliucord đã được cài đặt thành công + Đã hủy bỏ cài đặt Aliucord Failed to verify download Vui lòng gỡ cài đặt phiên bản Aliucord hiện tại của bạn để tiếp tục! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 08b7d25..a9acbc6 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -69,8 +69,8 @@ 獲得版本失敗 儲存到 %s 儲存到 %s 失敗 - 成功地安裝 Aliucord - 中斷 Aliucord 安裝 + 成功地安裝 Aliucord + 中斷 Aliucord 安裝 Failed to verify download 請解除安裝目前版本的 Aliucord 以繼續! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index e2bb1b6..fcf6024 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -69,8 +69,8 @@ 獲得版本失敗 儲存到 %s 儲存到 %s 失敗 - 成功地安裝 Aliucord - 中斷 Aliucord 安裝 + 成功地安裝 Aliucord + 中斷 Aliucord 安裝 Failed to verify download 請解除安裝目前版本的 Aliucord 以繼續! Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ed9177f..c402b62 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -80,19 +80,21 @@ Saved to %s Failed to save %s - Successfully installed Aliucord - Cancelled Aliucord installation - Failed to verify download + Successfully installed Aliucord + Cancelled Aliucord installation Please uninstall your current version of Aliucord in order to continue! + Successfully uninstalled Aliucord + Cancelled uninstallation + Failed to verify download Since battery optimizations have not been disabled, minimizing this screen may abort the installation process! - Failed to install (Unknown reason) + Installer failure (Unknown reason) Installation was blocked One or more APKs were invalid or corrupt Conflicts with an existing app, usually due to mismatched signatures Not enough available storage to install Application is incompatible with this device - Installation timed out + Installer timed out Prepare Download dependencies @@ -100,7 +102,7 @@ Install APK Fetching target Discord version - Checking for older installations + Checking for newer installations Downloading Discord APK Downloading Kotlin Stdlib Downloading Aliucord Injector