mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-06-17 21:13:11 +10:00
@@ -48,6 +48,8 @@ jobs:
|
||||
run: |
|
||||
mv ./dist/app-release.apk ./dist/rl-manager.apk
|
||||
cp patches/data.json ./dist/data.json
|
||||
# Point each release's data.json at its own assets so historical releases stay self-contained.
|
||||
sed -i "s|releases/download/latest/|releases/download/v${{ needs.Version.outputs.version }}/|g" ./dist/data.json
|
||||
cd patches && zip -r ../dist/patches.zip . -x "data.json" && cd ..
|
||||
|
||||
tidal_src=$(find tidal-apk -maxdepth 1 \( -name "*.apk" -o -name "*.apkm" \) | head -1)
|
||||
@@ -67,10 +69,10 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Publish release
|
||||
uses: marvinpinto/action-automatic-releases@latest
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
automatic_release_tag: latest
|
||||
tag_name: v${{ needs.Version.outputs.version }}
|
||||
name: v${{ needs.Version.outputs.version }}
|
||||
prerelease: false
|
||||
title: v${{ needs.Version.outputs.version }}
|
||||
make_latest: "true"
|
||||
files: ./dist/**
|
||||
|
||||
+6
@@ -30,6 +30,12 @@ private fun ComponentOptionsScreenPreview(
|
||||
selected = parameters.selected,
|
||||
onSelectComponent = {},
|
||||
onDeleteComponent = {},
|
||||
onImportFromUri = {},
|
||||
releasesExpanded = false,
|
||||
releasesState = com.meowarex.rlmobile.ui.screens.componentopts.ComponentOptionsModel.ReleasesState.Idle,
|
||||
onToggleReleases = {},
|
||||
onImportRelease = {},
|
||||
importingReleaseTag = null,
|
||||
onBackPressed = {},
|
||||
)
|
||||
}
|
||||
|
||||
+168
@@ -1,26 +1,44 @@
|
||||
package com.meowarex.rlmobile.ui.screens.componentopts
|
||||
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.*
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.meowarex.rlmobile.BuildConfig
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.manager.PathManager
|
||||
import com.meowarex.rlmobile.manager.download.KtorDownloadManager
|
||||
import com.meowarex.rlmobile.network.models.GithubRelease
|
||||
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
|
||||
import com.meowarex.rlmobile.network.utils.SemVer
|
||||
import com.meowarex.rlmobile.network.utils.fold
|
||||
import com.meowarex.rlmobile.ui.util.ScreenModelWithResult
|
||||
import com.meowarex.rlmobile.ui.util.ScreenResultKey
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import kotlin.time.Instant
|
||||
|
||||
class ComponentOptionsModel(
|
||||
screenResultKey: ScreenResultKey,
|
||||
private val paths: PathManager,
|
||||
private val context: Application,
|
||||
private val github: RadiantLyricsGithubService,
|
||||
private val downloader: KtorDownloadManager,
|
||||
) : ScreenModelWithResult<PatchComponent?>(screenResultKey) {
|
||||
val components = mutableStateListOf<PatchComponent>()
|
||||
var selected by mutableStateOf<PatchComponent?>(null)
|
||||
private set
|
||||
|
||||
var releasesExpanded by mutableStateOf(false)
|
||||
private set
|
||||
var releasesState by mutableStateOf<ReleasesState>(ReleasesState.Idle)
|
||||
private set
|
||||
var importingReleaseTag by mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
fun selectComponent(component: PatchComponent?) {
|
||||
selected = component
|
||||
}
|
||||
@@ -64,7 +82,157 @@ class ComponentOptionsModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun importFromUri(uri: Uri, type: PatchComponent.Type) = screenModelScope.launchIO {
|
||||
try {
|
||||
val targetDir = when (type) {
|
||||
PatchComponent.Type.TidalApk -> paths.customTidalApksDir
|
||||
PatchComponent.Type.Patches -> paths.customPatchesDir
|
||||
}
|
||||
val ext = when (type) {
|
||||
PatchComponent.Type.TidalApk -> "apk"
|
||||
PatchComponent.Type.Patches -> "zip"
|
||||
}
|
||||
targetDir.mkdirs()
|
||||
|
||||
val tempFile = targetDir.resolve("import-${System.currentTimeMillis()}.tmp")
|
||||
context.contentResolver.openInputStream(uri)?.use { input ->
|
||||
tempFile.outputStream().use { output -> input.copyTo(output) }
|
||||
} ?: throw IllegalStateException("Could not open input stream for $uri")
|
||||
|
||||
val sourceDisplayName = queryDisplayName(uri)
|
||||
val version = when (type) {
|
||||
PatchComponent.Type.TidalApk -> readApkVersion(tempFile)
|
||||
?: extractVersionFromName(sourceDisplayName)
|
||||
?: FALLBACK_VERSION
|
||||
PatchComponent.Type.Patches -> extractVersionFromName(sourceDisplayName)
|
||||
?: FALLBACK_VERSION
|
||||
}
|
||||
|
||||
val finalName = "${System.currentTimeMillis()}_$version.$ext"
|
||||
val finalFile = targetDir.resolve(finalName)
|
||||
if (!tempFile.renameTo(finalFile)) {
|
||||
tempFile.copyTo(finalFile, overwrite = true)
|
||||
tempFile.delete()
|
||||
}
|
||||
|
||||
refreshComponents(type)
|
||||
mainThread { context.showToast(R.string.intent_import_component_success, finalName) }
|
||||
} catch (t: Throwable) {
|
||||
Log.e(BuildConfig.TAG, "Failed to import custom component from $uri", t)
|
||||
mainThread { context.showToast(R.string.intent_import_component_failure) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun queryDisplayName(uri: Uri): String? = try {
|
||||
context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)
|
||||
?.use { cursor ->
|
||||
if (cursor.moveToFirst()) cursor.getString(0) else null
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun readApkVersion(apkFile: File): String? = try {
|
||||
@Suppress("DEPRECATION")
|
||||
context.packageManager.getPackageArchiveInfo(apkFile.absolutePath, 0)
|
||||
?.versionName
|
||||
?.let { SemVer.parseOrNull(it) }
|
||||
?.toString()
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun extractVersionFromName(name: String?): String? {
|
||||
if (name == null) return null
|
||||
return """(\d+\.\d+\.\d+)""".toRegex().find(name)?.value
|
||||
}
|
||||
|
||||
fun toggleReleasesExpanded(type: PatchComponent.Type) {
|
||||
releasesExpanded = !releasesExpanded
|
||||
if (releasesExpanded && releasesState is ReleasesState.Idle) {
|
||||
loadReleases(type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadReleases(type: PatchComponent.Type) = screenModelScope.launchIO {
|
||||
releasesState = ReleasesState.Loading
|
||||
github.getManagerReleases().fold(
|
||||
success = { all ->
|
||||
val assetName = assetNameFor(type)
|
||||
val filtered = all.filter { release ->
|
||||
release.assets.any { it.name == assetName }
|
||||
}
|
||||
releasesState = ReleasesState.Loaded(filtered)
|
||||
},
|
||||
fail = {
|
||||
Log.w(BuildConfig.TAG, "Failed to load GitHub releases", it)
|
||||
releasesState = ReleasesState.Failed
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun importFromRelease(release: GithubRelease, type: PatchComponent.Type) = screenModelScope.launchIO {
|
||||
val assetName = assetNameFor(type)
|
||||
val asset = release.assets.find { it.name == assetName } ?: run {
|
||||
mainThread { context.showToast(R.string.intent_import_component_failure) }
|
||||
return@launchIO
|
||||
}
|
||||
|
||||
val targetDir = when (type) {
|
||||
PatchComponent.Type.TidalApk -> paths.customTidalApksDir
|
||||
PatchComponent.Type.Patches -> paths.customPatchesDir
|
||||
}
|
||||
targetDir.mkdirs()
|
||||
|
||||
importingReleaseTag = release.tagName
|
||||
try {
|
||||
val tempFile = targetDir.resolve("release-${System.currentTimeMillis()}.tmp")
|
||||
val result = downloader.download(asset.browserDownloadUrl, tempFile)
|
||||
if (result !is com.meowarex.rlmobile.manager.download.IDownloadManager.Result.Success) {
|
||||
tempFile.delete()
|
||||
mainThread { context.showToast(R.string.intent_import_component_failure) }
|
||||
return@launchIO
|
||||
}
|
||||
|
||||
val ext = when (type) {
|
||||
PatchComponent.Type.TidalApk -> "apk"
|
||||
PatchComponent.Type.Patches -> "zip"
|
||||
}
|
||||
val version = SemVer.parseOrNull(release.tagName.removePrefix("v"))?.toString()
|
||||
?: FALLBACK_VERSION
|
||||
val finalName = "${System.currentTimeMillis()}_$version.$ext"
|
||||
val finalFile = targetDir.resolve(finalName)
|
||||
if (!tempFile.renameTo(finalFile)) {
|
||||
tempFile.copyTo(finalFile, overwrite = true)
|
||||
tempFile.delete()
|
||||
}
|
||||
refreshComponents(type)
|
||||
mainThread { context.showToast(R.string.intent_import_component_success, finalName) }
|
||||
} catch (t: Throwable) {
|
||||
Log.e(BuildConfig.TAG, "Failed to import release ${release.tagName}", t)
|
||||
mainThread { context.showToast(R.string.intent_import_component_failure) }
|
||||
} finally {
|
||||
importingReleaseTag = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun assetNameFor(type: PatchComponent.Type) = when (type) {
|
||||
PatchComponent.Type.TidalApk -> "tidal-stock.apk"
|
||||
PatchComponent.Type.Patches -> "patches.zip"
|
||||
}
|
||||
|
||||
override fun onDispose() {
|
||||
screenModelScope.launch { setResult(selected) }
|
||||
}
|
||||
|
||||
sealed interface ReleasesState {
|
||||
data object Idle : ReleasesState
|
||||
data object Loading : ReleasesState
|
||||
data class Loaded(val releases: List<GithubRelease>) : ReleasesState
|
||||
data object Failed : ReleasesState
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FALLBACK_VERSION = "0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
+232
@@ -1,13 +1,20 @@
|
||||
package com.meowarex.rlmobile.ui.screens.componentopts
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
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.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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 cafe.adriel.voyager.koin.koinScreenModel
|
||||
@@ -61,6 +68,12 @@ class ComponentOptionsScreen(
|
||||
selected = model.selected,
|
||||
onSelectComponent = model::selectComponent,
|
||||
onDeleteComponent = model::deleteComponent,
|
||||
onImportFromUri = { uri -> model.importFromUri(uri, componentType) },
|
||||
releasesExpanded = model.releasesExpanded,
|
||||
releasesState = model.releasesState,
|
||||
onToggleReleases = { model.toggleReleasesExpanded(componentType) },
|
||||
onImportRelease = { release -> model.importFromRelease(release, componentType) },
|
||||
importingReleaseTag = model.importingReleaseTag,
|
||||
onBackPressed = { navigator.back(null) },
|
||||
)
|
||||
}
|
||||
@@ -73,8 +86,22 @@ fun ComponentOptionsScreenContent(
|
||||
selected: PatchComponent?,
|
||||
onSelectComponent: (PatchComponent?) -> Unit,
|
||||
onDeleteComponent: (PatchComponent) -> Unit,
|
||||
onImportFromUri: (Uri) -> Unit,
|
||||
releasesExpanded: Boolean,
|
||||
releasesState: ComponentOptionsModel.ReleasesState,
|
||||
onToggleReleases: () -> Unit,
|
||||
onImportRelease: (com.meowarex.rlmobile.network.models.GithubRelease) -> Unit,
|
||||
importingReleaseTag: String?,
|
||||
onBackPressed: () -> Unit,
|
||||
) {
|
||||
val mime = when (componentType) {
|
||||
PatchComponent.Type.TidalApk -> "application/vnd.android.package-archive"
|
||||
PatchComponent.Type.Patches -> "application/zip"
|
||||
}
|
||||
val filePicker = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.OpenDocument(),
|
||||
) { uri -> uri?.let(onImportFromUri) }
|
||||
|
||||
Scaffold(
|
||||
topBar = { ComponentOptionsAppBar(componentType = componentType) },
|
||||
) { paddingValues ->
|
||||
@@ -98,6 +125,16 @@ fun ComponentOptionsScreenContent(
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "RELEASES_ACCORDION") {
|
||||
ReleasesAccordion(
|
||||
expanded = releasesExpanded,
|
||||
state = releasesState,
|
||||
importingTag = importingReleaseTag,
|
||||
onToggle = onToggleReleases,
|
||||
onImport = onImportRelease,
|
||||
)
|
||||
}
|
||||
|
||||
items(
|
||||
items = components,
|
||||
contentType = { "COMPONENT" },
|
||||
@@ -112,6 +149,10 @@ fun ComponentOptionsScreenContent(
|
||||
)
|
||||
}
|
||||
|
||||
item(key = "BROWSE") {
|
||||
BrowseImportCard(onClick = { filePicker.launch(arrayOf(mime)) })
|
||||
}
|
||||
|
||||
item("EXIT_BTN") {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.End,
|
||||
@@ -129,3 +170,194 @@ fun ComponentOptionsScreenContent(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReleasesAccordion(
|
||||
expanded: Boolean,
|
||||
state: ComponentOptionsModel.ReleasesState,
|
||||
importingTag: String?,
|
||||
onToggle: () -> Unit,
|
||||
onImport: (com.meowarex.rlmobile.network.models.GithubRelease) -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Column {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onToggle)
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_account_github_white_24dp),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_releases_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_releases_desc),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = 0.65f),
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
if (expanded) R.drawable.ic_arrow_up_small else R.drawable.ic_arrow_down_small
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = LocalContentColor.current.copy(alpha = 0.7f),
|
||||
)
|
||||
}
|
||||
|
||||
if (expanded) {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.35f),
|
||||
)
|
||||
ReleasesContent(
|
||||
state = state,
|
||||
importingTag = importingTag,
|
||||
onImport = onImport,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReleasesContent(
|
||||
state: ComponentOptionsModel.ReleasesState,
|
||||
importingTag: String?,
|
||||
onImport: (com.meowarex.rlmobile.network.models.GithubRelease) -> Unit,
|
||||
) {
|
||||
when (state) {
|
||||
is ComponentOptionsModel.ReleasesState.Idle,
|
||||
ComponentOptionsModel.ReleasesState.Loading -> Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(20.dp),
|
||||
) { CircularProgressIndicator(modifier = Modifier.size(24.dp)) }
|
||||
|
||||
ComponentOptionsModel.ReleasesState.Failed -> Text(
|
||||
text = stringResource(R.string.network_load_fail),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(20.dp),
|
||||
)
|
||||
|
||||
is ComponentOptionsModel.ReleasesState.Loaded -> {
|
||||
if (state.releases.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_releases_empty),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = 0.65f),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(20.dp),
|
||||
)
|
||||
} else {
|
||||
Column {
|
||||
state.releases.forEachIndexed { index, release ->
|
||||
ReleaseRow(
|
||||
release = release,
|
||||
importing = importingTag == release.tagName,
|
||||
anyImporting = importingTag != null,
|
||||
onImport = { onImport(release) },
|
||||
)
|
||||
if (index != state.releases.lastIndex) {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.2f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReleaseRow(
|
||||
release: com.meowarex.rlmobile.network.models.GithubRelease,
|
||||
importing: Boolean,
|
||||
anyImporting: Boolean,
|
||||
onImport: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = release.tagName,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
Text(
|
||||
text = release.name ?: release.tagName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = LocalContentColor.current.copy(alpha = 0.55f),
|
||||
)
|
||||
}
|
||||
FilledTonalButton(
|
||||
onClick = onImport,
|
||||
enabled = !anyImporting,
|
||||
) {
|
||||
if (importing) {
|
||||
CircularProgressIndicator(
|
||||
strokeWidth = 2.dp,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
} else {
|
||||
Text(stringResource(R.string.action_install))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BrowseImportCard(onClick: () -> Unit) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_add),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_browse_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_browse_desc),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = 0.65f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.components.SegmentedButton
|
||||
import com.meowarex.rlmobile.ui.components.Tag
|
||||
import com.meowarex.rlmobile.ui.screens.about.AboutScreen
|
||||
import com.meowarex.rlmobile.ui.screens.home.components.CommitList
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogsListScreen
|
||||
@@ -194,8 +195,8 @@ private fun ColumnScope.HomeContent(
|
||||
val tidalBehind = install != null && install.tidalUpToDate == false
|
||||
AnimatedVisibility(visible = patchesBehind || tidalBehind) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (patchesBehind) UpdateTag(text = "New Patches!")
|
||||
if (tidalBehind) UpdateTag(text = "TIDAL Update!")
|
||||
if (patchesBehind) Tag(text = "New Patches!")
|
||||
if (tidalBehind) Tag(text = "TIDAL Update!")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,20 +241,5 @@ private fun ColumnScope.HomeContent(
|
||||
|
||||
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||
CommitList(commits = commits.collectAsLazyPagingItems())
|
||||
'}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpdateTag(text: String) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
shape = RoundedCornerShape(6.dp),
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
android:pathData="M5.209,1.737C5.313,1.473 5.687,1.473 5.791,1.737L6.157,2.667C6.189,2.747 6.253,2.811 6.333,2.843L7.263,3.209C7.527,3.313 7.527,3.687 7.263,3.791L6.333,4.157C6.253,4.189 6.189,4.253 6.157,4.333L5.791,5.263C5.687,5.527 5.313,5.527 5.209,5.263L4.843,4.333C4.811,4.253 4.747,4.189 4.667,4.157L3.737,3.791C3.473,3.687 3.473,3.313 3.737,3.209L4.667,2.843C4.747,2.811 4.811,2.747 4.843,2.667L5.209,1.737Z" />
|
||||
</group>
|
||||
</vector>
|
||||
aight
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
<string name="permissions_notifs_title">Notifications</string>
|
||||
<string name="permissions_battery_title">Background Battery</string>
|
||||
<string name="permissions_battery_desc">Ensures the installation process does not get automatically cancelled if the app is minimized.</string>
|
||||
<string name="permissions_notifs_desc">Used only to show the download progress if Radiant Lyrics Manager is minimized during installation.</string>
|
||||
<string name="permissions_notifs_desc">Used to notify you when updates are available!</string>
|
||||
|
||||
<string name="permissions_root_denied">Failed to obtain root permissions</string>
|
||||
<string name="permissions_shizuku_denied">Failed to obtain Shizuku permissions</string>
|
||||
@@ -278,6 +278,11 @@
|
||||
<string name="componentopts_screen_desc">Select a custom build that was imported by Manager.</string>
|
||||
<string name="componentopts_selected_none">Latest</string>
|
||||
<string name="componentopts_deleted">Successfully deleted component!</string>
|
||||
<string name="componentopts_browse_title">Browse files…</string>
|
||||
<string name="componentopts_browse_desc">Import a file from your device</string>
|
||||
<string name="componentopts_releases_title">GitHub Releases</string>
|
||||
<string name="componentopts_releases_desc">Pick a historical release from the repo</string>
|
||||
<string name="componentopts_releases_empty">No releases with this component found</string>
|
||||
|
||||
<string name="log_title">Log</string>
|
||||
<string name="log_section_install_info">Installation Info</string>
|
||||
|
||||
Reference in New Issue
Block a user