mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-05-01 14:34:24 +02:00
fix patcher screen
Remaining WIP: update dashboard screen to feature a dialog
This commit is contained in:
parent
625abd72b0
commit
00c61b6adc
@ -6,7 +6,6 @@ import androidx.annotation.StringRes
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Check
|
|
||||||
import androidx.compose.material.icons.outlined.ErrorOutline
|
import androidx.compose.material.icons.outlined.ErrorOutline
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@ -21,35 +20,25 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.ui.model.InstallerModel
|
||||||
import com.github.materiiapps.enumutil.FromValue
|
import com.github.materiiapps.enumutil.FromValue
|
||||||
|
|
||||||
private typealias InstallerStatusDialogButtonHandler = ((model: InstallerModel) -> Unit)
|
private typealias InstallerStatusDialogButtonHandler = ((model: InstallerModel) -> Unit)
|
||||||
private typealias InstallerStatusDialogButton = @Composable (model: InstallerStatusDialogModel) -> Unit
|
private typealias InstallerStatusDialogButton = @Composable (model: InstallerModel, dismiss: () -> Unit) -> Unit
|
||||||
|
|
||||||
interface InstallerModel {
|
|
||||||
fun reinstall()
|
|
||||||
fun install()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InstallerStatusDialogModel : InstallerModel {
|
|
||||||
var packageInstallerStatus: Int?
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InstallerStatusDialog(model: InstallerStatusDialogModel) {
|
fun InstallerStatusDialog(installerStatus: Int, model: InstallerModel, onDismiss: () -> Unit) {
|
||||||
val dialogKind = remember {
|
val dialogKind = remember {
|
||||||
DialogKind.fromValue(model.packageInstallerStatus!!) ?: DialogKind.FAILURE
|
DialogKind.fromValue(installerStatus) ?: DialogKind.FAILURE
|
||||||
}
|
}
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = {
|
onDismissRequest = onDismiss,
|
||||||
model.packageInstallerStatus = null
|
|
||||||
},
|
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
dialogKind.confirmButton(model)
|
dialogKind.confirmButton(model, onDismiss)
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
dialogKind.dismissButton?.invoke(model)
|
dialogKind.dismissButton?.invoke(model, onDismiss)
|
||||||
},
|
},
|
||||||
icon = {
|
icon = {
|
||||||
Icon(dialogKind.icon, null)
|
Icon(dialogKind.icon, null)
|
||||||
@ -75,10 +64,10 @@ fun InstallerStatusDialog(model: InstallerStatusDialogModel) {
|
|||||||
private fun installerStatusDialogButton(
|
private fun installerStatusDialogButton(
|
||||||
@StringRes buttonStringResId: Int,
|
@StringRes buttonStringResId: Int,
|
||||||
buttonHandler: InstallerStatusDialogButtonHandler = { },
|
buttonHandler: InstallerStatusDialogButtonHandler = { },
|
||||||
): InstallerStatusDialogButton = { model ->
|
): InstallerStatusDialogButton = { model, dismiss ->
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
model.packageInstallerStatus = null
|
dismiss()
|
||||||
buttonHandler(model)
|
buttonHandler(model)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@ -154,6 +143,7 @@ enum class DialogKind(
|
|||||||
model.install()
|
model.install()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Needed due to the @FromValue annotation.
|
// Needed due to the @FromValue annotation.
|
||||||
companion object
|
companion object
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package app.revanced.manager.ui.model
|
||||||
|
|
||||||
|
interface InstallerModel {
|
||||||
|
fun reinstall()
|
||||||
|
fun install()
|
||||||
|
}
|
@ -3,7 +3,6 @@ package app.revanced.manager.ui.model
|
|||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
enum class StepCategory(@StringRes val displayName: Int) {
|
enum class StepCategory(@StringRes val displayName: Int) {
|
||||||
|
@ -74,8 +74,9 @@ fun PatcherScreen(
|
|||||||
onConfirm = vm::install
|
onConfirm = vm::install
|
||||||
)
|
)
|
||||||
|
|
||||||
if (vm.installerStatusDialogModel.packageInstallerStatus != null)
|
vm.packageInstallerStatus?.let {
|
||||||
InstallerStatusDialog(vm.installerStatusDialogModel)
|
InstallerStatusDialog(it, vm, vm::dismissPackageInstallerDialog)
|
||||||
|
}
|
||||||
|
|
||||||
AppScaffold(
|
AppScaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
|
@ -3,6 +3,7 @@ package app.revanced.manager.ui.viewmodel
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
@ -19,6 +20,7 @@ import app.revanced.manager.domain.bundles.RemotePatchBundle
|
|||||||
import app.revanced.manager.domain.manager.PreferencesManager
|
import app.revanced.manager.domain.manager.PreferencesManager
|
||||||
import app.revanced.manager.domain.repository.PatchBundleRepository
|
import app.revanced.manager.domain.repository.PatchBundleRepository
|
||||||
import app.revanced.manager.network.api.ReVancedAPI
|
import app.revanced.manager.network.api.ReVancedAPI
|
||||||
|
import app.revanced.manager.util.PM
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
import app.revanced.manager.util.uiSafe
|
import app.revanced.manager.util.uiSafe
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
@ -30,7 +32,8 @@ class DashboardViewModel(
|
|||||||
private val patchBundleRepository: PatchBundleRepository,
|
private val patchBundleRepository: PatchBundleRepository,
|
||||||
private val reVancedAPI: ReVancedAPI,
|
private val reVancedAPI: ReVancedAPI,
|
||||||
private val networkInfo: NetworkInfo,
|
private val networkInfo: NetworkInfo,
|
||||||
val prefs: PreferencesManager
|
val prefs: PreferencesManager,
|
||||||
|
private val pm: PM,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val availablePatches =
|
val availablePatches =
|
||||||
patchBundleRepository.bundles.map { it.values.sumOf { bundle -> bundle.patches.size } }
|
patchBundleRepository.bundles.map { it.values.sumOf { bundle -> bundle.patches.size } }
|
||||||
@ -44,6 +47,14 @@ class DashboardViewModel(
|
|||||||
var showBatteryOptimizationsWarning by mutableStateOf(false)
|
var showBatteryOptimizationsWarning by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Android 11 kills the app process after granting the "install apps" permission, which is a problem for the patcher screen.
|
||||||
|
* This value is true when the conditions that trigger the bug are met.
|
||||||
|
*
|
||||||
|
* See: https://github.com/ReVanced/revanced-manager/issues/2138
|
||||||
|
*/
|
||||||
|
val android11BugActive get() = Build.VERSION.SDK_INT == Build.VERSION_CODES.R && !pm.canInstallPackages()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
checkForManagerUpdates()
|
checkForManagerUpdates()
|
||||||
|
@ -7,6 +7,7 @@ import android.content.Intent
|
|||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.content.pm.PackageInstaller
|
import android.content.pm.PackageInstaller
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.ParcelUuid
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
@ -36,8 +37,8 @@ import app.revanced.manager.patcher.logger.Logger
|
|||||||
import app.revanced.manager.patcher.worker.PatcherWorker
|
import app.revanced.manager.patcher.worker.PatcherWorker
|
||||||
import app.revanced.manager.service.InstallService
|
import app.revanced.manager.service.InstallService
|
||||||
import app.revanced.manager.service.UninstallService
|
import app.revanced.manager.service.UninstallService
|
||||||
import app.revanced.manager.ui.component.InstallerStatusDialogModel
|
|
||||||
import app.revanced.manager.ui.destination.Destination
|
import app.revanced.manager.ui.destination.Destination
|
||||||
|
import app.revanced.manager.ui.model.InstallerModel
|
||||||
import app.revanced.manager.ui.model.ProgressKey
|
import app.revanced.manager.ui.model.ProgressKey
|
||||||
import app.revanced.manager.ui.model.SelectedApp
|
import app.revanced.manager.ui.model.SelectedApp
|
||||||
import app.revanced.manager.ui.model.State
|
import app.revanced.manager.ui.model.State
|
||||||
@ -62,15 +63,12 @@ import org.koin.core.component.inject
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
|
|
||||||
// @SuppressLint("AutoboxingStateCreation")
|
|
||||||
@Stable
|
@Stable
|
||||||
@OptIn(SavedStateHandleSaveableApi::class)
|
@OptIn(SavedStateHandleSaveableApi::class)
|
||||||
class PatcherViewModel(
|
class PatcherViewModel(
|
||||||
private val input: Destination.Patcher
|
private val input: Destination.Patcher
|
||||||
) : ViewModel(), KoinComponent, StepProgressProvider {
|
) : ViewModel(), KoinComponent, StepProgressProvider, InstallerModel {
|
||||||
private val app: Application by inject()
|
private val app: Application by inject()
|
||||||
private val fs: Filesystem by inject()
|
private val fs: Filesystem by inject()
|
||||||
private val pm: PM by inject()
|
private val pm: PM by inject()
|
||||||
@ -79,20 +77,6 @@ class PatcherViewModel(
|
|||||||
private val rootInstaller: RootInstaller by inject()
|
private val rootInstaller: RootInstaller by inject()
|
||||||
private val savedStateHandle: SavedStateHandle by inject()
|
private val savedStateHandle: SavedStateHandle by inject()
|
||||||
|
|
||||||
val installerStatusDialogModel : InstallerStatusDialogModel = object : InstallerStatusDialogModel {
|
|
||||||
override var packageInstallerStatus: Int? by mutableStateOf(null)
|
|
||||||
|
|
||||||
override fun reinstall() {
|
|
||||||
this@PatcherViewModel.reinstall()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun install() {
|
|
||||||
// Since this is a package installer status dialog,
|
|
||||||
// InstallType.ROOT is never used here.
|
|
||||||
install(InstallType.DEFAULT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var installedApp: InstalledApp? = null
|
private var installedApp: InstalledApp? = null
|
||||||
val packageName = input.selectedApp.packageName
|
val packageName = input.selectedApp.packageName
|
||||||
|
|
||||||
@ -105,6 +89,13 @@ class PatcherViewModel(
|
|||||||
}
|
}
|
||||||
private set
|
private set
|
||||||
private var ongoingPmSession: Boolean by savedStateHandle.saveableVar { false }
|
private var ongoingPmSession: Boolean by savedStateHandle.saveableVar { false }
|
||||||
|
var packageInstallerStatus: Int? by savedStateHandle.saveable(
|
||||||
|
key = "packageInstallerStatus",
|
||||||
|
stateSaver = autoSaver()
|
||||||
|
) {
|
||||||
|
mutableStateOf(null)
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
|
||||||
var isInstalling by mutableStateOf(ongoingPmSession)
|
var isInstalling by mutableStateOf(ongoingPmSession)
|
||||||
private set
|
private set
|
||||||
@ -142,7 +133,6 @@ class PatcherViewModel(
|
|||||||
key = "downloadProgress",
|
key = "downloadProgress",
|
||||||
stateSaver = autoSaver()
|
stateSaver = autoSaver()
|
||||||
) {
|
) {
|
||||||
viewModelScope
|
|
||||||
mutableStateOf<Pair<Float, Float>?>(null)
|
mutableStateOf<Pair<Float, Float>?>(null)
|
||||||
}
|
}
|
||||||
private set
|
private set
|
||||||
@ -166,15 +156,19 @@ class PatcherViewModel(
|
|||||||
|
|
||||||
private val workManager = WorkManager.getInstance(app)
|
private val workManager = WorkManager.getInstance(app)
|
||||||
|
|
||||||
private val patcherWorkerId by savedStateHandle.saveable<UUID> {
|
private val patcherWorkerId by savedStateHandle.saveable<ParcelUuid> {
|
||||||
workerRepository.launchExpedited<PatcherWorker, PatcherWorker.Args>(
|
ParcelUuid(workerRepository.launchExpedited<PatcherWorker, PatcherWorker.Args>(
|
||||||
"patching", PatcherWorker.Args(
|
"patching", PatcherWorker.Args(
|
||||||
input.selectedApp,
|
input.selectedApp,
|
||||||
outputFile.path,
|
outputFile.path,
|
||||||
input.selectedPatches,
|
input.selectedPatches,
|
||||||
input.options,
|
input.options,
|
||||||
logger,
|
logger,
|
||||||
onDownloadProgress = { withContext(Dispatchers.Main) { downloadProgress = it } },
|
onDownloadProgress = {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
downloadProgress = it
|
||||||
|
}
|
||||||
|
},
|
||||||
onPatchCompleted = { withContext(Dispatchers.Main) { completedPatchCount += 1 } },
|
onPatchCompleted = { withContext(Dispatchers.Main) { completedPatchCount += 1 } },
|
||||||
setInputFile = { withContext(Dispatchers.Main) { inputFile = it } },
|
setInputFile = { withContext(Dispatchers.Main) { inputFile = it } },
|
||||||
onProgress = { name, state, message ->
|
onProgress = { name, state, message ->
|
||||||
@ -196,11 +190,11 @@ class PatcherViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
val patcherSucceeded =
|
val patcherSucceeded =
|
||||||
workManager.getWorkInfoByIdLiveData(patcherWorkerId).map { workInfo: WorkInfo ->
|
workManager.getWorkInfoByIdLiveData(patcherWorkerId.uuid).map { workInfo: WorkInfo ->
|
||||||
when (workInfo.state) {
|
when (workInfo.state) {
|
||||||
WorkInfo.State.SUCCEEDED -> true
|
WorkInfo.State.SUCCEEDED -> true
|
||||||
WorkInfo.State.FAILED -> false
|
WorkInfo.State.FAILED -> false
|
||||||
@ -234,7 +228,7 @@ class PatcherViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
installerStatusDialogModel.packageInstallerStatus = pmStatus
|
packageInstallerStatus = pmStatus
|
||||||
|
|
||||||
isInstalling = false
|
isInstalling = false
|
||||||
}
|
}
|
||||||
@ -249,7 +243,7 @@ class PatcherViewModel(
|
|||||||
?.let(logger::trace)
|
?.let(logger::trace)
|
||||||
|
|
||||||
if (pmStatus != PackageInstaller.STATUS_SUCCESS) {
|
if (pmStatus != PackageInstaller.STATUS_SUCCESS) {
|
||||||
installerStatusDialogModel.packageInstallerStatus = pmStatus
|
packageInstallerStatus = pmStatus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,7 +271,7 @@ class PatcherViewModel(
|
|||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
app.unregisterReceiver(installerBroadcastReceiver)
|
app.unregisterReceiver(installerBroadcastReceiver)
|
||||||
workManager.cancelWorkById(patcherWorkerId)
|
workManager.cancelWorkById(patcherWorkerId.uuid)
|
||||||
|
|
||||||
if (input.selectedApp is SelectedApp.Installed && installedApp?.installType == InstallType.ROOT) {
|
if (input.selectedApp is SelectedApp.Installed && installedApp?.installType == InstallType.ROOT) {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
GlobalScope.launch(Dispatchers.Main) {
|
||||||
@ -332,7 +326,7 @@ class PatcherViewModel(
|
|||||||
// Check if the app version is less than the installed version
|
// Check if the app version is less than the installed version
|
||||||
if (pm.getVersionCode(currentPackageInfo) < pm.getVersionCode(existingPackageInfo)) {
|
if (pm.getVersionCode(currentPackageInfo) < pm.getVersionCode(existingPackageInfo)) {
|
||||||
// Exit if the selected app version is less than the installed version
|
// Exit if the selected app version is less than the installed version
|
||||||
installerStatusDialogModel.packageInstallerStatus = PackageInstaller.STATUS_FAILURE_CONFLICT
|
packageInstallerStatus = PackageInstaller.STATUS_FAILURE_CONFLICT
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,8 +351,7 @@ class PatcherViewModel(
|
|||||||
// If the app is not installed, check if the output file is a base apk
|
// If the app is not installed, check if the output file is a base apk
|
||||||
if (currentPackageInfo.splitNames != null) {
|
if (currentPackageInfo.splitNames != null) {
|
||||||
// Exit if there is no base APK package
|
// Exit if there is no base APK package
|
||||||
installerStatusDialogModel.packageInstallerStatus =
|
packageInstallerStatus = PackageInstaller.STATUS_FAILURE_INVALID
|
||||||
PackageInstaller.STATUS_FAILURE_INVALID
|
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,12 +397,17 @@ class PatcherViewModel(
|
|||||||
Log.e(tag, "Failed to install", e)
|
Log.e(tag, "Failed to install", e)
|
||||||
app.toast(app.getString(R.string.install_app_fail, e.simpleMessage()))
|
app.toast(app.getString(R.string.install_app_fail, e.simpleMessage()))
|
||||||
} finally {
|
} finally {
|
||||||
if (!pmInstallStarted)
|
if (!pmInstallStarted) isInstalling = false
|
||||||
isInstalling = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reinstall() = viewModelScope.launch {
|
override fun install() {
|
||||||
|
// InstallType.ROOT is never used here since this overload is for the package installer status dialog.
|
||||||
|
install(InstallType.DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reinstall() {
|
||||||
|
viewModelScope.launch {
|
||||||
uiSafe(app, R.string.reinstall_app_fail, "Failed to reinstall") {
|
uiSafe(app, R.string.reinstall_app_fail, "Failed to reinstall") {
|
||||||
pm.getPackageInfo(outputFile)?.packageName?.let { pm.uninstallPackage(it) }
|
pm.getPackageInfo(outputFile)?.packageName?.let { pm.uninstallPackage(it) }
|
||||||
?: throw Exception("Failed to load application info")
|
?: throw Exception("Failed to load application info")
|
||||||
@ -418,6 +416,11 @@ class PatcherViewModel(
|
|||||||
isInstalling = true
|
isInstalling = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissPackageInstallerDialog() {
|
||||||
|
packageInstallerStatus = null
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val TAG = "ReVanced Patcher"
|
const val TAG = "ReVanced Patcher"
|
||||||
|
@ -136,6 +136,8 @@ class PM(
|
|||||||
app.startActivity(it)
|
app.startActivity(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun canInstallPackages() = app.packageManager.canRequestPackageInstalls()
|
||||||
|
|
||||||
private fun PackageInstaller.Session.writeApk(apk: File) {
|
private fun PackageInstaller.Session.writeApk(apk: File) {
|
||||||
apk.inputStream().use { inputStream ->
|
apk.inputStream().use { inputStream ->
|
||||||
openWrite(apk.name, 0, apk.length()).use { outputStream ->
|
openWrite(apk.name, 0, apk.length()).use { outputStream ->
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package app.revanced.manager.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
class RequestInstallAppsContract : ActivityResultContract<String, Boolean>(), KoinComponent {
|
||||||
|
private val pm: PM by inject()
|
||||||
|
override fun createIntent(context: Context, input: String) = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.fromParts("package", input, null))
|
||||||
|
|
||||||
|
override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
|
||||||
|
println("Finished")
|
||||||
|
return pm.canInstallPackages()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user