diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c976d38..861d753 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -19,7 +19,9 @@ android { versionCode = 1 versionName = "0.0.1" - vectorDrawables.useSupportLibrary = true + vectorDrawables { + useSupportLibrary = true + } buildConfigField("String", "TAG", "\"AliucordManager\"") buildConfigField("String", "SUPPORT_SERVER", "\"EsNDvBaHVU\"") @@ -68,11 +70,20 @@ android { kotlinOptions { jvmTarget = "11" - freeCompilerArgs += "-Xcontext-receivers" + freeCompilerArgs += listOf( + "-Xcontext-receivers", + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=${buildDir.resolve("report").absolutePath}", + ) } - buildFeatures.compose = true - composeOptions.kotlinCompilerExtensionVersion = "1.3.1" + buildFeatures { + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = "1.3.1" + } } dependencies { @@ -114,6 +125,7 @@ dependencies { implementation("com.aliucord:axml:1.0.1") implementation("com.android.tools.build:apksig:7.4.0-beta04") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5") } fun getCurrentBranch(): String? = diff --git a/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt b/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt index 50aff20..40b25da 100644 --- a/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt +++ b/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt @@ -21,6 +21,7 @@ import com.aliucord.manager.ui.screen.* import com.aliucord.manager.ui.theme.ManagerTheme import com.aliucord.manager.ui.theme.Theme import dev.olshevski.navigation.reimagined.* +import kotlinx.collections.immutable.persistentListOf import org.koin.android.ext.android.inject val aliucordDir = Environment.getExternalStorageDirectory().resolve("Aliucord") @@ -63,7 +64,7 @@ class MainActivity : ComponentActivity() { when (val dest = this.currentHostEntry.destination) { is BaseScreenDestination -> BaseScreen( currentNavItem = dest, - bottomNavItems = listOf(AppDestination.Home, AppDestination.Plugins, AppDestination.Settings), + bottomNavItems = persistentListOf(AppDestination.Home, AppDestination.Plugins, AppDestination.Settings), onNavChanged = { navController.replaceLast(it) } ) { when (dest) { diff --git a/app/src/main/kotlin/com/aliucord/manager/domain/model/Manifest.kt b/app/src/main/kotlin/com/aliucord/manager/domain/model/Manifest.kt index eaa305c..96cc3cb 100644 --- a/app/src/main/kotlin/com/aliucord/manager/domain/model/Manifest.kt +++ b/app/src/main/kotlin/com/aliucord/manager/domain/model/Manifest.kt @@ -5,9 +5,10 @@ package com.aliucord.manager.domain.model +import androidx.compose.runtime.Immutable import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonObject +@Immutable @Serializable data class Manifest( val name: String, @@ -17,7 +18,6 @@ data class Manifest( val updateUrl: String, val changelog: String?, val changelogMedia: String?, - val links: JsonObject ) { val repositoryUrl = updateUrl .replace("raw.githubusercontent.com", "github.com") diff --git a/app/src/main/kotlin/com/aliucord/manager/domain/model/Plugin.kt b/app/src/main/kotlin/com/aliucord/manager/domain/model/Plugin.kt index 5c797bb..8cfd131 100644 --- a/app/src/main/kotlin/com/aliucord/manager/domain/model/Plugin.kt +++ b/app/src/main/kotlin/com/aliucord/manager/domain/model/Plugin.kt @@ -5,6 +5,7 @@ package com.aliucord.manager.domain.model +import androidx.compose.runtime.Immutable import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream @@ -17,6 +18,7 @@ private val json = Json { } @OptIn(ExperimentalSerializationApi::class) +@Immutable data class Plugin(val file: File) { val manifest: Manifest diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/component/home/CommitList.kt b/app/src/main/kotlin/com/aliucord/manager/ui/component/home/CommitList.kt index c7309c9..43c5f60 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/component/home/CommitList.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/component/home/CommitList.kt @@ -24,6 +24,7 @@ import com.aliucord.manager.R import com.aliucord.manager.network.dto.Commit import com.aliucord.manager.ui.component.LoadFailure import com.aliucord.manager.ui.util.annotatingStringResource +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.Flow @Composable @@ -142,8 +143,10 @@ fun CommitItem( Text( annotatingStringResource( R.string.contributors_commit_title, - commit.author.name, - commit.sha.take(8) + persistentListOf( + commit.author.name, + commit.sha.take(8), + ) ) { when (it) { "name" -> SpanStyle( diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/component/installer/InstallGroup.kt b/app/src/main/kotlin/com/aliucord/manager/ui/component/installer/InstallGroup.kt index 55e501d..55db420 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/component/installer/InstallGroup.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/component/installer/InstallGroup.kt @@ -9,7 +9,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.Circle import androidx.compose.material3.* -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -21,6 +21,7 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.unit.* import com.aliucord.manager.R +import kotlinx.collections.immutable.ImmutableList import kotlin.math.floor enum class InstallStatus { @@ -30,18 +31,23 @@ enum class InstallStatus { QUEUED } -data class InstallStepData( - var nameResId: Int, - var status: InstallStatus, - var duration: Float = 0f, - var cached: Boolean = false -) +@Stable +class InstallStepData( + val nameResId: Int, + status: InstallStatus, + duration: Float = 0f, + cached: Boolean = false +) { + var status by mutableStateOf(status) + var duration by mutableStateOf(duration) + var cached by mutableStateOf(cached) +} @Composable fun InstallGroup( name: String, isCurrent: Boolean, - subSteps: List, + subSteps: ImmutableList, onClick: () -> Unit ) { val status = when { diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/PluginCard.kt b/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/PluginCard.kt index 4fae717..a01b371 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/PluginCard.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/component/plugins/PluginCard.kt @@ -28,6 +28,7 @@ import com.aliucord.manager.R import com.aliucord.manager.domain.model.Plugin import com.aliucord.manager.ui.util.annotatingStringResource import com.aliucord.manager.ui.util.joinToAnnotatedString +import kotlinx.collections.immutable.persistentListOf @Composable fun PluginCard( @@ -51,8 +52,10 @@ fun PluginCard( Text( annotatingStringResource( R.string.plugins_plugin_title, - plugin.manifest.name, - plugin.manifest.version + persistentListOf( + plugin.manifest.name, + plugin.manifest.version, + ) ) { when (it) { "plugin" -> SpanStyle(fontWeight = FontWeight.Bold) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/navigation/BaseScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/navigation/BaseScreen.kt index 59a831a..81f41a8 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/navigation/BaseScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/navigation/BaseScreen.kt @@ -15,11 +15,12 @@ import androidx.compose.ui.res.stringResource import com.aliucord.manager.BuildConfig import com.aliucord.manager.R import com.aliucord.manager.ui.screen.* +import kotlinx.collections.immutable.ImmutableList @Composable fun BaseScreen( currentNavItem: BaseScreenDestination, - bottomNavItems: List, + bottomNavItems: ImmutableList, onNavChanged: (AppDestination) -> Unit, content: @Composable () -> Unit ) { diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screen/InstallScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screen/InstallScreen.kt index 45af877..23664a5 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screen/InstallScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screen/InstallScreen.kt @@ -26,11 +26,13 @@ import com.aliucord.manager.ui.dialog.DiscordType import com.aliucord.manager.ui.dialog.DownloadMethod import com.aliucord.manager.ui.viewmodel.InstallViewModel import com.aliucord.manager.ui.viewmodel.InstallViewModel.InstallStepGroup +import kotlinx.collections.immutable.toImmutableList import kotlinx.parcelize.Parcelize import org.koin.androidx.compose.getViewModel import org.koin.core.parameter.parametersOf import java.util.* +@Immutable // this isn't *really* stable, but this never gets modified after being passed to a composable, so... @Parcelize data class InstallData( val downloadMethod: DownloadMethod, @@ -91,7 +93,7 @@ fun InstallerScreen( name = stringResource(group.nameResId), isCurrent = expandedGroup == group, onClick = { expandedGroup = group }, - subSteps = viewModel.getSteps(group) + subSteps = viewModel.getSteps(group).toImmutableList(), ) } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt b/app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt index a67adcb..38844ef 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt @@ -4,6 +4,7 @@ import androidx.annotation.StringRes import androidx.compose.runtime.* import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.* +import kotlinx.collections.immutable.ImmutableList context(AnnotatedString.Builder) inline fun Iterable.joinToAnnotatedString( @@ -35,12 +36,11 @@ context(AnnotatedString.Builder) @Composable inline fun annotatingStringResource( @StringRes id: Int, - vararg args: Any, - crossinline annotationHandler: @Composable (annotationName: String) -> SpanStyle? + args: ImmutableList, + crossinline annotationHandler: @Composable (annotationName: String) -> SpanStyle?, ): AnnotatedString { - val string = stringResource(id, *args) + val string = stringResource(id, *args.toTypedArray()) - // TODO: figure out a way to put this in remember {} while still having @Composable annotationHandler val markerIndexes = string .mapIndexedNotNull { index, elem -> index.takeIf { elem == 'ยง' } } .takeIf { it.isNotEmpty() && it.size % 3 == 0 } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/viewmodel/InstallViewModel.kt b/app/src/main/kotlin/com/aliucord/manager/ui/viewmodel/InstallViewModel.kt index 4e73985..d8d8461 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/viewmodel/InstallViewModel.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/viewmodel/InstallViewModel.kt @@ -583,8 +583,7 @@ class InstallViewModel( @OptIn(ExperimentalTime::class) private inline fun step(step: InstallStep, block: InstallStepData.() -> T): T { - steps[step] = steps[step]!!.copy(status = InstallStatus.ONGOING) - + steps[step]!!.status = InstallStatus.ONGOING currentStep = step try { @@ -596,15 +595,15 @@ class InstallViewModel( Thread.sleep(1000 - millis) } - steps[step] = steps[step]!!.copy( - duration = millis.div(1000f), + steps[step]!!.apply { + duration = millis.div(1000f) status = InstallStatus.SUCCESSFUL - ) + } currentStep = step return value.value } catch (t: Throwable) { - steps[step] = steps[step]!!.copy(status = InstallStatus.UNSUCCESSFUL) + steps[step]!!.status = InstallStatus.UNSUCCESSFUL currentStep = step throw t @@ -656,6 +655,7 @@ class InstallViewModel( var currentStep: InstallStep? by mutableStateOf(null) val steps = mutableStateMapOf() + // TODO: cache this instead fun getSteps(group: InstallStepGroup): List { return steps .filterKeys { it.group == group }.entries