Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add installer status dialog #1473

Merged
merged 36 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b1a821d
feat: Add installer status dialog
oSumAtrIX Jan 23, 2024
d147cd6
Adjust strings
oSumAtrIX Jan 24, 2024
8270999
Log extra status message
oSumAtrIX Jan 24, 2024
887794c
Merge branch 'compose-dev' into feat/installer-dialog
BenjaminHalko Jun 24, 2024
7462f5f
Merge branch 'compose-dev' into feat/installer-dialog
BenjaminHalko Jun 30, 2024
72fc6d2
Update PatcherViewModel.kt
BenjaminHalko Jul 6, 2024
63b5e5d
Merge remote-tracking branch 'origin/compose-dev' into feat/installer…
BenjaminHalko Jul 6, 2024
ffe5826
Show conflict dialogue when attempting to install a lower version
BenjaminHalko Jul 7, 2024
6f62bfb
Check for fingerprint differences and base APKs
BenjaminHalko Jul 7, 2024
cadc3da
Remove fingerprint detection
BenjaminHalko Jul 7, 2024
fcb2242
Clean up code
BenjaminHalko Jul 7, 2024
9527c9a
Merge remote-tracking branch 'origin/compose-dev' into feat/installer…
BenjaminHalko Jul 27, 2024
874f8ba
Updated detection of lower versions
BenjaminHalko Jul 27, 2024
0d23d7e
Update app/src/main/java/app/revanced/manager/ui/component/InstallerS…
BenjaminHalko Jul 28, 2024
3c4eaf5
Update app/src/main/res/values/strings.xml
BenjaminHalko Jul 28, 2024
2fa5461
Update app/src/main/res/values/strings.xml
BenjaminHalko Jul 28, 2024
4539584
Applied fixes
BenjaminHalko Jul 28, 2024
dc05099
Added missing semicolon
BenjaminHalko Jul 28, 2024
e48013a
Hide install button when installing
BenjaminHalko Jul 28, 2024
3f92371
Get version code the correct way
BenjaminHalko Jul 28, 2024
3c268b7
Merge remote-tracking branch 'origin/compose-dev' into feat/installer…
BenjaminHalko Aug 4, 2024
32d4681
Fixed some issues
BenjaminHalko Aug 4, 2024
982d3e6
Set `isInstalling` to false when a root error occurs
BenjaminHalko Aug 4, 2024
970c3fc
Update app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherVie…
BenjaminHalko Aug 6, 2024
46e6cc8
Merge branch 'compose-dev' into feat/installer-dialog
BenjaminHalko Aug 11, 2024
23c41ac
Add finally block
BenjaminHalko Aug 11, 2024
8554692
Update PatcherViewModel.kt
BenjaminHalko Aug 12, 2024
dc7b419
Update getting package info
BenjaminHalko Aug 12, 2024
59f6903
Update app/src/main/java/app/revanced/manager/util/PM.kt
BenjaminHalko Aug 15, 2024
87261b7
Update PatcherViewModel.kt
BenjaminHalko Aug 15, 2024
d709990
Merge branch 'compose-dev' into feat/installer-dialog
BenjaminHalko Aug 15, 2024
800b138
Update app/src/main/res/values/strings.xml
BenjaminHalko Aug 18, 2024
6794349
Update strings.xml
BenjaminHalko Aug 21, 2024
cea0c66
Update app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherVie…
BenjaminHalko Aug 28, 2024
c636949
Merge branch 'compose-dev' into feat/installer-dialog
BenjaminHalko Aug 28, 2024
5c4899f
Use output APK
BenjaminHalko Aug 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ dependencies {
// Scrollbars
implementation(libs.scrollbars)

// EnumUtil
implementation(libs.enumutil)
ksp(libs.enumutil.ksp)

// Reorderable lists
implementation(libs.reorderable)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package app.revanced.manager.ui.component

import android.content.pm.PackageInstaller
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material.icons.outlined.ErrorOutline
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import app.revanced.manager.R
import com.github.materiiapps.enumutil.FromValue

private typealias InstallerStatusDialogButtonHandler = ((model: InstallerModel) -> Unit)
private typealias InstallerStatusDialogButton = @Composable (model: InstallerStatusDialogModel) -> Unit

interface InstallerModel {
fun reinstall()
fun install()
}

interface InstallerStatusDialogModel : InstallerModel {
var packageInstallerStatus: Int?
}

@Composable
fun InstallerStatusDialog(model: InstallerStatusDialogModel) {
val dialogKind = remember {
DialogKind.fromValue(model.packageInstallerStatus!!) ?: DialogKind.FAILURE
}

AlertDialog(
onDismissRequest = {
model.packageInstallerStatus = null
},
confirmButton = {
dialogKind.confirmButton(model)
},
dismissButton = {
dialogKind.dismissButton?.invoke(model)
},
icon = {
Icon(dialogKind.icon, null)
},
title = {
Text(
text = stringResource(dialogKind.title),
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
color = MaterialTheme.colorScheme.onSurface,
)
},
text = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Text(stringResource(dialogKind.contentStringResId))
}
}
)
}

