Improve ui (#23)
Co-authored-by: wingio <wingio@users.noreply.github.com> Co-authored-by: Diamond <33725716+DiamondMiner88@users.noreply.github.com>
This commit is contained in:
parent
15610c8809
commit
5a7698c049
31 changed files with 877 additions and 614 deletions
|
|
@ -60,6 +60,7 @@ android {
|
|||
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
freeCompilerArgs += "-Xcontext-receivers"
|
||||
}
|
||||
|
||||
buildFeatures.compose = true
|
||||
|
|
@ -67,7 +68,6 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree("./libs"))
|
||||
|
||||
// AndroidX
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
|
|
@ -104,7 +104,6 @@ dependencies {
|
|||
implementation("com.github.X1nto:Taxi:1.2.0")
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
||||
implementation("com.android:zipflinger:8.0.0-alpha01")
|
||||
// implementation("com.github.Aliucord:libzip:1.0.0")
|
||||
implementation("com.aliucord:axml:1.0.1")
|
||||
implementation("com.android.tools.build:apksig:7.4.0-beta01")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0")
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -17,11 +17,11 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|||
import com.aliucord.manager.domain.manager.PreferencesManager
|
||||
import com.aliucord.manager.ui.dialog.StoragePermissionsDialog
|
||||
import com.aliucord.manager.ui.navigation.AppDestination
|
||||
import com.aliucord.manager.ui.navigation.HomeDestination
|
||||
import com.aliucord.manager.ui.screen.*
|
||||
import com.aliucord.manager.ui.theme.ManagerTheme
|
||||
import com.aliucord.manager.ui.theme.Theme
|
||||
import com.xinto.taxi.Taxi
|
||||
import com.xinto.taxi.rememberBackstackNavigator
|
||||
import com.xinto.taxi.*
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
val aliucordDir = Environment.getExternalStorageDirectory().resolve("Aliucord")
|
||||
|
|
@ -41,9 +41,15 @@ class MainActivity : ComponentActivity() {
|
|||
isDynamicColor = preferences.dynamicColor
|
||||
) {
|
||||
val navigator = rememberBackstackNavigator<AppDestination>(AppDestination.Home)
|
||||
val homeRootNavigator = rememberNavigator(HomeDestination.HOME)
|
||||
|
||||
BackHandler {
|
||||
if (!navigator.pop()) finish()
|
||||
if (!navigator.pop()) {
|
||||
if (homeRootNavigator.currentDestination == HomeDestination.HOME)
|
||||
finish()
|
||||
else
|
||||
homeRootNavigator.replace(HomeDestination.HOME)
|
||||
}
|
||||
}
|
||||
|
||||
StoragePermissionsDialog()
|
||||
|
|
@ -55,20 +61,21 @@ class MainActivity : ComponentActivity() {
|
|||
) { destination ->
|
||||
when (destination) {
|
||||
is AppDestination.Home -> MainRootScreen(
|
||||
onClickInstall = { navigator.push(AppDestination.Install(it)) },
|
||||
onClickAbout = { navigator.push(AppDestination.About) },
|
||||
onClickSettings = { navigator.push(AppDestination.Settings) }
|
||||
mainRootNavigator = homeRootNavigator,
|
||||
onInstallClick = { navigator.push(AppDestination.Install(it)) },
|
||||
onAboutClick = { navigator.push(AppDestination.About) }
|
||||
)
|
||||
|
||||
is AppDestination.Install -> InstallerScreen(
|
||||
installData = destination.installData,
|
||||
onClickBack = navigator::pop
|
||||
)
|
||||
is AppDestination.Settings -> SettingsScreen(
|
||||
onClickBack = navigator::pop
|
||||
onBackClick = navigator::pop
|
||||
)
|
||||
|
||||
is AppDestination.About -> AboutScreen(
|
||||
onClickBack = navigator::pop
|
||||
onBackClick = navigator::pop
|
||||
)
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import org.koin.dsl.module
|
|||
import java.net.Inet4Address
|
||||
import java.net.InetAddress
|
||||
|
||||
val httpModule = module {
|
||||
val httpModule = module {
|
||||
fun provideJson() = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.aliucord.manager.di
|
||||
|
||||
import com.aliucord.manager.ui.viewmodel.*
|
||||
import org.koin.androidx.compose.get
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.androidx.viewmodel.dsl.viewModelOf
|
||||
import org.koin.dsl.module
|
||||
|
|
@ -10,6 +9,6 @@ val viewModelModule = module {
|
|||
viewModelOf(::HomeViewModel)
|
||||
viewModelOf(::PluginsViewModel)
|
||||
viewModelOf(::AboutViewModel)
|
||||
viewModel { params -> InstallViewModel(get(), get(), get(), get(), params.get())}
|
||||
viewModel { params -> InstallViewModel(get(), get(), get(), get(), params.get()) }
|
||||
viewModelOf(::SettingsViewModel)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import android.content.*
|
|||
import androidx.core.content.getSystemService
|
||||
import androidx.core.net.toUri
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
package com.aliucord.manager.domain.repository
|
||||
|
||||
import com.aliucord.manager.network.dto.GithubUser
|
||||
import com.aliucord.manager.network.dto.Version
|
||||
import com.aliucord.manager.network.service.GithubService
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class GithubRepository(
|
||||
private val service: GithubService
|
||||
|
|
|
|||
|
|
@ -18,16 +18,19 @@ class InstallService : Service() {
|
|||
|
||||
startActivity(confirmationIntent)
|
||||
}
|
||||
|
||||
PackageInstaller.STATUS_SUCCESS -> Toast.makeText(
|
||||
this,
|
||||
"Successfully installed Aliucord!",
|
||||
Toast.LENGTH_LONG
|
||||
this,
|
||||
"Successfully installed Aliucord!",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
|
||||
PackageInstaller.STATUS_FAILURE_ABORTED -> Toast.makeText(
|
||||
this,
|
||||
"Aborted installation",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
|
||||
else -> {
|
||||
Log.i(BuildConfig.TAG, "Installation failed: error code $statusCode")
|
||||
Toast.makeText(this, "Failed: Error $statusCode", Toast.LENGTH_LONG).show()
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ object ManifestPatcher {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> visitor
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Juby210 & zt
|
||||
* Licensed under the Open Software License version 3.0
|
||||
*/
|
||||
|
||||
package com.aliucord.manager.installer.util
|
||||
|
||||
import com.aliucord.libzip.Zip
|
||||
import java.io.InputStream
|
||||
|
||||
fun Zip.writeEntry(entryName: String, stream: InputStream) = writeEntry(entryName, stream.readBytes())
|
||||
fun Zip.writeEntry(entryName: String, bytes: ByteArray) {
|
||||
openEntry(entryName)
|
||||
writeEntry(bytes, bytes.size.toLong())
|
||||
closeEntry()
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ data class Commit(
|
|||
) {
|
||||
@Serializable
|
||||
data class Author(@SerialName("login") val name: String)
|
||||
|
||||
@Serializable
|
||||
data class Commit(val message: String)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Juby210 & zt
|
||||
* Licensed under the Open Software License version 3.0
|
||||
*/
|
||||
|
||||
package com.aliucord.manager.ui.component
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
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.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.network.dto.GithubUser
|
||||
|
||||
fun LazyListScope.contributors(contributors: List<GithubUser>) {
|
||||
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(R.string.contributors),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, bottom = 16.dp)
|
||||
)
|
||||
}
|
||||
|
||||
if (contributors.isNotEmpty()) {
|
||||
items(contributors) { user ->
|
||||
ContributorEntry(user = user)
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
content = { CircularProgressIndicator() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContributorEntry(user: GithubUser) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
uriHandler.openUri("https://github.com/${user.name}")
|
||||
}
|
||||
.padding(horizontal = 16.dp, vertical = 10.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
AsyncImage(
|
||||
model = "https://github.com/${user.name}.png",
|
||||
contentDescription = user.name,
|
||||
Modifier
|
||||
.size(45.dp)
|
||||
.clip(CircleShape)
|
||||
)
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = user.name,
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.contributions, user.contributions),
|
||||
style = MaterialTheme.typography.bodyMedium.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Juby210 & zt
|
||||
* Licensed under the Open Software License version 3.0
|
||||
*/
|
||||
|
||||
package com.aliucord.manager.ui.component
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.grid.*
|
||||
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.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.network.dto.GithubUser
|
||||
|
||||
@Composable
|
||||
fun ContributorsCard(contributors: List<GithubUser>) {
|
||||
ElevatedCard {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.contributors),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
if (contributors.isNotEmpty()) {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(48.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
userScrollEnabled = false
|
||||
) {
|
||||
items(contributors) { user -> ContributorEntry(user.name) }
|
||||
}
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
content = { CircularProgressIndicator() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContributorEntry(name: String) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
AsyncImage(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(CircleShape)
|
||||
.clickable { uriHandler.openUri("https://github.com/$name") },
|
||||
model = "https://github.com/$name.png",
|
||||
contentDescription = name
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
package com.aliucord.manager.ui.component.home
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
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.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.items
|
||||
import coil.compose.AsyncImage
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.network.dto.Commit
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Composable
|
||||
fun CommitList(
|
||||
commits: Flow<PagingData<Commit>>
|
||||
) {
|
||||
ElevatedCard(
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f, true)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
val lazyPagingItems = commits.collectAsLazyPagingItems()
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(20.dp, 20.dp, 20.dp),
|
||||
text = stringResource(R.string.commits),
|
||||
style = MaterialTheme.typography.titleLarge.copy(fontSize = 23.sp),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
if (lazyPagingItems.loadState.refresh == LoadState.Loading) {
|
||||
item {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentWidth(Alignment.CenterHorizontally),
|
||||
text = stringResource(R.string.paging_initial_load)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(lazyPagingItems) { commitData ->
|
||||
if (commitData == null) return@items
|
||||
|
||||
CommitItem(commitData)
|
||||
}
|
||||
|
||||
if (lazyPagingItems.loadState.append == LoadState.Loading) {
|
||||
item {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.wrapContentWidth(Alignment.CenterHorizontally)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CommitItem(
|
||||
commit: Commit
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri(commit.htmlUrl)
|
||||
}
|
||||
.padding(horizontal = 20.dp, vertical = 5.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
|
||||
Text(
|
||||
commit.commit.message.split("\n").first(),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
|
||||
if (commit.author != null) Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
AsyncImage(
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clip(CircleShape),
|
||||
model = "https://github.com/${commit.author.name}.png",
|
||||
contentDescription = commit.author.name
|
||||
)
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
withStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
) {
|
||||
append(commit.author.name)
|
||||
}
|
||||
|
||||
withStyle(
|
||||
SpanStyle(
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(
|
||||
alpha = 0.6f
|
||||
)
|
||||
)
|
||||
) {
|
||||
append(" authored ")
|
||||
}
|
||||
|
||||
withStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
) {
|
||||
append(commit.sha.substring(0, 7))
|
||||
}
|
||||
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package com.aliucord.manager.ui.component.home
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aliucord.manager.R
|
||||
|
||||
@Composable
|
||||
fun InfoCard(
|
||||
packageName: String,
|
||||
supportedVersion: String,
|
||||
currentVersion: String,
|
||||
onDownloadClick: () -> Unit,
|
||||
onUninstallClick: () -> Unit,
|
||||
onLaunchClick: () -> Unit
|
||||
) {
|
||||
ElevatedCard {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(20.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Aliucord${packageName.let { if (it != "com.aliucord") " ($it)" else "" }}",
|
||||
style = MaterialTheme.typography.titleLarge.copy(fontSize = 23.sp),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append("Supported version: ")
|
||||
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(supportedVersion)
|
||||
}
|
||||
|
||||
append("\nInstalled version: ")
|
||||
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(currentVersion)
|
||||
}
|
||||
},
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 10.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
|
||||
val (icon, description) = when (currentVersion) {
|
||||
"-" -> Icons.Default.Download to R.string.install
|
||||
supportedVersion -> Icons.Default.Refresh to R.string.reinstall
|
||||
else -> Icons.Default.Update to R.string.update
|
||||
}
|
||||
|
||||
FilledTonalIconButton(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.heightIn(min = 50.dp),
|
||||
onClick = onDownloadClick,
|
||||
shape = ShapeDefaults.Large
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(5.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = stringResource(description)
|
||||
)
|
||||
if (currentVersion == "-") Text(
|
||||
stringResource(description),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (currentVersion != "-") {
|
||||
FilledTonalIconButton(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.heightIn(min = 50.dp),
|
||||
onClick = onUninstallClick,
|
||||
shape = ShapeDefaults.Large
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.uninstall)
|
||||
)
|
||||
}
|
||||
|
||||
FilledTonalIconButton(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.heightIn(min = 50.dp),
|
||||
onClick = onLaunchClick,
|
||||
shape = ShapeDefaults.Large
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Launch,
|
||||
contentDescription = stringResource(R.string.launch)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -106,6 +106,7 @@ fun Changelog(
|
|||
modifier = Modifier.padding(top = 16.dp, bottom = 6.dp)
|
||||
)
|
||||
}
|
||||
|
||||
'*' -> {
|
||||
LinkText(
|
||||
modifier = Modifier.padding(bottom = 2.dp),
|
||||
|
|
@ -123,6 +124,7 @@ fun Changelog(
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
when {
|
||||
line.endsWith("marginTop}") -> {
|
||||
|
|
@ -135,7 +137,8 @@ fun Changelog(
|
|||
modifier = Modifier.padding(top = 16.dp, bottom = 6.dp)
|
||||
)
|
||||
}
|
||||
line.all { c -> c == '=' } -> { } // Discord ignores =======
|
||||
|
||||
line.all { c -> c == '=' } -> {} // Discord ignores =======
|
||||
else -> {
|
||||
LinkText(
|
||||
annotatedString = buildAnnotatedString {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.compose.material.icons.filled.History
|
|||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
|
|
@ -21,9 +22,11 @@ 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.R
|
||||
import com.aliucord.manager.domain.model.Plugin
|
||||
import com.aliucord.manager.ui.util.joinToAnnotatedString
|
||||
|
||||
@Composable
|
||||
fun PluginCard(
|
||||
|
|
@ -31,81 +34,86 @@ fun PluginCard(
|
|||
onClickDelete: () -> Unit,
|
||||
onClickShowChangelog: () -> Unit
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
var isEnabled by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
ElevatedCard {
|
||||
// Header
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable { isEnabled = !isEnabled }
|
||||
.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column {
|
||||
// Name
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(plugin.manifest.name)
|
||||
}
|
||||
|
||||
append(" v${plugin.manifest.version}")
|
||||
}
|
||||
)
|
||||
|
||||
// Authors
|
||||
val authors = buildAnnotatedString {
|
||||
withStyle(SpanStyle(color = MaterialTheme.colorScheme.primary)) {
|
||||
plugin.manifest.authors.joinToAnnotatedString(
|
||||
prefix = {
|
||||
withStyle(SpanStyle(color = MaterialTheme.colorScheme.secondary)) {
|
||||
append("By ")
|
||||
}
|
||||
}
|
||||
) { author ->
|
||||
val start = this@joinToAnnotatedString.length
|
||||
withStyle(
|
||||
SpanStyle(
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
) {
|
||||
append(author.name)
|
||||
}
|
||||
addStringAnnotation("authorId", author.id.toString(), start, start + author.name.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClickableText(
|
||||
text = authors,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
onClick = { offset ->
|
||||
authors.getStringAnnotations("authorId", offset, offset)
|
||||
.firstOrNull()
|
||||
?.let { (id) -> uriHandler.openUri("https://discord.com/users/$id") }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.weight(1f, true))
|
||||
|
||||
// Toggle Switch
|
||||
Switch(
|
||||
checked = isEnabled,
|
||||
onCheckedChange = { isEnabled = it }
|
||||
)
|
||||
}
|
||||
|
||||
Divider(modifier = Modifier
|
||||
.alpha(0.3f)
|
||||
.padding(horizontal = 16.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
var isEnabled by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
// Header
|
||||
Row(
|
||||
modifier = Modifier.clickable { isEnabled = !isEnabled }
|
||||
) {
|
||||
Column {
|
||||
// Name
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(plugin.manifest.name)
|
||||
}
|
||||
|
||||
append(" v${plugin.manifest.version}")
|
||||
}
|
||||
)
|
||||
|
||||
// Authors
|
||||
val authors = buildAnnotatedString {
|
||||
withStyle(SpanStyle(color = MaterialTheme.colorScheme.secondary)) {
|
||||
append("By ")
|
||||
}
|
||||
|
||||
val iterator = plugin.manifest.authors.iterator()
|
||||
|
||||
iterator.forEach { author ->
|
||||
withStyle(SpanStyle(color = MaterialTheme.colorScheme.primary)) {
|
||||
val start = this@buildAnnotatedString.length
|
||||
append(author.name)
|
||||
addStringAnnotation("authorId", author.id.toString(), start, start + author.name.length)
|
||||
}
|
||||
|
||||
if (iterator.hasNext()) {
|
||||
withStyle(SpanStyle(color = MaterialTheme.colorScheme.secondary)) {
|
||||
append(", ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClickableText(
|
||||
text = authors,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
onClick = { offset ->
|
||||
authors.getStringAnnotations("authorId", offset, offset)
|
||||
.firstOrNull()
|
||||
?.let { (id) -> uriHandler.openUri("https://discord.com/users/$id") }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.weight(1f, true))
|
||||
|
||||
// Toggle Switch
|
||||
Switch(
|
||||
checked = isEnabled,
|
||||
onCheckedChange = { isEnabled = it }
|
||||
)
|
||||
}
|
||||
|
||||
Divider(modifier = Modifier.alpha(0.3f))
|
||||
|
||||
// Description
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.heightIn(max = 150.dp, min = 40.dp)
|
||||
.padding(top = 10.dp, bottom = 20.dp),
|
||||
.padding(bottom = 20.dp),
|
||||
text = plugin.manifest.description,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package com.aliucord.manager.ui.component.settings
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun SettingsHeader(
|
||||
text: String
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = Modifier.padding(18.dp, 24.dp, 18.dp, 10.dp)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.aliucord.manager.ui.component.settings
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Composable
|
||||
fun SettingsItem(
|
||||
modifier: Modifier = Modifier,
|
||||
icon: @Composable (() -> Unit) = { },
|
||||
text: @Composable () -> Unit,
|
||||
secondaryText: @Composable (() -> Unit) = { },
|
||||
trailing: @Composable (() -> Unit) = { },
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.heightIn(min = 64.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 18.dp, vertical = 4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.padding(end = 5.dp)
|
||||
) {
|
||||
icon()
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.weight(0.95f, true)
|
||||
) {
|
||||
ProvideTextStyle(
|
||||
MaterialTheme.typography.titleLarge.copy(
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
) {
|
||||
text()
|
||||
}
|
||||
ProvideTextStyle(
|
||||
MaterialTheme.typography.bodyMedium.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(0.6f)
|
||||
)
|
||||
) {
|
||||
secondaryText()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.weight(0.05f, true))
|
||||
|
||||
trailing()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.aliucord.manager.ui.component.settings
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun SettingsSwitch(
|
||||
label: String,
|
||||
secondaryLabel: String? = null,
|
||||
disabled: Boolean = false,
|
||||
icon: @Composable () -> Unit = {},
|
||||
pref: Boolean,
|
||||
onPrefChange: (Boolean) -> Unit,
|
||||
) {
|
||||
SettingsItem(
|
||||
modifier = Modifier.clickable(enabled = !disabled) { onPrefChange(!pref) },
|
||||
text = { Text(text = label, softWrap = true) },
|
||||
icon = icon,
|
||||
secondaryText = {
|
||||
secondaryLabel?.let {
|
||||
Text(text = it)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Switch(
|
||||
checked = pref,
|
||||
enabled = !disabled,
|
||||
onCheckedChange = { onPrefChange(!pref) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.aliucord.manager.ui.component.settings
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsTextField(
|
||||
label: String,
|
||||
disabled: Boolean = false,
|
||||
pref: String,
|
||||
onPrefChange: (String) -> Unit,
|
||||
) {
|
||||
Box(modifier = Modifier.padding(horizontal = 18.dp, vertical = 10.dp)) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = pref,
|
||||
onValueChange = onPrefChange,
|
||||
enabled = !disabled,
|
||||
label = { Text(label) },
|
||||
singleLine = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,8 @@ package com.aliucord.manager.ui.navigation
|
|||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Extension
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.outlined.Extension
|
||||
import androidx.compose.material.icons.outlined.Home
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.screen.InstallData
|
||||
|
|
@ -35,5 +33,6 @@ enum class HomeDestination(
|
|||
@StringRes val label: Int
|
||||
) : Destination {
|
||||
HOME(Icons.Default.Home, Icons.Outlined.Home, R.string.home),
|
||||
PLUGINS(Icons.Default.Extension, Icons.Outlined.Extension, R.string.plugins)
|
||||
PLUGINS(Icons.Default.Extension, Icons.Outlined.Extension, R.string.plugins),
|
||||
SETTINGS(Icons.Default.Settings, Icons.Outlined.Settings, R.string.settings)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,17 @@
|
|||
|
||||
package com.aliucord.manager.ui.screen
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.NavigateBefore
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -18,9 +23,10 @@ import androidx.compose.ui.platform.LocalUriHandler
|
|||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.component.ContributorsCard
|
||||
import com.aliucord.manager.ui.component.contributors
|
||||
import com.aliucord.manager.ui.viewmodel.AboutViewModel
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
|
|
@ -28,16 +34,18 @@ import org.koin.androidx.compose.getViewModel
|
|||
@Composable
|
||||
fun AboutScreen(
|
||||
viewModel: AboutViewModel = getViewModel(),
|
||||
onClickBack: () -> Unit
|
||||
onBackClick: () -> Unit
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.about)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onClickBack) {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.NavigateBefore,
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back)
|
||||
)
|
||||
}
|
||||
|
|
@ -45,80 +53,120 @@ fun AboutScreen(
|
|||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.padding(horizontal = 12.dp, vertical = 14.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
ElevatedCard {
|
||||
item {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.team),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
AsyncImage(
|
||||
model = "https://github.com/Aliucord.png",
|
||||
contentDescription = "Aliucord",
|
||||
modifier = Modifier.size(71.dp)
|
||||
)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
Text(
|
||||
text = "Aliucord",
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
fontSize = 26.sp
|
||||
)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.aliucord_description),
|
||||
style = MaterialTheme.typography.titleSmall.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
UserEntry("Juby210", "Fox")
|
||||
UserEntry("Vendicated", "the ven")
|
||||
TextButton(onClick = { uriHandler.openUri("https://github.com/Aliucord") }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_account_github_white_24dp),
|
||||
contentDescription = stringResource(R.string.github)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
|
||||
Text(text = stringResource(id = R.string.github))
|
||||
}
|
||||
|
||||
TextButton(onClick = { uriHandler.openUri("https://aliucord.com") }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Link,
|
||||
contentDescription = stringResource(R.string.website)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
|
||||
Text(text = stringResource(id = R.string.website))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ContributorsCard(viewModel.contributors)
|
||||
|
||||
Spacer(Modifier.weight(1f, true))
|
||||
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { uriHandler.openUri("https://github.com/Aliucord/Aliucord") }
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_account_github_white_24dp),
|
||||
contentDescription = stringResource(R.string.github)
|
||||
)
|
||||
|
||||
Spacer(Modifier.width(ButtonDefaults.IconSpacing))
|
||||
|
||||
Text(stringResource(R.string.github))
|
||||
item {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp)
|
||||
) {
|
||||
UserEntry("Vendicated", "the ven")
|
||||
UserEntry("Juby210", "Fox")
|
||||
UserEntry("rushii", "explod", "DiamondMiner88")
|
||||
}
|
||||
}
|
||||
|
||||
contributors(viewModel.contributors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UserEntry(name: String, roles: String) {
|
||||
Column {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
private fun UserEntry(name: String, roles: String, username: String = name) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
onClick = { uriHandler.openUri("https://github.com/$username") },
|
||||
indication = rememberRipple(bounded = false, radius = 90.dp),
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
.widthIn(min = 100.dp)
|
||||
) {
|
||||
AsyncImage(
|
||||
modifier = Modifier
|
||||
.size(71.dp)
|
||||
.clip(CircleShape),
|
||||
model = "https://github.com/$username.png",
|
||||
contentDescription = username
|
||||
)
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AsyncImage(
|
||||
modifier = Modifier
|
||||
.size(54.dp)
|
||||
.clip(CircleShape),
|
||||
model = "https://github.com/$name.png",
|
||||
contentDescription = name
|
||||
Text(
|
||||
text = name,
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
fontSize = 18.sp
|
||||
)
|
||||
)
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = name,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
Text(
|
||||
text = roles,
|
||||
style = MaterialTheme.typography.titleSmall.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
|
||||
Text(roles)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,33 @@
|
|||
package com.aliucord.manager.ui.screen
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.aliucord.manager.BuildConfig
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.navigation.HomeDestination
|
||||
import com.xinto.taxi.RegularNavigator
|
||||
import com.xinto.taxi.Taxi
|
||||
import com.xinto.taxi.rememberNavigator
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun MainRootScreen(
|
||||
onClickInstall: (InstallData) -> Unit,
|
||||
onClickAbout: () -> Unit,
|
||||
onClickSettings: () -> Unit
|
||||
mainRootNavigator: RegularNavigator<HomeDestination>,
|
||||
onInstallClick: (InstallData) -> Unit,
|
||||
onAboutClick: () -> Unit
|
||||
) {
|
||||
val mainRootNavigator = rememberNavigator(HomeDestination.HOME)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
stringResource(mainRootNavigator.currentDestination.label),
|
||||
modifier = Modifier.padding(horizontal = 4.dp)
|
||||
)
|
||||
},
|
||||
title = { Text(stringResource(mainRootNavigator.currentDestination.label)) },
|
||||
actions = {
|
||||
val localUriHandler = LocalUriHandler.current
|
||||
|
||||
|
|
@ -50,19 +42,12 @@ fun MainRootScreen(
|
|||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = onClickAbout) {
|
||||
IconButton(onClick = onAboutClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
contentDescription = stringResource(R.string.about)
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = onClickSettings) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = stringResource(R.string.settings)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
|
|
@ -94,15 +79,19 @@ fun MainRootScreen(
|
|||
}
|
||||
) { paddingValues ->
|
||||
Taxi(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize(),
|
||||
navigator = mainRootNavigator,
|
||||
transitionSpec = { fadeIn() with fadeOut() }
|
||||
) { destination ->
|
||||
when (destination) {
|
||||
HomeDestination.HOME -> HomeScreen(
|
||||
onClickInstall = onClickInstall
|
||||
onClickInstall = onInstallClick
|
||||
)
|
||||
|
||||
HomeDestination.PLUGINS -> PluginsScreen()
|
||||
HomeDestination.SETTINGS -> SettingsScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,30 +8,17 @@ package com.aliucord.manager.ui.screen
|
|||
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.items
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.component.home.CommitList
|
||||
import com.aliucord.manager.ui.component.home.InfoCard
|
||||
import com.aliucord.manager.ui.dialog.DownloadMethod
|
||||
import com.aliucord.manager.ui.dialog.InstallerDialog
|
||||
import com.aliucord.manager.ui.viewmodel.HomeViewModel
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
viewModel: HomeViewModel = getViewModel(),
|
||||
|
|
@ -65,167 +52,29 @@ fun HomeScreen(
|
|||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(top = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
ElevatedCard {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = "Aliucord${viewModel.preferences.packageName.let { if (it != "com.aliucord") " ($it)" else "" }}",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append("Supported version: ")
|
||||
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(viewModel.supportedVersion)
|
||||
}
|
||||
|
||||
append("\nInstalled version: ")
|
||||
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(viewModel.installedVersion)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
val (icon, description) = when (viewModel.installedVersion) {
|
||||
"-" -> Icons.Default.Download to R.string.install
|
||||
viewModel.supportedVersion -> Icons.Default.Update to R.string.reinstall
|
||||
else -> Icons.Default.Update to R.string.update
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.weight(1f, true),
|
||||
onClick = {
|
||||
if (viewModel.preferences.devMode) {
|
||||
showOptionsDialog = true
|
||||
} else {
|
||||
onClickInstall(
|
||||
InstallData(
|
||||
DownloadMethod.DOWNLOAD,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
imageVector = icon,
|
||||
contentDescription = stringResource(description),
|
||||
tint = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
|
||||
Text(stringResource(description))
|
||||
}
|
||||
}
|
||||
|
||||
if (viewModel.installedVersion != "-") {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides MaterialTheme.colorScheme.secondaryContainer
|
||||
) {
|
||||
FilledTonalButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = viewModel::uninstallAliucord
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.uninstall)
|
||||
)
|
||||
}
|
||||
|
||||
FilledTonalButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = viewModel::launchAliucord
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
imageVector = Icons.Default.Launch,
|
||||
contentDescription = stringResource(R.string.launch)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ElevatedCard(
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f, true)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
val lazyPagingItems = viewModel.commits.collectAsLazyPagingItems()
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(16.dp, 16.dp, 16.dp),
|
||||
text = stringResource(R.string.commits),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
if (lazyPagingItems.loadState.refresh == LoadState.Loading) {
|
||||
item {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentWidth(Alignment.CenterHorizontally),
|
||||
text = stringResource(R.string.paging_initial_load)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(lazyPagingItems) { commitData ->
|
||||
if (commitData == null) return@items
|
||||
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { uriHandler.openUri(commitData.htmlUrl) },
|
||||
overlineText = { Text(commitData.sha.substring(0, 7)) },
|
||||
headlineText = { Text("${commitData.commit.message.split("\n").first()} - ${commitData.author?.name ?: "UNKNOWN"}") }
|
||||
InfoCard(
|
||||
packageName = viewModel.preferences.packageName,
|
||||
supportedVersion = viewModel.supportedVersion,
|
||||
currentVersion = viewModel.installedVersion,
|
||||
onDownloadClick = {
|
||||
if (viewModel.preferences.devMode) {
|
||||
showOptionsDialog = true
|
||||
} else {
|
||||
onClickInstall(
|
||||
InstallData(
|
||||
DownloadMethod.DOWNLOAD,
|
||||
)
|
||||
}
|
||||
|
||||
if (lazyPagingItems.loadState.append == LoadState.Loading) {
|
||||
item {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentWidth(Alignment.CenterHorizontally)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onLaunchClick = viewModel::launchAliucord,
|
||||
onUninstallClick = viewModel::uninstallAliucord
|
||||
)
|
||||
|
||||
CommitList(
|
||||
commits = viewModel.commits
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,19 +35,19 @@ data class InstallData(
|
|||
@Composable
|
||||
fun InstallerScreen(
|
||||
installData: InstallData,
|
||||
onClickBack: () -> Unit,
|
||||
onBackClick: () -> Unit,
|
||||
viewModel: InstallViewModel = getViewModel(parameters = { parametersOf(installData) })
|
||||
) {
|
||||
val navigateMain by viewModel.returnToHome.collectAsState(initial = false)
|
||||
|
||||
if (navigateMain) onClickBack()
|
||||
if (navigateMain) onBackClick()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Installer") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onClickBack) {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.NavigateBefore,
|
||||
contentDescription = stringResource(R.string.back)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ fun PluginsScreen(
|
|||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
verticalArrangement = Arrangement.spacedBy(14.dp)
|
||||
) {
|
||||
val search = viewModel.search
|
||||
|
||||
|
|
@ -119,8 +119,8 @@ fun PluginsScreen(
|
|||
)
|
||||
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(bottom = 8.dp, top = 6.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
contentPadding = PaddingValues(bottom = 15.dp, top = 6.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(14.dp)
|
||||
) {
|
||||
items(
|
||||
viewModel.plugins.filter { plugin ->
|
||||
|
|
|
|||
|
|
@ -15,186 +15,126 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.component.settings.*
|
||||
import com.aliucord.manager.ui.theme.Theme
|
||||
import com.aliucord.manager.ui.viewmodel.SettingsViewModel
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
viewModel: SettingsViewModel = getViewModel(),
|
||||
onClickBack: () -> Unit
|
||||
viewModel: SettingsViewModel = getViewModel()
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(state = rememberScrollState())
|
||||
) {
|
||||
val preferences = viewModel.preferences
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.settings)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onClickBack) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.NavigateBefore,
|
||||
contentDescription = stringResource(R.string.back)
|
||||
)
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
if (viewModel.showThemeDialog) {
|
||||
ThemeDialog(
|
||||
currentTheme = preferences.theme,
|
||||
onDismissRequest = viewModel::hideThemeDialog,
|
||||
onConfirm = viewModel::setTheme
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.padding(horizontal = 12.dp, vertical = 14.dp)
|
||||
.verticalScroll(state = rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
|
||||
SettingsHeader(stringResource(R.string.appearance))
|
||||
|
||||
SettingsItem(
|
||||
modifier = Modifier.clickable(onClick = viewModel::showThemeDialog),
|
||||
icon = { Icon(Icons.Default.Style, null) },
|
||||
text = { Text(stringResource(R.string.theme)) }
|
||||
) {
|
||||
val preferences = viewModel.preferences
|
||||
|
||||
if (viewModel.showThemeDialog) {
|
||||
ThemeDialog(
|
||||
currentTheme = preferences.theme,
|
||||
onDismissRequest = viewModel::hideThemeDialog,
|
||||
onConfirm = viewModel::setTheme
|
||||
)
|
||||
FilledTonalButton(onClick = viewModel::showThemeDialog) {
|
||||
Text(preferences.theme.displayName)
|
||||
}
|
||||
}
|
||||
|
||||
GroupHeader(stringResource(R.string.appearance))
|
||||
SettingsSwitch(
|
||||
label = stringResource(R.string.dynamic_color),
|
||||
pref = preferences.dynamicColor,
|
||||
icon = { Icon(Icons.Default.Palette, null) }
|
||||
) {
|
||||
preferences.dynamicColor = it
|
||||
}
|
||||
|
||||
ListItem(
|
||||
modifier = Modifier.clickable(onClick = viewModel::showThemeDialog),
|
||||
headlineText = { Text(stringResource(R.string.theme)) },
|
||||
supportingText = { Text(stringResource(R.string.theme_setting_description)) },
|
||||
leadingContent = { Icon(Icons.Default.Style, null) },
|
||||
trailingContent = {
|
||||
FilledTonalButton(onClick = viewModel::showThemeDialog) {
|
||||
Text(preferences.theme.displayName)
|
||||
}
|
||||
}
|
||||
)
|
||||
SettingsHeader(stringResource(R.string.advanced))
|
||||
|
||||
SwitchSetting(
|
||||
checked = preferences.dynamicColor,
|
||||
title = { Text(stringResource(R.string.dynamic_color)) },
|
||||
onCheckedChange = { preferences.dynamicColor = it },
|
||||
icon = { Icon(Icons.Default.Palette, null) }
|
||||
)
|
||||
SettingsSwitch(
|
||||
label = stringResource(R.string.replace_bg),
|
||||
secondaryLabel = stringResource(R.string.replace_bg_description),
|
||||
pref = preferences.replaceIcon,
|
||||
icon = { Icon(Icons.Default.AppShortcut, null) }
|
||||
) {
|
||||
preferences.replaceIcon = it
|
||||
}
|
||||
|
||||
GroupHeader(stringResource(R.string.advanced))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
SwitchSetting(
|
||||
checked = preferences.replaceIcon,
|
||||
title = { Text(stringResource(R.string.replace_bg)) },
|
||||
onCheckedChange = { preferences.replaceIcon = it },
|
||||
icon = { Icon(Icons.Default.AppShortcut, null) }
|
||||
)
|
||||
SettingsTextField(
|
||||
label = stringResource(R.string.app_name_setting),
|
||||
pref = preferences.appName,
|
||||
onPrefChange = viewModel::setAppName
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp),
|
||||
value = preferences.appName,
|
||||
onValueChange = viewModel::setAppName,
|
||||
label = { Text(stringResource(R.string.app_name_setting)) },
|
||||
placeholder = { Text("Aliucord") },
|
||||
singleLine = true
|
||||
)
|
||||
SettingsSwitch(
|
||||
label = stringResource(R.string.developer_options),
|
||||
pref = preferences.devMode,
|
||||
icon = { Icon(Icons.Default.Code, null) }
|
||||
) {
|
||||
preferences.devMode = it
|
||||
}
|
||||
|
||||
SwitchSetting(
|
||||
checked = preferences.devMode,
|
||||
title = { Text(stringResource(R.string.developer_options)) },
|
||||
onCheckedChange = { preferences.devMode = it },
|
||||
icon = { Icon(Icons.Default.Code, null) }
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = preferences.devMode,
|
||||
enter = expandVertically(),
|
||||
exit = shrinkVertically()
|
||||
AnimatedVisibility(
|
||||
visible = preferences.devMode,
|
||||
enter = expandVertically(),
|
||||
exit = shrinkVertically()
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
SettingsTextField(
|
||||
label = stringResource(R.string.package_name),
|
||||
pref = preferences.packageName,
|
||||
onPrefChange = viewModel::setPackageName
|
||||
)
|
||||
|
||||
SettingsTextField(
|
||||
label = stringResource(R.string.version),
|
||||
pref = preferences.version,
|
||||
onPrefChange = viewModel::setVersion
|
||||
)
|
||||
|
||||
SettingsSwitch(
|
||||
label = stringResource(R.string.debuggable),
|
||||
secondaryLabel = stringResource(R.string.debuggable_description),
|
||||
pref = preferences.debuggable,
|
||||
icon = { Icon(Icons.Default.BugReport, null) }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp),
|
||||
value = preferences.packageName,
|
||||
onValueChange = viewModel::setPackageName,
|
||||
label = { Text(stringResource(R.string.package_name)) },
|
||||
placeholder = { Text("com.aliucord") },
|
||||
singleLine = true
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp),
|
||||
value = preferences.version,
|
||||
onValueChange = viewModel::setVersion,
|
||||
label = { Text(stringResource(R.string.version)) },
|
||||
singleLine = true
|
||||
)
|
||||
|
||||
SwitchSetting(
|
||||
checked = preferences.debuggable,
|
||||
title = { Text(stringResource(R.string.debuggable)) },
|
||||
description = { Text(stringResource(R.string.debuggable_description)) },
|
||||
onCheckedChange = { preferences.debuggable = it },
|
||||
icon = { Icon(Icons.Default.BugReport, null) }
|
||||
)
|
||||
preferences.debuggable = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
onClick = viewModel::clearCacheDir
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.clear_files_cache),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(18.dp),
|
||||
shape = ShapeDefaults.Large,
|
||||
onClick = viewModel::clearCacheDir
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.clear_cache),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SwitchSetting(
|
||||
enabled: Boolean = true,
|
||||
checked: Boolean,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
title: @Composable () -> Unit,
|
||||
description: @Composable (() -> Unit)? = null,
|
||||
onCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { onCheckedChange(!checked) },
|
||||
leadingContent = icon,
|
||||
headlineText = title,
|
||||
supportingText = description,
|
||||
trailingContent = {
|
||||
Switch(
|
||||
enabled = enabled,
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ThemeDialog(
|
||||
currentTheme: Theme,
|
||||
|
|
@ -248,23 +188,3 @@ fun ThemeDialog(
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GroupHeader(
|
||||
title: String,
|
||||
color: Color = MaterialTheme.colorScheme.primary
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.padding(start = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
color = color,
|
||||
fontSize = LocalTextStyle.current.fontSize.times(0.95f),
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt
Normal file
23
app/src/main/kotlin/com/aliucord/manager/ui/util/Utils.kt
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package com.aliucord.manager.ui.util
|
||||
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
|
||||
context(AnnotatedString.Builder)
|
||||
inline fun <T> Iterable<T>.joinToAnnotatedString(
|
||||
separator: AnnotatedString.Builder.() -> Unit = { append(", ") },
|
||||
prefix: AnnotatedString.Builder.() -> Unit = {},
|
||||
postfix: AnnotatedString.Builder.() -> Unit = {},
|
||||
transform: (AnnotatedString.Builder.(T) -> Unit) = {
|
||||
when (it) {
|
||||
is Char -> append(it)
|
||||
else -> append(it.toString())
|
||||
}
|
||||
}
|
||||
) {
|
||||
prefix(this@Builder)
|
||||
for ((count, element) in this@joinToAnnotatedString.withIndex()) {
|
||||
if (count + 1 > 1) separator(this@Builder)
|
||||
transform(this@Builder, element)
|
||||
}
|
||||
postfix(this@Builder)
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@ package com.aliucord.manager.ui.viewmodel
|
|||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.aliucord.manager.network.dto.GithubUser
|
||||
import com.aliucord.manager.domain.repository.GithubRepository
|
||||
import com.aliucord.manager.network.dto.GithubUser
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AboutViewModel(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<resources>
|
||||
<string name="app_name">Aliucord Manager</string>
|
||||
<string name="clear_files_cache">Clear files cache</string>
|
||||
<string name="clear_cache">Clear cache</string>
|
||||
<string name="commits">Commits</string>
|
||||
<string name="dex_location">Dex location</string>
|
||||
<string name="open_settings">Open settings</string>
|
||||
<string name="replace_bg">Replace app icon with Aliucord variant</string>
|
||||
<string name="replace_bg">Replace app icon</string>
|
||||
<string name="replace_bg_description">Uses the Aliucord variant as opposed to Discord\'s own icon</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="show_menu">Show menu</string>
|
||||
<string name="theme">Theme</string>
|
||||
|
|
@ -28,7 +29,9 @@
|
|||
<string name="contributors">Contributors</string>
|
||||
<string name="search_plugins">Search Plugins</string>
|
||||
<string name="github">GitHub</string>
|
||||
<string name="website">Website</string>
|
||||
<string name="installer">Installer</string>
|
||||
<string name="contributions">%o contributions</string>
|
||||
<string name="theme_setting_description">Manager appearance</string>
|
||||
<string name="no_plugins_installed">No plugins installed!</string>
|
||||
<string name="paging_initial_load">Loading…</string>
|
||||
|
|
@ -51,4 +54,5 @@
|
|||
<string name="advanced">Advanced</string>
|
||||
<string name="permissions_grant">Grant Permissions</string>
|
||||
<string name="permissions_grant_body">In order for Aliucord Manager to work properly, you need to grant file permissions. Since shared data in stored in ~/Aliucord, permissions are required in order for manager to function.</string>
|
||||
<string name="aliucord_description">A modification for the Discord Android App</string>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue