-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e5b0307
commit 8ed6e41
Showing
26 changed files
with
854 additions
and
373 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
app/src/main/kotlin/com/aliucord/manager/installer/steps/KtStepContainer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.aliucord.manager.installer.steps | ||
|
||
import com.aliucord.manager.installer.steps.base.Step | ||
import com.aliucord.manager.installer.steps.download.* | ||
import com.aliucord.manager.installer.steps.prepare.FetchInfoStep | ||
import kotlinx.collections.immutable.persistentListOf | ||
|
||
/** | ||
* Used for installing the old Kotlin Discord app. | ||
*/ | ||
class KtStepContainer : StepContainer() { | ||
override val steps = persistentListOf<Step>( | ||
FetchInfoStep(), | ||
DownloadDiscordStep(), | ||
DownloadInjectorStep(), | ||
DownloadAliuhookStep(), | ||
DownloadKotlinStep(), | ||
) | ||
} |
48 changes: 48 additions & 0 deletions
48
app/src/main/kotlin/com/aliucord/manager/installer/steps/StepContainer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package com.aliucord.manager.installer.steps | ||
|
||
import com.aliucord.manager.installer.steps.base.Step | ||
import com.aliucord.manager.installer.steps.base.StepState | ||
import com.aliucord.manager.manager.PreferencesManager | ||
import kotlinx.collections.immutable.ImmutableList | ||
import kotlinx.coroutines.delay | ||
import org.koin.core.component.KoinComponent | ||
import org.koin.core.component.inject | ||
|
||
abstract class StepContainer : KoinComponent { | ||
private val preferences: PreferencesManager by inject() | ||
|
||
abstract val steps: ImmutableList<Step> | ||
|
||
/** | ||
* Get a step that has already been successfully executed. | ||
* This is used to retrieve previously executed dependency steps from a later step. | ||
*/ | ||
inline fun <reified T : Step> getCompletedStep(): T { | ||
val step = steps.asSequence() | ||
.filterIsInstance<T>() | ||
.filter { it.state == StepState.Success } | ||
.firstOrNull() | ||
|
||
if (step == null) { | ||
throw IllegalArgumentException("No completed step ${T::class.simpleName} exists in container") | ||
} | ||
|
||
return step | ||
} | ||
|
||
suspend fun executeAll(): Throwable? { | ||
for (step in steps) { | ||
val error = step.executeCatching(this@StepContainer) | ||
if (error != null) return error | ||
|
||
// Add delay for human psychology and | ||
// better group visibility in UI (the active group can change way too fast) | ||
if (!preferences.devMode && step.durationMs < 1000) { | ||
delay(1000L - step.durationMs) | ||
} | ||
|
||
} | ||
|
||
return null | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
app/src/main/kotlin/com/aliucord/manager/installer/steps/StepGroup.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.aliucord.manager.installer.steps | ||
|
||
import androidx.annotation.StringRes | ||
import androidx.compose.runtime.Immutable | ||
import com.aliucord.manager.R | ||
|
||
/** | ||
* A group of steps that is shown under one section in the install UI. | ||
* This has no functional impact. | ||
*/ | ||
@Immutable | ||
enum class StepGroup( | ||
/** | ||
* The UI name to display this group as | ||
*/ | ||
@get:StringRes | ||
val localizedName: Int, | ||
) { | ||
Prepare(R.string.install_group_prepare), | ||
Download(R.string.install_group_download), | ||
Patch(R.string.install_group_patch), | ||
Install(R.string.install_group_install) | ||
} |
85 changes: 85 additions & 0 deletions
85
app/src/main/kotlin/com/aliucord/manager/installer/steps/base/DownloadStep.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package com.aliucord.manager.installer.steps.base | ||
|
||
import android.content.Context | ||
import androidx.compose.runtime.Stable | ||
import com.aliucord.manager.R | ||
import com.aliucord.manager.installer.steps.StepContainer | ||
import com.aliucord.manager.installer.steps.StepGroup | ||
import com.aliucord.manager.manager.DownloadManager | ||
import com.aliucord.manager.util.showToast | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.withContext | ||
import org.koin.core.component.KoinComponent | ||
import org.koin.core.component.inject | ||
import java.io.File | ||
|
||
@Stable | ||
abstract class DownloadStep : Step(), KoinComponent { | ||
private val context: Context by inject() | ||
private val downloads: DownloadManager by inject() | ||
|
||
/** | ||
* The remote url to download | ||
*/ | ||
abstract val targetUrl: String | ||
|
||
/** | ||
* Target path to store the download in. If this file already exists, | ||
* then the cached version is used and the step is marked as cancelled/skipped. | ||
*/ | ||
abstract val targetFile: File | ||
|
||
/** | ||
* Verify that the download completely successfully without errors. | ||
* @throws Throwable If verification fails. | ||
*/ | ||
open suspend fun verify() { | ||
if (!targetFile.exists()) | ||
throw Error("Downloaded file is missing!") | ||
|
||
if (targetFile.length() <= 0) | ||
throw Error("Downloaded file is empty!") | ||
} | ||
|
||
override val group = StepGroup.Download | ||
|
||
override suspend fun execute(container: StepContainer) { | ||
if (targetFile.exists()) { | ||
if (targetFile.length() > 0) { | ||
state = StepState.Skipped | ||
return | ||
} | ||
|
||
targetFile.delete() | ||
} | ||
|
||
val result = downloads.download(targetUrl, targetFile) { newProgress -> | ||
progress = newProgress ?: -1f | ||
} | ||
|
||
when (result) { | ||
is DownloadManager.Result.Success -> { | ||
try { | ||
verify() | ||
} catch (t: Throwable) { | ||
withContext(Dispatchers.Main) { | ||
context.showToast(R.string.installer_dl_verify_fail) | ||
} | ||
|
||
throw t | ||
} | ||
} | ||
|
||
is DownloadManager.Result.Error -> { | ||
withContext(Dispatchers.Main) { | ||
context.showToast(result.localizedReason) | ||
} | ||
|
||
throw Error("Failed to download: ${result.debugReason}") | ||
} | ||
|
||
is DownloadManager.Result.Cancelled -> | ||
state = StepState.Cancelled | ||
} | ||
} | ||
} |
77 changes: 77 additions & 0 deletions
77
app/src/main/kotlin/com/aliucord/manager/installer/steps/base/Step.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
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.StepContainer | ||
import org.koin.core.time.measureTimedValue | ||
import kotlin.math.roundToInt | ||
|
||
/** | ||
* A base install process step. Steps are single-use | ||
*/ | ||
@Stable | ||
abstract class Step { | ||
/** | ||
* The group this step belongs to. | ||
*/ | ||
abstract val group: StepGroup | ||
|
||
/** | ||
* The UI name to display this step as | ||
*/ | ||
@get:StringRes | ||
abstract val localizedName: Int | ||
|
||
/** | ||
* Run the step's logic. | ||
* It can be assumed that this is executed in the correct order after other steps. | ||
*/ | ||
protected abstract suspend fun execute(container: StepContainer) | ||
|
||
/** | ||
* The current state of this step in the installation process. | ||
*/ | ||
var state by mutableStateOf(StepState.Pending) | ||
protected set | ||
|
||
/** | ||
* If the current state is [StepState.Running], then the progress of this step. | ||
* If the progress isn't currently measurable, then this should be set to `-1`. | ||
*/ | ||
var progress by mutableFloatStateOf(-1f) | ||
protected set | ||
|
||
/** | ||
* The total execution time once this step has finished execution. | ||
*/ | ||
// TODO: make this a live value | ||
var durationMs by mutableIntStateOf(0) | ||
private set | ||
|
||
/** | ||
* Thin wrapper over [execute] but handling errors. | ||
* @return An exception if the step failed to execute. | ||
*/ | ||
suspend fun executeCatching(container: StepContainer): Throwable? { | ||
if (state != StepState.Pending) | ||
throw IllegalStateException("Cannot execute a step that has already started") | ||
|
||
state = StepState.Running | ||
|
||
// Execute this steps logic while timing it | ||
val (error, executionTimeMs) = measureTimedValue { | ||
try { | ||
execute(container) | ||
state = StepState.Success | ||
null | ||
} catch (t: Throwable) { | ||
state = StepState.Error | ||
t | ||
} | ||
} | ||
|
||
durationMs = executionTimeMs.roundToInt() | ||
return error | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
app/src/main/kotlin/com/aliucord/manager/installer/steps/base/StepState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.aliucord.manager.installer.steps.base | ||
|
||
enum class StepState { | ||
Pending, | ||
Running, | ||
Success, | ||
Error, | ||
Skipped, | ||
Cancelled, // TODO: something like the discord dnd sign except its not red, but gray maybe | ||
} |
36 changes: 36 additions & 0 deletions
36
app/src/main/kotlin/com/aliucord/manager/installer/steps/download/DownloadAliuhookStep.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
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.StepContainer | ||
import com.aliucord.manager.installer.steps.base.DownloadStep | ||
import com.aliucord.manager.manager.PathManager | ||
import com.aliucord.manager.network.utils.getOrThrow | ||
import org.koin.core.component.KoinComponent | ||
import org.koin.core.component.inject | ||
|
||
/** | ||
* Download a packaged AAR of the latest Aliuhook build from the Aliucord maven. | ||
*/ | ||
@Stable | ||
class DownloadAliuhookStep : DownloadStep(), KoinComponent { | ||
private val paths: PathManager by inject() | ||
private val maven: AliucordMavenRepository by inject() | ||
|
||
/** | ||
* This is populated right before the download starts (ref: [execute]) | ||
*/ | ||
private lateinit var targetVersion: String | ||
|
||
override val localizedName = R.string.install_step_dl_aliuhook | ||
override val targetUrl get() = AliucordMavenRepository.getAliuhookUrl(targetVersion) | ||
override val targetFile get() = paths.cachedAliuhookAAR(targetVersion) | ||
|
||
override suspend fun execute(container: StepContainer) { | ||
targetVersion = maven.getAliuhookVersion().getOrThrow() | ||
|
||
super.execute(container) | ||
} | ||
} | ||
|
39 changes: 39 additions & 0 deletions
39
app/src/main/kotlin/com/aliucord/manager/installer/steps/download/DownloadDiscordStep.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package com.aliucord.manager.installer.steps.download | ||
|
||
import androidx.compose.runtime.Stable | ||
import com.aliucord.manager.BuildConfig | ||
import com.aliucord.manager.R | ||
import com.aliucord.manager.installer.steps.base.DownloadStep | ||
import com.aliucord.manager.manager.PathManager | ||
import org.koin.core.component.KoinComponent | ||
import org.koin.core.component.inject | ||
|
||
/** | ||
* If not already cached, then download the raw unmodified v126.21 (Kotlin) Discord APK | ||
* from a redirect to an APK mirror site provided by the Aliucord backend. | ||
*/ | ||
@Stable | ||
class DownloadDiscordStep : DownloadStep(), KoinComponent { | ||
private val paths: PathManager by inject() | ||
|
||
override val localizedName = R.string.install_step_dl_kt_apk | ||
override val targetUrl = getDiscordApkUrl(DISCORD_KT_VERSION) | ||
override val targetFile = paths.discordApkVersionCache(DISCORD_KT_VERSION) | ||
.resolve("discord.apk") | ||
|
||
override suspend fun verify() { | ||
super.verify() | ||
|
||
// TODO: verify signature | ||
} | ||
|
||
private companion object { | ||
/** | ||
* Last version of Discord before the RN transition. | ||
*/ | ||
const val DISCORD_KT_VERSION = 126021 | ||
|
||
fun getDiscordApkUrl(version: Int) = | ||
"${BuildConfig.BACKEND_URL}/download/discord?v=$version" | ||
} | ||
} |
Oops, something went wrong.