Skip to content

Commit

Permalink
feat(installer): Store install metadata in APK
Browse files Browse the repository at this point in the history
  • Loading branch information
rushiiMachine committed Jul 31, 2024
1 parent c5eded4 commit e4eddf7
Show file tree
Hide file tree
Showing 25 changed files with 160 additions and 28 deletions.
8 changes: 2 additions & 6 deletions app/src/main/kotlin/com/aliucord/manager/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.aliucord.manager.ui.screens.home.HomeScreen
import com.aliucord.manager.ui.screens.install.InstallScreen
import com.aliucord.manager.ui.screens.installopts.InstallOptionsScreen
import com.aliucord.manager.ui.widgets.updater.UpdaterDialog
import com.aliucord.manager.util.IS_CUSTOM_BUILD
import org.koin.android.ext.android.inject

// TODO: move to a path provider in DI
Expand All @@ -46,12 +47,7 @@ class MainActivity : ComponentActivity() {
) {
StoragePermissionsDialog()

@Suppress("KotlinConstantConditions")
if (
BuildConfig.GIT_BRANCH == "release" &&
!BuildConfig.GIT_LOCAL_CHANGES &&
!BuildConfig.GIT_LOCAL_COMMITS
) {
if (!IS_CUSTOM_BUILD) {
UpdaterDialog()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.aliucord.manager.installer

import com.aliucord.manager.ui.screens.installopts.InstallOptions
import kotlinx.serialization.Serializable

/**
* Data stored inside patched APKs as "aliucord.json" in order to preserve install-time information about Aliucord and the Manager.
*/
@Serializable
data class InstallMetadata(
/**
* The user-selected options for this installation.
*/
val options: InstallOptions,

/**
* The semver version of this manager that performed the installation.
*/
val managerVersionName: String,

/**
* Whether the manager is a real release or built from source.
*/
val customManager: Boolean,

/**
* Aliuhook release version injected into the APK.
*/
val aliuhookVersion: String,

/**
* Short commit hash of the commit the injector was built from.
*/
val injectorCommitHash: String,
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.aliucord.manager.installer.steps
package com.aliucord.manager.installer

import com.aliucord.manager.installer.steps.download.*
import com.aliucord.manager.installer.steps.install.*
Expand Down Expand Up @@ -29,6 +29,7 @@ class KotlinInstallRunner(options: InstallOptions) : StepRunner() {
PatchManifestStep(options),
AddInjectorStep(),
AddAliuhookStep(),
SaveMetadataStep(options),

// Install
AlignmentStep(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.aliucord.manager.installer.steps
package com.aliucord.manager.installer

import android.content.Context
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.base.DownloadStep
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.manager.PreferencesManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.content.Context
import androidx.compose.runtime.Stable
import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.manager.DownloadManager
import com.aliucord.manager.util.showToast
import kotlinx.coroutines.Dispatchers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.aliucord.manager.installer.steps.base
import androidx.annotation.StringRes
import androidx.compose.runtime.*
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.util.toPrecision
import kotlinx.coroutines.*
import org.koin.core.time.measureTimedValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.aliucord.manager.installer.steps.download

import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.manager.PathManager
import org.koin.core.component.KoinComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.aliucord.manager.installer.steps.download
import androidx.compose.runtime.Stable
import com.aliucord.manager.R
import com.aliucord.manager.domain.repository.AliucordMavenRepository
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.DownloadStep
import com.aliucord.manager.manager.PathManager
import com.aliucord.manager.network.utils.getOrThrow
Expand All @@ -21,7 +21,8 @@ class DownloadAliuhookStep : DownloadStep(), KoinComponent {
/**
* This is populated right before the download starts (ref: [execute])
*/
private lateinit var targetVersion: String
lateinit var targetVersion: String
private set

override val localizedName = R.string.install_step_dl_aliuhook
override val targetUrl get() = AliucordMavenRepository.getAliuhookUrl(targetVersion)
Expand All @@ -33,4 +34,3 @@ class DownloadAliuhookStep : DownloadStep(), KoinComponent {
super.execute(container)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.aliucord.manager.installer.steps.download

import androidx.compose.runtime.Stable
import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.DownloadStep
import com.aliucord.manager.installer.steps.prepare.FetchInfoStep
import com.aliucord.manager.manager.PathManager
Expand All @@ -21,7 +21,8 @@ class DownloadInjectorStep : DownloadStep(), KoinComponent {
* Populated from a dependency step ([FetchInfoStep]).
* This is used as cache invalidation (ref: [Version.aliucordHash])
*/
private lateinit var aliucordHash: String
lateinit var aliucordHash: String
private set

override val localizedName = R.string.install_step_dl_injector
override val targetUrl = URL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.aliucord.manager.installer.steps.install
import android.os.Build
import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.base.StepState
import com.aliucord.manager.installer.steps.download.CopyDependenciesStep
Expand All @@ -14,8 +14,6 @@ import org.koin.core.component.KoinComponent
* Align certain files in the APK to a 4KiB boundary.
*/
class AlignmentStep : Step(), KoinComponent {
private val currentDeviceArch = Build.SUPPORTED_ABIS.first()

override val group = StepGroup.Install
override val localizedName = R.string.install_step_alignment

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.aliucord.manager.installer.steps.install

import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.base.StepState
import com.aliucord.manager.manager.PathManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.content.Context
import androidx.lifecycle.*
import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.base.StepState
import com.aliucord.manager.installer.steps.download.CopyDependenciesStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.aliucord.manager.installer.steps.install

import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.download.CopyDependenciesStep
import com.aliucord.manager.installer.util.Signer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.aliucord.manager.installer.steps.patch
import android.os.Build
import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.download.CopyDependenciesStep
import com.aliucord.manager.installer.steps.download.DownloadAliuhookStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.aliucord.manager.installer.steps.patch

import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.download.*
import com.github.diamondminer88.zip.ZipReader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.aliucord.manager.installer.steps.patch

import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.download.CopyDependenciesStep
import com.aliucord.manager.installer.util.ManifestPatcher
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import android.os.Build
import androidx.compose.runtime.Stable
import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.base.StepState
import com.aliucord.manager.installer.steps.download.CopyDependenciesStep
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.aliucord.manager.installer.steps.patch

import com.aliucord.manager.BuildConfig
import com.aliucord.manager.R
import com.aliucord.manager.installer.InstallMetadata
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.download.*
import com.aliucord.manager.ui.screens.installopts.InstallOptions
import com.aliucord.manager.util.IS_CUSTOM_BUILD
import com.github.diamondminer88.zip.ZipWriter
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

/**
* Store the install options and additional data inside the APK for future use,
* for example checking what library versions were used, or performing "updates" while
* maintaining the same install options as what was used upon first install.
*/
class SaveMetadataStep(private val options: InstallOptions) : Step() {
override val group = StepGroup.Patch
override val localizedName = R.string.install_step_save_metadata

private val json = Json {
prettyPrint = true
}

override suspend fun execute(container: StepRunner) {
val apk = container.getStep<CopyDependenciesStep>().patchedApk
val aliuhook = container.getStep<DownloadAliuhookStep>()
val injector = container.getStep<DownloadInjectorStep>()

val metadata = InstallMetadata(
options = options,
managerVersionName = BuildConfig.VERSION_NAME,
customManager = IS_CUSTOM_BUILD,
aliuhookVersion = aliuhook.targetVersion,
injectorCommitHash = injector.aliucordHash,
)

ZipWriter(apk, /* append = */ true).use {
it.writeEntry("aliucord.json", json.encodeToString<InstallMetadata>(metadata))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.aliucord.manager.installer.steps.prepare
import android.content.Context
import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.base.StepState
import com.aliucord.manager.installer.util.uninstallApk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.aliucord.manager.installer.steps.prepare
import androidx.compose.runtime.Stable
import com.aliucord.manager.R
import com.aliucord.manager.installer.steps.StepGroup
import com.aliucord.manager.installer.steps.StepRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.network.dto.Version
import com.aliucord.manager.network.service.AliucordGithubService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import com.aliucord.manager.BuildConfig
import com.aliucord.manager.R
import com.aliucord.manager.installer.KotlinInstallRunner
import com.aliucord.manager.installer.StepRunner
import com.aliucord.manager.installer.steps.*
import com.aliucord.manager.installer.steps.base.Step
import com.aliucord.manager.installer.steps.base.StepState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import android.os.Parcelable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import com.aliucord.manager.util.ColorParceler
import com.aliucord.manager.util.ColorSerializer
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.TypeParceler
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Immutable
@Parcelize
@Serializable
data class InstallOptions(
/**
* The app name that's user-facing in launchers.
Expand Down Expand Up @@ -38,12 +42,15 @@ data class InstallOptions(
) : Parcelable {
@Immutable
@Parcelize
@Serializable
sealed interface IconReplacement : Parcelable {
/**
* Keeps the original icons that are present in the APK.
*/
@Immutable
@Parcelize
@Serializable
@SerialName("original")
data object Original : IconReplacement

/**
Expand All @@ -52,8 +59,11 @@ data class InstallOptions(
*/
@Immutable
@Parcelize
@Serializable
@SerialName("color")
data class CustomColor(
@TypeParceler<Color, ColorParceler>
@Serializable(ColorSerializer::class)
val color: Color,
) : IconReplacement

Expand All @@ -63,6 +73,8 @@ data class InstallOptions(
*/
@Immutable
@Parcelize
@Serializable
@SerialName("image")
data class CustomImage(val imageBytes: ByteArray) : IconReplacement {
override fun hashCode() = imageBytes.contentHashCode()
override fun equals(other: Any?) = this === other
Expand Down
28 changes: 28 additions & 0 deletions app/src/main/kotlin/com/aliucord/manager/util/ColorSerializer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.aliucord.manager.util

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

/**
* (De)serialize compose's [Color] into/from an ARGB hex string.
*/
object ColorSerializer : KSerializer<Color> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)

override fun serialize(encoder: Encoder, value: Color) {
val string = value
.toArgb().toUInt()
.toString(16)
.padStart(8, '0')
encoder.encodeString(string)
}

override fun deserialize(decoder: Decoder): Color {
val string = decoder.decodeString()
return Color(string.toInt(16))
}
}
Loading

0 comments on commit e4eddf7

Please sign in to comment.