mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-06-18 05:23:12 +10:00
Manager UI Updates & New patch logic <3
This commit is contained in:
@@ -102,8 +102,6 @@ class ManagerApplication : Application() {
|
||||
.build()
|
||||
}
|
||||
|
||||
// Schedule periodic update check only when the user has opted in,
|
||||
// so the disabled state survives app restarts instead of being re-enqueued.
|
||||
if (get<PreferencesManager>().autoUpdateCheck) {
|
||||
UpdateCheckWorker.schedule(this)
|
||||
} else {
|
||||
|
||||
@@ -47,8 +47,6 @@ fun Scope.provideHttpClient() = HttpClient(OkHttp) {
|
||||
override fun lookup(hostname: String): List<InetAddress> {
|
||||
val addresses = Dns.SYSTEM.lookup(hostname)
|
||||
|
||||
// Github's nameservers do not respond to IPv6 requests for raw.githubusercontent.com,
|
||||
// which causes CIO, Android and OkHTTP to all hang
|
||||
return if (hostname == "raw.githubusercontent.com") {
|
||||
addresses.filterIsInstance<Inet4Address>()
|
||||
} else {
|
||||
@@ -77,7 +75,7 @@ fun Scope.provideHttpClient() = HttpClient(OkHttp) {
|
||||
// Default storage is in-memory
|
||||
}
|
||||
|
||||
// Custom plugin to allow overriding response cache headers, and force caching
|
||||
// Custom plugin to allow overriding response cache headers (force caching)
|
||||
install("OverrideCacheControl") {
|
||||
receivePipeline.intercept(HttpReceivePipeline.Before) { response ->
|
||||
val customCacheControl = response.call.attributes.getOrNull(CustomCacheControl)
|
||||
|
||||
+1
-2
@@ -107,8 +107,7 @@ class DhizukuInstaller(
|
||||
)
|
||||
}
|
||||
|
||||
// Unregister PMResultReceiver when this coroutine finishes or errors
|
||||
// Explicitly cancel the install session if it did not finish.
|
||||
// cancel the install session if it did not finish.
|
||||
continuation.invokeOnCancellation {
|
||||
context.unregisterReceiver(relayReceiver)
|
||||
sessionCallback?.let { packageInstaller.unregisterSessionCallback(it) }
|
||||
|
||||
@@ -39,8 +39,6 @@ class PMResultReceiver(
|
||||
context.showToast(if (!isUninstall) R.string.installer_install_success else R.string.installer_uninstall_success)
|
||||
}
|
||||
|
||||
// The reason we don't do this in PMIntentReceiver is we can't tell whether it was
|
||||
// an old session that for which `abandonSession(...)` was called
|
||||
is InstallerResult.Cancelled -> {
|
||||
context.showToast(if (!isUninstall) R.string.installer_install_aborted else R.string.installer_uninstall_aborted)
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ class RootInstaller(private val context: Context) : Installer {
|
||||
}
|
||||
|
||||
private companion object {
|
||||
// We spoof Google Play Store to prevent unnecessary checks
|
||||
// spoof Google Play Store to prevent unnecessary checks
|
||||
const val PLAY_PACKAGE_NAME = "com.android.vending"
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import kotlin.coroutines.resume
|
||||
|
||||
/**
|
||||
* The package name of Google Play Store.
|
||||
* We spoof our installer to this when installing through Shizuku to prevent
|
||||
* spoof our installer to this when installing through Shizuku to prevent
|
||||
* potentially unnecessary scans/checks.
|
||||
*/
|
||||
private const val PLAY_PACKAGE_NAME = "com.android.vending"
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ class CopyDependenciesStep : Step(), KoinComponent {
|
||||
val targetFileStorageId = storageManager.getUuidForPath(apk)
|
||||
val fileSize = srcApk.length()
|
||||
|
||||
// We request 3.5x the size of the APK, to give space for the following:
|
||||
// request 3.5x the size of the APK, to give space for the following:
|
||||
// 1) A copy of the APK
|
||||
// 2) Modifying the copied APK (whether this is necessary I'm not sure)
|
||||
// 2) Extracting native libs and other various operations
|
||||
|
||||
+2
-2
@@ -272,7 +272,7 @@ class SmaliPatchStep(
|
||||
val matchPos = findContextMatch(result, sourceLines, delta.source.position)
|
||||
?: throw Exception(
|
||||
"Fuzzy match failed: could not locate context for hunk near line " +
|
||||
"${delta.source.position + 1} (${sourceLines.size} lines)"
|
||||
"${delta.source.position + 1} (${sourceLines.size} lines)"
|
||||
)
|
||||
|
||||
repeat(sourceLines.size) { result.removeAt(matchPos) }
|
||||
@@ -288,7 +288,7 @@ class SmaliPatchStep(
|
||||
// Try at the exact hint first.
|
||||
if (matchesAt(target, sourceLines, hint)) return hint
|
||||
|
||||
// Walk outward from the hint, alternating below/above, until we find a unique match.
|
||||
// Walk outward from the hint, alternating below/above, until finds a unique match.
|
||||
val maxRadius = target.size
|
||||
for (offset in 1..maxRadius) {
|
||||
val below = hint + offset
|
||||
|
||||
@@ -25,7 +25,7 @@ object ManifestPatcher {
|
||||
appName: String,
|
||||
debuggable: Boolean,
|
||||
): ByteArray {
|
||||
// Extract original package name so we can rewrite every reference to it
|
||||
// Extract original package name to rewrite every reference to it
|
||||
// (permissions, provider authorities) to the new packageName.
|
||||
var originalPackage: String? = null
|
||||
AxmlReader(manifestBytes).accept(object : AxmlVisitor() {
|
||||
@@ -58,11 +58,12 @@ object ManifestPatcher {
|
||||
COMPILE_SDK_VERSION_CODENAME to "6.0-2438415"
|
||||
)
|
||||
) {
|
||||
// Drop split-only manifest attributes — we merged all splits into one APK
|
||||
// Drop split-only manifest attributes because merged all splits into one APK
|
||||
override fun attr(ns: String?, name: String, resourceId: Int, type: Int, value: Any?) {
|
||||
if (name == IS_SPLIT_REQUIRED || name == REQUIRED_SPLIT_TYPES || name == SPLIT_TYPES) return
|
||||
super.attr(ns, name, resourceId, type, value)
|
||||
}
|
||||
|
||||
private var addExternalStoragePerm = false
|
||||
|
||||
override fun child(ns: String?, name: String): NodeVisitor {
|
||||
@@ -72,7 +73,13 @@ object ManifestPatcher {
|
||||
if (addExternalStoragePerm) {
|
||||
super
|
||||
.child(null, "uses-permission")
|
||||
.attr(ANDROID_NAMESPACE, "name", android.R.attr.name, TYPE_STRING, Manifest.permission.MANAGE_EXTERNAL_STORAGE)
|
||||
.attr(
|
||||
ANDROID_NAMESPACE,
|
||||
"name",
|
||||
android.R.attr.name,
|
||||
TYPE_STRING,
|
||||
Manifest.permission.MANAGE_EXTERNAL_STORAGE
|
||||
)
|
||||
addExternalStoragePerm = false
|
||||
}
|
||||
|
||||
@@ -141,7 +148,13 @@ object ManifestPatcher {
|
||||
if (addMetadata) {
|
||||
addMetadata = false
|
||||
super.child(ANDROID_NAMESPACE, "meta-data").apply {
|
||||
attr(ANDROID_NAMESPACE, "name", android.R.attr.name, TYPE_STRING, "isRadiantLyrics")
|
||||
attr(
|
||||
ANDROID_NAMESPACE,
|
||||
"name",
|
||||
android.R.attr.name,
|
||||
TYPE_STRING,
|
||||
"isRadiantLyrics"
|
||||
)
|
||||
attr(ANDROID_NAMESPACE, "value", android.R.attr.value, TYPE_INT_BOOLEAN, 1)
|
||||
}
|
||||
}
|
||||
@@ -149,7 +162,13 @@ object ManifestPatcher {
|
||||
return when (name) {
|
||||
"activity" -> ReplaceAttrsVisitor(visitor, mapOf("label" to appName))
|
||||
"provider" -> object : NodeVisitor(visitor) {
|
||||
override fun attr(ns: String?, name: String, resourceId: Int, type: Int, value: Any?) {
|
||||
override fun attr(
|
||||
ns: String?,
|
||||
name: String,
|
||||
resourceId: Int,
|
||||
type: Int,
|
||||
value: Any?
|
||||
) {
|
||||
super.attr(
|
||||
ns, name, resourceId, type,
|
||||
if (name == "authorities") {
|
||||
@@ -173,11 +192,23 @@ object ManifestPatcher {
|
||||
TYPE_INT_BOOLEAN,
|
||||
1
|
||||
)
|
||||
if (addDebuggable) super.attr(ANDROID_NAMESPACE, DEBUGGABLE, android.R.attr.debuggable, TYPE_INT_BOOLEAN, 1)
|
||||
if (addDebuggable) super.attr(
|
||||
ANDROID_NAMESPACE,
|
||||
DEBUGGABLE,
|
||||
android.R.attr.debuggable,
|
||||
TYPE_INT_BOOLEAN,
|
||||
1
|
||||
)
|
||||
|
||||
// Disable AOT (Necessary for AOSP Android 15)
|
||||
if (Build.VERSION.SDK_INT >= 29 && addUseEmbeddedDex) {
|
||||
super.attr(ANDROID_NAMESPACE, USE_EMBEDDED_DEX, android.R.attr.useEmbeddedDex, TYPE_INT_BOOLEAN, 1)
|
||||
super.attr(
|
||||
ANDROID_NAMESPACE,
|
||||
USE_EMBEDDED_DEX,
|
||||
android.R.attr.useEmbeddedDex,
|
||||
TYPE_INT_BOOLEAN,
|
||||
1
|
||||
)
|
||||
}
|
||||
|
||||
if (addExtractNativeLibs) super.attr(
|
||||
@@ -209,7 +240,13 @@ object ManifestPatcher {
|
||||
val replace = attrs.containsKey(name)
|
||||
val newValue = attrs[name]
|
||||
|
||||
super.attr(ns, name, resourceId, if (newValue is String) TYPE_STRING else type, if (replace) newValue else value)
|
||||
super.attr(
|
||||
ns,
|
||||
name,
|
||||
resourceId,
|
||||
if (newValue is String) TYPE_STRING else type,
|
||||
if (replace) newValue else value
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -150,15 +150,13 @@ class PatchingScreenModel(
|
||||
.toUnsafeImmutable()
|
||||
mainThread { steps = newSteps }
|
||||
|
||||
// Intentionally delay to show the state change of the first step when it runs in the UI.
|
||||
// Without this, on a fast internet connection the step just immediately shows as "Success".
|
||||
// Tiny delay so the first step's progress is visible on fast connections
|
||||
delay(400)
|
||||
|
||||
// Execute all the steps and catch any errors
|
||||
val error = when (val error = runner.executeAll()) {
|
||||
null -> {
|
||||
// If install step is marked skipped then the installation was manually aborted
|
||||
// and if so, immediately close install screen
|
||||
// Skipped install step means user aborted, close screen
|
||||
if (runner.getStep<InstallStep>().state == StepState.Skipped) {
|
||||
mutableState.value = PatchingScreenState.CloseScreen
|
||||
|
||||
@@ -215,6 +213,7 @@ class PatchingScreenModel(
|
||||
R.string.fun_fact_10,
|
||||
R.string.fun_fact_11,
|
||||
R.string.fun_fact_12,
|
||||
R.string.fun_fact_13,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+41
-14
@@ -8,37 +8,64 @@ enum class KnownPatch(
|
||||
@StringRes val titleRes: Int,
|
||||
@StringRes val descRes: Int,
|
||||
val requires: List<KnownPatch> = emptyList(),
|
||||
val disables: List<KnownPatch> = emptyList(),
|
||||
) {
|
||||
// Dependency-first order (later refs need backward resolution)
|
||||
LyricsDisableCover(
|
||||
fileNames = listOf("lyrics-disable-cover.patch"),
|
||||
titleRes = R.string.patch_lyrics_disable_cover_title,
|
||||
descRes = R.string.patch_lyrics_disable_cover_desc,
|
||||
),
|
||||
LyricsProgressPill(
|
||||
LyricsReplaceLyricsButton(
|
||||
fileNames = listOf(
|
||||
"lyrics-progress-pill.patch",
|
||||
"lyrics-fade-region.patch",
|
||||
"lyrics-active-line-only.patch",
|
||||
),
|
||||
titleRes = R.string.patch_lyrics_progress_pill_title,
|
||||
descRes = R.string.patch_lyrics_progress_pill_desc,
|
||||
requires = listOf(LyricsDisableCover),
|
||||
),
|
||||
LyricsReplaceLyricButton(
|
||||
fileNames = listOf(
|
||||
"lyrics-replace-lyric-button-1-remove.patch",
|
||||
"lyrics-replace-lyric-button-2-sparkle.patch",
|
||||
"lyrics-replace-lyrics-button.patch",
|
||||
"lyrics-sparkle-conditional-visibility.patch",
|
||||
),
|
||||
titleRes = R.string.patch_lyrics_replace_button_title,
|
||||
descRes = R.string.patch_lyrics_replace_button_desc,
|
||||
),
|
||||
LyricsReplaceShareButton(
|
||||
fileNames = listOf("lyrics-replace-share-button.patch"),
|
||||
titleRes = R.string.patch_lyrics_replace_share_button_title,
|
||||
descRes = R.string.patch_lyrics_replace_share_button_desc,
|
||||
),
|
||||
PlayerBackdrop(
|
||||
fileNames = listOf("player-backdrop.patch"),
|
||||
titleRes = R.string.patch_player_backdrop_title,
|
||||
descRes = R.string.patch_player_backdrop_desc,
|
||||
),
|
||||
DebugMenuUnlock(
|
||||
fileNames = listOf("debug-menu-unlock.patch"),
|
||||
titleRes = R.string.patch_debug_menu_unlock_title,
|
||||
descRes = R.string.patch_debug_menu_unlock_desc,
|
||||
),
|
||||
LyricsProgressPill(
|
||||
fileNames = listOf(
|
||||
"lyrics-progress-pill.patch",
|
||||
"lyrics-fade-region.patch",
|
||||
),
|
||||
titleRes = R.string.patch_lyrics_progress_pill_title,
|
||||
descRes = R.string.patch_lyrics_progress_pill_desc,
|
||||
requires = listOf(LyricsDisableCover, LyricsReplaceShareButton),
|
||||
),
|
||||
EnableLegacyUi(
|
||||
fileNames = listOf("enable-legacy-ui.patch"),
|
||||
titleRes = R.string.patch_enable_legacy_ui_title,
|
||||
descRes = R.string.patch_enable_legacy_ui_desc,
|
||||
requires = listOf(DebugMenuUnlock),
|
||||
disables = listOf(
|
||||
LyricsDisableCover,
|
||||
LyricsReplaceLyricsButton,
|
||||
LyricsReplaceShareButton,
|
||||
PlayerBackdrop,
|
||||
LyricsProgressPill,
|
||||
),
|
||||
);
|
||||
|
||||
companion object {
|
||||
val All: List<KnownPatch> = entries.sortedBy { it.fileNames.first() }
|
||||
// Alphabetical by first filename, but pin DebugMenuUnlock to the bottom
|
||||
val All: List<KnownPatch> = entries.sortedWith(
|
||||
compareBy({ it == DebugMenuUnlock }, { it.fileNames.first() })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+17
-29
@@ -18,7 +18,6 @@ class PatchOptionsModel(
|
||||
private val context: Context,
|
||||
private val prefs: PreferencesManager,
|
||||
) : ScreenModel {
|
||||
// ---------- Package name state ----------
|
||||
var packageName by mutableStateOf(prefilledOptions.packageName)
|
||||
private set
|
||||
|
||||
@@ -30,7 +29,6 @@ class PatchOptionsModel(
|
||||
fetchPkgNameStateDebounced()
|
||||
}
|
||||
|
||||
// ---------- App name state ----------
|
||||
var appName by mutableStateOf(prefilledOptions.appName)
|
||||
private set
|
||||
|
||||
@@ -42,7 +40,6 @@ class PatchOptionsModel(
|
||||
appNameIsError = newAppName.length !in (1..150)
|
||||
}
|
||||
|
||||
// ---------- Debuggable state ----------
|
||||
var debuggable by mutableStateOf(prefilledOptions.debuggable)
|
||||
private set
|
||||
|
||||
@@ -50,7 +47,6 @@ class PatchOptionsModel(
|
||||
debuggable = value
|
||||
}
|
||||
|
||||
// ---------- Patch selection state ----------
|
||||
var disabledPatches by mutableStateOf(prefilledOptions.disabledPatches)
|
||||
private set
|
||||
|
||||
@@ -58,38 +54,33 @@ class PatchOptionsModel(
|
||||
patch.fileNames.none { it in disabledPatches }
|
||||
|
||||
fun setPatchEnabled(patch: KnownPatch, enabled: Boolean) {
|
||||
val units = if (enabled) {
|
||||
fun closure(seed: KnownPatch, step: (KnownPatch) -> List<KnownPatch>): Set<KnownPatch> =
|
||||
buildSet {
|
||||
fun addWithDeps(p: KnownPatch) {
|
||||
if (add(p)) p.requires.forEach(::addWithDeps)
|
||||
}
|
||||
addWithDeps(patch)
|
||||
fun walk(p: KnownPatch) { if (add(p)) step(p).forEach(::walk) }
|
||||
walk(seed)
|
||||
}
|
||||
|
||||
val enableUnits: Set<KnownPatch>
|
||||
val disableUnits: Set<KnownPatch>
|
||||
if (enabled) {
|
||||
enableUnits = closure(patch) { it.requires }
|
||||
disableUnits = enableUnits.flatMap { it.disables }
|
||||
.flatMapTo(mutableSetOf()) { d ->
|
||||
closure(d) { dep -> KnownPatch.All.filter { dep in it.requires } }
|
||||
}
|
||||
} else {
|
||||
buildSet {
|
||||
fun addWithDependents(p: KnownPatch) {
|
||||
if (add(p)) {
|
||||
KnownPatch.All
|
||||
.filter { p in it.requires }
|
||||
.forEach(::addWithDependents)
|
||||
}
|
||||
}
|
||||
addWithDependents(patch)
|
||||
}
|
||||
enableUnits = emptySet()
|
||||
disableUnits = closure(patch) { p -> KnownPatch.All.filter { p in it.requires } }
|
||||
}
|
||||
|
||||
val affectedFiles = units.flatMap { it.fileNames }.toSet()
|
||||
disabledPatches = if (enabled) {
|
||||
disabledPatches - affectedFiles
|
||||
} else {
|
||||
disabledPatches + affectedFiles
|
||||
}
|
||||
val enableFiles = enableUnits.flatMap { it.fileNames }.toSet()
|
||||
val disableFiles = disableUnits.flatMap { it.fileNames }.toSet()
|
||||
disabledPatches = (disabledPatches - enableFiles) + disableFiles
|
||||
}
|
||||
|
||||
val enabledPatchCount: Int
|
||||
get() = KnownPatch.All.count { isPatchEnabled(it) }
|
||||
|
||||
// ---------- Custom components state ----------
|
||||
var customInjector by mutableStateOf<PatchComponent?>(null)
|
||||
private set
|
||||
var customPatches by mutableStateOf<PatchComponent?>(null)
|
||||
@@ -113,7 +104,6 @@ class PatchOptionsModel(
|
||||
)
|
||||
}
|
||||
|
||||
// ---------- Config generation ----------
|
||||
val isConfigValid by derivedStateOf {
|
||||
val invalidChecks = arrayOf(
|
||||
packageNameState == PackageNameState.Invalid,
|
||||
@@ -136,11 +126,9 @@ class PatchOptionsModel(
|
||||
)
|
||||
}
|
||||
|
||||
// ---------- Other ----------
|
||||
val isDevMode: Boolean
|
||||
get() = prefs.devMode
|
||||
|
||||
// A throttled variant of fetchPkgNameState()
|
||||
private val fetchPkgNameStateDebounced: () -> Unit =
|
||||
screenModelScope.debounce(100L, function = ::fetchPkgNameState)
|
||||
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts.components
|
||||
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.background
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ fun InstallersDialog(
|
||||
}
|
||||
|
||||
InstallerSetting.Intent -> {
|
||||
// We don't know whether this device supports this method until we try.
|
||||
// don't know whether this device supports this method
|
||||
}
|
||||
|
||||
InstallerSetting.Shizuku -> {
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ class UpdaterViewModel(
|
||||
// Fetch releases from GitHub (60s local cache)
|
||||
val releases = github.getManagerReleases().getOrThrow()
|
||||
|
||||
// Find the latest release — version is parsed from the release title (e.g. "v1.0.5")
|
||||
// Find the latest release by parsed version
|
||||
val (version, release, apkUrl) = releases
|
||||
.mapNotNull { release ->
|
||||
val version = SemVer.parseOrNull(release.name?.removePrefix("v") ?: "")
|
||||
|
||||
@@ -21,3 +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
|
||||
|
||||
@@ -57,7 +57,9 @@
|
||||
<string name="permissions_install_title">Install from Unknown Sources</string>
|
||||
<string name="permissions_install_desc">Permissions are required to initiate an installation from this app.</string>
|
||||
<string name="permissions_storage_title">External Storage</string>
|
||||
<string name="permissions_storage_desc">Radiant Lyrics Manager stores shared data in ~/RadiantLyrics, which requires full storage permissions. Scoped storage is not currently supported.</string>
|
||||
<string
|
||||
name="permissions_storage_desc"
|
||||
>Radiant Lyrics Manager stores shared data in ~/RadiantLyrics, which requires full storage permissions. Scoped storage is not currently supported.</string>
|
||||
<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>
|
||||
@@ -83,7 +85,9 @@
|
||||
<string name="setting_installer">Installation Method</string>
|
||||
<string name="setting_installer_desc">Various methods are supported to interface with the system\'s package installer.</string>
|
||||
<string name="setting_keep_patched_apks">Keep patched APKs</string>
|
||||
<string name="setting_keep_patched_apks_desc">Keep all patched files and APKs after installation for debugging purposes. Note that exporting the APK is only useful after a successful patching session.</string>
|
||||
<string
|
||||
name="setting_keep_patched_apks_desc"
|
||||
>Keep all patched files and APKs after installation for debugging purposes. Note that exporting the APK is only useful after a successful patching session.</string>
|
||||
<string name="settings_clear_cache">Clear cache</string>
|
||||
<string name="settings_export_apk">Export APK</string>
|
||||
|
||||
@@ -94,7 +98,9 @@
|
||||
<string name="installer_intent_desc">Launches an intent to handle installation through the system\'s configured default installer.</string>
|
||||
<string name="installer_root_desc">Invokes PackageManager directly using device root such as Magisk or KernelSU.</string>
|
||||
<string name="installer_shizuku">Shizuku</string>
|
||||
<string name="installer_shizuku_desc">Invokes the PackageInstaller API remotely through Shizuku. ADB, Wireless ADB, and Root backends are all supported.</string>
|
||||
<string
|
||||
name="installer_shizuku_desc"
|
||||
>Invokes the PackageInstaller API remotely through Shizuku. ADB, Wireless ADB, and Root backends are all supported.</string>
|
||||
<string name="installer_dhizuku">Dhizuku</string>
|
||||
<string name="installer_dhizuku_desc">Invokes the PackageInstaller API remotely through Dhizuku using DeviceOwner permissions.</string>
|
||||
|
||||
@@ -119,13 +125,15 @@
|
||||
<string name="navigation_settings">Settings</string>
|
||||
<string name="navigation_refresh">Refresh</string>
|
||||
|
||||
<string name="contributors_lead">Lead</string>
|
||||
<string name="contributors_lead">Developer</string>
|
||||
<string name="contributors">Contributors</string>
|
||||
<string name="contributors_contributions">%s contributions</string>
|
||||
|
||||
<string name="network_load_fail">Failed to load</string>
|
||||
<string name="network_warning_title">Metered network</string>
|
||||
<string name="network_warning_body">It appears you are connected to mobile data or a potentially metered network. Continuing with the installation may result in several hundred megabytes being downloaded. Are you sure you want to continue?</string>
|
||||
<string
|
||||
name="network_warning_body"
|
||||
>It appears you are connected to mobile data or a potentially metered network. Continuing with the installation may result in several hundred megabytes being downloaded. Are you sure you want to continue?</string>
|
||||
<string name="network_warning_disable">Don\'t show this again</string>
|
||||
|
||||
<string name="version_none">None</string>
|
||||
@@ -144,15 +152,21 @@
|
||||
<string name="installer_uninstall_aborted">Cancelled uninstallation</string>
|
||||
<string name="installer_dl_verify_fail">Failed to verify download</string>
|
||||
<string name="installer_insufficient_storage">Insufficient storage space!</string>
|
||||
<string name="installer_banner_minimization">Since battery optimizations have not been disabled, minimizing this screen may abort the installation process!</string>
|
||||
<string name="installer_banner_failure">Installation failed! You can either retry or click this banner to open the GitHub repository for help.</string>
|
||||
<string name="installer_banner_success">Successfully installed Radiant Lyrics! Do *NOT* uninstall the manager (this app) as it is required to perform certain types of updates.</string>
|
||||
<string
|
||||
name="installer_banner_minimization"
|
||||
>Since battery optimizations have not been disabled, minimizing this screen may abort the installation process!</string>
|
||||
<string
|
||||
name="installer_banner_failure"
|
||||
>Installation failed! You can either retry or click this banner to open the GitHub repository for help.</string>
|
||||
<string
|
||||
name="installer_banner_success"
|
||||
>Successfully installed Radiant Lyrics! Do *NOT* uninstall the manager (this app) as it is required to perform certain types of updates.</string>
|
||||
|
||||
<string name="install_error_unknown">Installer failure (Unknown reason)</string>
|
||||
<string name="install_error_unhandled_intent">No handlers available for %s!</string>
|
||||
<string name="install_error_blocked">Installation was blocked</string>
|
||||
<string name="install_error_invalid">One or more APKs were invalid or corrupt</string>
|
||||
<string name="install_error_conflict">Conflicts with an existing app, usually due to mismatched signatures</string>
|
||||
<string name="install_error_conflict">Conflicts with an existing app (Change Name & Com values)</string>
|
||||
<string name="install_error_storage">Not enough storage available to install</string>
|
||||
<string name="install_error_incompatible">Application is incompatible with this device</string>
|
||||
<string name="install_error_timeout">Installer timed out</string>
|
||||
@@ -162,16 +176,16 @@
|
||||
<string name="install_group_patch">Patch APK</string>
|
||||
<string name="install_group_install">Install APK</string>
|
||||
|
||||
<string name="patch_step_fetch_info">Fetching target TIDAL version</string>
|
||||
<string name="patch_step_fetch_info">Fetching TIDAL version</string>
|
||||
<string name="patch_step_downgrade_check">Checking for newer installations</string>
|
||||
<string name="patch_step_restore_cache">Restoring from cache</string>
|
||||
<string name="patch_step_dl_tidal_apk">Downloading TIDAL APK</string>
|
||||
<string name="patch_step_dl_smali">Downloading smali patches</string>
|
||||
<string name="patch_step_dl_smali">Downloading Patches</string>
|
||||
<string name="patch_step_copy_deps">Copying dependencies</string>
|
||||
<string name="patch_step_patch_manifests">Patching APK manifest</string>
|
||||
<string name="patch_step_patch_certs">Adding modern root certificates</string>
|
||||
<string name="patch_step_patch_icon">Patching app icon</string>
|
||||
<string name="patch_step_patch_smali">Applying smali patches</string>
|
||||
<string name="patch_step_patch_smali">Applying Patches</string>
|
||||
<string name="patch_step_reorganize_dex">Reorganizing dex files</string>
|
||||
<string name="patch_step_save_metadata">Store metadata</string>
|
||||
<string name="patch_step_alignment">Aligning APK</string>
|
||||
@@ -189,7 +203,9 @@
|
||||
<string name="installer_abort_body">Are you sure you really want to abort an in-progress installation?</string>
|
||||
|
||||
<string name="updater_title">Update to v%s</string>
|
||||
<string name="updater_body">A new update has been released for Radiant Lyrics Manager! It may be required in order to function properly. Would you like to update?</string>
|
||||
<string
|
||||
name="updater_body"
|
||||
>A new update has been released for Radiant Lyrics Manager! It may be required in order to function properly. Would you like to update?</string>
|
||||
<string name="updater_open_github">View release on GitHub</string>
|
||||
<string name="updater_check_fail">Failed to check for updates!</string>
|
||||
<string name="updater_update_fail">Failed to update! Please download and install the update manually!</string>
|
||||
@@ -203,14 +219,20 @@
|
||||
|
||||
<string name="home_network_fail">Failed to fetch data!</string>
|
||||
|
||||
<string name="patchopts_title">Radiant Lyrics provides several customizations at installation time that are not able to be changed once installed.</string>
|
||||
<string
|
||||
name="patchopts_title"
|
||||
>Radiant Lyrics provides several customizations at installation time that are not able to be changed once installed.</string>
|
||||
<string name="patchopts_pkgname_title">Package Name</string>
|
||||
<string name="patchopts_pkgname_desc">The package name is a unique identifier for all apps. Using different ones can allow for multiple installations of Radiant Lyrics.</string>
|
||||
<string
|
||||
name="patchopts_pkgname_desc"
|
||||
>The package name is a unique identifier for all apps. Using different ones can allow for multiple installations of Radiant Lyrics.</string>
|
||||
<string name="patchopts_pkgname_invalid">Invalid package name!</string>
|
||||
<string name="patchopts_pkgname_taken">Target app will be overwritten!</string>
|
||||
<string name="patchopts_pkgname_ok">Valid package name!</string>
|
||||
<string name="patchopts_appname_title">App Name</string>
|
||||
<string name="patchopts_appname_desc">The app name is what\'s displayed in your home launcher. This should be changed on secondary installations for ease of use.</string>
|
||||
<string
|
||||
name="patchopts_appname_desc"
|
||||
>The app name is what\'s displayed in your home launcher. This should be changed on secondary installations for ease of use.</string>
|
||||
<string name="patchopts_debuggable_title">Debuggable</string>
|
||||
<string name="patchopts_debuggable_desc">Enable the debuggable manifest flag. Only use this if you know what you are doing!</string>
|
||||
<string name="patchopts_custom_injector_title">Custom Injector</string>
|
||||
@@ -224,13 +246,25 @@
|
||||
<string name="patchopts_patches_summary">%1$d of %2$d enabled</string>
|
||||
|
||||
<string name="patch_lyrics_disable_cover_title">Disable Lyrics Cover</string>
|
||||
<string name="patch_lyrics_disable_cover_desc">Prevents the album cover from being hidden when lyrics are showing.</string>
|
||||
<string name="patch_lyrics_disable_cover_desc">Removes the mini track cover from the lyrics screen.</string>
|
||||
<string name="patch_lyrics_progress_pill_title">Lyrics Progress Pill</string>
|
||||
<string name="patch_lyrics_progress_pill_desc">Shows a progress pill while the upcoming lyric line loads in, with a soft fade above and below the lyrics list.</string>
|
||||
<string name="patch_lyrics_progress_pill_desc">Restores the old Track Progress Pill in the top of the Lyrics screen!</string>
|
||||
<string name="patch_lyrics_replace_button_title">Replace Lyrics Button</string>
|
||||
<string name="patch_lyrics_replace_button_desc">Replaces the Lyrics button with the RL Sparkle!</string>
|
||||
<string name="patch_player_backdrop_title">Player Backdrop</string>
|
||||
<string name="patch_player_backdrop_desc">Restores the legacy translucent backdrop blur behind the player.</string>
|
||||
<string name="patch_lyrics_replace_share_button_title">Replace Share Button</string>
|
||||
<string
|
||||
name="patch_lyrics_replace_share_button_desc"
|
||||
>Replaces the share button with the Tidal Connect button from the top of the screen. (it\'s in the menu already)</string>
|
||||
<string name="patch_debug_menu_unlock_title">Unlock Debug Menu</string>
|
||||
<string
|
||||
name="patch_debug_menu_unlock_desc"
|
||||
>Reveals TIDAL\'s hidden Debug Menu in Settings. (Unlocks Feature Flags & the legacy Debug Options)</string>
|
||||
<string name="patch_enable_legacy_ui_title">Enable Legacy UI</string>
|
||||
<string
|
||||
name="patch_enable_legacy_ui_desc"
|
||||
>[This patch will stop working soon] - Replaces the New Compose based UI with the Legacy UI (disables player-market-ui feature flag)</string>
|
||||
|
||||
<string name="componentopts_screen_title">Custom Component (%s)</string>
|
||||
<string name="componentopts_screen_desc">Select a custom build that was imported by Manager.</string>
|
||||
@@ -261,24 +295,27 @@
|
||||
<string name="notif_install_fail_desc">Click to view more info…</string>
|
||||
|
||||
<string name="play_protect_warning_title">Play Protect</string>
|
||||
<string name="play_protect_warning_desc">Google Play Protect appears to be enabled on your device. It may attempt to interfere with a new installation due to the usage of a unique signing key. You can disable it in Play Protect\'s settings.\n\nIf it does show a warning dialog, press\n\"More Details\" -> \"Install anyway\"</string>
|
||||
<string
|
||||
name="play_protect_warning_desc"
|
||||
>Google Play Protect appears to be enabled on your device. It may attempt to interfere with a new installation due to the usage of a unique signing key. You can disable it in Play Protect\'s settings.\n\nIf it does show a warning dialog, press\n\"More Details\" -> \"Install anyway\"</string>
|
||||
<string name="play_protect_warning_ok">Continue</string>
|
||||
<string name="play_protect_warning_open_gpp">Open Play Protect</string>
|
||||
<string name="play_protect_warning_disable">Don\'t show this again</string>
|
||||
|
||||
<string name="fun_fact_prefix">Fun Fact: %s</string>
|
||||
<string name="fun_fact_1">Did you know that Google Play Protect is useless?</string>
|
||||
<string name="fun_fact_2">Radiant Lyrics restores the blurred album art lyrics dialog from TIDAL\'s legacy player.</string>
|
||||
<string name="fun_fact_3">The legacy LyricsDialog uses a BlurTransformation with radius 5 via RenderScript.</string>
|
||||
<string name="fun_fact_4">i am in your walls</string>
|
||||
<string name="fun_fact_5">The TIDAL app has two coexisting player implementations — old View-based and new Compose.</string>
|
||||
<string name="fun_fact_1">Did you know that TIDAL no longer like blur!</string>
|
||||
<string name="fun_fact_2">Radiant Lyrics is also available for DESKTOP!!!</string>
|
||||
<string name="fun_fact_3">i am in your walls!</string>
|
||||
<string name="fun_fact_4">TIDAL is Rewriting the UI with Compose</string>
|
||||
<string name="fun_fact_5">The TIDAL app has a hidden Debug Menu!</string>
|
||||
<string name="fun_fact_6">Having issues? Open an issue on GitHub!</string>
|
||||
<string name="fun_fact_7">The lyricsButton was hidden because TIDAL rerouted lyrics data to the new Compose player.</string>
|
||||
<string name="fun_fact_8" translatable="false">\"just patch it lol\"</string>
|
||||
<string name="fun_fact_9">Radiant Lyrics targets TIDAL v2.192.0</string>
|
||||
<string name="fun_fact_7">This installer/manager was forked from Aliucord</string>
|
||||
<string name="fun_fact_8">I have CATS!!</string>
|
||||
<string name="fun_fact_9">Radiant Lyrics is made by JUST meoware.exe</string>
|
||||
<string name="fun_fact_10">Smali patches are distributed as unified diffs inside patches.zip</string>
|
||||
<string name="fun_fact_11">The blurred background uses Coil + BlurTransformation(radius=5) crossfaded onto an ImageView.</string>
|
||||
<string name="fun_fact_12" translatable="false">meowarex made this</string>
|
||||
<string name="fun_fact_11">I wrote these facts while under the influence of brain damage..</string>
|
||||
<string name="fun_fact_12" translatable="false">i love you!</string>
|
||||
<string name="fun_fact_13" translatable="false">TIDAL devs if you\'re reading this, please add all this to the app</string>
|
||||
|
||||
<string name="time_elapsed_seconds">%.2fs</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user