private fun installerStatusDialogButton(
@StringRes buttonStringResId: Int,
buttonHandler: InstallerStatusDialogButtonHandler = { },
): InstallerStatusDialogButton = { model ->
TextButton(
onClick = {
model.packageInstallerStatus = null
buttonHandler(model)
}
) {
Text(stringResource(buttonStringResId))
}
}

@FromValue("flag")
enum class DialogKind(
val flag: Int,
val title: Int,
@StringRes val contentStringResId: Int,
val icon: ImageVector = Icons.Outlined.ErrorOutline,
val confirmButton: InstallerStatusDialogButton = installerStatusDialogButton(R.string.ok),
val dismissButton: InstallerStatusDialogButton? = null,
) {
FAILURE(
flag = PackageInstaller.STATUS_FAILURE,
title = R.string.installation_failed_dialog_title,
contentStringResId = R.string.installation_failed_description,
confirmButton = installerStatusDialogButton(R.string.install_app) { model ->
model.install()
}
),
FAILURE_ABORTED(
flag = PackageInstaller.STATUS_FAILURE_ABORTED,
title = R.string.installation_cancelled_dialog_title,
contentStringResId = R.string.installation_aborted_description,
confirmButton = installerStatusDialogButton(R.string.install_app) { model ->
model.install()
}
),
FAILURE_BLOCKED(
flag = PackageInstaller.STATUS_FAILURE_BLOCKED,
title = R.string.installation_blocked_dialog_title,
contentStringResId = R.string.installation_blocked_description,
),
FAILURE_CONFLICT(
flag = PackageInstaller.STATUS_FAILURE_CONFLICT,
title = R.string.installation_conflict_dialog_title,
contentStringResId = R.string.installation_conflict_description,
confirmButton = installerStatusDialogButton(R.string.reinstall) { model ->
model.reinstall()
},
dismissButton = installerStatusDialogButton(R.string.cancel),
),
FAILURE_INCOMPATIBLE(
flag = PackageInstaller.STATUS_FAILURE_INCOMPATIBLE,
title = R.string.installation_incompatible_dialog_title,
contentStringResId = R.string.installation_incompatible_description,
),
FAILURE_INVALID(
flag = PackageInstaller.STATUS_FAILURE_INVALID,
title = R.string.installation_invalid_dialog_title,
contentStringResId = R.string.installation_invalid_description,
confirmButton = installerStatusDialogButton(R.string.reinstall) { model ->
model.reinstall()
},
dismissButton = installerStatusDialogButton(R.string.cancel),
),
FAILURE_STORAGE(
flag = PackageInstaller.STATUS_FAILURE_STORAGE,
title = R.string.installation_storage_issue_dialog_title,
contentStringResId = R.string.installation_storage_issue_description,
),

@RequiresApi(34)
FAILURE_TIMEOUT(
flag = PackageInstaller.STATUS_FAILURE_TIMEOUT,
title = R.string.installation_timeout_dialog_title,
contentStringResId = R.string.installation_timeout_description,
confirmButton = installerStatusDialogButton(R.string.install_app) { model ->
model.install()
},
);
// Needed due to the @FromValue annotation.
companion object
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import app.revanced.manager.R
import app.revanced.manager.ui.component.AppScaffold
import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.InstallerStatusDialog
import app.revanced.manager.ui.component.patcher.InstallPickerDialog
import app.revanced.manager.ui.component.patcher.Steps
import app.revanced.manager.ui.model.State
Expand All @@ -61,6 +62,7 @@ fun PatcherScreen(

val patcherSucceeded by vm.patcherSucceeded.observeAsState(null)
val canInstall by remember { derivedStateOf { patcherSucceeded == true && (vm.installedPackageName != null || !vm.isInstalling) } }
val canSaveApk by remember { derivedStateOf { patcherSucceeded == true && (vm.installedPackageName != null) } }
BenjaminHalko marked this conversation as resolved.
Show resolved Hide resolved
var showInstallPicker by rememberSaveable { mutableStateOf(false) }

val steps by remember {
Expand Down Expand Up @@ -91,6 +93,9 @@ fun PatcherScreen(
onConfirm = vm::install
)

if (vm.installerStatusDialogModel.packageInstallerStatus != null)
InstallerStatusDialog(vm.installerStatusDialogModel)

AppScaffold(
topBar = {
AppTopBar(
Expand All @@ -103,7 +108,7 @@ fun PatcherScreen(
actions = {
IconButton(
onClick = { exportApkLauncher.launch("${vm.packageName}.apk") },
enabled = canInstall
enabled = canSaveApk
) {
Icon(Icons.Outlined.Save, stringResource(id = R.string.save_apk))
}
Expand Down Expand Up @@ -172,4 +177,4 @@ fun PatcherScreen(
}
}
}
}
}
Loading