From 47737d1c1901351b2fb4f7c7ba01520c99ea2bd7 Mon Sep 17 00:00:00 2001 From: kmmiio99o <164557248+kmmiio99o@users.noreply.github.com> Date: Fri, 21 Nov 2025 22:27:54 +0100 Subject: [PATCH] ``` feat: Bump app version to 1.1.1 ``` - Fix: Use correct version name - Remove: Unused code, plugins screen, and contributor model - Refactor: Use new `Developer` model - Feat: Update about screen to display developer list - Feat: Add github icon to plugin card - Chore: Fix installer icon --- app/build.gradle.kts | 2 +- .../aliucord/manager/ManagerApplication.kt | 4 +- .../aliucord/manager/manager/PathManager.kt | 16 -- .../manager/network/models/Contributor.kt | 23 -- .../manager/network/models/Developer.kt | 18 ++ .../manager/ui/components/Contributors.kt | 4 +- .../dialogs/UninstallPluginDialogPreview.kt | 20 -- .../screens/about/AboutScreenLoadedPreview.kt | 52 ----- .../screens/home/HomeScreenLoadedPreview.kt | 1 - .../plugins/PluginsScreenFailedPreview.kt | 29 --- .../plugins/PluginsScreenLoadedPreview.kt | 92 -------- .../plugins/PluginsScreenNonePreview.kt | 29 --- .../manager/ui/screens/about/AboutModel.kt | 60 +++--- .../manager/ui/screens/about/AboutScreen.kt | 164 +++++++------- .../ui/screens/about/AboutScreenState.kt | 4 +- .../about/components/ContributorListItem.kt | 96 +++++++++ .../manager/ui/screens/home/HomeScreen.kt | 4 - .../home/components/InstalledItemCard.kt | 10 +- .../ui/screens/plugins/PluginsModel.kt | 203 ------------------ .../ui/screens/plugins/PluginsScreen.kt | 138 ------------ .../screens/plugins/components/Changelog.kt | 159 -------------- .../screens/plugins/components/PluginCard.kt | 153 ------------- .../plugins/components/PluginSearch.kt | 46 ---- .../plugins/components/PluginsError.kt | 35 --- .../screens/plugins/components/PluginsNone.kt | 31 --- .../dialogs/UninstallPluginDialog.kt | 56 ----- .../ui/screens/plugins/model/PluginItem.kt | 12 -- .../screens/plugins/model/PluginManifest.kt | 35 --- .../ui/screens/settings/SettingsScreen.kt | 4 +- .../settings/components/InstallerDialog.kt | 4 +- app/src/main/res/drawable/ic_hashtag.xml | 12 ++ app/src/main/res/drawable/ic_shizuku.xml | 17 ++ app/src/main/res/values/strings.xml | 13 +- 33 files changed, 263 insertions(+), 1283 deletions(-) delete mode 100644 app/src/main/kotlin/com/aliucord/manager/network/models/Contributor.kt create mode 100644 app/src/main/kotlin/com/aliucord/manager/network/models/Developer.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/previews/dialogs/UninstallPluginDialogPreview.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/about/AboutScreenLoadedPreview.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenFailedPreview.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenLoadedPreview.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenNonePreview.kt create mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/about/components/ContributorListItem.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsModel.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsScreen.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/Changelog.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginCard.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginSearch.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginsError.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginsNone.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/dialogs/UninstallPluginDialog.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/model/PluginItem.kt delete mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/model/PluginManifest.kt create mode 100644 app/src/main/res/drawable/ic_hashtag.xml create mode 100644 app/src/main/res/drawable/ic_shizuku.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 66b0809..5a281a7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -26,7 +26,7 @@ android { minSdk = 28 targetSdk = 36 versionCode = 10_11_00 - versionName = "1.1.0" + versionName = "1.1.1" vectorDrawables { useSupportLibrary = true } diff --git a/app/src/main/kotlin/com/aliucord/manager/ManagerApplication.kt b/app/src/main/kotlin/com/aliucord/manager/ManagerApplication.kt index f59d3fc..7651ed7 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ManagerApplication.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ManagerApplication.kt @@ -17,7 +17,7 @@ import com.aliucord.manager.ui.screens.log.LogScreenModel import com.aliucord.manager.ui.screens.logs.LogsListScreenModel import com.aliucord.manager.ui.screens.patching.PatchingScreenModel import com.aliucord.manager.ui.screens.patchopts.PatchOptionsModel -import com.aliucord.manager.ui.screens.plugins.PluginsModel + import com.aliucord.manager.ui.screens.settings.SettingsModel import com.aliucord.manager.ui.widgets.updater.UpdaterViewModel import com.aliucord.manager.installers.root.RootInstaller @@ -57,7 +57,7 @@ class ManagerApplication : Application() { // UI Models modules(module { factory { HomeModel(get(), get(), get(), get(), get(), get(), get()) } - factoryOf(::PluginsModel) + factoryOf(::AboutModel) factoryOf(::PatchingScreenModel) factory { SettingsModel(get(), get(), get(), get()) } diff --git a/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt b/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt index 5c5c773..930a162 100644 --- a/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt +++ b/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt @@ -11,28 +11,12 @@ import java.io.File class PathManager( private val context: Context, ) { - /** - * The Aliucord folder in which plugins/settings/themes are stored. - * Standard path: `~/Aliucord` - */ - val aliucordDir = Environment.getExternalStorageDirectory().resolve("Aliucord") - /** * The Shiggy folder where shared Shiggy files are stored. * Standard path: `~/Shiggy` */ val shiggyDir = Environment.getExternalStorageDirectory().resolve("Shiggy") - /** - * The directory in external storage in which plugins are stored by Aliucord. - */ - val pluginsDir = aliucordDir.resolve("plugins") - - /** - * The settings file in which Aliucord's core uses. - */ - val coreSettingsFile = aliucordDir.resolve("settings/Aliucord.json") - /** * Global keystore used for signing APKs. */ diff --git a/app/src/main/kotlin/com/aliucord/manager/network/models/Contributor.kt b/app/src/main/kotlin/com/aliucord/manager/network/models/Contributor.kt deleted file mode 100644 index 46c761f..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/network/models/Contributor.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.aliucord.manager.network.models - -import androidx.compose.runtime.Immutable -import com.aliucord.manager.util.serialization.ImmutableListSerializer -import kotlinx.collections.immutable.ImmutableList -import kotlinx.serialization.Serializable - -@Immutable -@Serializable -data class Contributor( - val username: String, - val avatarUrl: String, - val commits: Int, - @Serializable(with = ImmutableListSerializer::class) - val repositories: ImmutableList, -) { - @Immutable - @Serializable - data class Repository( - val name: String, - val commits: Int, - ) -} diff --git a/app/src/main/kotlin/com/aliucord/manager/network/models/Developer.kt b/app/src/main/kotlin/com/aliucord/manager/network/models/Developer.kt new file mode 100644 index 0000000..2d05626 --- /dev/null +++ b/app/src/main/kotlin/com/aliucord/manager/network/models/Developer.kt @@ -0,0 +1,18 @@ +package com.aliucord.manager.network.models + +import kotlinx.serialization.Serializable + +@Serializable +data class Developer( + val username: String, + val avatarUrl: String, + val commits: Int, + val repositories: List, + val role: String? = null +) { + @Serializable + data class Repository( + val name: String, + val commits: Int + ) +} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/components/Contributors.kt b/app/src/main/kotlin/com/aliucord/manager/ui/components/Contributors.kt index 77f6a08..bb735f3 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/components/Contributors.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/components/Contributors.kt @@ -17,13 +17,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.unit.dp import coil3.compose.SubcomposeAsyncImage -import com.aliucord.manager.network.models.Contributor +import com.aliucord.manager.network.models.Developer import com.valentinilk.shimmer.shimmer import dev.shiggy.manager.R @Composable fun ContributorCommitsItem( - user: Contributor, + user: Developer, modifier: Modifier = Modifier, ) { val uriHandler = LocalUriHandler.current diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/previews/dialogs/UninstallPluginDialogPreview.kt b/app/src/main/kotlin/com/aliucord/manager/ui/previews/dialogs/UninstallPluginDialogPreview.kt deleted file mode 100644 index 72cd062..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/previews/dialogs/UninstallPluginDialogPreview.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.aliucord.manager.ui.previews.dialogs - -import android.content.res.Configuration -import androidx.compose.runtime.Composable -import androidx.compose.ui.tooling.preview.Preview -import com.aliucord.manager.ui.screens.plugins.components.dialogs.UninstallPluginDialog -import com.aliucord.manager.ui.theme.ManagerTheme - -@Composable -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) -private fun UninstallPluginDialogPreview() { - ManagerTheme { - UninstallPluginDialog( - pluginName = "FakeNitro", - onConfirm = {}, - onDismiss = {}, - ) - } -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/about/AboutScreenLoadedPreview.kt b/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/about/AboutScreenLoadedPreview.kt deleted file mode 100644 index bc4c3b2..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/about/AboutScreenLoadedPreview.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.aliucord.manager.ui.previews.screens.about - -import android.content.res.Configuration -import androidx.compose.runtime.* -import androidx.compose.ui.tooling.preview.* -import com.aliucord.manager.network.models.Contributor -import com.aliucord.manager.ui.screens.about.AboutScreenContent -import com.aliucord.manager.ui.screens.about.AboutScreenState -import com.aliucord.manager.ui.theme.ManagerTheme -import com.aliucord.manager.ui.util.emptyImmutableList -import com.aliucord.manager.util.serialization.ImmutableListSerializer -import kotlinx.collections.immutable.* -import kotlinx.serialization.json.Json - -// This preview has scrollable content that cannot be properly viewed from an IDE preview - -@Composable -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) -private fun AboutScreenLoadedPreview( - @PreviewParameter(ContributorsProvider::class) contributors: ImmutableList, -) { - ManagerTheme { - AboutScreenContent( - state = remember { mutableStateOf(AboutScreenState.Loaded(contributors)) }, - ) - } -} - -private class ContributorsProvider : PreviewParameterProvider> { - private val realDataRaw = - "[{\"username\":\"maisymoe\",\"avatarUrl\":\"https://avatars.githubusercontent.com/u/61095277?v=4\",\"repositories\":]" - private val realData = - Json.decodeFromString(ImmutableListSerializer(Contributor.serializer()), realDataRaw) - - override val values = - sequenceOf( - emptyImmutableList(), - persistentListOf( - Contributor( - username = - "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", - avatarUrl = "UNUSED", - commits = Int.MAX_VALUE, - repositories = - (realData[0].repositories + realData[0].repositories) - .toImmutableList(), - ) - ), - realData, - ) -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/home/HomeScreenLoadedPreview.kt b/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/home/HomeScreenLoadedPreview.kt index c0bf477..468a65a 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/home/HomeScreenLoadedPreview.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/home/HomeScreenLoadedPreview.kt @@ -34,7 +34,6 @@ private fun HomeScreenLoadedPreview( onUpdate = {}, onOpenApp = {}, onOpenAppInfo = {}, - onOpenPlugins = {}, onUninstall = {}, ) } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenFailedPreview.kt b/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenFailedPreview.kt deleted file mode 100644 index 2463b1d..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenFailedPreview.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.aliucord.manager.ui.previews.screens.plugins - -import android.content.res.Configuration -import androidx.compose.runtime.* -import androidx.compose.ui.tooling.preview.Preview -import com.aliucord.manager.ui.screens.plugins.PluginsScreenContent -import com.aliucord.manager.ui.theme.ManagerTheme -import com.aliucord.manager.ui.util.emptyImmutableList - -// This preview has interactable content that cannot be tested from an IDE preview - -@Composable -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) -private fun PluginsScreenFailedPreview() { - val filterState = remember { mutableStateOf("") } - - ManagerTheme { - PluginsScreenContent( - searchText = filterState, - setSearchText = filterState::value::set, - isError = true, - plugins = emptyImmutableList(), - onPluginUninstall = {}, - onPluginChangelog = {}, - onPluginToggle = { name, enabled -> }, - ) - } -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenLoadedPreview.kt b/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenLoadedPreview.kt deleted file mode 100644 index 57bf31f..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenLoadedPreview.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.aliucord.manager.ui.previews.screens.plugins - -import android.content.res.Configuration -import androidx.compose.runtime.* -import androidx.compose.ui.tooling.preview.Preview -import com.aliucord.manager.ui.screens.plugins.PluginsScreenContent -import com.aliucord.manager.ui.screens.plugins.model.PluginItem -import com.aliucord.manager.ui.screens.plugins.model.PluginManifest -import com.aliucord.manager.ui.theme.ManagerTheme -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf -import java.util.UUID - -// This preview has scrollable/interactable content that cannot be tested from an IDE preview - -@Composable -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) -private fun PluginsScreenLoadedPreview() { - val filterState = remember { mutableStateOf("test") } - - ManagerTheme { - PluginsScreenContent( - searchText = filterState, - setSearchText = filterState::value::set, - isError = false, - plugins = plugins, - onPluginUninstall = {}, - onPluginChangelog = {}, - onPluginToggle = { name, enabled -> }, - ) - } -} - -private val plugins: ImmutableList = persistentListOf( - PluginItem( - path = UUID.randomUUID().toString(), - manifest = PluginManifest( - name = "CloseDMs", - authors = persistentListOf( - PluginManifest.Author(name = "Diamond", id = 0L), - ), - description = "Shortcut to close DMs in the DM context menu.", - version = "1.0.0", - updateUrl = "", - changelog = "", - changelogMedia = null, - ) - ), - PluginItem( - path = UUID.randomUUID().toString(), - manifest = PluginManifest( - name = "ConfigurableStickerSizes", - authors = persistentListOf( - PluginManifest.Author(name = "rushii", id = 0L, hyperlink = false), - ), - description = "Makes sticker sizes configurable.", - version = "1.1.5", - updateUrl = "", - changelog = "", - changelogMedia = null, - ) - ), - PluginItem( - path = UUID.randomUUID().toString(), - manifest = PluginManifest( - name = "AudioPlayer", - authors = persistentListOf( - PluginManifest.Author(name = "rushii", id = 0L, hyperlink = false), - ), - description = "Makes audio files playable.", - version = "0.0.1", - updateUrl = "", - changelog = "", - changelogMedia = null, - ) - ), - PluginItem( - path = UUID.randomUUID().toString(), - manifest = PluginManifest( - name = "TypingIndicators", - authors = persistentListOf( - PluginManifest.Author(name = "rushii", id = 0L, hyperlink = false), - ), - description = "Adds typing indicators to channels that people are currently typing in.", - version = "1.1.0", - updateUrl = "", - changelog = "", - changelogMedia = null, - ) - ), -) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenNonePreview.kt b/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenNonePreview.kt deleted file mode 100644 index fde4c00..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/previews/screens/plugins/PluginsScreenNonePreview.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.aliucord.manager.ui.previews.screens.plugins - -import android.content.res.Configuration -import androidx.compose.runtime.* -import androidx.compose.ui.tooling.preview.Preview -import com.aliucord.manager.ui.screens.plugins.PluginsScreenContent -import com.aliucord.manager.ui.theme.ManagerTheme -import com.aliucord.manager.ui.util.emptyImmutableList - -// This preview has interactable content that cannot be tested from an IDE preview - -@Composable -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) -private fun PluginsScreenNonePreview() { - val filterState = remember { mutableStateOf("") } - - ManagerTheme { - PluginsScreenContent( - searchText = filterState, - setSearchText = filterState::value::set, - isError = false, - plugins = emptyImmutableList(), - onPluginUninstall = {}, - onPluginChangelog = {}, - onPluginToggle = { name, enabled -> }, - ) - } -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutModel.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutModel.kt index 6078302..943a73e 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutModel.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutModel.kt @@ -2,56 +2,60 @@ package com.aliucord.manager.ui.screens.about import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope -import com.aliucord.manager.network.models.Contributor +import com.aliucord.manager.network.models.Developer import com.aliucord.manager.network.services.HttpService -import com.aliucord.manager.network.utils.fold import com.aliucord.manager.ui.util.toUnsafeImmutable import com.aliucord.manager.util.launchIO -import io.ktor.client.request.url -import kotlinx.serialization.Serializable -import kotlinx.serialization.SerialName import kotlinx.collections.immutable.persistentListOf class AboutModel( private val http: HttpService, ) : StateScreenModel(AboutScreenState.Loading) { init { - fetchContributors() + fetchDevelopers() } - fun fetchContributors() = screenModelScope.launchIO { + fun fetchDevelopers() = screenModelScope.launchIO { mutableState.value = AboutScreenState.Loading - // Use a curated static list of GitHub usernames (profiles + static avatar URLs). - // This avoids pulling from a specific repo and lets us include people - // who are not contributors to any particular repository. - val staticContributors = persistentListOf( - Contributor( - username = "rushiiMachine", - avatarUrl = "https://github.com/rushiiMachine.png", - commits = 0, - repositories = persistentListOf() - ), - Contributor( - username = "maisymoe", - avatarUrl = "https://github.com/maisymoe.png", - commits = 0, - repositories = persistentListOf() - ), - Contributor( + val allDevelopers = persistentListOf( + Developer( username = "kmmiio99o", avatarUrl = "https://github.com/kmmiio99o.png", commits = 0, - repositories = persistentListOf() + repositories = emptyList(), + role = "ShiggyCord - Creator" ), - Contributor( + Developer( + username = "C0C0B01", + avatarUrl = "https://github.com/C0C0B01.png", + commits = 0, + repositories = emptyList(), + role = "Kettu - Creator" + ), + Developer( username = "pylixonly", avatarUrl = "https://github.com/pylixonly.png", commits = 0, - repositories = persistentListOf() + repositories = emptyList(), + role = "Bunny - Creator" + ), + Developer( + username = "maisymoe", + avatarUrl = "https://github.com/maisymoe.png", + commits = 0, + repositories = emptyList(), + role = "Creator - Vendetta" + ), + Developer( + username = "rushiiMachine", + avatarUrl = "https://github.com/rushiiMachine.png", + commits = 0, + repositories = emptyList(), + role = "Manager - Creator" ) ) - mutableState.value = AboutScreenState.Loaded(staticContributors.toUnsafeImmutable()) + mutableState.value = AboutScreenState.Loaded(allDevelopers.toUnsafeImmutable()) } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutScreen.kt index 5cf84e8..da85381 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -20,7 +21,7 @@ import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.koin.koinScreenModel import com.aliucord.manager.ui.components.* -import com.aliucord.manager.ui.screens.about.components.LeadContributor +import com.aliucord.manager.ui.screens.about.components.ContributorListItem import com.aliucord.manager.ui.util.paddings.* import dev.shiggy.manager.R import kotlinx.parcelize.IgnoredOnParcel @@ -41,107 +42,90 @@ class AboutScreen : Screen, Parcelable { @Composable fun AboutScreenContent(state: State) { Scaffold( - topBar = { - TopAppBar( - title = { Text(stringResource(R.string.navigation_about_manager)) }, - navigationIcon = { BackButton() }, - ) - } + topBar = { + TopAppBar( + title = { Text(stringResource(R.string.navigation_about_manager)) }, + navigationIcon = { BackButton() }, + ) + } ) { paddingValues -> LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp), - horizontalAlignment = Alignment.CenterHorizontally, - contentPadding = - paddingValues - .exclude(PaddingValuesSides.Horizontal + PaddingValuesSides.Top) - .add(PaddingValues(vertical = 16.dp)), - modifier = - Modifier.padding(paddingValues.exclude(PaddingValuesSides.Bottom)) - .padding(horizontal = 14.dp), + verticalArrangement = Arrangement.spacedBy(2.dp), + horizontalAlignment = Alignment.CenterHorizontally, + contentPadding = paddingValues + .exclude(PaddingValuesSides.Horizontal + PaddingValuesSides.Top) + .add(PaddingValues(vertical = 16.dp)), + modifier = Modifier + .padding(paddingValues.exclude(PaddingValuesSides.Bottom)) ) { item(key = "MANAGER_CREDIT_DISCLAIMER_BANNER") { Box( - contentAlignment = Alignment.Center, - modifier = - Modifier.padding(bottom = 24.dp) - .border( - width = 2.dp, - color = MaterialTheme.colorScheme.surfaceVariant, - shape = MaterialTheme.shapes.medium - ) - .clip(MaterialTheme.shapes.medium) - .background(MaterialTheme.colorScheme.surfaceContainerHigh) - ) { - Row( - horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(horizontal = 20.dp, vertical = 14.dp) - ) { - Text( - text = - stringResource( - R.string.about_manager_credit_disclaimer_notice - ), - style = MaterialTheme.typography.labelMedium, + contentAlignment = Alignment.Center, + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 8.dp) + .fillMaxWidth() + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f), + shape = MaterialTheme.shapes.large ) + .clip(MaterialTheme.shapes.large) + .background(MaterialTheme.colorScheme.surfaceContainerLow) + ) { + Text( + text = stringResource(R.string.about_manager_credit_disclaimer_notice), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(horizontal = 20.dp, vertical = 16.dp) + ) + } + } + + item(key = "PROJECT_HEADER") { + Box(modifier = Modifier.padding(horizontal = 16.dp)) { + ProjectHeader() + } + } + + item(key = "TEAM_SPACER") { + Spacer(modifier = Modifier.height(32.dp)) + } + + when (val currentState = state.value) { + AboutScreenState.Loading -> { + item(key = "LOADING") { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 48.dp) + ) { + CircularProgressIndicator() + } } } - } - - item(key = "PROJECT_HEADER") { ProjectHeader() } - - item(key = "HEADER_DIVIDER") { - TextDivider( - text = stringResource(R.string.contributors_lead), - modifier = Modifier.padding(top = 18.dp, bottom = 20.dp), - ) - } - - item(key = "MAIN_CONTRIBUTORS") { - Row( - horizontalArrangement = Arrangement.SpaceEvenly, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth(), - ) { - LeadContributor("C0C0B01", stringResource(R.string.lead_role_kettu)) - LeadContributor("kmmiio99o", stringResource(R.string.lead_role_shiggycord)) - LeadContributor("pylixonly", stringResource(R.string.lead_role_bunny)) - } - } - - item(key = "CONTRIBUTORS_DIVIDER") { - TextDivider( - text = stringResource(R.string.contributors), - modifier = Modifier.padding(top = 16.dp, bottom = 6.dp) - ) - } - - when (val state = state.value) { - AboutScreenState.Loading -> - item(key = "CONTRIBUTIONS_LOADING") { - Box( - contentAlignment = Alignment.Center, - content = { CircularProgressIndicator() }, - modifier = Modifier.fillMaxWidth().padding(vertical = 38.dp), - ) - } - AboutScreenState.Failure -> - item(key = "LOAD_FAILURE") { - LoadFailure(modifier = Modifier.fillMaxSize().padding(vertical = 38.dp)) - } - is AboutScreenState.Loaded -> { - // Render the two requested contributors as LeadContributor entries - item(key = "SELECTED_CONTRIBUTORS") { - Row( - horizontalArrangement = Arrangement.SpaceEvenly, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth(), + AboutScreenState.Failure -> { + item(key = "LOAD_FAILURE") { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 48.dp) ) { - LeadContributor("maisymoe", "Creator - Vendetta") - LeadContributor("rushiiMachine", "Manager - Creator") + LoadFailure() } } } + is AboutScreenState.Loaded -> { + itemsIndexed( + items = currentState.contributors, + key = { index, developer -> developer.username } + ) { index, developer -> + ContributorListItem( + contributor = developer, + isLast = index == currentState.contributors.lastIndex + ) + } + } } } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutScreenState.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutScreenState.kt index b9e2332..56e6636 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutScreenState.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/AboutScreenState.kt @@ -1,10 +1,10 @@ package com.aliucord.manager.ui.screens.about -import com.aliucord.manager.network.models.Contributor +import com.aliucord.manager.network.models.Developer import kotlinx.collections.immutable.ImmutableList sealed interface AboutScreenState { data object Loading : AboutScreenState data object Failure : AboutScreenState - data class Loaded(val contributors: ImmutableList) : AboutScreenState + data class Loaded(val contributors: ImmutableList) : AboutScreenState } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/components/ContributorListItem.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/components/ContributorListItem.kt new file mode 100644 index 0000000..3ffd0e6 --- /dev/null +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/about/components/ContributorListItem.kt @@ -0,0 +1,96 @@ +package com.aliucord.manager.ui.screens.about.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import coil3.compose.SubcomposeAsyncImage +import com.aliucord.manager.network.models.Developer +import com.valentinilk.shimmer.shimmer + +@Composable +fun ContributorListItem( + contributor: Developer, + isLast: Boolean = false, + modifier: Modifier = Modifier +) { + val uriHandler = LocalUriHandler.current + + Column(modifier = modifier.fillMaxWidth()) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .clickable( + onClick = { uriHandler.openUri("https://github.com/${contributor.username}") } + ) + .padding(horizontal = 24.dp, vertical = 16.dp) + ) { + SubcomposeAsyncImage( + model = contributor.avatarUrl, + contentDescription = contributor.username, + contentScale = ContentScale.Crop, + loading = { + Surface( + color = MaterialTheme.colorScheme.surfaceVariant, + modifier = Modifier + .size(56.dp) + .clip(CircleShape) + .shimmer() + ) {} + }, + error = { + Surface( + color = MaterialTheme.colorScheme.surfaceVariant, + modifier = Modifier + .size(56.dp) + .clip(CircleShape) + ) {} + }, + modifier = Modifier + .size(56.dp) + .clip(CircleShape) + ) + + Spacer(modifier = Modifier.width(20.dp)) + + Column( + modifier = Modifier.weight(1f) + ) { + Text( + text = contributor.username, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + Spacer(modifier = Modifier.height(4.dp)) + + contributor.role?.let { role -> + Text( + text = role, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } + + if (!isLast) { + Divider( + color = MaterialTheme.colorScheme.outline.copy(alpha = 0.1f), + thickness = 1.dp, + modifier = Modifier.padding(start = 100.dp, end = 24.dp) + ) + } + } +} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/home/HomeScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/home/HomeScreen.kt index 925a6b7..be2c6ff 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/home/HomeScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/home/HomeScreen.kt @@ -30,7 +30,6 @@ import com.aliucord.manager.ui.components.LoadFailure import com.aliucord.manager.ui.components.ProjectHeader import com.aliucord.manager.ui.screens.home.components.* import com.aliucord.manager.ui.screens.patchopts.PatchOptionsScreen -import com.aliucord.manager.ui.screens.plugins.PluginsScreen import com.aliucord.manager.ui.util.paddings.PaddingValuesSides import com.aliucord.manager.ui.util.paddings.exclude import com.aliucord.manager.util.* @@ -78,7 +77,6 @@ class HomeScreen : Screen, Parcelable { }, onOpenApp = model::openApp, onOpenAppInfo = model::openAppInfo, - onOpenPlugins = { navigator.push(PluginsScreen()) }, // TODO: install-specific plugins onUninstall = model::uninstallApp, ) @@ -129,7 +127,6 @@ fun HomeScreenLoadedContent( onUpdate: (packageName: String) -> Unit, onOpenApp: (packageName: String) -> Unit, onOpenAppInfo: (packageName: String) -> Unit, - onOpenPlugins: (packageName: String) -> Unit, onUninstall: (packageName: String) -> Unit, ) { LazyColumn( @@ -163,7 +160,6 @@ fun HomeScreenLoadedContent( onUpdate = { onUpdate(item.packageName) }, onOpenApp = { onOpenApp(item.packageName) }, onOpenInfo = { onOpenAppInfo(item.packageName) }, - onOpenPlugins = { onOpenPlugins(item.packageName) }, onUninstall = { onUninstall(item.packageName) }, modifier = Modifier.fillMaxWidth() ) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/home/components/InstalledItemCard.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/home/components/InstalledItemCard.kt index 68dd9fb..aa1cacc 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/home/components/InstalledItemCard.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/home/components/InstalledItemCard.kt @@ -26,7 +26,6 @@ fun InstalledItemCard( onUpdate: () -> Unit, onOpenApp: () -> Unit, onOpenInfo: () -> Unit, - onOpenPlugins: () -> Unit, onUninstall: () -> Unit, modifier: Modifier = Modifier, ) { @@ -89,13 +88,6 @@ fun InstalledItemCard( horizontalArrangement = Arrangement.spacedBy(2.dp), modifier = Modifier.clip(MaterialTheme.shapes.large), ) { - // // Plugins cannot be managed externally yet in Shiggy - // SegmentedButton( - // icon = painterResource(R.drawable.ic_extension), - // text = stringResource(R.string.plugins_title), - // onClick = onOpenPlugins, - // ) - // If the up-to-date status cannot be determined, assume it is up-to-date if (data.isUpToDate != false) SegmentedButton( @@ -126,7 +118,7 @@ fun InstalledItemCard( ) SegmentedButton( icon = painterResource(R.drawable.ic_delete_forever), - text = stringResource(R.string.action_uninstall), // Assuming R.string.action_uninstall exists or will be added + text = stringResource(R.string.action_uninstall), iconColor = MaterialTheme.colorScheme.error, textColor = MaterialTheme.colorScheme.error, onClick = onUninstall, diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsModel.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsModel.kt deleted file mode 100644 index f166d64..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsModel.kt +++ /dev/null @@ -1,203 +0,0 @@ -package com.aliucord.manager.ui.screens.plugins - -import android.app.Application -import android.util.Log -import androidx.compose.runtime.* -import cafe.adriel.voyager.core.model.ScreenModel -import cafe.adriel.voyager.core.model.screenModelScope -import com.aliucord.manager.manager.PathManager -import com.aliucord.manager.ui.screens.plugins.model.PluginItem -import com.aliucord.manager.ui.screens.plugins.model.PluginManifest -import com.aliucord.manager.ui.util.emptyImmutableList -import com.aliucord.manager.ui.util.toUnsafeImmutable -import com.aliucord.manager.util.* -import com.github.diamondminer88.zip.ZipReader -import dev.shiggy.manager.BuildConfig -import dev.shiggy.manager.R -import kotlinx.collections.immutable.ImmutableList -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.plus -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.json.* -import java.io.File -import kotlin.time.Duration - -class PluginsModel( - private val context: Application, - private val paths: PathManager, - private val json: Json, -) : ScreenModel { - private val plugins = MutableStateFlow>(emptyImmutableList()) - private val aliucordJsonMutex = Mutex() - - var error by mutableStateOf(false) - private set - - var showChangelogDialog by mutableStateOf(null) - private set - - var showUninstallDialog by mutableStateOf(null) - private set - - val searchText: StateFlow - private field = MutableStateFlow("") - - val filteredPlugins: StateFlow> = searchText - .combine(plugins) { searchText, plugins -> - if (searchText.isBlank()) { - plugins - } else { - plugins.filter { plugin -> - plugin.manifest.name.contains(searchText, ignoreCase = true) - || plugin.manifest.description.contains(searchText, ignoreCase = true) - || plugin.manifest.authors.any { (name) -> name.contains(searchText, ignoreCase = true) } - }.toUnsafeImmutable() - } - }.stateIn( - scope = screenModelScope + Dispatchers.Default, - started = SharingStarted.WhileSubscribed(replayExpiration = Duration.ZERO), - initialValue = plugins.value, - ) - - fun setSearchText(search: String) { - searchText.value = search - } - - fun showChangelogDialog(plugin: PluginItem) { - showChangelogDialog = plugin - } - - fun hideChangelogDialog() { - showChangelogDialog = null - } - - fun showUninstallDialog(plugin: PluginItem) { - showUninstallDialog = plugin - } - - fun hideUninstallDialog() { - showUninstallDialog = null - } - - fun uninstallPlugin(plugin: PluginItem) = screenModelScope.launchIO { - if (!plugins.value.any { it.path == plugin.path }) { - mainThread { hideUninstallDialog() } - return@launchIO - } - - val deleteSuccess = try { - File(plugin.path).delete() - } catch (t: Throwable) { - Log.e(BuildConfig.TAG, "Failed to delete plugin", t) - false - } - - if (!deleteSuccess) { - mainThread { - hideUninstallDialog() - context.showToast(R.string.plugins_error) - } - return@launchIO - } - - plugins.update { (it - plugin).toUnsafeImmutable() } - mainThread { hideUninstallDialog() } - } - - fun setPluginEnabled(pluginName: String, enabled: Boolean) = screenModelScope.launchIO { - aliucordJsonMutex.withLock { - @OptIn(ExperimentalSerializationApi::class) - try { - val settings = paths.coreSettingsFile.inputStream() - .use { Json.decodeFromStream>(it) } - .toMutableMap() - - settings["AC_PM_$pluginName"] = JsonPrimitive(enabled) - - paths.coreSettingsFile.outputStream() - .use { json.encodeToStream(settings, it) } - } catch (t: Throwable) { - Log.e(BuildConfig.TAG, "Failed to write Aliucord.json", t) - mainThread { error = true } - } - } - - mainThread { - plugins.value.forEach { - if (it.manifest.name == pluginName) - it.enabled = enabled - } - } - } - - // Called by screen to load initial data - fun refreshData() = screenModelScope.launchIO { - loadPlugins() - loadPluginsEnabled() - } - - private suspend fun loadPluginsEnabled() { - if (!paths.coreSettingsFile.exists()) return - - aliucordJsonMutex.withLock { - @OptIn(ExperimentalSerializationApi::class) - val pluginToggles = try { - paths.coreSettingsFile.inputStream() - .use { Json.decodeFromStream>(it) } - .filterKeys { it.startsWith("AC_PM_") } - .mapKeys { it.key.substring("AC_PM_".length) } - .mapValues { it.value.jsonPrimitive.boolean } - } catch (t: Throwable) { - Log.e(BuildConfig.TAG, "Failed to load Aliucord.json", t) - mainThread { error = true } - emptyMap() - } - - mainThread { - plugins.value.forEach { - if (!pluginToggles.getOrDefault(it.manifest.name, true)) - it.enabled = false - } - } - } - } - - private suspend fun loadPlugins() { - try { - if (!paths.pluginsDir.exists() && !paths.pluginsDir.mkdirs()) - throw IllegalStateException("Failed to create plugins directory") - - val pluginFiles = paths.pluginsDir.listFiles { file -> file.extension == "zip" } - ?: throw IllegalStateException("Failed to read plugins directory") - - val pluginItems = pluginFiles.map { - PluginItem( - manifest = loadPluginManifest(it), - path = it.absolutePath, - ) - } - - plugins.value = pluginItems.toUnsafeImmutable() - } catch (t: Throwable) { - Log.e(BuildConfig.TAG, "Failed to load plugins", t) - mainThread { error = true } - } - } - - private fun loadPluginManifest(pluginFile: File): PluginManifest { - return ZipReader(pluginFile).use { - val manifest = it.openEntry("manifest.json") - ?: throw Exception("Plugin ${pluginFile.name} has no manifest") - - try { - @OptIn(ExperimentalSerializationApi::class) - json.decodeFromStream(manifest.read().inputStream()) - } catch (t: Throwable) { - throw Exception("Failed to parse plugin manifest for ${pluginFile.name}", t) - } - } - } -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsScreen.kt deleted file mode 100644 index cabae2c..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsScreen.kt +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2022 Juby210 & zt - * Licensed under the Open Software License version 3.0 - */ - -package com.aliucord.manager.ui.screens.plugins - -import android.os.Parcelable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.LifecycleResumeEffect -import cafe.adriel.voyager.core.screen.Screen -import cafe.adriel.voyager.koin.koinScreenModel -import com.aliucord.manager.ui.components.BackButton -import com.aliucord.manager.ui.screens.plugins.components.* -import com.aliucord.manager.ui.screens.plugins.components.dialogs.UninstallPluginDialog -import com.aliucord.manager.ui.screens.plugins.model.PluginItem -import com.aliucord.manager.ui.util.paddings.* -import dev.shiggy.manager.R -import kotlinx.collections.immutable.ImmutableList -import kotlinx.parcelize.IgnoredOnParcel -import kotlinx.parcelize.Parcelize - -@Parcelize -class PluginsScreen : Screen, Parcelable { - @IgnoredOnParcel - override val key = "Plugins" - - @Composable - override fun Content() { - val model = koinScreenModel() - - // Refresh plugins list on activity resume or when this initially opens - LifecycleResumeEffect(Unit) { - model.refreshData() - - onPauseOrDispose {} - } - - model.showUninstallDialog?.let { plugin -> - UninstallPluginDialog( - pluginName = plugin.manifest.name, - onConfirm = { model.uninstallPlugin(plugin) }, - onDismiss = model::hideUninstallDialog - ) - } - - model.showChangelogDialog?.let { plugin -> - Changelog( - plugin = plugin, - onDismiss = model::hideChangelogDialog - ) - } - - PluginsScreenContent( - searchText = model.searchText.collectAsState(), - setSearchText = model::setSearchText, - isError = model.error, - plugins = model.filteredPlugins.collectAsState().value, - onPluginUninstall = model::showUninstallDialog, - onPluginChangelog = model::showChangelogDialog, - onPluginToggle = model::setPluginEnabled, - ) - } -} - -@Composable -fun PluginsScreenContent( - searchText: State, - setSearchText: (String) -> Unit, - isError: Boolean, - plugins: ImmutableList, - onPluginUninstall: (PluginItem) -> Unit, - onPluginChangelog: (PluginItem) -> Unit, - onPluginToggle: (name: String, enabled: Boolean) -> Unit, -) { - Scaffold( - topBar = { - TopAppBar( - title = { Text(stringResource(R.string.plugins_title)) }, - navigationIcon = { BackButton() }, - ) - } - ) { paddingValues -> - Column( - verticalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier - .fillMaxSize() - .padding(paddingValues.exclude(PaddingValuesSides.Bottom)) - .padding(horizontal = 20.dp) - ) { - PluginSearch( - currentFilter = searchText, - onFilterChange = setSearchText, - modifier = Modifier.fillMaxWidth() - ) - - LazyColumn( - verticalArrangement = Arrangement.spacedBy(16.dp), - contentPadding = paddingValues - .exclude(PaddingValuesSides.Horizontal + PaddingValuesSides.Top) - .add(PaddingValues(vertical = 12.dp)), - modifier = Modifier.fillMaxSize(), - ) { - when { - isError -> item(key = "ERROR") { - PluginsError(modifier = Modifier.fillParentMaxSize()) - } - - plugins.isNotEmpty() -> { - items( - items = plugins, - contentType = { "PLUGIN" }, - key = { it.path }, - ) { plugin -> - PluginCard( - plugin = plugin, - onClickDelete = { onPluginUninstall(plugin) }, - onClickShowChangelog = { onPluginChangelog(plugin) }, - onSetEnabled = { onPluginToggle(plugin.manifest.name, it) }, - ) - } - } - - else -> item(key = "PLUGINS_NONE") { - PluginsNone(modifier = Modifier.fillParentMaxSize()) - } - } - } - } - } -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/Changelog.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/Changelog.kt deleted file mode 100644 index 3c0342a..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/Changelog.kt +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2022 Juby210 & zt - * Licensed under the Open Software License version 3.0 - */ - -package com.aliucord.manager.ui.screens.plugins.components - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.* -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.unit.dp -import coil3.compose.AsyncImage -import com.aliucord.manager.ui.screens.plugins.model.PluginItem -import dev.shiggy.manager.R - -private val hyperLinkPattern = Regex("\\[(.+?)]\\((.+?\\))") - -@Suppress("RegExpRedundantEscape") // It is very much not redundant and causes a crash lol -private val headerStylePattern = Regex("\\{(improved|added|fixed)( marginTop)?\\}") - -@Composable -private fun AnnotatedString.Builder.MarkdownHyperlink(content: String) { - var idx = 0 - - with(hyperLinkPattern.toPattern().matcher(content)) { - while (find()) { - val start = start() - val end = end() - val title = group(1)!! - val url = group(2)!! - - append(content.substring(idx, start)) - - // @formatter:off - pushLink(LinkAnnotation.Url( - url, - TextLinkStyles(SpanStyle( - color = MaterialTheme.colorScheme.primary, - textDecoration = TextDecoration.Underline - )) - )) - append(title) - pop() - // @formatter:on - - idx = end - } - } - - if (idx < content.length) append(content.substring(idx)) -} - -@Composable -fun Changelog( - plugin: PluginItem, - onDismiss: () -> Unit, -) { - AlertDialog( - onDismissRequest = onDismiss, - icon = { - Icon( - painter = painterResource(R.drawable.ic_history), - contentDescription = stringResource(R.string.plugins_view_changelog, plugin.manifest.name) - ) - }, - title = { Text(plugin.manifest.name) }, - text = { - Column { - plugin.manifest.changelogMedia?.let { mediaUrl -> - AsyncImage( - modifier = Modifier - .fillMaxWidth() - .heightIn(max = 90.dp) - .clip(RoundedCornerShape(14.dp)), - model = mediaUrl, - contentDescription = stringResource(R.string.plugins_changelog_media) - ) - } - - LazyColumn { - items(plugin.manifest.changelog!!.lines()) { - var line = it.trim() - - if (line.isNotEmpty()) { - when (line[0]) { - '#' -> { - do { - line = line.substring(1) - } while (line.startsWith("#")) - - Text( - text = line.trimStart(), - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(top = 16.dp, bottom = 6.dp) - ) - } - - '*' -> { - Text( - modifier = Modifier.padding(bottom = 2.dp), - text = buildAnnotatedString { - withStyle( - SpanStyle( - color = MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Bold - ) - ) { - append("● ") - } - - MarkdownHyperlink(line.substring(1)) - } - ) - } - - else -> { - when { - line.endsWith("marginTop}") -> { - val color = MaterialTheme.colorScheme.onSurface - - Text( - text = line, - fontWeight = FontWeight.Bold, - color = color, - modifier = Modifier.padding(top = 16.dp, bottom = 6.dp) - ) - } - - line.all { c -> c == '=' } -> {} // Discord ignores ======= - else -> { - Text(buildAnnotatedString { - MarkdownHyperlink(line) - }) - } - } - } - } - } - } - } - } - }, - confirmButton = { - Button(onClick = onDismiss) { - Text(stringResource(R.string.action_close)) - } - } - ) -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginCard.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginCard.kt deleted file mode 100644 index 45c42eb..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginCard.kt +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2022 Juby210 & zt - * Licensed under the Open Software License version 3.0 - */ - -package com.aliucord.manager.ui.screens.plugins.components - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.* -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.unit.dp -import com.aliucord.manager.ui.screens.plugins.model.PluginItem -import dev.shiggy.manager.R - -@Composable -fun PluginCard( - plugin: PluginItem, - onClickDelete: () -> Unit, - onClickShowChangelog: () -> Unit, - onSetEnabled: (Boolean) -> Unit, -) { - val uriHandler = LocalUriHandler.current - - ElevatedCard { - // Header - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .clickable { onSetEnabled(!plugin.enabled) } - .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 14.dp), - ) { - Column { - // Name - Text( - text = buildAnnotatedString { - withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { - append(plugin.manifest.name) - } - append(" v") - append(plugin.manifest.version) - } - ) - - // Authors - val authors = buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colorScheme.primary)) { - for ((idx, author) in plugin.manifest.authors.withIndex()) { - if (idx > 0) append(", ") - - if (author.hyperlink) pushLink( - LinkAnnotation.Url( - url = author.discordUrl, - styles = TextLinkStyles( - SpanStyle( - textDecoration = TextDecoration.Underline, - ) - ), - ) - ) - append(author.name) - if (author.hyperlink) pop() - } - } - } - Text( - text = authors, - style = MaterialTheme.typography.labelLarge, - ) - } - - Spacer(Modifier.weight(1f, true)) - - // Toggle Switch - Switch( - checked = plugin.enabled, - onCheckedChange = { onSetEnabled(!plugin.enabled) } - ) - } - - HorizontalDivider( - modifier = Modifier - .alpha(0.3f) - .padding(horizontal = 16.dp) - ) - - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - // Description - Text( - text = plugin.manifest.description, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier - .heightIn(max = 150.dp, min = 40.dp) - .padding(bottom = 20.dp), - ) - - Row( - horizontalArrangement = Arrangement.spacedBy(10.dp) - ) { - IconButton( - onClick = { uriHandler.openUri(plugin.manifest.repositoryUrl) }, - modifier = Modifier.size(25.dp), - ) { - Icon( - modifier = Modifier.fillMaxSize(), - painter = painterResource(R.drawable.ic_account_github_white_24dp), - contentDescription = stringResource(R.string.github) - ) - } - - if (plugin.manifest.changelog != null) { - IconButton( - onClick = onClickShowChangelog, - modifier = Modifier.size(25.dp), - ) { - Icon( - painter = painterResource(R.drawable.ic_history), - contentDescription = stringResource(R.string.plugins_view_changelog), - modifier = Modifier.fillMaxSize(), - ) - } - } - - Spacer(Modifier.weight(1f, true)) - - IconButton( - onClick = onClickDelete, - modifier = Modifier.size(25.dp), - ) { - Icon( - modifier = Modifier.fillMaxSize(), - painter = painterResource(R.drawable.ic_delete_forever), - contentDescription = stringResource(R.string.action_uninstall), - tint = MaterialTheme.colorScheme.error - ) - } - } - } - } -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginSearch.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginSearch.kt deleted file mode 100644 index b62ba1c..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginSearch.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.aliucord.manager.ui.screens.plugins.components - -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.unit.dp -import com.aliucord.manager.ui.components.ResetToDefaultButton -import dev.shiggy.manager.R - -@Composable -fun PluginSearch( - currentFilter: State, - onFilterChange: (String) -> Unit, - modifier: Modifier = Modifier.Companion, -) { - val focusManager = LocalFocusManager.current - - OutlinedTextField( - value = currentFilter.value, - onValueChange = onFilterChange, - singleLine = true, - shape = MaterialTheme.shapes.medium, - label = { Text(stringResource(R.string.action_search)) }, - trailingIcon = { - val isFilterBlank by remember { derivedStateOf { currentFilter.value.isEmpty() } } - - ResetToDefaultButton( - enabled = !isFilterBlank, - onClick = { onFilterChange("") }, - modifier = Modifier.padding(end = 4.dp), - ) - }, - keyboardOptions = KeyboardOptions( - autoCorrectEnabled = false, - imeAction = ImeAction.Companion.Search - ), - keyboardActions = KeyboardActions { focusManager.clearFocus() }, - modifier = modifier, - ) -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginsError.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginsError.kt deleted file mode 100644 index 9d94a50..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginsError.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.aliucord.manager.ui.screens.plugins.components - -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import dev.shiggy.manager.R - -@Composable -fun PluginsError(modifier: Modifier = Modifier) { - Box( - contentAlignment = Alignment.Center, - modifier = modifier, - ) { - Column( - modifier = Modifier.align(Alignment.Center), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - Icon( - painter = painterResource(R.drawable.ic_warning), - contentDescription = null, - tint = MaterialTheme.colorScheme.error, - ) - Text( - text = stringResource(R.string.plugins_error), - color = MaterialTheme.colorScheme.error, - ) - } - } -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginsNone.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginsNone.kt deleted file mode 100644 index 315ed02..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/PluginsNone.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.aliucord.manager.ui.screens.plugins.components - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import dev.shiggy.manager.R - -@Composable -fun PluginsNone(modifier: Modifier = Modifier) { - Box( - contentAlignment = Alignment.Center, - modifier = modifier, - ) { - Column( - modifier = Modifier.align(Alignment.Center), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Icon( - painter = painterResource(R.drawable.ic_extension_off), - contentDescription = null - ) - Text(stringResource(R.string.plugins_none_installed)) - } - } -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/dialogs/UninstallPluginDialog.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/dialogs/UninstallPluginDialog.kt deleted file mode 100644 index 812752f..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/components/dialogs/UninstallPluginDialog.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.aliucord.manager.ui.screens.plugins.components.dialogs - -import androidx.compose.foundation.layout.size -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import dev.shiggy.manager.R - -@Composable -fun UninstallPluginDialog( - pluginName: String, - onConfirm: () -> Unit, - onDismiss: () -> Unit, -) { - AlertDialog( - onDismissRequest = onDismiss, - icon = { - Icon( - painter = painterResource(R.drawable.ic_delete_forever), - contentDescription = null, - modifier = Modifier.size(32.dp), - ) - }, - title = { - Text(stringResource(R.string.plugins_delete_plugin, pluginName)) - }, - text = { - Text( - text = stringResource(R.string.plugins_delete_plugin_body, pluginName), - textAlign = TextAlign.Center, - ) - }, - confirmButton = { - Button( - onClick = onConfirm, - ) { - Text(stringResource(R.string.action_confirm)) - } - }, - dismissButton = { - Button( - onClick = onDismiss, - colors = ButtonDefaults.buttonColors( - contentColor = MaterialTheme.colorScheme.onSecondaryContainer, - containerColor = MaterialTheme.colorScheme.secondaryContainer - ) - ) { - Text(stringResource(R.string.action_cancel)) - } - } - ) -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/model/PluginItem.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/model/PluginItem.kt deleted file mode 100644 index 3d764f3..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/model/PluginItem.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.aliucord.manager.ui.screens.plugins.model - -import androidx.compose.runtime.* - -@Stable -data class PluginItem( - val manifest: PluginManifest, - val path: String, -) { - // Plugins are enabled by default unless disabled - var enabled by mutableStateOf(true) -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/model/PluginManifest.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/model/PluginManifest.kt deleted file mode 100644 index 9687bdf..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/model/PluginManifest.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.aliucord.manager.ui.screens.plugins.model - -import androidx.compose.runtime.Immutable -import com.aliucord.manager.util.serialization.ImmutableListSerializer -import kotlinx.collections.immutable.ImmutableList -import kotlinx.serialization.Serializable - -@Immutable -@Serializable -data class PluginManifest( - val name: String, - @Serializable(with = ImmutableListSerializer::class) - val authors: ImmutableList, - val description: String, - val version: String, - val updateUrl: String, - val changelog: String?, - val changelogMedia: String?, -) { - val repositoryUrl: String - get() = updateUrl - .replace("raw.githubusercontent.com", "github.com") - .replaceFirst("/builds.*".toRegex(), "") - - @Immutable - @Serializable - data class Author( - val name: String, - val id: Long, - val hyperlink: Boolean = true, - ) { - val discordUrl: String - get() = "https://discord.com/users/$id" - } -} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/settings/SettingsScreen.kt index 189ef91..607d12e 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/settings/SettingsScreen.kt @@ -159,8 +159,8 @@ class SettingsScreen : Screen, Parcelable { painter = painterResource( when (preferences.installer) { InstallerSetting.PM -> R.drawable.ic_android - InstallerSetting.ROOT -> R.drawable.ic_code - InstallerSetting.Shizuku -> R.drawable.ic_tools + InstallerSetting.ROOT -> R.drawable.ic_hashtag + InstallerSetting.Shizuku -> R.drawable.ic_shizuku } ), contentDescription = null, diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/settings/components/InstallerDialog.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/settings/components/InstallerDialog.kt index 8c13701..a23536d 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/settings/components/InstallerDialog.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/settings/components/InstallerDialog.kt @@ -56,8 +56,8 @@ fun InstallerDialog( painter = painterResource( when (installer) { InstallerSetting.PM -> R.drawable.ic_android - InstallerSetting.ROOT -> R.drawable.ic_code - InstallerSetting.Shizuku -> R.drawable.ic_tools + InstallerSetting.ROOT -> R.drawable.ic_hashtag + InstallerSetting.Shizuku -> R.drawable.ic_shizuku } ), contentDescription = null, diff --git a/app/src/main/res/drawable/ic_hashtag.xml b/app/src/main/res/drawable/ic_hashtag.xml new file mode 100644 index 0000000..43ab10a --- /dev/null +++ b/app/src/main/res/drawable/ic_hashtag.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_shizuku.xml b/app/src/main/res/drawable/ic_shizuku.xml new file mode 100644 index 0000000..280aebe --- /dev/null +++ b/app/src/main/res/drawable/ic_shizuku.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index df2db3c..a51ce93 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -73,28 +73,19 @@ Dark Light - Plugins - View changelog - Uninstall %s - Are you sure you want to uninstall the %s plugin? - No plugins installed! - Changelog Media - An error occurred while processing plugins! - Back About About Manager Open logs Settings - Lead Developers + %d contributions + Contributors Kettu - Creator ShiggyCord - Creator Bunny - Creator Creator of Vendetta Original Manager Creator - Other Developers - %s contributions Failed to load Metered network