feat: new home page (#65)
This commit is contained in:
parent
d4c575f496
commit
15464fabe5
20 changed files with 646 additions and 350 deletions
|
|
@ -91,10 +91,11 @@ android {
|
|||
jvmTarget = "11"
|
||||
freeCompilerArgs += listOf(
|
||||
"-Xcontext-receivers",
|
||||
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
||||
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
||||
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
|
||||
// "-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=${reportsDir}",
|
||||
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
||||
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
||||
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
||||
"-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=${reportsDir}",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -106,6 +107,10 @@ android {
|
|||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
|
||||
}
|
||||
|
||||
lint {
|
||||
disable += "ModifierParameter"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -4,20 +4,14 @@
|
|||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission
|
||||
android:name="android.permission.REQUEST_DELETE_PACKAGES"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
<uses-permission
|
||||
android:name="android.permission.DELETE_PACKAGES"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
package com.aliucord.manager.ui.components
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
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.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 com.aliucord.manager.BuildConfig
|
||||
import com.aliucord.manager.R
|
||||
|
||||
@Composable
|
||||
fun ProjectHeader(modifier: Modifier = Modifier) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = modifier,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_aliucord_logo),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(88.dp)
|
||||
.padding(bottom = 8.dp),
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.aliucord),
|
||||
style = MaterialTheme.typography.titleMedium.copy(fontSize = 26.sp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.app_description),
|
||||
style = MaterialTheme.typography.titleSmall.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = .6f)
|
||||
)
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
TextButton(onClick = { uriHandler.openUri("https://github.com/Aliucord") }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_account_github_white_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = ButtonDefaults.IconSpacing),
|
||||
)
|
||||
Text(text = stringResource(R.string.github))
|
||||
}
|
||||
|
||||
TextButton(onClick = { uriHandler.openUri("https://discord.gg/${BuildConfig.SUPPORT_SERVER}") }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_discord),
|
||||
contentDescription = stringResource(R.string.support_server),
|
||||
modifier = Modifier
|
||||
.padding(end = ButtonDefaults.IconSpacing)
|
||||
.size(22.dp),
|
||||
)
|
||||
Text(text = stringResource(R.string.discord))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.aliucord.manager.ui.components
|
||||
|
||||
import androidx.compose.foundation.*
|
||||
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.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun RowScope.SegmentedButton(
|
||||
icon: Painter,
|
||||
iconColor: Color = MaterialTheme.colorScheme.primary,
|
||||
iconDescription: String? = null,
|
||||
text: String,
|
||||
textColor: Color = MaterialTheme.colorScheme.primary,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically),
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onClick)
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(LocalAbsoluteTonalElevation.current + 2.dp))
|
||||
.weight(1f)
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = icon,
|
||||
contentDescription = iconDescription,
|
||||
tint = iconColor,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.basicMarquee(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
package com.aliucord.manager.ui.components.home
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
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.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
|
||||
import com.aliucord.manager.ui.util.DiscordVersion
|
||||
|
||||
@Composable
|
||||
fun InfoCard(
|
||||
packageName: String,
|
||||
supportedVersion: DiscordVersion,
|
||||
currentVersion: DiscordVersion,
|
||||
onDownloadClick: () -> Unit,
|
||||
onUninstallClick: () -> Unit,
|
||||
onLaunchClick: () -> Unit,
|
||||
) {
|
||||
ElevatedCard {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(20.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "${stringResource(R.string.aliucord)} ($packageName)",
|
||||
style = MaterialTheme.typography.titleLarge.copy(fontSize = 23.sp),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append(stringResource(R.string.version_supported))
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(" ")
|
||||
if (supportedVersion is DiscordVersion.Existing) {
|
||||
append(supportedVersion.name)
|
||||
append(" - ")
|
||||
}
|
||||
append(supportedVersion.toDisplayName())
|
||||
}
|
||||
|
||||
append('\n')
|
||||
append(stringResource(R.string.version_installed))
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(" ")
|
||||
if (currentVersion is DiscordVersion.Existing) {
|
||||
append(currentVersion.name)
|
||||
append(" - ")
|
||||
}
|
||||
append(currentVersion.toDisplayName())
|
||||
}
|
||||
},
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 10.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
val (icon, description) = when {
|
||||
currentVersion !is DiscordVersion.Existing ->
|
||||
R.drawable.ic_download to R.string.action_install
|
||||
|
||||
currentVersion < supportedVersion ->
|
||||
R.drawable.ic_refresh to R.string.action_reinstall
|
||||
|
||||
else ->
|
||||
R.drawable.ic_update to R.string.action_update
|
||||
}
|
||||
|
||||
FilledTonalIconButton(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.heightIn(min = 50.dp),
|
||||
onClick = onDownloadClick,
|
||||
shape = ShapeDefaults.Large,
|
||||
enabled = supportedVersion !is DiscordVersion.Error
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(5.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(icon),
|
||||
contentDescription = stringResource(description)
|
||||
)
|
||||
if (currentVersion is DiscordVersion.None) {
|
||||
Text(
|
||||
stringResource(description),
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentVersion is DiscordVersion.Existing) {
|
||||
FilledTonalIconButton(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.heightIn(min = 50.dp),
|
||||
onClick = onUninstallClick,
|
||||
shape = ShapeDefaults.Large
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.action_uninstall)
|
||||
)
|
||||
}
|
||||
|
||||
FilledTonalIconButton(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.heightIn(min = 50.dp),
|
||||
onClick = onLaunchClick,
|
||||
shape = ShapeDefaults.Large
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_launch),
|
||||
contentDescription = stringResource(R.string.action_launch)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ 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.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
|
@ -47,6 +46,7 @@ class AboutScreen : Screen {
|
|||
}
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
contentPadding = paddingValues
|
||||
.exclude(PaddingValuesSides.Horizontal + PaddingValuesSides.Top),
|
||||
modifier = Modifier
|
||||
|
|
@ -68,7 +68,7 @@ class AboutScreen : Screen {
|
|||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 12.dp, bottom = 16.dp)
|
||||
.padding(start = 16.dp, top = 12.dp, bottom = 12.dp)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -101,61 +101,6 @@ class AboutScreen : Screen {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProjectHeader(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp)
|
||||
) {
|
||||
AsyncImage(
|
||||
model = "https://github.com/Aliucord.png",
|
||||
contentDescription = stringResource(R.string.aliucord),
|
||||
modifier = Modifier.size(71.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.aliucord),
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
fontSize = 26.sp
|
||||
)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.app_description),
|
||||
style = MaterialTheme.typography.titleSmall.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
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(
|
||||
painter = painterResource(R.drawable.ic_link),
|
||||
contentDescription = stringResource(R.string.website)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
|
||||
Text(text = stringResource(id = R.string.website))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainContributors(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -1,101 +1,44 @@
|
|||
package com.aliucord.manager.ui.screens.home
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.net.toUri
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.aliucord.manager.BuildConfig
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.domain.repository.GithubRepository
|
||||
import com.aliucord.manager.installer.util.uninstallApk
|
||||
import com.aliucord.manager.manager.PreferencesManager
|
||||
import com.aliucord.manager.network.utils.fold
|
||||
import com.aliucord.manager.ui.util.DiscordVersion
|
||||
import com.aliucord.manager.util.getPackageVersion
|
||||
import com.aliucord.manager.util.launchBlock
|
||||
import com.aliucord.manager.util.showToast
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
class HomeModel(
|
||||
private val application: Application,
|
||||
private val github: GithubRepository,
|
||||
val preferences: PreferencesManager,
|
||||
) : ScreenModel {
|
||||
var supportedVersion by mutableStateOf<DiscordVersion>(DiscordVersion.None)
|
||||
private set
|
||||
|
||||
var installedVersion by mutableStateOf<DiscordVersion>(DiscordVersion.None)
|
||||
var installations by mutableStateOf<InstallsState>(InstallsState.Fetching)
|
||||
private set
|
||||
|
||||
init {
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
_fetchInstalledVersion()
|
||||
_fetchSupportedVersion()
|
||||
}
|
||||
fetchInstallations()
|
||||
fetchSupportedVersion()
|
||||
}
|
||||
|
||||
private suspend fun _fetchInstalledVersion() {
|
||||
try {
|
||||
val (versionName, versionCode) = application.getPackageVersion(preferences.packageName)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
installedVersion = DiscordVersion.Existing(
|
||||
type = DiscordVersion.parseVersionType(versionCode),
|
||||
name = versionName.split("-")[0].trim(),
|
||||
code = versionCode,
|
||||
)
|
||||
}
|
||||
} catch (t: PackageManager.NameNotFoundException) {
|
||||
withContext(Dispatchers.Main) {
|
||||
installedVersion = DiscordVersion.None
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
Log.e(BuildConfig.TAG, Log.getStackTraceString(t))
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
installedVersion = DiscordVersion.Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun _fetchSupportedVersion() {
|
||||
val version = github.getDataJson()
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
version.fold(
|
||||
success = {
|
||||
val versionCode = it.versionCode.toIntOrNull() ?: return@fold
|
||||
|
||||
supportedVersion = DiscordVersion.Existing(
|
||||
type = DiscordVersion.parseVersionType(versionCode),
|
||||
name = it.versionName.split("-")[0].trim(),
|
||||
code = versionCode,
|
||||
)
|
||||
},
|
||||
fail = {
|
||||
Log.e(BuildConfig.TAG, Log.getStackTraceString(it))
|
||||
supportedVersion = DiscordVersion.Error
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchInstalledVersion() {
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
_fetchInstalledVersion()
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchSupportedVersion() {
|
||||
screenModelScope.launch(Dispatchers.IO) {
|
||||
_fetchSupportedVersion()
|
||||
}
|
||||
}
|
||||
|
||||
fun launchAliucord() {
|
||||
fun launchApp(packageName: String) {
|
||||
val launchIntent = application.packageManager
|
||||
.getLaunchIntentForPackage(preferences.packageName)
|
||||
.getLaunchIntentForPackage(packageName)
|
||||
|
||||
if (launchIntent != null) {
|
||||
application.startActivity(launchIntent)
|
||||
|
|
@ -104,7 +47,79 @@ class HomeModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun uninstallAliucord() {
|
||||
application.uninstallApk(preferences.packageName)
|
||||
fun openAppInfo(packageName: String) {
|
||||
val launchIntent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.setData("package:$packageName".toUri())
|
||||
|
||||
application.startActivity(launchIntent)
|
||||
}
|
||||
|
||||
private fun fetchInstallations() = screenModelScope.launchBlock {
|
||||
try {
|
||||
val packageManager = application.packageManager
|
||||
|
||||
val installedPackages = packageManager
|
||||
.getInstalledPackages(PackageManager.GET_META_DATA)
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?: throw IllegalStateException("Failed to fetch installed packages (returned none)")
|
||||
|
||||
val aliucordPackages = installedPackages
|
||||
.asSequence()
|
||||
.filter {
|
||||
val isAliucordPkg = it.packageName == "com.aliucord"
|
||||
val hasAliucordMeta = it.applicationInfo.metaData?.containsKey("isAliucord") == true
|
||||
isAliucordPkg || hasAliucordMeta
|
||||
}
|
||||
|
||||
val aliucordInstallations = aliucordPackages
|
||||
.map {
|
||||
// `longVersionCode` is unnecessary since Discord doesn't use `versionCodeMajor`
|
||||
@Suppress("DEPRECATION")
|
||||
val versionCode = it.versionCode
|
||||
|
||||
val baseVersion = it.applicationInfo.metaData?.getInt("aliucordBaseVersion")
|
||||
val isBaseUpdated = /* TODO: remote data json instead */ baseVersion == 0
|
||||
|
||||
InstallData(
|
||||
name = packageManager.getApplicationLabel(it.applicationInfo).toString(),
|
||||
packageName = it.packageName,
|
||||
baseUpdated = isBaseUpdated,
|
||||
icon = packageManager
|
||||
.getApplicationIcon(it.applicationInfo)
|
||||
.toBitmap()
|
||||
.asImageBitmap()
|
||||
.let(::BitmapPainter),
|
||||
version = DiscordVersion.Existing(
|
||||
type = DiscordVersion.parseVersionType(versionCode),
|
||||
name = it.versionName.split("-")[0].trim(),
|
||||
code = versionCode,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
installations = InstallsState.Fetched(data = aliucordInstallations.toImmutableList())
|
||||
} catch (t: Throwable) {
|
||||
Log.e(BuildConfig.TAG, "Failed to query Aliucord installations", t)
|
||||
installations = InstallsState.Error
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchSupportedVersion() = screenModelScope.launchBlock {
|
||||
github.getDataJson().fold(
|
||||
success = {
|
||||
val versionCode = it.versionCode.toIntOrNull() ?: return@fold
|
||||
|
||||
supportedVersion = DiscordVersion.Existing(
|
||||
type = DiscordVersion.parseVersionType(versionCode),
|
||||
name = it.versionName.split("-")[0].trim(),
|
||||
code = versionCode,
|
||||
)
|
||||
},
|
||||
fail = {
|
||||
Log.e(BuildConfig.TAG, Log.getStackTraceString(it))
|
||||
supportedVersion = DiscordVersion.Error
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,28 +6,33 @@
|
|||
|
||||
package com.aliucord.manager.ui.screens.home
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Scaffold
|
||||
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.painterResource
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.koin.getScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.aliucord.manager.BuildConfig
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.components.ProjectHeader
|
||||
import com.aliucord.manager.ui.components.dialogs.InstallerDialog
|
||||
import com.aliucord.manager.ui.components.home.InfoCard
|
||||
import com.aliucord.manager.ui.screens.about.AboutScreen
|
||||
import com.aliucord.manager.ui.screens.home.components.*
|
||||
import com.aliucord.manager.ui.screens.install.InstallScreen
|
||||
import com.aliucord.manager.ui.screens.plugins.PluginsScreen
|
||||
import com.aliucord.manager.ui.screens.settings.SettingsScreen
|
||||
import com.aliucord.manager.ui.util.DiscordVersion
|
||||
import com.aliucord.manager.ui.util.paddings.PaddingValuesSides
|
||||
import com.aliucord.manager.ui.util.paddings.exclude
|
||||
|
||||
class HomeScreen : Screen {
|
||||
override val key = "Home"
|
||||
|
|
@ -38,11 +43,6 @@ class HomeScreen : Screen {
|
|||
val model = getScreenModel<HomeModel>()
|
||||
|
||||
var showInstallerDialog by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(model.preferences.packageName) {
|
||||
model.fetchInstalledVersion()
|
||||
}
|
||||
|
||||
if (showInstallerDialog) {
|
||||
InstallerDialog(
|
||||
onDismiss = { showInstallerDialog = false },
|
||||
|
|
@ -56,66 +56,72 @@ class HomeScreen : Screen {
|
|||
Scaffold(
|
||||
topBar = { HomeAppBar() },
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
contentPadding = paddingValues
|
||||
.exclude(PaddingValuesSides.Horizontal + PaddingValuesSides.Top),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(top = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
.padding(paddingValues.exclude(PaddingValuesSides.Bottom))
|
||||
.padding(top = 16.dp, start = 16.dp, end = 16.dp),
|
||||
) {
|
||||
InfoCard(
|
||||
packageName = model.preferences.packageName,
|
||||
supportedVersion = model.supportedVersion,
|
||||
currentVersion = model.installedVersion,
|
||||
onDownloadClick = { showInstallerDialog = true },
|
||||
onLaunchClick = model::launchAliucord,
|
||||
onUninstallClick = model::uninstallAliucord
|
||||
)
|
||||
item(key = "PROJECT_HEADER") {
|
||||
ProjectHeader()
|
||||
}
|
||||
|
||||
item(key = "ADD_INSTALL_BUTTON") {
|
||||
InstallButton(
|
||||
// TODO: install options screen to configure pkg name
|
||||
enabled = (model.installations as? InstallsState.Fetched)?.data?.isEmpty() ?: false,
|
||||
onClick = { showInstallerDialog = true },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 10.dp)
|
||||
)
|
||||
}
|
||||
|
||||
item(key = "SUPPORTED_VERSION") {
|
||||
AnimatedVisibility(
|
||||
enter = fadeIn() + slideInVertically { it * -2 },
|
||||
exit = fadeOut() + slideOutVertically { it * -2 },
|
||||
visible = model.supportedVersion !is DiscordVersion.None,
|
||||
) {
|
||||
VersionDisplay(
|
||||
version = model.supportedVersion,
|
||||
prefix = {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(stringResource(R.string.version_supported))
|
||||
append(" ")
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.alpha(.5f)
|
||||
.padding(bottom = 22.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val installations = (model.installations as? InstallsState.Fetched)?.data
|
||||
?: return@LazyColumn
|
||||
|
||||
items(installations, key = { it.packageName }) {
|
||||
AnimatedVisibility(
|
||||
enter = fadeIn() + slideInHorizontally { it * -2 },
|
||||
exit = fadeOut() + slideOutHorizontally { it * 2 },
|
||||
visible = model.supportedVersion !is DiscordVersion.None,
|
||||
) {
|
||||
InstalledItemCard(
|
||||
data = it,
|
||||
onUpdate = { showInstallerDialog = true }, // TODO: prefilled install options screen
|
||||
onOpenApp = { model.launchApp(it.packageName) },
|
||||
onOpenInfo = { model.openAppInfo(it.packageName) },
|
||||
onOpenPlugins = { navigator.push(PluginsScreen()) }, // TODO: install-specific plugins
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HomeAppBar() {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.navigation_home)) },
|
||||
actions = {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
IconButton(
|
||||
onClick = {
|
||||
uriHandler.openUri("https://discord.gg/${BuildConfig.SUPPORT_SERVER}")
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_discord),
|
||||
contentDescription = stringResource(R.string.support_server)
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { navigator.push(AboutScreen()) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
contentDescription = stringResource(R.string.navigation_about)
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { navigator.push(PluginsScreen()) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_extension),
|
||||
contentDescription = stringResource(R.string.navigation_about)
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { navigator.push(SettingsScreen()) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_settings),
|
||||
contentDescription = stringResource(R.string.navigation_settings)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package com.aliucord.manager.ui.screens.home
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import com.aliucord.manager.ui.util.DiscordVersion
|
||||
|
||||
@Immutable
|
||||
data class InstallData(
|
||||
val name: String,
|
||||
val packageName: String,
|
||||
val version: DiscordVersion,
|
||||
val icon: BitmapPainter,
|
||||
val baseUpdated: Boolean,
|
||||
)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.aliucord.manager.ui.screens.home
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
sealed interface InstallsState {
|
||||
data object None : InstallsState
|
||||
data object Error : InstallsState
|
||||
data object Fetching : InstallsState
|
||||
data class Fetched(val data: ImmutableList<InstallData>) : InstallsState
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.aliucord.manager.ui.screens.home.components
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.screens.about.AboutScreen
|
||||
import com.aliucord.manager.ui.screens.settings.SettingsScreen
|
||||
|
||||
@Composable
|
||||
fun HomeAppBar() {
|
||||
TopAppBar(
|
||||
title = {},
|
||||
actions = {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
IconButton(onClick = { navigator.push(AboutScreen()) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Info,
|
||||
contentDescription = stringResource(R.string.navigation_about)
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { navigator.push(SettingsScreen()) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_settings),
|
||||
contentDescription = stringResource(R.string.navigation_settings)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.aliucord.manager.ui.screens.home.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
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 com.aliucord.manager.R
|
||||
|
||||
@Composable
|
||||
fun InstallButton(
|
||||
enabled: Boolean = true,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
FilledTonalIconButton(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
enabled = enabled,
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_add),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.action_add_install),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
package com.aliucord.manager.ui.screens.home.components
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
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.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.unit.dp
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.components.SegmentedButton
|
||||
import com.aliucord.manager.ui.screens.home.InstallData
|
||||
|
||||
@Composable
|
||||
fun InstalledItemCard(
|
||||
data: InstallData,
|
||||
onUpdate: () -> Unit,
|
||||
onOpenApp: () -> Unit,
|
||||
onOpenInfo: () -> Unit,
|
||||
onOpenPlugins: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ElevatedCard(
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
elevation = CardDefaults.elevatedCardElevation(
|
||||
defaultElevation = 3.dp,
|
||||
),
|
||||
modifier = modifier
|
||||
.width(IntrinsicSize.Max)
|
||||
.shadow(
|
||||
clip = false,
|
||||
elevation = 2.dp,
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp),
|
||||
modifier = Modifier.padding(20.dp),
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Image(
|
||||
painter = data.icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(34.dp)
|
||||
.clip(CircleShape),
|
||||
)
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = "\"${data.name}\"",
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = .94f),
|
||||
)
|
||||
|
||||
Text(
|
||||
text = data.packageName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier
|
||||
.padding(start = 1.dp)
|
||||
.offset(y = (-2).dp)
|
||||
.alpha(.7f)
|
||||
.basicMarquee(),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.weight(1f, fill = true))
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
horizontalAlignment = Alignment.End,
|
||||
modifier = Modifier
|
||||
.alpha(.6f)
|
||||
.padding(end = 4.dp),
|
||||
) {
|
||||
VersionDisplay(
|
||||
version = data.version,
|
||||
prefix = { append("v") },
|
||||
)
|
||||
|
||||
// TODO: display install core commit version
|
||||
// Text(
|
||||
// text = data.commit,
|
||||
// style = MaterialTheme.typography.labelLarge,
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.clip(MaterialTheme.shapes.large),
|
||||
) {
|
||||
SegmentedButton(
|
||||
icon = painterResource(R.drawable.ic_extension),
|
||||
text = stringResource(R.string.plugins_title),
|
||||
onClick = onOpenPlugins,
|
||||
)
|
||||
SegmentedButton(
|
||||
icon = painterResource(R.drawable.ic_info),
|
||||
text = stringResource(R.string.action_open_info),
|
||||
onClick = onOpenInfo,
|
||||
)
|
||||
|
||||
if (data.baseUpdated) {
|
||||
SegmentedButton(
|
||||
icon = painterResource(R.drawable.ic_launch),
|
||||
text = stringResource(R.string.action_launch),
|
||||
onClick = onOpenApp,
|
||||
)
|
||||
} else {
|
||||
val warningColor = Color(0xFFFFBB33)
|
||||
|
||||
SegmentedButton(
|
||||
icon = painterResource(R.drawable.ic_update),
|
||||
text = stringResource(R.string.action_update),
|
||||
iconColor = warningColor,
|
||||
textColor = warningColor,
|
||||
onClick = onUpdate,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LabelTextItem(
|
||||
label: String,
|
||||
value: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append("• ")
|
||||
append(label)
|
||||
}
|
||||
append(" ")
|
||||
append(value)
|
||||
},
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = .9f),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.aliucord.manager.ui.screens.home.components
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.*
|
||||
import com.aliucord.manager.ui.util.DiscordVersion
|
||||
|
||||
@Composable
|
||||
fun VersionDisplay(
|
||||
version: DiscordVersion,
|
||||
prefix: (@Composable AnnotatedString.Builder.() -> Unit)? = null,
|
||||
style: TextStyle = MaterialTheme.typography.labelLarge,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
prefix?.invoke(this)
|
||||
|
||||
if (version is DiscordVersion.Existing) {
|
||||
append(version.name)
|
||||
append(" - ")
|
||||
}
|
||||
append(version.toDisplayName())
|
||||
},
|
||||
style = style,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
package com.aliucord.manager.ui.util
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.aliucord.manager.R
|
||||
|
||||
@Immutable
|
||||
sealed interface DiscordVersion : Comparable<DiscordVersion> {
|
||||
object Error : DiscordVersion
|
||||
object None : DiscordVersion
|
||||
data object Error : DiscordVersion
|
||||
data object None : DiscordVersion
|
||||
|
||||
data class Existing(
|
||||
val type: Type,
|
||||
|
|
|
|||
22
app/src/main/kotlin/com/aliucord/manager/util/Coroutines.kt
Normal file
22
app/src/main/kotlin/com/aliucord/manager/util/Coroutines.kt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.aliucord.manager.util
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
/**
|
||||
* Launch a Job for a block without returning anything
|
||||
*/
|
||||
inline fun CoroutineScope.launchBlock(
|
||||
context: CoroutineContext = Dispatchers.Main,
|
||||
noinline block: suspend CoroutineScope.() -> Unit,
|
||||
) {
|
||||
launch(context, block = block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper util to run a block with the main thread context
|
||||
*/
|
||||
suspend inline fun mainThread(noinline block: CoroutineScope.() -> Unit) =
|
||||
withContext(Dispatchers.Main, block)
|
||||
9
app/src/main/res/drawable/ic_add.xml
Normal file
9
app/src/main/res/drawable/ic_add.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M440,520L240,520q-17,0 -28.5,-11.5T200,480q0,-17 11.5,-28.5T240,440h200v-200q0,-17 11.5,-28.5T480,200q17,0 28.5,11.5T520,240v200h200q17,0 28.5,11.5T760,480q0,17 -11.5,28.5T720,520L520,520v200q0,17 -11.5,28.5T480,760q-17,0 -28.5,-11.5T440,720v-200Z" />
|
||||
</vector>
|
||||
16
app/src/main/res/drawable/ic_aliucord_logo.xml
Normal file
16
app/src/main/res/drawable/ic_aliucord_logo.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#33FF88"
|
||||
android:pathData="M15.672,16.224c-1.333,3.194 -6.808,2.414 -6.808,-0.36l0,-7.961c0,-2.87 5.403,-3.53 7.048,-0.648l4.995,-1.921c-1.945,-4.119 -5.536,-5.319 -8.513,-5.319c-4.851,0 -9.642,2.678 -9.642,7.901l0,7.961c0,5.259 4.791,7.901 9.546,7.901c3.038,0 6.7,-1.441 8.718,-5.187L15.672,16.224z" />
|
||||
<path
|
||||
android:fillColor="#00C853"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M9.08,0.59l5.644,0L23.477,24.005l-6.652,0l-1.597,-4.695l-6.544,0L7.17,24.005l-6.652,0l3.638,-9.954l5.92,0l3.674,0l-1.693,-6.844l-0.168,0l-1.057,4.011l-4.167,-3.999L9.08,0.59z" />
|
||||
<path
|
||||
android:fillColor="#33FF88"
|
||||
android:pathData="M21.015,18.578l-5.331,-2.353c-0.612,1.813 -1.561,2.39 -3.71,3.074l4.479,3.566C18.217,22.072 19.874,20.691 21.015,18.578z" />
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_info.xml
Normal file
9
app/src/main/res/drawable/ic_info.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M440,680h80v-240h-80v240ZM480,360q17,0 28.5,-11.5T520,320q0,-17 -11.5,-28.5T480,280q-17,0 -28.5,11.5T440,320q0,17 11.5,28.5T480,360ZM480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880ZM480,800q134,0 227,-93t93,-227q0,-134 -93,-227t-227,-93q-134,0 -227,93t-93,227q0,134 93,227t227,93ZM480,480Z" />
|
||||
</vector>
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
<string name="app_description">A modification for the Discord Android App</string>
|
||||
|
||||
<string name="aliucord">Aliucord</string>
|
||||
<string name="discord">Discord</string>
|
||||
<string name="github">GitHub</string>
|
||||
<string name="support_server">Support Server</string>
|
||||
<string name="commits">Commits</string>
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
<string name="action_confirm">Confirm</string>
|
||||
<string name="action_dismiss">Dismiss</string>
|
||||
<string name="action_install">Install</string>
|
||||
<string name="action_add_install">Add Installation</string>
|
||||
<string name="action_reinstall">Reinstall</string>
|
||||
<string name="action_update">Update</string>
|
||||
<string name="action_clear">Clear</string>
|
||||
|
|
@ -29,6 +31,7 @@
|
|||
<string name="action_copied">Copied!</string>
|
||||
<string name="action_cleared_cache">Cleared cache!</string>
|
||||
<string name="action_exit_anyways">Exit anyways</string>
|
||||
<string name="action_open_info">Open info</string>
|
||||
|
||||
<string name="permissions_grant_title">Grant Permissions</string>
|
||||
<string name="permissions_grant_body">In order for Aliucord Manager to function, file permissions are required. Since shared data is stored in ~/Aliucord, permissions are required in order to access it.</string>
|
||||
|
|
@ -78,6 +81,9 @@
|
|||
|
||||
<string name="network_load_fail">Failed to load</string>
|
||||
|
||||
<string name="label_pkg_name">Package:</string>
|
||||
<string name="label_version_discord">Discord version:</string>
|
||||
|
||||
<string name="version_supported">Supported version:</string>
|
||||
<string name="version_installed">Installed version:</string>
|
||||
<string name="version_none">None</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue