mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-08-26 22:17:44 +10:00
Merge pull request #54 from meowarex/dev
Advanced Options + New Patch Options <3
This commit is contained in:
+58
-3
@@ -8,7 +8,9 @@ import com.meowarex.rlmobile.patcher.steps.base.IDexProvider
|
|||||||
import com.meowarex.rlmobile.patcher.steps.base.Step
|
import com.meowarex.rlmobile.patcher.steps.base.Step
|
||||||
import com.meowarex.rlmobile.patcher.steps.download.CopyDependenciesStep
|
import com.meowarex.rlmobile.patcher.steps.download.CopyDependenciesStep
|
||||||
import com.meowarex.rlmobile.patcher.steps.download.DownloadPatchesStep
|
import com.meowarex.rlmobile.patcher.steps.download.DownloadPatchesStep
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.PatchManifest
|
||||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.builtinPatchSpecs
|
||||||
import com.android.tools.smali.baksmali.Baksmali
|
import com.android.tools.smali.baksmali.Baksmali
|
||||||
import com.android.tools.smali.baksmali.BaksmaliOptions
|
import com.android.tools.smali.baksmali.BaksmaliOptions
|
||||||
import com.android.tools.smali.dexlib2.Opcodes
|
import com.android.tools.smali.dexlib2.Opcodes
|
||||||
@@ -19,6 +21,7 @@ import com.github.diamondminer88.zip.ZipReader
|
|||||||
import com.github.difflib.DiffUtils
|
import com.github.difflib.DiffUtils
|
||||||
import com.github.difflib.UnifiedDiffUtils
|
import com.github.difflib.UnifiedDiffUtils
|
||||||
import com.github.difflib.patch.Patch
|
import com.github.difflib.patch.Patch
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import java.io.*
|
import java.io.*
|
||||||
@@ -27,6 +30,7 @@ class SmaliPatchStep(
|
|||||||
private val options: PatchOptions,
|
private val options: PatchOptions,
|
||||||
) : Step(), IDexProvider, KoinComponent {
|
) : Step(), IDexProvider, KoinComponent {
|
||||||
private val paths: PathManager by inject()
|
private val paths: PathManager by inject()
|
||||||
|
private val json: Json by inject()
|
||||||
|
|
||||||
override val group = StepGroup.Patch
|
override val group = StepGroup.Patch
|
||||||
override val localizedName = R.string.patch_step_patch_smali
|
override val localizedName = R.string.patch_step_patch_smali
|
||||||
@@ -41,7 +45,29 @@ class SmaliPatchStep(
|
|||||||
|
|
||||||
val patches = mutableListOf<LoadedPatch>()
|
val patches = mutableListOf<LoadedPatch>()
|
||||||
val localsBumps = mutableMapOf<Pair<String, String>, Int>()
|
val localsBumps = mutableMapOf<Pair<String, String>, Int>()
|
||||||
val disabledFiles = options.disabledPatchFiles()
|
|
||||||
|
// The patch list/options metadata lives in the zip's manifest.json
|
||||||
|
val manifestSpecs = try {
|
||||||
|
ZipReader(patchesZip).use { it.openEntry("manifest.json")?.read() }
|
||||||
|
?.let { json.decodeFromString(PatchManifest.serializer(), it.decodeToString()).patches }
|
||||||
|
?.takeIf { it.isNotEmpty() }
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
container.log("Failed to parse manifest.json (${t.message}); using built-in patch specs")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
val specs = manifestSpecs ?: builtinPatchSpecs { "" }
|
||||||
|
container.log(
|
||||||
|
"Loaded ${specs.size} patch specs from " +
|
||||||
|
if (manifestSpecs != null) "manifest.json" else "built-in list"
|
||||||
|
)
|
||||||
|
|
||||||
|
val disabledFiles = options.disabledPatchFiles(specs)
|
||||||
|
val knownExtensionFiles = options.knownExtensionFiles(specs)
|
||||||
|
val enabledExtensionFiles = options.enabledExtensionFiles(specs)
|
||||||
|
val substitutions = options.smaliSubstitutions(specs)
|
||||||
|
if (substitutions.isNotEmpty()) {
|
||||||
|
container.log("Patch option substitutions: ${substitutions.entries.joinToString { "${it.key}=${it.value}" }}")
|
||||||
|
}
|
||||||
|
|
||||||
// Load and parse all the patches from the smali archive.
|
// Load and parse all the patches from the smali archive.
|
||||||
container.log("Loading patches from smali patch archive: ${patchesZip.absolutePath}")
|
container.log("Loading patches from smali patch archive: ${patchesZip.absolutePath}")
|
||||||
@@ -54,6 +80,11 @@ class SmaliPatchStep(
|
|||||||
|
|
||||||
if (patchFile.endsWith(".smali") && patchFile.startsWith("extension/")) {
|
if (patchFile.endsWith(".smali") && patchFile.startsWith("extension/")) {
|
||||||
val relative = patchFile.removePrefix("extension/")
|
val relative = patchFile.removePrefix("extension/")
|
||||||
|
// Only bundle helper smali patch/variant/sub-option is enabled
|
||||||
|
if (relative in knownExtensionFiles && relative !in enabledExtensionFiles) {
|
||||||
|
container.log("Skipping disabled extension smali: $relative")
|
||||||
|
continue
|
||||||
|
}
|
||||||
val out = smaliDir.resolve(relative)
|
val out = smaliDir.resolve(relative)
|
||||||
// Guard against zip-slip: a crafted entry could otherwise escape smaliDir.
|
// Guard against zip-slip: a crafted entry could otherwise escape smaliDir.
|
||||||
val baseCanonical = smaliDir.canonicalPath + File.separator
|
val baseCanonical = smaliDir.canonicalPath + File.separator
|
||||||
@@ -77,11 +108,34 @@ class SmaliPatchStep(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val lines = zip.openEntry(patchFile)!!.read()
|
var patchText = zip.openEntry(patchFile)!!.read()
|
||||||
.decodeToString()
|
.decodeToString()
|
||||||
.replace("\r\n", "\n") // Replace CRLF endings with LF
|
.replace("\r\n", "\n") // Replace CRLF endings with LF
|
||||||
.trimEnd { it == '\n' } // Remove trailing new lines
|
.trimEnd { it == '\n' } // Remove trailing new lines
|
||||||
.split('\n')
|
|
||||||
|
// Bake advanced option values into the patch
|
||||||
|
for ((token, value) in substitutions) {
|
||||||
|
if (patchText.contains(token)) {
|
||||||
|
patchText = patchText.replace(token, value)
|
||||||
|
container.log("Applied substitution $token -> $value in $patchFile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail fast
|
||||||
|
UNRESOLVED_TOKEN.find(patchText)?.let { match ->
|
||||||
|
throw Error("Unresolved option placeholder ${match.value} in $patchFile")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip a patch that references a helper class which won't be bundled
|
||||||
|
val missingHelper = (knownExtensionFiles - enabledExtensionFiles).firstOrNull { rel ->
|
||||||
|
patchText.contains("L${rel.removeSuffix(".smali")};")
|
||||||
|
}
|
||||||
|
if (missingHelper != null) {
|
||||||
|
container.log("Skipping $patchFile: references gated-off helper class $missingHelper")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val lines = patchText.split('\n')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (directive in lines) {
|
for (directive in lines) {
|
||||||
@@ -321,6 +375,7 @@ class SmaliPatchStep(
|
|||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val LOCALS_DIRECTIVE = Regex("""^#\s*rl-locals:\s+(\S+)\s+(\S+)\s+(\d+)\s*$""")
|
val LOCALS_DIRECTIVE = Regex("""^#\s*rl-locals:\s+(\S+)\s+(\S+)\s+(\d+)\s*$""")
|
||||||
|
val UNRESOLVED_TOKEN = Regex("""__RL_[A-Z0-9_]+__""")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+8
-1
@@ -2,6 +2,8 @@ package com.meowarex.rlmobile.ui.previews.screens
|
|||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.*
|
import androidx.compose.ui.tooling.preview.*
|
||||||
import com.meowarex.rlmobile.network.utils.SemVer
|
import com.meowarex.rlmobile.network.utils.SemVer
|
||||||
import com.meowarex.rlmobile.ui.screens.componentopts.PatchComponent
|
import com.meowarex.rlmobile.ui.screens.componentopts.PatchComponent
|
||||||
@@ -18,6 +20,9 @@ private fun PatchOptionsScreenPreview(
|
|||||||
@PreviewParameter(PatchOptionsParametersProvider::class)
|
@PreviewParameter(PatchOptionsParametersProvider::class)
|
||||||
parameters: PatchOptionsParameters,
|
parameters: PatchOptionsParameters,
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val specs = remember { builtinPatchSpecs { context.getString(it) } }
|
||||||
|
|
||||||
ManagerTheme {
|
ManagerTheme {
|
||||||
PatchOptionsScreenContent(
|
PatchOptionsScreenContent(
|
||||||
isUpdate = parameters.isUpdate,
|
isUpdate = parameters.isUpdate,
|
||||||
@@ -34,12 +39,14 @@ private fun PatchOptionsScreenPreview(
|
|||||||
onSelectCustomTidalApk = {},
|
onSelectCustomTidalApk = {},
|
||||||
customPatches = parameters.customPatches,
|
customPatches = parameters.customPatches,
|
||||||
onSelectCustomPatches = {},
|
onSelectCustomPatches = {},
|
||||||
enabledPatchCount = KnownPatch.All.size,
|
specs = specs,
|
||||||
|
enabledPatchCount = specs.size,
|
||||||
isPatchEnabled = { true },
|
isPatchEnabled = { true },
|
||||||
onTogglePatch = { _, _ -> },
|
onTogglePatch = { _, _ -> },
|
||||||
patchLockState = { PatchLock.Free },
|
patchLockState = { PatchLock.Free },
|
||||||
variantIndex = { 0 },
|
variantIndex = { 0 },
|
||||||
onSelectVariant = { _, _ -> },
|
onSelectVariant = { _, _ -> },
|
||||||
|
optionState = PatchOptionState.Preview,
|
||||||
isConfigValid = parameters.isConfigValid,
|
isConfigValid = parameters.isConfigValid,
|
||||||
onInstall = {},
|
onInstall = {},
|
||||||
)
|
)
|
||||||
|
|||||||
+86
-4
@@ -8,12 +8,14 @@ import com.meowarex.rlmobile.ui.screens.patchopts.PatchDefault.Enabled
|
|||||||
data class PatchVariant(
|
data class PatchVariant(
|
||||||
@StringRes val titleRes: Int,
|
@StringRes val titleRes: Int,
|
||||||
val fileNames: List<String>,
|
val fileNames: List<String>,
|
||||||
|
val extensionFiles: List<String> = emptyList(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
enum class KnownPatch(
|
enum class KnownPatch(
|
||||||
val order: Int, // Patch order in the UI List (lower = higher up) [Main Patches: multiples of 10 | Sub Patches: multiples of 1]
|
val order: Int, // Patch order in the UI List (lower = higher up) [Main Patches: multiples of 10 | Sub Patches: multiples of 1]
|
||||||
val fileNames: List<String>,
|
val fileNames: List<String>,
|
||||||
|
val extensionFiles: List<String> = emptyList(),
|
||||||
@StringRes val titleRes: Int,
|
@StringRes val titleRes: Int,
|
||||||
@StringRes val descRes: Int,
|
@StringRes val descRes: Int,
|
||||||
val default: PatchDefault, // Default state of the patch in the UI List (enabled/disabled)
|
val default: PatchDefault, // Default state of the patch in the UI List (enabled/disabled)
|
||||||
@@ -21,6 +23,7 @@ enum class KnownPatch(
|
|||||||
val disables: List<KnownPatch> = emptyList(),
|
val disables: List<KnownPatch> = emptyList(),
|
||||||
val variants: List<PatchVariant> = emptyList(),
|
val variants: List<PatchVariant> = emptyList(),
|
||||||
val defaultVariantIndex: Int = 0,
|
val defaultVariantIndex: Int = 0,
|
||||||
|
val advancedOptions: List<PatchOption> = emptyList(),
|
||||||
) {
|
) {
|
||||||
LyricsDisableCover(
|
LyricsDisableCover(
|
||||||
order = 41,
|
order = 41,
|
||||||
@@ -69,6 +72,30 @@ enum class KnownPatch(
|
|||||||
titleRes = R.string.patch_player_backdrop_title,
|
titleRes = R.string.patch_player_backdrop_title,
|
||||||
descRes = R.string.patch_player_backdrop_desc,
|
descRes = R.string.patch_player_backdrop_desc,
|
||||||
default = Enabled,
|
default = Enabled,
|
||||||
|
advancedOptions = listOf(
|
||||||
|
PatchOption.Slider(
|
||||||
|
key = "blur_strength",
|
||||||
|
titleRes = R.string.patch_opt_backdrop_blur_title,
|
||||||
|
descRes = R.string.patch_opt_backdrop_blur_desc,
|
||||||
|
default = 50f,
|
||||||
|
valueRange = 0f..100f,
|
||||||
|
steps = 19, // dots every 5% (0,5,…,100); snap only when locked
|
||||||
|
displayAsPercent = true,
|
||||||
|
token = "RL_BLUR_BITS",
|
||||||
|
encode = SmaliEncode(EncodeKind.FloatBits, scale = 1.8f),
|
||||||
|
),
|
||||||
|
PatchOption.Slider(
|
||||||
|
key = "dimming",
|
||||||
|
titleRes = R.string.patch_opt_backdrop_dimming_title,
|
||||||
|
descRes = R.string.patch_opt_backdrop_dimming_desc,
|
||||||
|
default = 50f, // 50% == the original -0x80000000
|
||||||
|
valueRange = 0f..100f,
|
||||||
|
steps = 19, // dots every 5%
|
||||||
|
displayAsPercent = true,
|
||||||
|
token = "RL_SCRIM_ARGB",
|
||||||
|
encode = SmaliEncode(EncodeKind.ArgbAlpha),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
QualityBadgeColors(
|
QualityBadgeColors(
|
||||||
order = 36,
|
order = 36,
|
||||||
@@ -121,19 +148,73 @@ enum class KnownPatch(
|
|||||||
default = Disabled,
|
default = Disabled,
|
||||||
defaultVariantIndex = 2,
|
defaultVariantIndex = 2,
|
||||||
variants = listOf(
|
variants = listOf(
|
||||||
PatchVariant(
|
PatchVariant( // 0: Floating — stock rounded pill (no patch; the progress border is an option)
|
||||||
titleRes = R.string.patch_mini_player_variant_floating_title,
|
titleRes = R.string.patch_mini_player_variant_floating_title,
|
||||||
fileNames = listOf("mini-player-floating.patch"),
|
fileNames = emptyList(),
|
||||||
),
|
),
|
||||||
PatchVariant(
|
PatchVariant( // 1: Grey — square, theme background (shown as "Legacy" when Dynamic BG is on)
|
||||||
titleRes = R.string.patch_mini_player_variant_square_grey_title,
|
titleRes = R.string.patch_mini_player_variant_square_grey_title,
|
||||||
fileNames = listOf("mini-player-grey.patch"),
|
fileNames = listOf("mini-player-grey.patch"),
|
||||||
),
|
),
|
||||||
PatchVariant(
|
PatchVariant( // 2: Black — square, forced black background (hidden when Dynamic BG is on)
|
||||||
titleRes = R.string.patch_mini_player_variant_square_black_title,
|
titleRes = R.string.patch_mini_player_variant_square_black_title,
|
||||||
fileNames = listOf("mini-player-black.patch"),
|
fileNames = listOf("mini-player-black.patch"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
advancedOptions = listOf(
|
||||||
|
PatchOption.Toggle(
|
||||||
|
key = "dynamic_bg",
|
||||||
|
titleRes = R.string.patch_mini_player_dynamic_bg_title,
|
||||||
|
descRes = R.string.patch_mini_player_dynamic_bg_desc,
|
||||||
|
default = false,
|
||||||
|
inline = true,
|
||||||
|
fileNames = listOf("mini-player-dynamic-bg.patch"),
|
||||||
|
extensionFiles = listOf("radiant/MiniPlayerBackground.smali"),
|
||||||
|
hidesVariants = listOf(2),
|
||||||
|
relabelVariants = mapOf(1 to R.string.patch_mini_player_variant_legacy_title),
|
||||||
|
),
|
||||||
|
// Animated progress border around the floating pill
|
||||||
|
PatchOption.Toggle(
|
||||||
|
key = "border",
|
||||||
|
titleRes = R.string.patch_mini_player_border_title,
|
||||||
|
descRes = R.string.patch_mini_player_border_desc,
|
||||||
|
default = false,
|
||||||
|
requiresVariant = 0,
|
||||||
|
fileNames = listOf("mini-player-floating-border.patch"),
|
||||||
|
extensionFiles = listOf("radiant/MiniSeekerFloating.smali"),
|
||||||
|
),
|
||||||
|
// Swipe up to open the full player
|
||||||
|
PatchOption.Toggle(
|
||||||
|
key = "gestures",
|
||||||
|
titleRes = R.string.patch_mini_player_gestures_title,
|
||||||
|
descRes = R.string.patch_mini_player_gestures_desc,
|
||||||
|
default = true,
|
||||||
|
fileNames = listOf("mini-player-gestures.patch"),
|
||||||
|
extensionFiles = listOf(
|
||||||
|
"radiant/MiniPlayerGestures.smali",
|
||||||
|
"radiant/MiniPlayerGestures\$Gesture.smali",
|
||||||
|
"radiant/MiniPlayerGestures\$RootGesture.smali",
|
||||||
|
"radiant/MiniPlayerGestures\$ApplyPending.smali",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Swipe left/right to skip
|
||||||
|
PatchOption.Toggle(
|
||||||
|
key = "next_prev",
|
||||||
|
titleRes = R.string.patch_mini_player_left_right_gestures_title,
|
||||||
|
descRes = R.string.patch_mini_player_left_right_gestures_desc,
|
||||||
|
default = false,
|
||||||
|
requiresOption = "gestures",
|
||||||
|
fileNames = listOf("mini-player-gestures-left-right.patch"),
|
||||||
|
extensionFiles = listOf(
|
||||||
|
"radiant/MiniPlayerTrackGestures.smali",
|
||||||
|
"radiant/MiniPlayerTrackGestures\$Gesture.smali",
|
||||||
|
"radiant/MiniPlayerTrackGestures\$OffsetLayer.smali",
|
||||||
|
"radiant/MiniPlayerTrackGestures\$ResetAnimator.smali",
|
||||||
|
"radiant/MiniPlayerTrackGestures\$TextDraw.smali",
|
||||||
|
"com/tidal/android/feature/appscaffold/ui/q\$c.smali",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
EnableLegacyUi(
|
EnableLegacyUi(
|
||||||
order = 10,
|
order = 10,
|
||||||
@@ -152,6 +233,7 @@ enum class KnownPatch(
|
|||||||
QualityBadgeColors,
|
QualityBadgeColors,
|
||||||
LyricsProgressPill,
|
LyricsProgressPill,
|
||||||
CoverEverywhere,
|
CoverEverywhere,
|
||||||
|
MiniPlayerRedesign,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.meowarex.rlmobile.ui.screens.patchopts
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
|
sealed interface PatchOption {
|
||||||
|
/** Stable identifier, unique within a single patch. */
|
||||||
|
val key: String
|
||||||
|
|
||||||
|
@get:StringRes
|
||||||
|
val titleRes: Int
|
||||||
|
|
||||||
|
@get:StringRes
|
||||||
|
val descRes: Int
|
||||||
|
|
||||||
|
/** A simple on/off switch. */
|
||||||
|
data class Toggle(
|
||||||
|
override val key: String,
|
||||||
|
@StringRes override val titleRes: Int,
|
||||||
|
@StringRes override val descRes: Int,
|
||||||
|
val default: Boolean,
|
||||||
|
/** Patch files applied only while this toggle is on (and its patch is enabled). */
|
||||||
|
val fileNames: List<String> = emptyList(),
|
||||||
|
/** Helper smali extracted only while this toggle is on. */
|
||||||
|
val extensionFiles: List<String> = emptyList(),
|
||||||
|
/** Render inline beneath the variant selector instead of inside the advanced sheet. */
|
||||||
|
val inline: Boolean = false,
|
||||||
|
/** Greyed out (with the lock dialog) unless this variant index is the selected one. */
|
||||||
|
val requiresVariant: Int? = null,
|
||||||
|
/** Greyed out unless the sibling option with this key is currently on. */
|
||||||
|
val requiresOption: String? = null,
|
||||||
|
/** Variant indices hidden from the picker while this toggle is on. */
|
||||||
|
val hidesVariants: List<Int> = emptyList(),
|
||||||
|
/** Variant title overrides (index -> @StringRes) applied while this toggle is on. */
|
||||||
|
val relabelVariants: Map<Int, Int> = emptyMap(),
|
||||||
|
) : PatchOption
|
||||||
|
|
||||||
|
/** A continuous (or stepped) numeric value within [valueRange]. */
|
||||||
|
data class Slider(
|
||||||
|
override val key: String,
|
||||||
|
@StringRes override val titleRes: Int,
|
||||||
|
@StringRes override val descRes: Int,
|
||||||
|
val default: Float,
|
||||||
|
val valueRange: ClosedFloatingPointRange<Float>,
|
||||||
|
/** Number of discrete steps between the range ends (0 = continuous). */
|
||||||
|
val steps: Int = 0,
|
||||||
|
/** Render the value as a rounded percentage (e.g. "50%"). */
|
||||||
|
val displayAsPercent: Boolean = false,
|
||||||
|
/** Optional unit suffix shown after the value (e.g. "dp"). */
|
||||||
|
@StringRes val unitRes: Int? = null,
|
||||||
|
/** Placeholder name (without the surrounding `__`) baked into the `.patch` files. */
|
||||||
|
val token: String? = null,
|
||||||
|
/** How the chosen value becomes the smali literal that replaces the [token]. */
|
||||||
|
val encode: SmaliEncode? = null,
|
||||||
|
) : PatchOption
|
||||||
|
|
||||||
|
/** A single choice out of a small list, rendered as a segmented control. */
|
||||||
|
data class Choice(
|
||||||
|
override val key: String,
|
||||||
|
@StringRes override val titleRes: Int,
|
||||||
|
@StringRes override val descRes: Int,
|
||||||
|
val entries: List<ChoiceEntry>,
|
||||||
|
val defaultIndex: Int = 0,
|
||||||
|
) : PatchOption
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ChoiceEntry(
|
||||||
|
@StringRes val labelRes: Int,
|
||||||
|
)
|
||||||
+74
-10
@@ -35,23 +35,87 @@ data class PatchOptions(
|
|||||||
val patchStates: Map<String, Boolean> = emptyMap(),
|
val patchStates: Map<String, Boolean> = emptyMap(),
|
||||||
|
|
||||||
val selectedVariants: Map<String, Int> = emptyMap(),
|
val selectedVariants: Map<String, Int> = emptyMap(),
|
||||||
|
val optionFloats: Map<String, Float> = emptyMap(),
|
||||||
|
val optionBools: Map<String, Boolean> = emptyMap(),
|
||||||
|
val optionInts: Map<String, Int> = emptyMap(),
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
fun isEnabled(patch: KnownPatch): Boolean =
|
fun isEnabled(spec: PatchSpec): Boolean =
|
||||||
patchStates[patch.name] ?: patch.default.isEnabled
|
patchStates[spec.id] ?: spec.defaultEnabled
|
||||||
|
|
||||||
fun disabledPatchFiles(): Set<String> = buildSet<String> {
|
fun variantIndex(spec: PatchSpec): Int {
|
||||||
for (patch in KnownPatch.All) {
|
val stored = (selectedVariants[spec.id] ?: spec.defaultVariantIndex)
|
||||||
val enabled = isEnabled(patch)
|
.coerceIn(0, spec.variants.lastIndex.coerceAtLeast(0))
|
||||||
if (patch.variants.isEmpty()) {
|
return spec.resolveVariantIndex(stored) { isToggleOn(spec, it) }
|
||||||
if (!enabled) addAll(patch.fileNames)
|
}
|
||||||
|
|
||||||
|
fun sliderValue(spec: PatchSpec, option: OptionSpec.Slider): Float =
|
||||||
|
(optionFloats["${spec.id}/${option.key}"] ?: option.default).coerceIn(option.min, option.max)
|
||||||
|
|
||||||
|
fun isToggleOn(spec: PatchSpec, option: OptionSpec.Toggle): Boolean =
|
||||||
|
optionBools["${spec.id}/${option.key}"] ?: option.default
|
||||||
|
|
||||||
|
fun isToggleActive(spec: PatchSpec, option: OptionSpec.Toggle): Boolean {
|
||||||
|
if (!isToggleOn(spec, option)) return false
|
||||||
|
option.requiresVariant?.let { if (variantIndex(spec) != it) return false }
|
||||||
|
option.requiresOption?.let { key ->
|
||||||
|
val required = spec.advancedOptions.filterIsInstance<OptionSpec.Toggle>()
|
||||||
|
.firstOrNull { it.key == key }
|
||||||
|
if (required != null && !isToggleOn(spec, required)) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disabledPatchFiles(specs: List<PatchSpec>): Set<String> = buildSet {
|
||||||
|
for (spec in specs) {
|
||||||
|
val enabled = isEnabled(spec)
|
||||||
|
if (spec.variants.isEmpty()) {
|
||||||
|
if (!enabled) addAll(spec.fileNames)
|
||||||
} else {
|
} else {
|
||||||
val selected = (selectedVariants[patch.name] ?: patch.defaultVariantIndex)
|
val selected = variantIndex(spec)
|
||||||
.coerceIn(0, patch.variants.lastIndex)
|
spec.variants.forEachIndexed { index, variant ->
|
||||||
patch.variants.forEachIndexed { index, variant ->
|
|
||||||
if (!enabled || index != selected) addAll(variant.fileNames)
|
if (!enabled || index != selected) addAll(variant.fileNames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Toggle sub-options that gate patch files.
|
||||||
|
for (option in spec.advancedOptions) {
|
||||||
|
if (option is OptionSpec.Toggle && option.fileNames.isNotEmpty()) {
|
||||||
|
if (!enabled || !isToggleActive(spec, option)) addAll(option.fileNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun knownExtensionFiles(specs: List<PatchSpec>): Set<String> = buildSet {
|
||||||
|
for (spec in specs) {
|
||||||
|
addAll(spec.extensionFiles)
|
||||||
|
spec.variants.forEach { addAll(it.extensionFiles) }
|
||||||
|
spec.advancedOptions.forEach { if (it is OptionSpec.Toggle) addAll(it.extensionFiles) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enabledExtensionFiles(specs: List<PatchSpec>): Set<String> = buildSet {
|
||||||
|
for (spec in specs) {
|
||||||
|
if (!isEnabled(spec)) continue
|
||||||
|
addAll(spec.extensionFiles)
|
||||||
|
if (spec.variants.isNotEmpty()) {
|
||||||
|
spec.variants.getOrNull(variantIndex(spec))?.let { addAll(it.extensionFiles) }
|
||||||
|
}
|
||||||
|
for (option in spec.advancedOptions) {
|
||||||
|
if (option is OptionSpec.Toggle && isToggleActive(spec, option)) addAll(option.extensionFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun smaliSubstitutions(specs: List<PatchSpec>): Map<String, String> = buildMap {
|
||||||
|
for (spec in specs) {
|
||||||
|
if (!isEnabled(spec)) continue
|
||||||
|
for (option in spec.advancedOptions) {
|
||||||
|
if (option !is OptionSpec.Slider) continue
|
||||||
|
val token = option.token ?: continue
|
||||||
|
val encode = option.encode ?: continue
|
||||||
|
put("__${token}__", encode.encode(sliderValue(spec, option)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+210
-39
@@ -2,21 +2,35 @@ package com.meowarex.rlmobile.ui.screens.patchopts
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager.NameNotFoundException
|
import android.content.pm.PackageManager.NameNotFoundException
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import cafe.adriel.voyager.core.model.ScreenModel
|
import cafe.adriel.voyager.core.model.ScreenModel
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
import cafe.adriel.voyager.navigator.Navigator
|
||||||
|
import com.github.diamondminer88.zip.ZipReader
|
||||||
|
import com.meowarex.rlmobile.BuildConfig
|
||||||
|
import com.meowarex.rlmobile.manager.PathManager
|
||||||
import com.meowarex.rlmobile.manager.PreferencesManager
|
import com.meowarex.rlmobile.manager.PreferencesManager
|
||||||
import com.meowarex.rlmobile.ui.screens.componentopts.ComponentOptionsScreen
|
import com.meowarex.rlmobile.ui.screens.componentopts.ComponentOptionsScreen
|
||||||
import com.meowarex.rlmobile.ui.screens.componentopts.PatchComponent
|
import com.meowarex.rlmobile.ui.screens.componentopts.PatchComponent
|
||||||
import com.meowarex.rlmobile.ui.util.pushForResult
|
import com.meowarex.rlmobile.ui.util.pushForResult
|
||||||
import com.meowarex.rlmobile.util.*
|
import com.meowarex.rlmobile.util.*
|
||||||
|
import com.meowarex.rlmobile.manager.download.IDownloadManager
|
||||||
|
import com.meowarex.rlmobile.manager.download.KtorDownloadManager
|
||||||
|
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
|
||||||
|
import com.meowarex.rlmobile.network.utils.getOrThrow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class PatchOptionsModel(
|
class PatchOptionsModel(
|
||||||
prefilledOptions: PatchOptions,
|
prefilledOptions: PatchOptions,
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val prefs: PreferencesManager,
|
private val prefs: PreferencesManager,
|
||||||
|
private val paths: PathManager,
|
||||||
|
private val json: Json,
|
||||||
|
private val github: RadiantLyricsGithubService,
|
||||||
|
private val downloader: KtorDownloadManager,
|
||||||
) : ScreenModel {
|
) : ScreenModel {
|
||||||
var packageName by mutableStateOf(prefilledOptions.packageName)
|
var packageName by mutableStateOf(prefilledOptions.packageName)
|
||||||
private set
|
private set
|
||||||
@@ -47,76 +61,153 @@ class PatchOptionsModel(
|
|||||||
debuggable = value
|
debuggable = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The accordion renders from [specs]. It defaults to the compiled-in (localized) list and is
|
||||||
|
// rebuilt from a custom zip's manifest.json whenever a custom patch set is selected. (Rminder for others about the new custom patch selection flow)
|
||||||
|
|
||||||
|
private val builtinSpecs: List<PatchSpec> = builtinPatchSpecs { context.getString(it) }
|
||||||
|
|
||||||
|
var specs by mutableStateOf(builtinSpecs)
|
||||||
|
private set
|
||||||
|
|
||||||
|
var specsLoading by mutableStateOf(false)
|
||||||
|
private set
|
||||||
|
|
||||||
var patchStates by mutableStateOf(prefilledOptions.patchStates)
|
var patchStates by mutableStateOf(prefilledOptions.patchStates)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var selectedVariants by mutableStateOf(prefilledOptions.selectedVariants)
|
var selectedVariants by mutableStateOf(prefilledOptions.selectedVariants)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun variantIndex(patch: KnownPatch): Int = selectedVariants[patch.name]
|
fun variantIndex(spec: PatchSpec): Int =
|
||||||
?.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0))
|
(selectedVariants[spec.id] ?: spec.defaultVariantIndex)
|
||||||
?: patch.defaultVariantIndex.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0))
|
.coerceIn(0, spec.variants.lastIndex.coerceAtLeast(0))
|
||||||
|
|
||||||
fun isPatchEnabled(patch: KnownPatch): Boolean =
|
fun isPatchEnabled(spec: PatchSpec): Boolean =
|
||||||
patchStates[patch.name] ?: patch.default.isEnabled
|
patchStates[spec.id] ?: spec.defaultEnabled
|
||||||
|
|
||||||
fun setPatchEnabled(patch: KnownPatch, enabled: Boolean) {
|
fun setPatchEnabled(spec: PatchSpec, enabled: Boolean) {
|
||||||
fun closure(seed: KnownPatch, step: (KnownPatch) -> List<KnownPatch>): Set<KnownPatch> =
|
val byId = specs.associateBy { it.id }
|
||||||
|
fun closure(seed: PatchSpec, step: (PatchSpec) -> List<PatchSpec>): Set<PatchSpec> =
|
||||||
buildSet {
|
buildSet {
|
||||||
fun walk(p: KnownPatch) { if (add(p)) step(p).forEach(::walk) }
|
fun walk(p: PatchSpec) { if (add(p)) step(p).forEach(::walk) }
|
||||||
walk(seed)
|
walk(seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
val enableUnits: Set<KnownPatch>
|
val requiresOf = { p: PatchSpec -> p.requires.mapNotNull(byId::get) }
|
||||||
val disableUnits: Set<KnownPatch>
|
val dependentsOf = { p: PatchSpec -> specs.filter { p.id in it.requires } }
|
||||||
|
|
||||||
|
val enableUnits: Set<PatchSpec>
|
||||||
|
val disableUnits: Set<PatchSpec>
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
enableUnits = closure(patch) { it.requires }
|
enableUnits = closure(spec, requiresOf)
|
||||||
disableUnits = enableUnits.flatMap { it.disables }
|
disableUnits = enableUnits.flatMap { it.disables }
|
||||||
.flatMapTo(mutableSetOf()) { d ->
|
.mapNotNull(byId::get)
|
||||||
closure(d) { dep -> KnownPatch.All.filter { dep in it.requires } }
|
.flatMapTo(mutableSetOf()) { d -> closure(d, dependentsOf) }
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
enableUnits = emptySet()
|
enableUnits = emptySet()
|
||||||
disableUnits = closure(patch) { p -> KnownPatch.All.filter { p in it.requires } }
|
disableUnits = closure(spec, dependentsOf)
|
||||||
}
|
}
|
||||||
|
|
||||||
patchStates = patchStates.toMutableMap().apply {
|
patchStates = patchStates.toMutableMap().apply {
|
||||||
enableUnits.forEach { this[it.name] = true }
|
enableUnits.forEach { this[it.id] = true }
|
||||||
disableUnits.forEach { this[it.name] = false }
|
disableUnits.forEach { this[it.id] = false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectVariant(patch: KnownPatch, index: Int) {
|
fun selectVariant(spec: PatchSpec, index: Int) {
|
||||||
if (patch.variants.isEmpty() || index !in patch.variants.indices) return
|
if (spec.variants.isEmpty() || index !in spec.variants.indices) return
|
||||||
selectedVariants = selectedVariants + (patch.name to index)
|
selectedVariants = selectedVariants + (spec.id to index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun lockState(patch: KnownPatch): PatchLock {
|
// Advanced (per-patch) options
|
||||||
if (patch.variants.isNotEmpty()) return PatchLock.Free
|
// Values are seeded from the prefilled config, edited here, and written back out in
|
||||||
|
// generateConfig(). At patch time PatchOptions.smaliSubstitutions() turns slider values into
|
||||||
|
// the literals baked into the matching `.patch` files (see SmaliPatchStep). ()
|
||||||
|
|
||||||
fun closure(seed: KnownPatch, step: (KnownPatch) -> List<KnownPatch>): Set<KnownPatch> =
|
private var optionBools by mutableStateOf(prefilledOptions.optionBools)
|
||||||
|
private var optionFloats by mutableStateOf(prefilledOptions.optionFloats)
|
||||||
|
private var optionInts by mutableStateOf(prefilledOptions.optionInts)
|
||||||
|
|
||||||
|
private fun keyOf(spec: PatchSpec, option: OptionSpec): String = "${spec.id}/${option.key}"
|
||||||
|
|
||||||
|
fun toggleValue(spec: PatchSpec, option: OptionSpec.Toggle): Boolean =
|
||||||
|
optionBools[keyOf(spec, option)] ?: option.default
|
||||||
|
|
||||||
|
fun setToggleValue(spec: PatchSpec, option: OptionSpec.Toggle, value: Boolean) {
|
||||||
|
optionBools = optionBools + (keyOf(spec, option) to value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sliderValue(spec: PatchSpec, option: OptionSpec.Slider): Float =
|
||||||
|
(optionFloats[keyOf(spec, option)] ?: option.default).coerceIn(option.min, option.max)
|
||||||
|
|
||||||
|
fun setSliderValue(spec: PatchSpec, option: OptionSpec.Slider, value: Float) {
|
||||||
|
optionFloats = optionFloats + (keyOf(spec, option) to value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun choiceValue(spec: PatchSpec, option: OptionSpec.Choice): Int =
|
||||||
|
(optionInts[keyOf(spec, option)] ?: option.defaultIndex)
|
||||||
|
.coerceIn(0, option.entries.lastIndex.coerceAtLeast(0))
|
||||||
|
|
||||||
|
fun setChoiceValue(spec: PatchSpec, option: OptionSpec.Choice, index: Int) {
|
||||||
|
if (index !in option.entries.indices) return
|
||||||
|
optionInts = optionInts + (keyOf(spec, option) to index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isAdvancedModified(spec: PatchSpec): Boolean = spec.advancedOptions.any { option ->
|
||||||
|
when (option) {
|
||||||
|
is OptionSpec.Toggle -> toggleValue(spec, option) != option.default
|
||||||
|
is OptionSpec.Slider -> sliderValue(spec, option) != option.default
|
||||||
|
is OptionSpec.Choice -> choiceValue(spec, option) != option.defaultIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetAdvanced(spec: PatchSpec) {
|
||||||
|
val prefix = "${spec.id}/"
|
||||||
|
optionBools = optionBools.filterKeys { !it.startsWith(prefix) }
|
||||||
|
optionFloats = optionFloats.filterKeys { !it.startsWith(prefix) }
|
||||||
|
optionInts = optionInts.filterKeys { !it.startsWith(prefix) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val optionState: PatchOptionState = PatchOptionState(
|
||||||
|
toggle = ::toggleValue,
|
||||||
|
setToggle = ::setToggleValue,
|
||||||
|
slider = ::sliderValue,
|
||||||
|
setSlider = ::setSliderValue,
|
||||||
|
choice = ::choiceValue,
|
||||||
|
setChoice = ::setChoiceValue,
|
||||||
|
isModified = ::isAdvancedModified,
|
||||||
|
reset = ::resetAdvanced,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun lockState(spec: PatchSpec): PatchLock {
|
||||||
|
if (spec.variants.isNotEmpty()) return PatchLock.Free
|
||||||
|
|
||||||
|
val byId = specs.associateBy { it.id }
|
||||||
|
fun closure(seed: PatchSpec, step: (PatchSpec) -> List<PatchSpec>): Set<PatchSpec> =
|
||||||
buildSet {
|
buildSet {
|
||||||
fun walk(p: KnownPatch) { if (add(p)) step(p).forEach(::walk) }
|
fun walk(p: PatchSpec) { if (add(p)) step(p).forEach(::walk) }
|
||||||
walk(seed)
|
walk(seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (other in KnownPatch.All) {
|
val requiresOf = { p: PatchSpec -> p.requires.mapNotNull(byId::get) }
|
||||||
if (other == patch || !isPatchEnabled(other)) continue
|
val dependentsOf = { p: PatchSpec -> specs.filter { p.id in it.requires } }
|
||||||
|
|
||||||
val requiresClosure = closure(other) { it.requires }
|
for (other in specs) {
|
||||||
if (patch in requiresClosure - other) return PatchLock.LockedOn(other)
|
if (other.id == spec.id || !isPatchEnabled(other)) continue
|
||||||
|
|
||||||
|
val requiresClosure = closure(other, requiresOf)
|
||||||
|
if (spec in requiresClosure - other) return PatchLock.LockedOn(other)
|
||||||
|
|
||||||
val disablesClosure = requiresClosure.flatMap { it.disables }
|
val disablesClosure = requiresClosure.flatMap { it.disables }
|
||||||
.flatMapTo(mutableSetOf()) { d ->
|
.mapNotNull(byId::get)
|
||||||
closure(d) { dep -> KnownPatch.All.filter { dep in it.requires } }
|
.flatMapTo(mutableSetOf()) { d -> closure(d, dependentsOf) }
|
||||||
}
|
if (spec in disablesClosure) return PatchLock.LockedOff(other)
|
||||||
if (patch in disablesClosure) return PatchLock.LockedOff(other)
|
|
||||||
}
|
}
|
||||||
return PatchLock.Free
|
return PatchLock.Free
|
||||||
}
|
}
|
||||||
|
|
||||||
val enabledPatchCount: Int
|
val enabledPatchCount: Int
|
||||||
get() = KnownPatch.All.count { isPatchEnabled(it) }
|
get() = specs.count { isPatchEnabled(it) }
|
||||||
|
|
||||||
var customTidalApk by mutableStateOf<PatchComponent?>(null)
|
var customTidalApk by mutableStateOf<PatchComponent?>(null)
|
||||||
private set
|
private set
|
||||||
@@ -139,6 +230,79 @@ class PatchOptionsModel(
|
|||||||
componentType = PatchComponent.Type.Patches,
|
componentType = PatchComponent.Type.Patches,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
reloadSpecs(customPatches)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuilds [specs] from the selected patch set
|
||||||
|
*/
|
||||||
|
private fun reloadSpecs(component: PatchComponent?) = screenModelScope.launchIO {
|
||||||
|
mainThread { specsLoading = true }
|
||||||
|
val loaded = if (component == null) builtinSpecs else loadManifestSpecs(component) ?: builtinSpecs
|
||||||
|
mainThread {
|
||||||
|
specs = loaded
|
||||||
|
validatePatchSelection()
|
||||||
|
specsLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadManifestSpecs(component: PatchComponent): List<PatchSpec>? =
|
||||||
|
loadManifestSpecs(component.getFile(paths))
|
||||||
|
|
||||||
|
private fun loadManifestSpecs(file: File): List<PatchSpec>? {
|
||||||
|
if (!file.exists()) return null
|
||||||
|
return try {
|
||||||
|
val bytes = ZipReader(file).use { it.openEntry(MANIFEST_NAME)?.read() } ?: return null
|
||||||
|
json.decodeFromString(PatchManifest.serializer(), bytes.decodeToString())
|
||||||
|
.patches
|
||||||
|
.takeIf { it.isNotEmpty() }
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.w(BuildConfig.TAG, "Failed to parse $MANIFEST_NAME; using built-in list", t)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pulls the patch list from the latest GitHub release's patches.zip manifest
|
||||||
|
*/
|
||||||
|
private suspend fun loadLatestReleaseSpecs(): List<PatchSpec>? = try {
|
||||||
|
val release = github.getLatestRelease().getOrThrow()
|
||||||
|
val url = release.assets
|
||||||
|
.find { it.name == RadiantLyricsGithubService.PATCHES_ASSET_NAME }
|
||||||
|
?.browserDownloadUrl
|
||||||
|
if (url == null) {
|
||||||
|
Log.w(BuildConfig.TAG, "Latest release ${release.tagName} has no patches.zip asset; using built-in list")
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
// Cached per release tag so re-opening the screen doesn't re-download the same zip.
|
||||||
|
val dest = paths.cacheDownloadDir.resolve("manifest-${release.tagName}.zip")
|
||||||
|
.apply { parentFile?.mkdirs() }
|
||||||
|
if (dest.exists() || downloader.download(url, dest) is IDownloadManager.Result.Success) {
|
||||||
|
loadManifestSpecs(dest).also { loaded ->
|
||||||
|
if (loaded != null) {
|
||||||
|
Log.i(BuildConfig.TAG, "Loaded ${loaded.size} patches from latest release ${release.tagName} manifest")
|
||||||
|
} else {
|
||||||
|
Log.w(BuildConfig.TAG, "Latest release ${release.tagName} ships no manifest.json; using built-in list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.w(BuildConfig.TAG, "Failed to load latest release manifest; using built-in list", t)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Default (non-custom) source: the latest release manifest, falling back to the built-in list. */
|
||||||
|
private fun loadDefaultSpecs() = screenModelScope.launchIO {
|
||||||
|
mainThread { specsLoading = true }
|
||||||
|
val loaded = loadLatestReleaseSpecs() ?: builtinSpecs
|
||||||
|
mainThread {
|
||||||
|
specs = loaded
|
||||||
|
validatePatchSelection()
|
||||||
|
specsLoading = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val isConfigValid by derivedStateOf {
|
val isConfigValid by derivedStateOf {
|
||||||
@@ -161,6 +325,9 @@ class PatchOptionsModel(
|
|||||||
customPatches = customPatches,
|
customPatches = customPatches,
|
||||||
patchStates = patchStates,
|
patchStates = patchStates,
|
||||||
selectedVariants = selectedVariants,
|
selectedVariants = selectedVariants,
|
||||||
|
optionFloats = optionFloats,
|
||||||
|
optionBools = optionBools,
|
||||||
|
optionInts = optionInts,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,9 +353,9 @@ class PatchOptionsModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun validatePatchSelection() {
|
private fun validatePatchSelection() {
|
||||||
for (patch in KnownPatch.All) {
|
for (spec in specs) {
|
||||||
if (isPatchEnabled(patch)) {
|
if (isPatchEnabled(spec)) {
|
||||||
setPatchEnabled(patch, true)
|
setPatchEnabled(spec, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,9 +363,13 @@ class PatchOptionsModel(
|
|||||||
init {
|
init {
|
||||||
validatePatchSelection()
|
validatePatchSelection()
|
||||||
screenModelScope.launchBlock { fetchPkgNameState() }
|
screenModelScope.launchBlock { fetchPkgNameState() }
|
||||||
|
// Default source is the latest release's manifest; custom selections drive themselves.
|
||||||
|
if (customPatches == null) loadDefaultSpecs()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val MANIFEST_NAME = "manifest.json"
|
||||||
|
|
||||||
private val PACKAGE_REGEX = """^[a-z]\w*(\.[a-z]\w*)+$"""
|
private val PACKAGE_REGEX = """^[a-z]\w*(\.[a-z]\w*)+$"""
|
||||||
.toRegex(RegexOption.IGNORE_CASE)
|
.toRegex(RegexOption.IGNORE_CASE)
|
||||||
}
|
}
|
||||||
@@ -211,7 +382,7 @@ enum class PackageNameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed class PatchLock {
|
sealed class PatchLock {
|
||||||
object Free : PatchLock()
|
data object Free : PatchLock()
|
||||||
data class LockedOn(val by: KnownPatch) : PatchLock()
|
data class LockedOn(val by: PatchSpec) : PatchLock()
|
||||||
data class LockedOff(val by: KnownPatch) : PatchLock()
|
data class LockedOff(val by: PatchSpec) : PatchLock()
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-6
@@ -60,12 +60,14 @@ class PatchOptionsScreen(
|
|||||||
onSelectCustomTidalApk = { model.selectCustomTidalApk(navigator) },
|
onSelectCustomTidalApk = { model.selectCustomTidalApk(navigator) },
|
||||||
onSelectCustomPatches = { model.selectCustomPatches(navigator) },
|
onSelectCustomPatches = { model.selectCustomPatches(navigator) },
|
||||||
|
|
||||||
|
specs = model.specs,
|
||||||
enabledPatchCount = model.enabledPatchCount,
|
enabledPatchCount = model.enabledPatchCount,
|
||||||
isPatchEnabled = model::isPatchEnabled,
|
isPatchEnabled = model::isPatchEnabled,
|
||||||
onTogglePatch = model::setPatchEnabled,
|
onTogglePatch = model::setPatchEnabled,
|
||||||
patchLockState = model::lockState,
|
patchLockState = model::lockState,
|
||||||
variantIndex = model::variantIndex,
|
variantIndex = model::variantIndex,
|
||||||
onSelectVariant = model::selectVariant,
|
onSelectVariant = model::selectVariant,
|
||||||
|
optionState = model.optionState,
|
||||||
|
|
||||||
isConfigValid = model.isConfigValid,
|
isConfigValid = model.isConfigValid,
|
||||||
onInstall = {
|
onInstall = {
|
||||||
@@ -96,12 +98,14 @@ fun PatchOptionsScreenContent(
|
|||||||
customPatches: PatchComponent?,
|
customPatches: PatchComponent?,
|
||||||
onSelectCustomPatches: () -> Unit,
|
onSelectCustomPatches: () -> Unit,
|
||||||
|
|
||||||
|
specs: List<PatchSpec>,
|
||||||
enabledPatchCount: Int,
|
enabledPatchCount: Int,
|
||||||
isPatchEnabled: (KnownPatch) -> Boolean,
|
isPatchEnabled: (PatchSpec) -> Boolean,
|
||||||
onTogglePatch: (KnownPatch, Boolean) -> Unit,
|
onTogglePatch: (PatchSpec, Boolean) -> Unit,
|
||||||
patchLockState: (KnownPatch) -> PatchLock,
|
patchLockState: (PatchSpec) -> PatchLock,
|
||||||
variantIndex: (KnownPatch) -> Int,
|
variantIndex: (PatchSpec) -> Int,
|
||||||
onSelectVariant: (KnownPatch, Int) -> Unit,
|
onSelectVariant: (PatchSpec, Int) -> Unit,
|
||||||
|
optionState: PatchOptionState,
|
||||||
|
|
||||||
isConfigValid: Boolean,
|
isConfigValid: Boolean,
|
||||||
onInstall: () -> Unit,
|
onInstall: () -> Unit,
|
||||||
@@ -164,13 +168,15 @@ fun PatchOptionsScreenContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
PatchSelectionAccordion(
|
PatchSelectionAccordion(
|
||||||
|
specs = specs,
|
||||||
enabledCount = enabledPatchCount,
|
enabledCount = enabledPatchCount,
|
||||||
totalCount = KnownPatch.All.size,
|
totalCount = specs.size,
|
||||||
isEnabled = isPatchEnabled,
|
isEnabled = isPatchEnabled,
|
||||||
onToggle = onTogglePatch,
|
onToggle = onTogglePatch,
|
||||||
lockState = patchLockState,
|
lockState = patchLockState,
|
||||||
variantIndex = variantIndex,
|
variantIndex = variantIndex,
|
||||||
onSelectVariant = onSelectVariant,
|
onSelectVariant = onSelectVariant,
|
||||||
|
optionState = optionState,
|
||||||
modifier = Modifier.padding(top = 4.dp),
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,268 @@
|
|||||||
|
package com.meowarex.rlmobile.ui.screens.patchopts
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PatchManifest(
|
||||||
|
val version: Int = 1,
|
||||||
|
val patches: List<PatchSpec> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@Serializable
|
||||||
|
data class PatchSpec(
|
||||||
|
val id: String,
|
||||||
|
val order: Int = 0,
|
||||||
|
val fileNames: List<String> = emptyList(),
|
||||||
|
val extensionFiles: List<String> = emptyList(),
|
||||||
|
val title: String,
|
||||||
|
val description: String = "",
|
||||||
|
@SerialName("default") val defaultEnabled: Boolean = false,
|
||||||
|
/** ids of patches that get force-enabled when this one is enabled. */
|
||||||
|
val requires: List<String> = emptyList(),
|
||||||
|
/** ids of patches that get force-disabled when this one is enabled. */
|
||||||
|
val disables: List<String> = emptyList(),
|
||||||
|
val variants: List<VariantSpec> = emptyList(),
|
||||||
|
val defaultVariantIndex: Int = 0,
|
||||||
|
val advancedOptions: List<OptionSpec> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@Serializable
|
||||||
|
data class VariantSpec(
|
||||||
|
val title: String,
|
||||||
|
val fileNames: List<String> = emptyList(),
|
||||||
|
val extensionFiles: List<String> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
/** A single advanced option. Discriminated by `"type"` in JSON. */
|
||||||
|
@Serializable
|
||||||
|
sealed interface OptionSpec {
|
||||||
|
val key: String
|
||||||
|
val title: String
|
||||||
|
val description: String
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@Serializable
|
||||||
|
@SerialName("toggle")
|
||||||
|
data class Toggle(
|
||||||
|
override val key: String,
|
||||||
|
override val title: String,
|
||||||
|
override val description: String = "",
|
||||||
|
val default: Boolean = false,
|
||||||
|
/** Patch files applied only while this toggle is on (and its patch is enabled). */
|
||||||
|
val fileNames: List<String> = emptyList(),
|
||||||
|
/** Helper smali extracted only while this toggle is on. */
|
||||||
|
val extensionFiles: List<String> = emptyList(),
|
||||||
|
/** Render inline beneath the variant selector instead of inside the advanced sheet. */
|
||||||
|
val inline: Boolean = false,
|
||||||
|
/** Greyed out (with the lock dialog) unless this variant index is the selected one. */
|
||||||
|
val requiresVariant: Int? = null,
|
||||||
|
/** Greyed out unless the sibling option with this key is currently on. */
|
||||||
|
val requiresOption: String? = null,
|
||||||
|
/** Variant indices hidden from the picker while this toggle is on. */
|
||||||
|
val hidesVariants: List<Int> = emptyList(),
|
||||||
|
/** Variant title overrides (index -> title) applied while this toggle is on. */
|
||||||
|
val relabelVariants: Map<Int, String> = emptyMap(),
|
||||||
|
) : OptionSpec
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@Serializable
|
||||||
|
@SerialName("slider")
|
||||||
|
data class Slider(
|
||||||
|
override val key: String,
|
||||||
|
override val title: String,
|
||||||
|
override val description: String = "",
|
||||||
|
val default: Float = 0f,
|
||||||
|
val min: Float = 0f,
|
||||||
|
val max: Float = 100f,
|
||||||
|
val steps: Int = 0,
|
||||||
|
val displayAsPercent: Boolean = false,
|
||||||
|
val unit: String? = null,
|
||||||
|
/** Placeholder name (without the surrounding `__`) baked into the `.patch` files. */
|
||||||
|
val token: String? = null,
|
||||||
|
/** How the chosen value becomes the smali literal that replaces the token. */
|
||||||
|
val encode: SmaliEncode? = null,
|
||||||
|
) : OptionSpec {
|
||||||
|
val valueRange: ClosedFloatingPointRange<Float> get() = min..max
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@Serializable
|
||||||
|
@SerialName("choice")
|
||||||
|
data class Choice(
|
||||||
|
override val key: String,
|
||||||
|
override val title: String,
|
||||||
|
override val description: String = "",
|
||||||
|
val entries: List<String> = emptyList(),
|
||||||
|
val defaultIndex: Int = 0,
|
||||||
|
) : OptionSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class EncodeKind {
|
||||||
|
@SerialName("floatBits")
|
||||||
|
FloatBits,
|
||||||
|
|
||||||
|
@SerialName("argbAlpha")
|
||||||
|
ArgbAlpha,
|
||||||
|
|
||||||
|
@SerialName("int")
|
||||||
|
IntValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@Serializable
|
||||||
|
data class SmaliEncode(
|
||||||
|
val kind: EncodeKind,
|
||||||
|
val scale: Float = 1f,
|
||||||
|
) {
|
||||||
|
fun encode(value: Float): String = when (kind) {
|
||||||
|
// Float dp bit-pattern: const vX, <bits>; Dp.constructor-impl(F)F
|
||||||
|
EncodeKind.FloatBits -> (value * scale).toRawBits().toString()
|
||||||
|
// ARGB int with black RGB and a percentage-derived alpha: const vX, <argb>
|
||||||
|
EncodeKind.ArgbAlpha -> ((value / 100f * 255f).roundToInt() shl 24).toString()
|
||||||
|
// Plain (optionally scaled) integer literal.
|
||||||
|
EncodeKind.IntValue -> (value * scale).roundToInt().toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builtinPatchSpecs(resolve: (Int) -> String): List<PatchSpec> =
|
||||||
|
KnownPatch.All.map { patch ->
|
||||||
|
PatchSpec(
|
||||||
|
id = patch.name,
|
||||||
|
order = patch.order,
|
||||||
|
fileNames = patch.fileNames,
|
||||||
|
extensionFiles = patch.extensionFiles,
|
||||||
|
title = resolve(patch.titleRes),
|
||||||
|
description = resolve(patch.descRes),
|
||||||
|
defaultEnabled = patch.default.isEnabled,
|
||||||
|
requires = patch.requires.map { it.name },
|
||||||
|
disables = patch.disables.map { it.name },
|
||||||
|
variants = patch.variants.map { VariantSpec(resolve(it.titleRes), it.fileNames, it.extensionFiles) },
|
||||||
|
defaultVariantIndex = patch.defaultVariantIndex,
|
||||||
|
advancedOptions = patch.advancedOptions.map { it.toSpec(resolve) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PatchOption.toSpec(resolve: (Int) -> String): OptionSpec = when (this) {
|
||||||
|
is PatchOption.Toggle -> OptionSpec.Toggle(
|
||||||
|
key = key,
|
||||||
|
title = resolve(titleRes),
|
||||||
|
description = resolve(descRes),
|
||||||
|
default = default,
|
||||||
|
fileNames = fileNames,
|
||||||
|
extensionFiles = extensionFiles,
|
||||||
|
inline = inline,
|
||||||
|
requiresVariant = requiresVariant,
|
||||||
|
requiresOption = requiresOption,
|
||||||
|
hidesVariants = hidesVariants,
|
||||||
|
relabelVariants = relabelVariants.mapValues { resolve(it.value) },
|
||||||
|
)
|
||||||
|
|
||||||
|
is PatchOption.Slider -> OptionSpec.Slider(
|
||||||
|
key = key,
|
||||||
|
title = resolve(titleRes),
|
||||||
|
description = resolve(descRes),
|
||||||
|
default = default,
|
||||||
|
min = valueRange.start,
|
||||||
|
max = valueRange.endInclusive,
|
||||||
|
steps = steps,
|
||||||
|
displayAsPercent = displayAsPercent,
|
||||||
|
unit = unitRes?.let(resolve),
|
||||||
|
token = token,
|
||||||
|
encode = encode,
|
||||||
|
)
|
||||||
|
|
||||||
|
is PatchOption.Choice -> OptionSpec.Choice(
|
||||||
|
key = key,
|
||||||
|
title = resolve(titleRes),
|
||||||
|
description = resolve(descRes),
|
||||||
|
entries = entries.map { resolve(it.labelRes) },
|
||||||
|
defaultIndex = defaultIndex,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class EffectiveVariant(val originalIndex: Int, val title: String)
|
||||||
|
|
||||||
|
fun PatchSpec.effectiveVariants(isOptionOn: (OptionSpec.Toggle) -> Boolean): List<EffectiveVariant> {
|
||||||
|
if (variants.isEmpty()) return emptyList()
|
||||||
|
val hidden = mutableSetOf<Int>()
|
||||||
|
val relabel = mutableMapOf<Int, String>()
|
||||||
|
for (option in advancedOptions) {
|
||||||
|
if (option is OptionSpec.Toggle && isOptionOn(option)) {
|
||||||
|
hidden += option.hidesVariants
|
||||||
|
relabel += option.relabelVariants
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return variants.mapIndexedNotNull { index, variant ->
|
||||||
|
if (index in hidden) null else EffectiveVariant(index, relabel[index] ?: variant.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PatchSpec.resolveVariantIndex(stored: Int, isOptionOn: (OptionSpec.Toggle) -> Boolean): Int {
|
||||||
|
val visible = effectiveVariants(isOptionOn)
|
||||||
|
if (visible.isEmpty() || visible.any { it.originalIndex == stored }) return stored
|
||||||
|
val relabelKeys = buildSet {
|
||||||
|
for (option in advancedOptions) {
|
||||||
|
if (option is OptionSpec.Toggle && isOptionOn(option)) addAll(option.relabelVariants.keys)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return visible.firstOrNull { it.originalIndex in relabelKeys }?.originalIndex
|
||||||
|
?: visible.first().originalIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface OptionLock {
|
||||||
|
data object Free : OptionLock
|
||||||
|
data class NeedsVariant(val title: String) : OptionLock
|
||||||
|
data class NeedsOption(val title: String) : OptionLock
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PatchSpec.optionLock(
|
||||||
|
option: OptionSpec,
|
||||||
|
selectedVariant: Int,
|
||||||
|
isOptionOn: (OptionSpec.Toggle) -> Boolean,
|
||||||
|
): OptionLock {
|
||||||
|
if (option !is OptionSpec.Toggle) return OptionLock.Free
|
||||||
|
option.requiresVariant?.let { required ->
|
||||||
|
if (selectedVariant != required) {
|
||||||
|
variants.getOrNull(required)?.let { return OptionLock.NeedsVariant(it.title) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
option.requiresOption?.let { key ->
|
||||||
|
val required = advancedOptions.filterIsInstance<OptionSpec.Toggle>().firstOrNull { it.key == key }
|
||||||
|
if (required != null && !isOptionOn(required)) return OptionLock.NeedsOption(required.title)
|
||||||
|
}
|
||||||
|
return OptionLock.Free
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class PatchOptionState(
|
||||||
|
val toggle: (PatchSpec, OptionSpec.Toggle) -> Boolean,
|
||||||
|
val setToggle: (PatchSpec, OptionSpec.Toggle, Boolean) -> Unit,
|
||||||
|
val slider: (PatchSpec, OptionSpec.Slider) -> Float,
|
||||||
|
val setSlider: (PatchSpec, OptionSpec.Slider, Float) -> Unit,
|
||||||
|
val choice: (PatchSpec, OptionSpec.Choice) -> Int,
|
||||||
|
val setChoice: (PatchSpec, OptionSpec.Choice, Int) -> Unit,
|
||||||
|
val isModified: (PatchSpec) -> Boolean,
|
||||||
|
val reset: (PatchSpec) -> Unit,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
/** Read-only stub returning defaults — for Compose previews. */
|
||||||
|
val Preview = PatchOptionState(
|
||||||
|
toggle = { _, option -> option.default },
|
||||||
|
setToggle = { _, _, _ -> },
|
||||||
|
slider = { _, option -> option.default },
|
||||||
|
setSlider = { _, _, _ -> },
|
||||||
|
choice = { _, option -> option.defaultIndex },
|
||||||
|
setChoice = { _, _, _ -> },
|
||||||
|
isModified = { false },
|
||||||
|
reset = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+386
@@ -0,0 +1,386 @@
|
|||||||
|
package com.meowarex.rlmobile.ui.screens.patchopts.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.fromHtml
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.meowarex.rlmobile.R
|
||||||
|
import com.meowarex.rlmobile.ui.components.ResetToDefaultButton
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.OptionLock
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.OptionSpec
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionState
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.optionLock
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.PatchSpec
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun PatchAdvancedOptionsSheet(
|
||||||
|
patch: PatchSpec,
|
||||||
|
state: PatchOptionState,
|
||||||
|
selectedVariant: Int,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
) {
|
||||||
|
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
var lockDialog by remember { mutableStateOf<OptionLock?>(null) }
|
||||||
|
|
||||||
|
fun dismiss() {
|
||||||
|
scope.launch { sheetState.hide() }.invokeOnCompletion {
|
||||||
|
if (!sheetState.isVisible) onDismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
sheetState = sheetState,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.padding(bottom = 16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(20.dp),
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.patchopts_advanced_sheet_label),
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = patch.title,
|
||||||
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetToDefaultButton(
|
||||||
|
enabled = state.isModified(patch),
|
||||||
|
onClick = { state.reset(patch) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalDivider()
|
||||||
|
|
||||||
|
val sheetOptions = patch.advancedOptions.filter { it !is OptionSpec.Toggle || !it.inline }
|
||||||
|
for (option in sheetOptions) key(option.key) {
|
||||||
|
val lock = patch.optionLock(option, selectedVariant) { state.toggle(patch, it) }
|
||||||
|
when (option) {
|
||||||
|
is OptionSpec.Toggle -> ToggleOptionRow(
|
||||||
|
title = option.title,
|
||||||
|
description = option.description,
|
||||||
|
checked = state.toggle(patch, option),
|
||||||
|
onCheckedChange = { state.setToggle(patch, option, it) },
|
||||||
|
locked = lock != OptionLock.Free,
|
||||||
|
onLockedTap = { lockDialog = lock },
|
||||||
|
)
|
||||||
|
|
||||||
|
is OptionSpec.Slider -> SliderOptionRow(
|
||||||
|
title = option.title,
|
||||||
|
description = option.description,
|
||||||
|
value = state.slider(patch, option),
|
||||||
|
valueLabel = formatSliderValue(option, state.slider(patch, option)),
|
||||||
|
valueRange = option.valueRange,
|
||||||
|
steps = option.steps,
|
||||||
|
onValueChange = { state.setSlider(patch, option, it) },
|
||||||
|
)
|
||||||
|
|
||||||
|
is OptionSpec.Choice -> ChoiceOptionRow(
|
||||||
|
title = option.title,
|
||||||
|
description = option.description,
|
||||||
|
entries = option.entries,
|
||||||
|
selectedIndex = state.choice(patch, option),
|
||||||
|
onSelect = { state.setChoice(patch, option, it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = { dismiss() },
|
||||||
|
colors = ButtonDefaults.filledTonalButtonColors(
|
||||||
|
contentColor = MaterialTheme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.End)
|
||||||
|
.padding(top = 4.dp),
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.action_done))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lockDialog?.let { lock ->
|
||||||
|
OptionLockDialog(lock = lock, onDismiss = { lockDialog = null })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ToggleOptionRow(
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
checked: Boolean,
|
||||||
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
|
locked: Boolean = false,
|
||||||
|
onLockedTap: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
val interactionSource = remember(::MutableInteractionSource)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
indication = null,
|
||||||
|
role = Role.Switch,
|
||||||
|
) { if (locked) onLockedTap() else onCheckedChange(!checked) }
|
||||||
|
.alpha(if (locked) 0.45f else 1f),
|
||||||
|
) {
|
||||||
|
OptionText(
|
||||||
|
title = title,
|
||||||
|
description = description,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
|
||||||
|
Box {
|
||||||
|
Switch(
|
||||||
|
checked = checked,
|
||||||
|
enabled = !locked,
|
||||||
|
onCheckedChange = onCheckedChange,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
)
|
||||||
|
if (locked) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.matchParentSize()
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember(::MutableInteractionSource),
|
||||||
|
indication = null,
|
||||||
|
role = Role.Switch,
|
||||||
|
) { onLockedTap() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SliderOptionRow(
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
value: Float,
|
||||||
|
valueLabel: String,
|
||||||
|
valueRange: ClosedFloatingPointRange<Float>,
|
||||||
|
steps: Int,
|
||||||
|
onValueChange: (Float) -> Unit,
|
||||||
|
) {
|
||||||
|
// Whether the slider snaps to the step dots
|
||||||
|
var snap by rememberSaveable { mutableStateOf(true) }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = valueLabel,
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
if (steps > 0) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
snap = !snap
|
||||||
|
if (snap) onValueChange(snapToNearestStep(value, valueRange, steps))
|
||||||
|
},
|
||||||
|
modifier = Modifier.size(32.dp),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(
|
||||||
|
if (snap) R.drawable.ic_lock else R.drawable.ic_lock_open,
|
||||||
|
),
|
||||||
|
contentDescription = stringResource(R.string.patchopts_toggle_snap),
|
||||||
|
tint = if (snap) MaterialTheme.colorScheme.primary
|
||||||
|
else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.size(18.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = description,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
modifier = Modifier.alpha(.7f),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Locked -> native stepped slider (dots + snapping), left exactly as-is
|
||||||
|
// Unlocked -> plain continuous slider with the trailing stop-indicator dot removed
|
||||||
|
if (snap) {
|
||||||
|
Slider(
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
valueRange = valueRange,
|
||||||
|
steps = steps,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Slider(
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
valueRange = valueRange,
|
||||||
|
track = { SliderDefaults.Track(sliderState = it, drawStopIndicator = null) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun snapToNearestStep(
|
||||||
|
value: Float,
|
||||||
|
range: ClosedFloatingPointRange<Float>,
|
||||||
|
steps: Int,
|
||||||
|
): Float {
|
||||||
|
if (steps <= 0) return value
|
||||||
|
val stepSize = (range.endInclusive - range.start) / (steps + 1)
|
||||||
|
if (stepSize <= 0f) return value
|
||||||
|
val snapped = range.start + Math.round((value - range.start) / stepSize) * stepSize
|
||||||
|
return snapped.coerceIn(range.start, range.endInclusive)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ChoiceOptionRow(
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
entries: List<String>,
|
||||||
|
selectedIndex: Int,
|
||||||
|
onSelect: (Int) -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
OptionText(title = title, description = description)
|
||||||
|
|
||||||
|
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
entries.forEachIndexed { index, entry ->
|
||||||
|
SegmentedButton(
|
||||||
|
selected = index == selectedIndex,
|
||||||
|
onClick = { onSelect(index) },
|
||||||
|
shape = SegmentedButtonDefaults.itemShape(index = index, count = entries.size),
|
||||||
|
icon = {},
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
text = entry,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun OptionText(
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = description,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
modifier = Modifier.alpha(.7f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatSliderValue(option: OptionSpec.Slider, value: Float): String {
|
||||||
|
val rounded = value.roundToInt()
|
||||||
|
val unit = option.unit
|
||||||
|
return when {
|
||||||
|
option.displayAsPercent -> "$rounded%"
|
||||||
|
unit != null -> "$rounded $unit"
|
||||||
|
else -> rounded.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun OptionLockDialog(lock: OptionLock, onDismiss: () -> Unit) {
|
||||||
|
val message = when (lock) {
|
||||||
|
is OptionLock.NeedsVariant -> stringResource(R.string.patch_opt_lock_needs_variant, lock.title)
|
||||||
|
is OptionLock.NeedsOption -> stringResource(R.string.patch_opt_lock_needs_option, lock.title)
|
||||||
|
OptionLock.Free -> return
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicAlertDialog(onDismissRequest = onDismiss) {
|
||||||
|
Surface(
|
||||||
|
shape = MaterialTheme.shapes.large,
|
||||||
|
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
|
tonalElevation = 6.dp,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(start = 24.dp, end = 12.dp, top = 20.dp, bottom = 8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.patch_opt_lock_title),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = AnnotatedString.fromHtml(message),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
)
|
||||||
|
Box(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
TextButton(
|
||||||
|
onClick = onDismiss,
|
||||||
|
modifier = Modifier.align(Alignment.CenterEnd),
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.action_got_it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+150
-16
@@ -7,6 +7,7 @@ import androidx.compose.foundation.background
|
|||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
@@ -22,20 +23,26 @@ import androidx.compose.ui.text.AnnotatedString
|
|||||||
import androidx.compose.ui.text.fromHtml
|
import androidx.compose.ui.text.fromHtml
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.meowarex.rlmobile.R
|
import com.meowarex.rlmobile.R
|
||||||
import com.meowarex.rlmobile.ui.screens.patchopts.KnownPatch
|
import com.meowarex.rlmobile.ui.screens.patchopts.OptionSpec
|
||||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchLock
|
import com.meowarex.rlmobile.ui.screens.patchopts.PatchLock
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionState
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.PatchSpec
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.effectiveVariants
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.resolveVariantIndex
|
||||||
|
|
||||||
private data class LockInfo(val patch: KnownPatch, val lock: PatchLock)
|
private data class LockInfo(val patch: PatchSpec, val lock: PatchLock)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PatchSelectionAccordion(
|
fun PatchSelectionAccordion(
|
||||||
|
specs: List<PatchSpec>,
|
||||||
enabledCount: Int,
|
enabledCount: Int,
|
||||||
totalCount: Int,
|
totalCount: Int,
|
||||||
isEnabled: (KnownPatch) -> Boolean,
|
isEnabled: (PatchSpec) -> Boolean,
|
||||||
onToggle: (KnownPatch, Boolean) -> Unit,
|
onToggle: (PatchSpec, Boolean) -> Unit,
|
||||||
lockState: (KnownPatch) -> PatchLock,
|
lockState: (PatchSpec) -> PatchLock,
|
||||||
variantIndex: (KnownPatch) -> Int,
|
variantIndex: (PatchSpec) -> Int,
|
||||||
onSelectVariant: (KnownPatch, Int) -> Unit,
|
onSelectVariant: (PatchSpec, Int) -> Unit,
|
||||||
|
optionState: PatchOptionState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||||
@@ -45,6 +52,7 @@ fun PatchSelectionAccordion(
|
|||||||
)
|
)
|
||||||
|
|
||||||
var lockInfo by remember { mutableStateOf<LockInfo?>(null) }
|
var lockInfo by remember { mutableStateOf<LockInfo?>(null) }
|
||||||
|
var advancedFor by remember { mutableStateOf<PatchSpec?>(null) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@@ -104,12 +112,12 @@ fun PatchSelectionAccordion(
|
|||||||
.padding(bottom = 8.dp),
|
.padding(bottom = 8.dp),
|
||||||
)
|
)
|
||||||
|
|
||||||
for (patch in KnownPatch.All) key(patch) {
|
for (patch in specs) key(patch.id) {
|
||||||
val checked = isEnabled(patch)
|
val checked = isEnabled(patch)
|
||||||
val lock = lockState(patch)
|
val lock = lockState(patch)
|
||||||
PatchSwitchRow(
|
PatchSwitchRow(
|
||||||
title = stringResource(patch.titleRes),
|
title = patch.title,
|
||||||
description = stringResource(patch.descRes),
|
description = patch.description,
|
||||||
checked = checked,
|
checked = checked,
|
||||||
lock = lock,
|
lock = lock,
|
||||||
onCheckedChange = { onToggle(patch, it) },
|
onCheckedChange = { onToggle(patch, it) },
|
||||||
@@ -123,10 +131,58 @@ fun PatchSelectionAccordion(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 4.dp, end = 4.dp, top = 4.dp, bottom = 4.dp),
|
.padding(start = 4.dp, end = 4.dp, top = 4.dp, bottom = 4.dp),
|
||||||
) {
|
) {
|
||||||
|
val effective = patch.effectiveVariants { optionState.toggle(patch, it) }
|
||||||
|
val resolved = patch.resolveVariantIndex(variantIndex(patch)) {
|
||||||
|
optionState.toggle(patch, it)
|
||||||
|
}
|
||||||
PatchVariantSelector(
|
PatchVariantSelector(
|
||||||
variants = patch.variants,
|
variants = effective,
|
||||||
selectedIndex = variantIndex(patch),
|
selectedIndex = effective
|
||||||
onSelect = { idx -> onSelectVariant(patch, idx) },
|
.indexOfFirst { it.originalIndex == resolved }
|
||||||
|
.coerceAtLeast(0),
|
||||||
|
onSelect = { pos ->
|
||||||
|
effective.getOrNull(pos)?.let { onSelectVariant(patch, it.originalIndex) }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val inlineToggles = patch.advancedOptions
|
||||||
|
.filterIsInstance<OptionSpec.Toggle>()
|
||||||
|
.filter { it.inline }
|
||||||
|
if (inlineToggles.isNotEmpty()) {
|
||||||
|
AnimatedVisibility(visible = checked) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 4.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
|
) {
|
||||||
|
for (option in inlineToggles) key(option.key) {
|
||||||
|
InlineToggleRow(
|
||||||
|
title = option.title,
|
||||||
|
description = option.description,
|
||||||
|
checked = optionState.toggle(patch, option),
|
||||||
|
onCheckedChange = { optionState.setToggle(patch, option, it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val hasSheetOptions = patch.advancedOptions.any { it !is OptionSpec.Toggle || !it.inline }
|
||||||
|
if (hasSheetOptions) {
|
||||||
|
AnimatedVisibility(visible = checked) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 4.dp),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
AdvancedOptionsButton(
|
||||||
|
modified = optionState.isModified(patch),
|
||||||
|
onClick = { advancedFor = patch },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,6 +199,84 @@ fun PatchSelectionAccordion(
|
|||||||
onDismiss = { lockInfo = null },
|
onDismiss = { lockInfo = null },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
advancedFor?.let { patch ->
|
||||||
|
PatchAdvancedOptionsSheet(
|
||||||
|
patch = patch,
|
||||||
|
state = optionState,
|
||||||
|
selectedVariant = patch.resolveVariantIndex(variantIndex(patch)) { optionState.toggle(patch, it) },
|
||||||
|
onDismiss = { advancedFor = null },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AdvancedOptionsButton(
|
||||||
|
modified: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_tune),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(18.dp),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
Text(stringResource(R.string.patchopts_advanced_button))
|
||||||
|
if (modified) {
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(8.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(MaterialTheme.colorScheme.primary),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun InlineToggleRow(
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
checked: Boolean,
|
||||||
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
val interactionSource = remember(::MutableInteractionSource)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
indication = null,
|
||||||
|
role = Role.Switch,
|
||||||
|
) { onCheckedChange(!checked) }
|
||||||
|
.padding(vertical = 4.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
) {
|
||||||
|
Text(text = title, style = MaterialTheme.typography.titleSmall)
|
||||||
|
Text(
|
||||||
|
text = description,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
modifier = Modifier.alpha(.7f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Switch(
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = onCheckedChange,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -212,7 +346,7 @@ private fun PatchSwitchRow(
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun PatchLockDialog(
|
private fun PatchLockDialog(
|
||||||
thisPatch: KnownPatch,
|
thisPatch: PatchSpec,
|
||||||
lock: PatchLock,
|
lock: PatchLock,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -220,12 +354,12 @@ private fun PatchLockDialog(
|
|||||||
is PatchLock.LockedOn -> Triple(
|
is PatchLock.LockedOn -> Triple(
|
||||||
R.string.patch_lock_required_title,
|
R.string.patch_lock_required_title,
|
||||||
R.string.patch_lock_required_msg,
|
R.string.patch_lock_required_msg,
|
||||||
stringResource(lock.by.titleRes),
|
lock.by.title,
|
||||||
)
|
)
|
||||||
is PatchLock.LockedOff -> Triple(
|
is PatchLock.LockedOff -> Triple(
|
||||||
R.string.patch_lock_blocked_title,
|
R.string.patch_lock_blocked_title,
|
||||||
R.string.patch_lock_blocked_msg,
|
R.string.patch_lock_blocked_msg,
|
||||||
stringResource(lock.by.titleRes),
|
lock.by.title,
|
||||||
)
|
)
|
||||||
PatchLock.Free -> return
|
PatchLock.Free -> return
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -4,15 +4,14 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchVariant
|
import com.meowarex.rlmobile.ui.screens.patchopts.EffectiveVariant
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PatchVariantSelector(
|
fun PatchVariantSelector(
|
||||||
variants: List<PatchVariant>,
|
variants: List<EffectiveVariant>,
|
||||||
selectedIndex: Int,
|
selectedIndex: Int,
|
||||||
onSelect: (Int) -> Unit,
|
onSelect: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -31,7 +30,7 @@ fun PatchVariantSelector(
|
|||||||
icon = {},
|
icon = {},
|
||||||
label = {
|
label = {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(variant.titleRes),
|
text = variant.title,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<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="#e8eaed"
|
||||||
|
android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8H8.9V6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<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="#e8eaed"
|
||||||
|
android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1s3.1,1.39 3.1,3.1v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zM18,20H6V10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<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="#e8eaed"
|
||||||
|
android:pathData="M3,17v2h6v-2H3zM3,5v2h10V5H3zM13,21v-2h8v-2h-8v-2h-2v6h2zM7,9v2H3v2h4v2h2V9H7zM21,13v-2H11v2h10zM15,7h2V5h4V3h-4V1h-2v6z" />
|
||||||
|
</vector>
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
<string name="action_retry_install">Retry installation</string>
|
<string name="action_retry_install">Retry installation</string>
|
||||||
<string name="action_open_error_log">Open error log</string>
|
<string name="action_open_error_log">Open error log</string>
|
||||||
<string name="action_apply">Apply</string>
|
<string name="action_apply">Apply</string>
|
||||||
|
<string name="action_done">Done</string>
|
||||||
<string name="action_confirm">Confirm</string>
|
<string name="action_confirm">Confirm</string>
|
||||||
<string name="action_dismiss">Dismiss</string>
|
<string name="action_dismiss">Dismiss</string>
|
||||||
<string name="action_install">Install</string>
|
<string name="action_install">Install</string>
|
||||||
@@ -251,6 +252,9 @@
|
|||||||
<string name="patchopts_patches_title">Patches</string>
|
<string name="patchopts_patches_title">Patches</string>
|
||||||
<string name="patchopts_patches_desc">Toggle which patches are applied during installation. Unchecked patches are skipped entirely.</string>
|
<string name="patchopts_patches_desc">Toggle which patches are applied during installation. Unchecked patches are skipped entirely.</string>
|
||||||
<string name="patchopts_patches_summary">%1$d of %2$d enabled</string>
|
<string name="patchopts_patches_summary">%1$d of %2$d enabled</string>
|
||||||
|
<string name="patchopts_advanced_button">Advanced Options</string>
|
||||||
|
<string name="patchopts_advanced_sheet_label">Advanced Options</string>
|
||||||
|
<string name="patchopts_toggle_snap">Toggle snapping to steps</string>
|
||||||
|
|
||||||
<string name="patch_lyrics_disable_cover_title">Disable Lyrics Cover</string>
|
<string name="patch_lyrics_disable_cover_title">Disable Lyrics Cover</string>
|
||||||
<string name="patch_lyrics_disable_cover_desc">Removes the mini track cover from the lyrics screen.</string>
|
<string name="patch_lyrics_disable_cover_desc">Removes the mini track cover from the lyrics screen.</string>
|
||||||
@@ -289,14 +293,32 @@
|
|||||||
|
|
||||||
<string name="patch_mini_player_redesign_title">Redesigned Mini-Player</string>
|
<string name="patch_mini_player_redesign_title">Redesigned Mini-Player</string>
|
||||||
<string name="patch_mini_player_redesign_desc">Integrates the Seekbar into the control panel at the bottom of the screen.</string>
|
<string name="patch_mini_player_redesign_desc">Integrates the Seekbar into the control panel at the bottom of the screen.</string>
|
||||||
|
<string name="patch_mini_player_gestures_title">Mini-Player Gestures</string>
|
||||||
|
<string name="patch_mini_player_gestures_desc">Swipe up on the mini-player to open the full player.</string>
|
||||||
|
<string name="patch_mini_player_left_right_gestures_title">Next/Previous Gestures</string>
|
||||||
|
<string name="patch_mini_player_left_right_gestures_desc">Swipe left or right on the mini-player to skip tracks.</string>
|
||||||
|
<string name="patch_mini_player_dynamic_bg_title">Mini-Player Dynamic Background</string>
|
||||||
|
<string name="patch_mini_player_dynamic_bg_desc">Colorizes the Mini-Player depending on the currently playing track.</string>
|
||||||
<string name="patch_mini_player_variant_floating_title">Floating</string>
|
<string name="patch_mini_player_variant_floating_title">Floating</string>
|
||||||
<string name="patch_mini_player_variant_square_grey_title">Grey</string>
|
<string name="patch_mini_player_variant_square_grey_title">Grey</string>
|
||||||
<string name="patch_mini_player_variant_square_black_title">Black</string>
|
<string name="patch_mini_player_variant_square_black_title">Black</string>
|
||||||
|
<string name="patch_mini_player_variant_legacy_title">Legacy</string>
|
||||||
|
<string name="patch_mini_player_border_title">Progress Border</string>
|
||||||
|
<string name="patch_mini_player_border_desc">Draws an animated progress ring around the floating mini-player. Requires the Floating style.</string>
|
||||||
|
|
||||||
|
<!-- Advanced option labels -->
|
||||||
|
<string name="patch_opt_backdrop_blur_title">Blur Strength</string>
|
||||||
|
<string name="patch_opt_backdrop_blur_desc">How strongly the album art behind the player is blurred.</string>
|
||||||
|
<string name="patch_opt_backdrop_dimming_title">Backdrop Dimming</string>
|
||||||
|
<string name="patch_opt_backdrop_dimming_desc">Adjusts the level of darkening on the backdrop to keep foreground controls legible.</string>
|
||||||
|
|
||||||
<string name="patch_lock_required_title">Patch Required!</string>
|
<string name="patch_lock_required_title">Patch Required!</string>
|
||||||
<string name="patch_lock_blocked_title">Patch Blocked!</string>
|
<string name="patch_lock_blocked_title">Patch Blocked!</string>
|
||||||
<string name="patch_lock_required_msg">Patch Required by <b>%s</b>.</string>
|
<string name="patch_lock_required_msg">Patch Required by <b>%s</b>.</string>
|
||||||
<string name="patch_lock_blocked_msg">Patch Blocked by <b>%s</b>.</string>
|
<string name="patch_lock_blocked_msg">Patch Blocked by <b>%s</b>.</string>
|
||||||
|
<string name="patch_opt_lock_title">Option Locked</string>
|
||||||
|
<string name="patch_opt_lock_needs_variant">Requires the <b>%s</b> style.</string>
|
||||||
|
<string name="patch_opt_lock_needs_option">Requires the <b>%s</b> option.</string>
|
||||||
<string name="action_got_it">Got it</string>
|
<string name="action_got_it">Got it</string>
|
||||||
|
|
||||||
<string name="componentopts_screen_title">Custom Component (%s)</string>
|
<string name="componentopts_screen_title">Custom Component (%s)</string>
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"tidalVersionCode": 9090,
|
"tidalVersionCode": 9090,
|
||||||
"tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk",
|
"tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk",
|
||||||
"patchesVersion": "0.9.10"
|
"patchesVersion": "0.9.12"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
.class public final Lcom/tidal/android/feature/appscaffold/ui/q$c;
|
||||||
|
.super Lcom/tidal/android/feature/appscaffold/ui/q;
|
||||||
|
.source "SourceFile"
|
||||||
|
|
||||||
|
# Synthetic mini-player event used by right swipe (previous/rewind).
|
||||||
|
# TIDAL already exposes q$a for next in this path
|
||||||
|
|
||||||
|
# annotations
|
||||||
|
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||||
|
value = Lcom/tidal/android/feature/appscaffold/ui/q;
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
.annotation system Ldalvik/annotation/InnerClass;
|
||||||
|
accessFlags = 0x19
|
||||||
|
name = "c"
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
|
||||||
|
# static fields
|
||||||
|
.field public static final a:Lcom/tidal/android/feature/appscaffold/ui/q$c;
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method static constructor <clinit>()V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
new-instance v0, Lcom/tidal/android/feature/appscaffold/ui/q$c;
|
||||||
|
|
||||||
|
invoke-direct {v0}, Lcom/tidal/android/feature/appscaffold/ui/q;-><init>()V
|
||||||
|
|
||||||
|
sput-object v0, Lcom/tidal/android/feature/appscaffold/ui/q$c;->a:Lcom/tidal/android/feature/appscaffold/ui/q$c;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
.method public final equals(Ljava/lang/Object;)Z
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
const/4 v0, 0x1
|
||||||
|
|
||||||
|
if-ne p0, p1, :cond_0
|
||||||
|
|
||||||
|
return v0
|
||||||
|
|
||||||
|
:cond_0
|
||||||
|
instance-of p1, p1, Lcom/tidal/android/feature/appscaffold/ui/q$c;
|
||||||
|
|
||||||
|
if-nez p1, :cond_1
|
||||||
|
|
||||||
|
const/4 p1, 0x0
|
||||||
|
|
||||||
|
return p1
|
||||||
|
|
||||||
|
:cond_1
|
||||||
|
return v0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public final hashCode()I
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
const v0, -0x5d6a0f8
|
||||||
|
|
||||||
|
return v0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public final toString()Ljava/lang/String;
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
const-string v0, "PreviousButtonClicked"
|
||||||
|
|
||||||
|
return-object v0
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
.class public final Lradiant/MiniPlayerBackground;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
|
||||||
|
|
||||||
|
# static fields
|
||||||
|
.field public static final colorState:Landroidx/compose/runtime/MutableState; # player bg color
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method static constructor <clinit>()V
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
const/4 v1, 0x2
|
||||||
|
|
||||||
|
invoke-static {v0, v0, v1, v0}, Landroidx/compose/runtime/SnapshotStateKt;->mutableStateOf$default(Ljava/lang/Object;Landroidx/compose/runtime/SnapshotMutationPolicy;ILjava/lang/Object;)Landroidx/compose/runtime/MutableState;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
sput-object v0, Lradiant/MiniPlayerBackground;->colorState:Landroidx/compose/runtime/MutableState;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
.method private constructor <init>()V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
.method public static color(JLandroidx/compose/runtime/Composer;I)J
|
||||||
|
.locals 8
|
||||||
|
.annotation build Landroidx/compose/runtime/Composable;
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
const v0, 0x4d504442 # compose group key
|
||||||
|
|
||||||
|
invoke-interface {p2, v0}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerBackground;->colorState:Landroidx/compose/runtime/MutableState;
|
||||||
|
|
||||||
|
invoke-interface {v0}, Landroidx/compose/runtime/MutableState;->getValue()Ljava/lang/Object; # read player color
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
check-cast v0, Landroidx/compose/ui/graphics/Color;
|
||||||
|
|
||||||
|
if-eqz v0, :fallback
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroidx/compose/ui/graphics/Color;->unbox-impl()J
|
||||||
|
|
||||||
|
move-result-wide p0
|
||||||
|
|
||||||
|
:fallback
|
||||||
|
const/16 v0, 0x2ee # 750ms
|
||||||
|
|
||||||
|
const/4 v1, 0x0
|
||||||
|
|
||||||
|
const/4 v2, 0x0
|
||||||
|
|
||||||
|
const/4 v3, 0x6
|
||||||
|
|
||||||
|
const/4 v4, 0x0
|
||||||
|
|
||||||
|
invoke-static {v0, v1, v2, v3, v4}, Landroidx/compose/animation/core/AnimationSpecKt;->tween$default(IILandroidx/compose/animation/core/Easing;ILjava/lang/Object;)Landroidx/compose/animation/core/TweenSpec;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
move-wide v0, p0
|
||||||
|
|
||||||
|
const-string v3, "miniPlayerBackgroundColorAnimation"
|
||||||
|
|
||||||
|
const/4 v4, 0x0
|
||||||
|
|
||||||
|
move-object v5, p2
|
||||||
|
|
||||||
|
const/16 v6, 0x1b0
|
||||||
|
|
||||||
|
const/16 v7, 0x8
|
||||||
|
|
||||||
|
invoke-static/range {v0 .. v7}, Landroidx/compose/animation/SingleValueAnimationKt;->animateColorAsState-euL9pac(JLandroidx/compose/animation/core/AnimationSpec;Ljava/lang/String;Lyl0/l;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
invoke-interface {v0}, Landroidx/compose/runtime/State;->getValue()Ljava/lang/Object;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
check-cast v0, Landroidx/compose/ui/graphics/Color;
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroidx/compose/ui/graphics/Color;->unbox-impl()J
|
||||||
|
|
||||||
|
move-result-wide p0
|
||||||
|
|
||||||
|
invoke-interface {p2}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V
|
||||||
|
|
||||||
|
return-wide p0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
.method public static setHex(Ljava/lang/String;)V
|
||||||
|
.locals 3
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
if-eqz p0, :set_color
|
||||||
|
|
||||||
|
:try_start
|
||||||
|
invoke-static {p0}, Landroid/graphics/Color;->parseColor(Ljava/lang/String;)I
|
||||||
|
|
||||||
|
move-result p0
|
||||||
|
|
||||||
|
invoke-static {p0}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack ARGB color
|
||||||
|
|
||||||
|
move-result-wide v1
|
||||||
|
|
||||||
|
invoke-static {v1, v2}, Landroidx/compose/ui/graphics/Color;->box-impl(J)Landroidx/compose/ui/graphics/Color;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
:try_end
|
||||||
|
.catch Ljava/lang/Throwable; {:try_start .. :try_end} :catch_parse
|
||||||
|
|
||||||
|
goto :set_color
|
||||||
|
|
||||||
|
:catch_parse
|
||||||
|
move-exception p0
|
||||||
|
|
||||||
|
:set_color
|
||||||
|
sget-object p0, Lradiant/MiniPlayerBackground;->colorState:Landroidx/compose/runtime/MutableState;
|
||||||
|
|
||||||
|
invoke-interface {p0, v0}, Landroidx/compose/runtime/MutableState;->setValue(Ljava/lang/Object;)V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
.class public final Lradiant/MiniPlayerGestures$ApplyPending;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.implements Ljava/lang/Runnable;
|
||||||
|
|
||||||
|
# annotations
|
||||||
|
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||||
|
value = Lradiant/MiniPlayerGestures;
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
.annotation system Ldalvik/annotation/InnerClass;
|
||||||
|
accessFlags = 0x19
|
||||||
|
name = "ApplyPending"
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
|
||||||
|
# instance fields
|
||||||
|
.field public final a:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
.field public final b:I
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method public constructor <init>(Landroidx/compose/material3/SheetState;I)V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
iput-object p1, p0, Lradiant/MiniPlayerGestures$ApplyPending;->a:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
iput p2, p0, Lradiant/MiniPlayerGestures$ApplyPending;->b:I
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
# Apply once, then re schedule if there is still pending movement
|
||||||
|
# -> Sheet anchors can appear one (or more) frames after opening the player, so this keeps applying the latest drag until no delta remains
|
||||||
|
.method public final run()V
|
||||||
|
.locals 3
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/MiniPlayerGestures$ApplyPending;->a:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
invoke-static {v0}, Lradiant/MiniPlayerGestures;->applyPendingDrag(Landroidx/compose/material3/SheetState;)V
|
||||||
|
|
||||||
|
iget v1, p0, Lradiant/MiniPlayerGestures$ApplyPending;->b:I
|
||||||
|
|
||||||
|
add-int/lit8 v1, v1, -0x1
|
||||||
|
|
||||||
|
if-lez v1, :done
|
||||||
|
|
||||||
|
invoke-static {v0}, Lradiant/MiniPlayerGestures;->needsApply(Landroidx/compose/material3/SheetState;)Z
|
||||||
|
|
||||||
|
move-result v2
|
||||||
|
|
||||||
|
if-eqz v2, :done
|
||||||
|
|
||||||
|
invoke-static {v0, v1}, Lradiant/MiniPlayerGestures;->scheduleApply(Landroidx/compose/material3/SheetState;I)V
|
||||||
|
|
||||||
|
:done
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,408 @@
|
|||||||
|
.class public final Lradiant/MiniPlayerGestures$Gesture;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.implements Lyl0/l;
|
||||||
|
|
||||||
|
# annotations
|
||||||
|
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||||
|
value = Lradiant/MiniPlayerGestures;
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
.annotation system Ldalvik/annotation/InnerClass;
|
||||||
|
accessFlags = 0x19
|
||||||
|
name = "Gesture"
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
|
||||||
|
# instance fields
|
||||||
|
.field public final a:Lyl0/l;
|
||||||
|
|
||||||
|
# Y at ACTION_DOWN
|
||||||
|
.field public b:F
|
||||||
|
|
||||||
|
# Last Y seen (on this pointer stream)
|
||||||
|
.field public c:F
|
||||||
|
|
||||||
|
# True while this listener owns an active pointer stream
|
||||||
|
.field public d:Z
|
||||||
|
|
||||||
|
# True when sheet drag has started
|
||||||
|
.field public e:Z
|
||||||
|
|
||||||
|
.field public final f:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
.field public g:F
|
||||||
|
|
||||||
|
# X at ACTION_DOWN
|
||||||
|
.field public i:F
|
||||||
|
|
||||||
|
# True once horizontal movement wins (prevents later vertical start)
|
||||||
|
.field public j:Z
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method public constructor <init>(Lyl0/l;Landroidx/compose/material3/SheetState;)V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
iput-object p1, p0, Lradiant/MiniPlayerGestures$Gesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
iput-object p2, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private static dp(F)F
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
invoke-static {}, Landroid/content/res/Resources;->getSystem()Landroid/content/res/Resources;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroid/content/res/Resources;->getDisplayMetrics()Landroid/util/DisplayMetrics;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
iget v0, v0, Landroid/util/DisplayMetrics;->density:F
|
||||||
|
|
||||||
|
mul-float/2addr p0, v0
|
||||||
|
|
||||||
|
return p0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private dragSheet(FJ)V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
invoke-static {v0, p1, p2, p3}, Lradiant/MiniPlayerGestures;->updateDragTimed(Landroidx/compose/material3/SheetState;FJ)V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private finishSheet(F)V
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
iget-object v1, p0, Lradiant/MiniPlayerGestures$Gesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
invoke-static {v0, v1, p1}, Lradiant/MiniPlayerGestures;->finishDrag(Landroidx/compose/material3/SheetState;Lyl0/l;F)V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private openPlayer()V
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/MiniPlayerGestures$Gesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
sget-object v1, Lcom/tidal/android/feature/appscaffold/ui/b$b;->a:Lcom/tidal/android/feature/appscaffold/ui/b$b;
|
||||||
|
|
||||||
|
invoke-interface {v0, v1}, Lyl0/l;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private reset()V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->d:Z
|
||||||
|
|
||||||
|
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||||
|
|
||||||
|
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->j:Z
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->i:F
|
||||||
|
|
||||||
|
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->g:F
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private startSheet(FJ)V
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
# empty player guard
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerGestures;->hasMiniPlayerMedia()Z
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
if-nez v0, :start_guard
|
||||||
|
|
||||||
|
return-void
|
||||||
|
|
||||||
|
:start_guard
|
||||||
|
|
||||||
|
# avoid starting twice
|
||||||
|
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||||
|
|
||||||
|
if-eqz v0, :start
|
||||||
|
|
||||||
|
return-void
|
||||||
|
|
||||||
|
:start
|
||||||
|
const/4 v0, 0x1
|
||||||
|
|
||||||
|
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||||
|
|
||||||
|
iget-object v1, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
iget v0, p0, Lradiant/MiniPlayerGestures$Gesture;->b:F
|
||||||
|
|
||||||
|
invoke-static {v1, p1, v0, p2, p3}, Lradiant/MiniPlayerGestures;->beginDrag(Landroidx/compose/material3/SheetState;FFJ)V
|
||||||
|
|
||||||
|
invoke-direct {p0}, Lradiant/MiniPlayerGestures$Gesture;->openPlayer()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
# Listens for MotionEvents
|
||||||
|
.method public final invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||||
|
.locals 11
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getActionMasked()I
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
if-eqz v0, :down
|
||||||
|
|
||||||
|
const/4 v1, 0x1
|
||||||
|
|
||||||
|
if-eq v0, v1, :up
|
||||||
|
|
||||||
|
const/4 v1, 0x2
|
||||||
|
|
||||||
|
if-eq v0, v1, :move
|
||||||
|
|
||||||
|
const/4 v1, 0x3
|
||||||
|
|
||||||
|
if-eq v0, v1, :cancel
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_DOWN
|
||||||
|
:down
|
||||||
|
invoke-static {p1}, Lradiant/MiniPlayerGestures;->beginMotion(Landroid/view/MotionEvent;)V
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->b:F
|
||||||
|
|
||||||
|
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->c:F
|
||||||
|
|
||||||
|
const/4 v0, 0x1
|
||||||
|
|
||||||
|
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->d:Z
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->g:F
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->i:F
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->j:Z
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_MOVE
|
||||||
|
:move
|
||||||
|
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->d:Z
|
||||||
|
|
||||||
|
if-eqz v0, :done
|
||||||
|
|
||||||
|
invoke-static {p1}, Lradiant/MiniPlayerGestures;->trackMotion(Landroid/view/MotionEvent;)V
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getEventTime()J
|
||||||
|
|
||||||
|
move-result-wide v5
|
||||||
|
|
||||||
|
iget v1, p0, Lradiant/MiniPlayerGestures$Gesture;->c:F
|
||||||
|
|
||||||
|
sub-float v1, v0, v1
|
||||||
|
|
||||||
|
iput v1, p0, Lradiant/MiniPlayerGestures$Gesture;->g:F
|
||||||
|
|
||||||
|
iget v2, p0, Lradiant/MiniPlayerGestures$Gesture;->b:F
|
||||||
|
|
||||||
|
sub-float v2, v0, v2
|
||||||
|
|
||||||
|
iget-boolean v3, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||||
|
|
||||||
|
if-eqz v3, :maybe_start
|
||||||
|
|
||||||
|
invoke-direct {p0, v2, v5, v6}, Lradiant/MiniPlayerGestures$Gesture;->dragSheet(FJ)V
|
||||||
|
|
||||||
|
goto :store_last
|
||||||
|
|
||||||
|
:maybe_start
|
||||||
|
iget-boolean v3, p0, Lradiant/MiniPlayerGestures$Gesture;->j:Z
|
||||||
|
|
||||||
|
if-eqz v3, :axis_check
|
||||||
|
|
||||||
|
goto :store_last
|
||||||
|
|
||||||
|
:axis_check
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||||
|
|
||||||
|
move-result v7
|
||||||
|
|
||||||
|
iget v8, p0, Lradiant/MiniPlayerGestures$Gesture;->i:F
|
||||||
|
|
||||||
|
sub-float/2addr v7, v8
|
||||||
|
|
||||||
|
invoke-static {v7}, Ljava/lang/Math;->abs(F)F
|
||||||
|
|
||||||
|
move-result v7
|
||||||
|
|
||||||
|
invoke-static {v2}, Ljava/lang/Math;->abs(F)F
|
||||||
|
|
||||||
|
move-result v8
|
||||||
|
|
||||||
|
const/high16 v9, 0x41800000 # 16.0f
|
||||||
|
|
||||||
|
invoke-static {v9}, Lradiant/MiniPlayerGestures$Gesture;->dp(F)F
|
||||||
|
|
||||||
|
move-result v9
|
||||||
|
|
||||||
|
cmpg-float v10, v7, v9
|
||||||
|
|
||||||
|
if-gez v10, :vertical_check
|
||||||
|
|
||||||
|
const/high16 v10, 0x3fc00000 # 1.5f
|
||||||
|
|
||||||
|
mul-float/2addr v10, v8
|
||||||
|
|
||||||
|
cmpl-float v10, v7, v10
|
||||||
|
|
||||||
|
if-lez v10, :vertical_check
|
||||||
|
|
||||||
|
const/4 v10, 0x1
|
||||||
|
|
||||||
|
iput-boolean v10, p0, Lradiant/MiniPlayerGestures$Gesture;->j:Z
|
||||||
|
|
||||||
|
goto :store_last
|
||||||
|
|
||||||
|
# At least 16dp up to open the player
|
||||||
|
:vertical_check
|
||||||
|
neg-float v9, v9
|
||||||
|
|
||||||
|
cmpg-float v10, v2, v9
|
||||||
|
|
||||||
|
if-lez v10, :vertical_dominance
|
||||||
|
|
||||||
|
goto :store_last
|
||||||
|
|
||||||
|
:vertical_dominance
|
||||||
|
const/high16 v10, 0x3fc00000 # 1.5f -> has to dominate horizontal mov. by 1.5x
|
||||||
|
|
||||||
|
mul-float/2addr v7, v10
|
||||||
|
|
||||||
|
cmpl-float v10, v8, v7
|
||||||
|
|
||||||
|
if-gtz v10, :begin
|
||||||
|
|
||||||
|
goto :store_last
|
||||||
|
|
||||||
|
:begin
|
||||||
|
invoke-direct {p0, v2, v5, v6}, Lradiant/MiniPlayerGestures$Gesture;->startSheet(FJ)V
|
||||||
|
|
||||||
|
invoke-direct {p0, v2, v5, v6}, Lradiant/MiniPlayerGestures$Gesture;->dragSheet(FJ)V
|
||||||
|
|
||||||
|
|
||||||
|
:store_last
|
||||||
|
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->c:F
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_UP
|
||||||
|
:up
|
||||||
|
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->d:Z
|
||||||
|
|
||||||
|
if-eqz v0, :done
|
||||||
|
|
||||||
|
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||||
|
|
||||||
|
if-eqz v0, :finish
|
||||||
|
|
||||||
|
invoke-static {p1}, Lradiant/MiniPlayerGestures;->trackMotion(Landroid/view/MotionEvent;)V
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getEventTime()J
|
||||||
|
|
||||||
|
move-result-wide v5
|
||||||
|
|
||||||
|
iget-object v1, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
invoke-static {v1, v0, v5, v6}, Lradiant/MiniPlayerGestures;->updateDragToYTimed(Landroidx/compose/material3/SheetState;FJ)V
|
||||||
|
|
||||||
|
invoke-static {v1}, Lradiant/MiniPlayerGestures;->releaseVelocity(Landroidx/compose/material3/SheetState;)F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
invoke-direct {p0, v0}, Lradiant/MiniPlayerGestures$Gesture;->finishSheet(F)V
|
||||||
|
|
||||||
|
|
||||||
|
:finish
|
||||||
|
invoke-direct {p0}, Lradiant/MiniPlayerGestures$Gesture;->reset()V
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_CANCEL
|
||||||
|
:cancel
|
||||||
|
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||||
|
|
||||||
|
if-eqz v0, :cancel_reset
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
iget-object v1, p0, Lradiant/MiniPlayerGestures$Gesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
invoke-static {v0, v1}, Lradiant/MiniPlayerGestures;->cancelDrag(Landroidx/compose/material3/SheetState;Lyl0/l;)V
|
||||||
|
|
||||||
|
:cancel_reset
|
||||||
|
invoke-direct {p0}, Lradiant/MiniPlayerGestures$Gesture;->reset()V
|
||||||
|
|
||||||
|
|
||||||
|
:done
|
||||||
|
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
check-cast p1, Landroid/view/MotionEvent;
|
||||||
|
|
||||||
|
invoke-virtual {p0, p1}, Lradiant/MiniPlayerGestures$Gesture;->invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||||
|
|
||||||
|
move-result-object p1
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
.class public final Lradiant/MiniPlayerGestures$RootGesture;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.implements Lyl0/l;
|
||||||
|
|
||||||
|
|
||||||
|
# annotations
|
||||||
|
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||||
|
value = Lradiant/MiniPlayerGestures;
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
.annotation system Ldalvik/annotation/InnerClass;
|
||||||
|
accessFlags = 0x19
|
||||||
|
name = "RootGesture"
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
|
||||||
|
# instance fields
|
||||||
|
.field public final a:Lyl0/l;
|
||||||
|
|
||||||
|
.field public final b:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method public constructor <init>(Lyl0/l;Landroidx/compose/material3/SheetState;)V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
iput-object p1, p0, Lradiant/MiniPlayerGestures$RootGesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
iput-object p2, p0, Lradiant/MiniPlayerGestures$RootGesture;->b:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
.method public final invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||||
|
.locals 6
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/MiniPlayerGestures$RootGesture;->b:Landroidx/compose/material3/SheetState;
|
||||||
|
|
||||||
|
invoke-static {v0}, Lradiant/MiniPlayerGestures;->isDragging(Landroidx/compose/material3/SheetState;)Z
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
if-eqz v1, :done
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getActionMasked()I
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
const/4 v2, 0x1
|
||||||
|
|
||||||
|
if-eq v1, v2, :up
|
||||||
|
|
||||||
|
const/4 v2, 0x2
|
||||||
|
|
||||||
|
if-eq v1, v2, :move
|
||||||
|
|
||||||
|
const/4 v2, 0x3
|
||||||
|
|
||||||
|
if-eq v1, v2, :cancel
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_MOVE
|
||||||
|
:move
|
||||||
|
invoke-static {p1}, Lradiant/MiniPlayerGestures;->trackMotion(Landroid/view/MotionEvent;)V
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||||
|
|
||||||
|
move-result v2
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getEventTime()J
|
||||||
|
|
||||||
|
move-result-wide v4
|
||||||
|
|
||||||
|
invoke-static {v0, v2, v4, v5}, Lradiant/MiniPlayerGestures;->updateDragToYTimed(Landroidx/compose/material3/SheetState;FJ)V
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_UP
|
||||||
|
:up
|
||||||
|
invoke-static {p1}, Lradiant/MiniPlayerGestures;->trackMotion(Landroid/view/MotionEvent;)V
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getEventTime()J
|
||||||
|
|
||||||
|
move-result-wide v4
|
||||||
|
|
||||||
|
invoke-static {v0, v1, v4, v5}, Lradiant/MiniPlayerGestures;->updateDragToYTimed(Landroidx/compose/material3/SheetState;FJ)V
|
||||||
|
|
||||||
|
invoke-static {v0}, Lradiant/MiniPlayerGestures;->releaseVelocity(Landroidx/compose/material3/SheetState;)F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
iget-object v2, p0, Lradiant/MiniPlayerGestures$RootGesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
invoke-static {v0, v2, v1}, Lradiant/MiniPlayerGestures;->finishDrag(Landroidx/compose/material3/SheetState;Lyl0/l;F)V
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_CANCEL
|
||||||
|
:cancel
|
||||||
|
iget-object v2, p0, Lradiant/MiniPlayerGestures$RootGesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
invoke-static {v0, v2}, Lradiant/MiniPlayerGestures;->cancelDrag(Landroidx/compose/material3/SheetState;Lyl0/l;)V
|
||||||
|
|
||||||
|
:done
|
||||||
|
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
check-cast p1, Landroid/view/MotionEvent;
|
||||||
|
|
||||||
|
invoke-virtual {p0, p1}, Lradiant/MiniPlayerGestures$RootGesture;->invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||||
|
|
||||||
|
move-result-object p1
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
.end method
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,150 @@
|
|||||||
|
.class public final Lradiant/MiniPlayerTrackGestures$Gesture;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.implements Lyl0/l;
|
||||||
|
|
||||||
|
|
||||||
|
# annotations
|
||||||
|
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||||
|
value = Lradiant/MiniPlayerTrackGestures;
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
.annotation system Ldalvik/annotation/InnerClass;
|
||||||
|
accessFlags = 0x19
|
||||||
|
name = "Gesture"
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
|
||||||
|
# instance fields
|
||||||
|
.field public final a:Lyl0/l;
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method public constructor <init>(Lyl0/l;)V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
iput-object p1, p0, Lradiant/MiniPlayerTrackGestures$Gesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private nextTrack()V
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/MiniPlayerTrackGestures$Gesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
sget-object v1, Lcom/tidal/android/feature/appscaffold/ui/q$a;->a:Lcom/tidal/android/feature/appscaffold/ui/q$a; # Tidal's built-in next track mini-player event
|
||||||
|
|
||||||
|
invoke-interface {v0, v1}, Lyl0/l;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private previousTrack()V
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/MiniPlayerTrackGestures$Gesture;->a:Lyl0/l;
|
||||||
|
|
||||||
|
sget-object v1, Lcom/tidal/android/feature/appscaffold/ui/q$c;->a:Lcom/tidal/android/feature/appscaffold/ui/q$c; # Our synthetic previous track event
|
||||||
|
|
||||||
|
invoke-interface {v0, v1}, Lyl0/l;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
.method public final invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||||
|
.locals 3
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getActionMasked()I
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
if-eqz v0, :down
|
||||||
|
|
||||||
|
const/4 v1, 0x1
|
||||||
|
|
||||||
|
if-eq v0, v1, :up
|
||||||
|
|
||||||
|
const/4 v1, 0x2
|
||||||
|
|
||||||
|
if-eq v0, v1, :move
|
||||||
|
|
||||||
|
const/4 v1, 0x3
|
||||||
|
|
||||||
|
if-eq v0, v1, :cancel
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_DOWN
|
||||||
|
:down
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
invoke-static {v0, v1}, Lradiant/MiniPlayerTrackGestures;->beginDrag(FF)V
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_MOVE
|
||||||
|
:move
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
invoke-static {v0, v1}, Lradiant/MiniPlayerTrackGestures;->moveDrag(FF)V
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_UP
|
||||||
|
:up
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
invoke-static {v0, v1}, Lradiant/MiniPlayerTrackGestures;->finishDrag(FF)I
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
if-eqz v0, :done
|
||||||
|
|
||||||
|
# Negative result = swipe left -> next. Positive result = swipe right -> previous
|
||||||
|
if-gez v0, :previous
|
||||||
|
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->suppressTapOpen()V
|
||||||
|
invoke-direct {p0}, Lradiant/MiniPlayerTrackGestures$Gesture;->nextTrack()V
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
:previous
|
||||||
|
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->suppressTapOpen()V
|
||||||
|
invoke-direct {p0}, Lradiant/MiniPlayerTrackGestures$Gesture;->previousTrack()V
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
# ACTION_CANCEL
|
||||||
|
:cancel
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->cancelDrag()V
|
||||||
|
|
||||||
|
:done
|
||||||
|
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
check-cast p1, Landroid/view/MotionEvent;
|
||||||
|
|
||||||
|
invoke-virtual {p0, p1}, Lradiant/MiniPlayerTrackGestures$Gesture;->invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||||
|
|
||||||
|
move-result-object p1
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
.class public final Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.implements Lyl0/l;
|
||||||
|
|
||||||
|
|
||||||
|
# annotations
|
||||||
|
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||||
|
value = Lradiant/MiniPlayerTrackGestures;
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
.annotation system Ldalvik/annotation/InnerClass;
|
||||||
|
accessFlags = 0x19
|
||||||
|
name = "OffsetLayer"
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
|
||||||
|
# static fields
|
||||||
|
.field public static final INSTANCE:Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method static constructor <clinit>()V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
new-instance v0, Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||||
|
|
||||||
|
invoke-direct {v0}, Lradiant/MiniPlayerTrackGestures$OffsetLayer;-><init>()V
|
||||||
|
|
||||||
|
sput-object v0, Lradiant/MiniPlayerTrackGestures$OffsetLayer;->INSTANCE:Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private constructor <init>()V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
.method public final invoke(Landroidx/compose/ui/graphics/GraphicsLayerScope;)Lkotlin/u;
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||||
|
|
||||||
|
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
invoke-interface {p1, v0}, Landroidx/compose/ui/graphics/GraphicsLayerScope;->setTranslationX(F)V
|
||||||
|
|
||||||
|
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
check-cast p1, Landroidx/compose/ui/graphics/GraphicsLayerScope;
|
||||||
|
|
||||||
|
invoke-virtual {p0, p1}, Lradiant/MiniPlayerTrackGestures$OffsetLayer;->invoke(Landroidx/compose/ui/graphics/GraphicsLayerScope;)Lkotlin/u;
|
||||||
|
|
||||||
|
move-result-object p1
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
.class public final Lradiant/MiniPlayerTrackGestures$ResetAnimator;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.implements Landroid/animation/ValueAnimator$AnimatorUpdateListener;
|
||||||
|
|
||||||
|
|
||||||
|
# annotations
|
||||||
|
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||||
|
value = Lradiant/MiniPlayerTrackGestures;
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
.annotation system Ldalvik/annotation/InnerClass;
|
||||||
|
accessFlags = 0x19
|
||||||
|
name = "ResetAnimator"
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method public constructor <init>()V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
.method public final onAnimationUpdate(Landroid/animation/ValueAnimator;)V
|
||||||
|
.locals 3
|
||||||
|
|
||||||
|
move-object v0, p1
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/animation/ValueAnimator;->getAnimatedValue()Ljava/lang/Object;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
check-cast v1, Ljava/lang/Float;
|
||||||
|
|
||||||
|
invoke-virtual {v1}, Ljava/lang/Float;->floatValue()F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
invoke-static {v1}, Lradiant/MiniPlayerTrackGestures;->setDragOffsetDirect(F)V
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroid/animation/ValueAnimator;->getAnimatedFraction()F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
const/high16 v2, 0x3f800000 # 1.0f
|
||||||
|
|
||||||
|
cmpg-float v1, v1, v2
|
||||||
|
|
||||||
|
if-gez v1, :done
|
||||||
|
|
||||||
|
invoke-static {v0}, Lradiant/MiniPlayerTrackGestures;->clearResetAnimator(Landroid/animation/ValueAnimator;)V
|
||||||
|
|
||||||
|
:done
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
.class public final Lradiant/MiniPlayerTrackGestures$TextDraw;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.implements Lyl0/l;
|
||||||
|
|
||||||
|
|
||||||
|
# annotations
|
||||||
|
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||||
|
value = Lradiant/MiniPlayerTrackGestures;
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
.annotation system Ldalvik/annotation/InnerClass;
|
||||||
|
accessFlags = 0x19
|
||||||
|
name = "TextDraw"
|
||||||
|
.end annotation
|
||||||
|
|
||||||
|
|
||||||
|
# static fields
|
||||||
|
.field public static final INSTANCE:Lradiant/MiniPlayerTrackGestures$TextDraw;
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method static constructor <clinit>()V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
new-instance v0, Lradiant/MiniPlayerTrackGestures$TextDraw;
|
||||||
|
|
||||||
|
invoke-direct {v0}, Lradiant/MiniPlayerTrackGestures$TextDraw;-><init>()V
|
||||||
|
|
||||||
|
sput-object v0, Lradiant/MiniPlayerTrackGestures$TextDraw;->INSTANCE:Lradiant/MiniPlayerTrackGestures$TextDraw;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private constructor <init>()V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
.method public final invoke(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)Lkotlin/u;
|
||||||
|
.locals 12
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||||
|
|
||||||
|
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F
|
||||||
|
|
||||||
|
move-result v10
|
||||||
|
|
||||||
|
invoke-interface {p1}, Landroidx/compose/ui/graphics/drawscope/DrawScope;->getDrawContext()Landroidx/compose/ui/graphics/drawscope/DrawContext;
|
||||||
|
|
||||||
|
move-result-object v7
|
||||||
|
|
||||||
|
invoke-interface {v7}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->getSize-NH-jbRc()J
|
||||||
|
|
||||||
|
move-result-wide v8
|
||||||
|
|
||||||
|
const/16 v0, 0x20
|
||||||
|
|
||||||
|
shr-long v1, v8, v0
|
||||||
|
|
||||||
|
long-to-int v1, v1
|
||||||
|
|
||||||
|
invoke-static {v1}, Ljava/lang/Float;->intBitsToFloat(I)F
|
||||||
|
|
||||||
|
move-result v4
|
||||||
|
|
||||||
|
invoke-interface {v7}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->getCanvas()Landroidx/compose/ui/graphics/Canvas;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
invoke-interface {v0}, Landroidx/compose/ui/graphics/Canvas;->save()V
|
||||||
|
|
||||||
|
:try_start_0
|
||||||
|
sget-object v0, Landroidx/compose/ui/graphics/ClipOp;->Companion:Landroidx/compose/ui/graphics/ClipOp$Companion;
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroidx/compose/ui/graphics/ClipOp$Companion;->getIntersect-rtfAjoo()I
|
||||||
|
|
||||||
|
move-result v6
|
||||||
|
|
||||||
|
invoke-interface {v7}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->getTransform()Landroidx/compose/ui/graphics/drawscope/DrawTransform;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
const v2, -0x800001 # -Float.MAX_VALUE -> unbounded left
|
||||||
|
|
||||||
|
const v3, -0x800001 # -Float.MAX_VALUE -> unbounded top
|
||||||
|
|
||||||
|
const v5, 0x7f7fffff # Float.MAX_VALUE -> unbounded bottom
|
||||||
|
|
||||||
|
invoke-interface/range {v1 .. v6}, Landroidx/compose/ui/graphics/drawscope/DrawTransform;->clipRect-N_I0leg(FFFFI)V
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
invoke-interface {v1, v10, v0}, Landroidx/compose/ui/graphics/drawscope/DrawTransform;->translate(FF)V
|
||||||
|
|
||||||
|
invoke-interface {p1}, Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;->drawContent()V
|
||||||
|
:try_end_0
|
||||||
|
.catchall {:try_start_0 .. :try_end_0} :catchall_0
|
||||||
|
|
||||||
|
invoke-static {v7, v8, v9}, Landroidx/compose/animation/i;->b(Landroidx/compose/ui/graphics/drawscope/DrawContext;J)V
|
||||||
|
|
||||||
|
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
|
||||||
|
:catchall_0
|
||||||
|
move-exception v0
|
||||||
|
|
||||||
|
invoke-static {v7, v8, v9}, Landroidx/compose/animation/i;->b(Landroidx/compose/ui/graphics/drawscope/DrawContext;J)V
|
||||||
|
|
||||||
|
throw v0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
check-cast p1, Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;
|
||||||
|
|
||||||
|
invoke-virtual {p0, p1}, Lradiant/MiniPlayerTrackGestures$TextDraw;->invoke(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)Lkotlin/u;
|
||||||
|
|
||||||
|
move-result-object p1
|
||||||
|
|
||||||
|
return-object p1
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,564 @@
|
|||||||
|
.class public final Lradiant/MiniPlayerTrackGestures;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
|
||||||
|
|
||||||
|
# static fields
|
||||||
|
.field private static a:J
|
||||||
|
|
||||||
|
.field public static b:Landroidx/compose/runtime/MutableFloatState;
|
||||||
|
|
||||||
|
.field private static c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
.field private static d:Ljava/lang/Object;
|
||||||
|
|
||||||
|
# X at ACTION_DOWN
|
||||||
|
.field private static e:F
|
||||||
|
|
||||||
|
# Y at ACTION_DOWN
|
||||||
|
.field private static f:F
|
||||||
|
|
||||||
|
.field private static g:Z
|
||||||
|
|
||||||
|
.field private static h:Z
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method static constructor <clinit>()V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
invoke-static {v0}, Landroidx/compose/runtime/PrimitiveSnapshotStateKt;->mutableFloatStateOf(F)Landroidx/compose/runtime/MutableFloatState;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
sput-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private constructor <init>()V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static animateReset()V
|
||||||
|
.locals 7
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
if-eqz v0, :read_offset
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroid/animation/ValueAnimator;->cancel()V
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
sput-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
:read_offset
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||||
|
|
||||||
|
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
invoke-static {v0}, Ljava/lang/Math;->abs(F)F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
const/4 v3, 0x0
|
||||||
|
|
||||||
|
const/high16 v2, 0x3f000000 # 0.5f
|
||||||
|
|
||||||
|
cmpg-float v1, v1, v2
|
||||||
|
|
||||||
|
if-lez v1, :snap_zero
|
||||||
|
|
||||||
|
const/4 v1, 0x2
|
||||||
|
|
||||||
|
new-array v1, v1, [F
|
||||||
|
|
||||||
|
const/4 v2, 0x0
|
||||||
|
|
||||||
|
aput v0, v1, v2
|
||||||
|
|
||||||
|
const/4 v0, 0x1
|
||||||
|
|
||||||
|
aput v3, v1, v0
|
||||||
|
|
||||||
|
invoke-static {v1}, Landroid/animation/ValueAnimator;->ofFloat([F)Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
const-wide/16 v4, 0xb4 # 180ms
|
||||||
|
|
||||||
|
invoke-virtual {v1, v4, v5}, Landroid/animation/ValueAnimator;->setDuration(J)Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
new-instance v0, Landroid/view/animation/DecelerateInterpolator;
|
||||||
|
|
||||||
|
const/high16 v4, 0x3fc00000 # 1.5f
|
||||||
|
|
||||||
|
invoke-direct {v0, v4}, Landroid/view/animation/DecelerateInterpolator;-><init>(F)V
|
||||||
|
|
||||||
|
invoke-virtual {v1, v0}, Landroid/animation/ValueAnimator;->setInterpolator(Landroid/animation/TimeInterpolator;)V
|
||||||
|
|
||||||
|
new-instance v0, Lradiant/MiniPlayerTrackGestures$ResetAnimator;
|
||||||
|
|
||||||
|
invoke-direct {v0}, Lradiant/MiniPlayerTrackGestures$ResetAnimator;-><init>()V
|
||||||
|
|
||||||
|
invoke-virtual {v1, v0}, Landroid/animation/ValueAnimator;->addUpdateListener(Landroid/animation/ValueAnimator$AnimatorUpdateListener;)V
|
||||||
|
|
||||||
|
sput-object v1, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
invoke-virtual {v1}, Landroid/animation/ValueAnimator;->start()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
|
||||||
|
:snap_zero
|
||||||
|
invoke-static {v3}, Lradiant/MiniPlayerTrackGestures;->setDragOffsetDirect(F)V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static beginDrag(FF)V
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
sput p0, Lradiant/MiniPlayerTrackGestures;->e:F
|
||||||
|
|
||||||
|
sput p1, Lradiant/MiniPlayerTrackGestures;->f:F
|
||||||
|
|
||||||
|
const/4 v0, 0x1
|
||||||
|
|
||||||
|
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
if-eqz v0, :zero
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroid/animation/ValueAnimator;->cancel()V
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
sput-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
:zero
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
invoke-static {v0}, Lradiant/MiniPlayerTrackGestures;->setDragOffsetDirect(F)V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static cancelDrag()V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||||
|
|
||||||
|
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||||
|
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static clearResetAnimator(Landroid/animation/ValueAnimator;)V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
if-ne v0, p0, :done
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
sput-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
:done
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static finishDrag(FF)I
|
||||||
|
.locals 5
|
||||||
|
|
||||||
|
sget-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||||
|
|
||||||
|
if-nez v0, :active
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
return v0
|
||||||
|
|
||||||
|
:active
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||||
|
|
||||||
|
sget-boolean v1, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||||
|
|
||||||
|
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||||
|
|
||||||
|
sget v2, Lradiant/MiniPlayerTrackGestures;->e:F
|
||||||
|
|
||||||
|
sub-float/2addr p0, v2
|
||||||
|
|
||||||
|
sget v2, Lradiant/MiniPlayerTrackGestures;->f:F
|
||||||
|
|
||||||
|
sub-float/2addr p1, v2
|
||||||
|
|
||||||
|
if-eqz v1, :reset
|
||||||
|
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->suppressTapOpen()V
|
||||||
|
|
||||||
|
:reset
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||||
|
|
||||||
|
invoke-static {p0}, Ljava/lang/Math;->abs(F)F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
const/high16 v2, 0x42400000 # 48.0f -> at least 48dp horizontal swipe
|
||||||
|
|
||||||
|
invoke-static {v2}, Lradiant/MiniPlayerTrackGestures;->dp(F)F
|
||||||
|
|
||||||
|
move-result v2
|
||||||
|
|
||||||
|
cmpg-float v3, v1, v2
|
||||||
|
|
||||||
|
if-gez v3, :check_axis
|
||||||
|
|
||||||
|
return v0
|
||||||
|
|
||||||
|
:check_axis
|
||||||
|
invoke-static {p1}, Ljava/lang/Math;->abs(F)F
|
||||||
|
|
||||||
|
move-result p1
|
||||||
|
|
||||||
|
const/high16 v2, 0x3fc00000 # 1.5f -> horizontal distance must dominate vertical mov. by 1.5x
|
||||||
|
|
||||||
|
mul-float/2addr p1, v2
|
||||||
|
|
||||||
|
cmpg-float v1, v1, p1
|
||||||
|
|
||||||
|
if-lez v1, :no_swipe
|
||||||
|
|
||||||
|
const/4 p1, 0x0
|
||||||
|
|
||||||
|
cmpg-float p0, p0, p1
|
||||||
|
|
||||||
|
if-ltz p0, :next
|
||||||
|
|
||||||
|
const/4 v0, 0x1
|
||||||
|
|
||||||
|
return v0
|
||||||
|
|
||||||
|
:next
|
||||||
|
const/4 v0, -0x1
|
||||||
|
|
||||||
|
return v0
|
||||||
|
|
||||||
|
:no_swipe
|
||||||
|
return v0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static moveDrag(FF)V
|
||||||
|
.locals 6
|
||||||
|
|
||||||
|
sget-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||||
|
|
||||||
|
if-eqz v0, :done
|
||||||
|
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerGestures;->isAnyDragging()Z
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
if-eqz v0, :move_continue
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||||
|
|
||||||
|
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||||
|
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
:move_continue
|
||||||
|
sget v0, Lradiant/MiniPlayerTrackGestures;->e:F
|
||||||
|
|
||||||
|
sub-float/2addr p0, v0
|
||||||
|
|
||||||
|
sget v0, Lradiant/MiniPlayerTrackGestures;->f:F
|
||||||
|
|
||||||
|
sub-float/2addr p1, v0
|
||||||
|
|
||||||
|
invoke-static {p0}, Ljava/lang/Math;->abs(F)F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
invoke-static {p1}, Ljava/lang/Math;->abs(F)F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
sget-boolean v2, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||||
|
|
||||||
|
if-nez v2, :publish
|
||||||
|
|
||||||
|
const/high16 v2, 0x40c00000 # 6.0f dp dead zone
|
||||||
|
|
||||||
|
invoke-static {v2}, Lradiant/MiniPlayerTrackGestures;->dp(F)F
|
||||||
|
|
||||||
|
move-result v2
|
||||||
|
|
||||||
|
cmpg-float v3, v0, v2
|
||||||
|
|
||||||
|
if-gez v3, :move_axis
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
:move_axis
|
||||||
|
const v2, 0x3f8ccccd # 1.1f horizontal dominance while dragging
|
||||||
|
|
||||||
|
mul-float/2addr v1, v2
|
||||||
|
|
||||||
|
cmpg-float v2, v0, v1
|
||||||
|
|
||||||
|
if-gez v2, :accept
|
||||||
|
|
||||||
|
goto :done
|
||||||
|
|
||||||
|
:accept
|
||||||
|
const/4 v2, 0x1
|
||||||
|
|
||||||
|
sput-boolean v2, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||||
|
|
||||||
|
:publish
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->suppressTapOpen()V
|
||||||
|
|
||||||
|
invoke-static {p0}, Lradiant/MiniPlayerTrackGestures;->setDragOffset(F)V
|
||||||
|
|
||||||
|
:done
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private static dp(F)F
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
invoke-static {}, Landroid/content/res/Resources;->getSystem()Landroid/content/res/Resources;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroid/content/res/Resources;->getDisplayMetrics()Landroid/util/DisplayMetrics;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
iget v0, v0, Landroid/util/DisplayMetrics;->density:F
|
||||||
|
|
||||||
|
mul-float/2addr p0, v0
|
||||||
|
|
||||||
|
return p0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static feedbackModifier(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
const/high16 v0, 0x3f800000 # 1.0f
|
||||||
|
|
||||||
|
invoke-static {p0, v0}, Landroidx/compose/ui/ZIndexModifierKt;->zIndex(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier;
|
||||||
|
|
||||||
|
move-result-object p0
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures$OffsetLayer;->INSTANCE:Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||||
|
|
||||||
|
invoke-static {p0, v0}, Landroidx/compose/ui/graphics/GraphicsLayerModifierKt;->graphicsLayer(Landroidx/compose/ui/Modifier;Lyl0/l;)Landroidx/compose/ui/Modifier;
|
||||||
|
|
||||||
|
move-result-object p0
|
||||||
|
|
||||||
|
return-object p0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static textFeedbackModifier(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures$TextDraw;->INSTANCE:Lradiant/MiniPlayerTrackGestures$TextDraw; # text clip on right only
|
||||||
|
|
||||||
|
invoke-static {p0, v0}, Landroidx/compose/ui/draw/DrawModifierKt;->drawWithContent(Landroidx/compose/ui/Modifier;Lyl0/l;)Landroidx/compose/ui/Modifier;
|
||||||
|
|
||||||
|
move-result-object p0
|
||||||
|
|
||||||
|
return-object p0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static onRenderedItem(Ljava/lang/Object;)V
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures;->d:Ljava/lang/Object;
|
||||||
|
|
||||||
|
if-eqz v0, :store_initial
|
||||||
|
|
||||||
|
invoke-virtual {v0, p0}, Ljava/lang/Object;->equals(Ljava/lang/Object;)Z
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
if-nez v1, :done
|
||||||
|
|
||||||
|
sput-object p0, Lradiant/MiniPlayerTrackGestures;->d:Ljava/lang/Object;
|
||||||
|
|
||||||
|
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
|
||||||
|
:store_initial
|
||||||
|
sput-object p0, Lradiant/MiniPlayerTrackGestures;->d:Ljava/lang/Object;
|
||||||
|
|
||||||
|
:done
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static consumeTapOpenSuppression()Z
|
||||||
|
.locals 5
|
||||||
|
|
||||||
|
sget-wide v0, Lradiant/MiniPlayerTrackGestures;->a:J
|
||||||
|
|
||||||
|
const-wide/16 v2, 0x0
|
||||||
|
|
||||||
|
cmp-long v4, v0, v2
|
||||||
|
|
||||||
|
if-nez v4, :check_time
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
return v0
|
||||||
|
|
||||||
|
:check_time
|
||||||
|
sput-wide v2, Lradiant/MiniPlayerTrackGestures;->a:J
|
||||||
|
|
||||||
|
invoke-static {}, Landroid/os/SystemClock;->uptimeMillis()J
|
||||||
|
|
||||||
|
move-result-wide v2
|
||||||
|
|
||||||
|
cmp-long v4, v2, v0
|
||||||
|
|
||||||
|
if-lez v4, :yes
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
return v0
|
||||||
|
|
||||||
|
:yes
|
||||||
|
const/4 v0, 0x1
|
||||||
|
|
||||||
|
return v0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static suppressTapOpen()V
|
||||||
|
.locals 4
|
||||||
|
|
||||||
|
invoke-static {}, Landroid/os/SystemClock;->uptimeMillis()J
|
||||||
|
|
||||||
|
move-result-wide v0
|
||||||
|
|
||||||
|
const-wide/16 v2, 0x258
|
||||||
|
|
||||||
|
add-long/2addr v0, v2
|
||||||
|
|
||||||
|
sput-wide v0, Lradiant/MiniPlayerTrackGestures;->a:J
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static setDragOffset(F)V
|
||||||
|
.locals 5
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
if-eqz v0, :rubber_band
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroid/animation/ValueAnimator;->cancel()V
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
sput-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||||
|
|
||||||
|
:rubber_band
|
||||||
|
invoke-static {p0}, Ljava/lang/Math;->abs(F)F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
const/high16 v1, 0x42800000 # 64.0f dp free travel
|
||||||
|
|
||||||
|
invoke-static {v1}, Lradiant/MiniPlayerTrackGestures;->dp(F)F
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
cmpg-float v2, v0, v1
|
||||||
|
|
||||||
|
if-lez v2, :publish
|
||||||
|
|
||||||
|
sub-float/2addr v0, v1
|
||||||
|
|
||||||
|
const/high16 v2, 0x3e800000 # 0.25f resistance after 64dp
|
||||||
|
|
||||||
|
mul-float/2addr v0, v2
|
||||||
|
|
||||||
|
const v2, 0x3eb33333 # 0.35f max extra travel
|
||||||
|
|
||||||
|
mul-float/2addr v2, v1
|
||||||
|
|
||||||
|
invoke-static {v0, v2}, Ljava/lang/Math;->min(FF)F
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
add-float/2addr v0, v1
|
||||||
|
|
||||||
|
const/4 v1, 0x0
|
||||||
|
|
||||||
|
cmpg-float v2, p0, v1
|
||||||
|
|
||||||
|
if-gez v2, :positive
|
||||||
|
|
||||||
|
neg-float p0, v0
|
||||||
|
|
||||||
|
goto :publish
|
||||||
|
|
||||||
|
:positive
|
||||||
|
move p0, v0
|
||||||
|
|
||||||
|
|
||||||
|
:publish
|
||||||
|
invoke-static {p0}, Lradiant/MiniPlayerTrackGestures;->setDragOffsetDirect(F)V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static setDragOffsetDirect(F)V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||||
|
|
||||||
|
invoke-interface {v0, p0}, Landroidx/compose/runtime/MutableFloatState;->setFloatValue(F)V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static trackModifier(Landroidx/compose/ui/Modifier;Lyl0/l;)Landroidx/compose/ui/Modifier;
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
new-instance v0, Lradiant/MiniPlayerTrackGestures$Gesture;
|
||||||
|
|
||||||
|
invoke-direct {v0, p1}, Lradiant/MiniPlayerTrackGestures$Gesture;-><init>(Lyl0/l;)V
|
||||||
|
|
||||||
|
invoke-static {p0, v0}, Landroidx/compose/ui/input/pointer/PointerInteropFilter_androidKt;->motionEventSpy(Landroidx/compose/ui/Modifier;Lyl0/l;)Landroidx/compose/ui/Modifier;
|
||||||
|
|
||||||
|
move-result-object p0
|
||||||
|
|
||||||
|
return-object p0
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"patches": [
|
||||||
|
{
|
||||||
|
"id": "EnableLegacyUi",
|
||||||
|
"order": 10,
|
||||||
|
"fileNames": ["enable-legacy-ui.patch"],
|
||||||
|
"title": "Enable Legacy UI",
|
||||||
|
"description": "[This patch will stop working soon] - Replaces the New Compose based UI with the Legacy UI (disables player-market-ui feature flag)",
|
||||||
|
"default": false,
|
||||||
|
"requires": ["DebugMenuUnlock"],
|
||||||
|
"disables": [
|
||||||
|
"LyricsDisableCover",
|
||||||
|
"LyricsReplaceLyricsButton",
|
||||||
|
"LyricsReplaceShareButton",
|
||||||
|
"LyricsRlApi",
|
||||||
|
"LyricsKeepControlsVisible",
|
||||||
|
"PlayerBackdrop",
|
||||||
|
"QualityBadgeColors",
|
||||||
|
"LyricsProgressPill",
|
||||||
|
"CoverEverywhere",
|
||||||
|
"MiniPlayerRedesign"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "LyricsRlApi",
|
||||||
|
"order": 20,
|
||||||
|
"fileNames": ["lyrics-rl-api.patch", "lyrics-rl-api-observer.patch"],
|
||||||
|
"title": "Radiant Lyrics API - WIP",
|
||||||
|
"description": "Use Radiant Lyrics API to fetch Lyrics (Higher quality & More providers)",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "PlayerBackdrop",
|
||||||
|
"order": 30,
|
||||||
|
"fileNames": ["player-backdrop.patch"],
|
||||||
|
"title": "Player Backdrop",
|
||||||
|
"description": "Restores the legacy translucent backdrop blur behind the player.",
|
||||||
|
"default": true,
|
||||||
|
"advancedOptions": [
|
||||||
|
{
|
||||||
|
"type": "slider",
|
||||||
|
"key": "blur_strength",
|
||||||
|
"title": "Blur Strength",
|
||||||
|
"description": "How strongly the album art behind the player is blurred. 0% is sharp, 50% matches the original look.",
|
||||||
|
"default": 50,
|
||||||
|
"min": 0,
|
||||||
|
"max": 100,
|
||||||
|
"steps": 19,
|
||||||
|
"displayAsPercent": true,
|
||||||
|
"token": "RL_BLUR_BITS",
|
||||||
|
"encode": { "kind": "floatBits", "scale": 1.8 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "slider",
|
||||||
|
"key": "dimming",
|
||||||
|
"title": "Backdrop Dimming",
|
||||||
|
"description": "Darkens the backdrop so the foreground controls stay legible.",
|
||||||
|
"default": 50,
|
||||||
|
"min": 0,
|
||||||
|
"max": 100,
|
||||||
|
"steps": 19,
|
||||||
|
"displayAsPercent": true,
|
||||||
|
"token": "RL_SCRIM_ARGB",
|
||||||
|
"encode": { "kind": "argbAlpha" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CoverEverywhere",
|
||||||
|
"order": 35,
|
||||||
|
"fileNames": ["home-backdrop.patch", "collection-backdrop.patch", "cover-capture.patch"],
|
||||||
|
"title": "Cover Everywhere - WIP",
|
||||||
|
"description": "Applies the blurred backdrop to every Compose based page (Home & Collection)",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "QualityBadgeColors",
|
||||||
|
"order": 36,
|
||||||
|
"fileNames": ["player-quality-badge-colors.patch"],
|
||||||
|
"title": "Quality Badge Colors",
|
||||||
|
"description": "Brings color back to the player quality badge.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "PlayerOneHanded",
|
||||||
|
"order": 37,
|
||||||
|
"fileNames": ["player-one-handed.patch"],
|
||||||
|
"title": "One-Handed Mode",
|
||||||
|
"description": "Mirrors the bottom player buttons left-to-right for easier one-handed reach.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "LyricsProgressPill",
|
||||||
|
"order": 40,
|
||||||
|
"fileNames": ["lyrics-progress-pill.patch", "lyrics-fade-region.patch"],
|
||||||
|
"title": "Lyrics Progress Pill",
|
||||||
|
"description": "Restores the old Track Progress Pill in the top of the Lyrics screen!",
|
||||||
|
"default": true,
|
||||||
|
"requires": ["LyricsDisableCover", "LyricsReplaceLyricsButton", "LyricsReplaceShareButton"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "LyricsDisableCover",
|
||||||
|
"order": 41,
|
||||||
|
"fileNames": ["lyrics-disable-cover.patch"],
|
||||||
|
"title": "Disable Lyrics Cover",
|
||||||
|
"description": "Removes the mini track cover from the lyrics screen.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "LyricsReplaceLyricsButton",
|
||||||
|
"order": 42,
|
||||||
|
"fileNames": ["lyrics-replace-lyrics-button.patch", "lyrics-sparkle-conditional-visibility.patch"],
|
||||||
|
"title": "Replace Lyrics Button",
|
||||||
|
"description": "Replaces the Lyrics button with the RL Sparkle!",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "LyricsReplaceShareButton",
|
||||||
|
"order": 43,
|
||||||
|
"fileNames": ["lyrics-replace-share-button.patch"],
|
||||||
|
"title": "Replace Share Button",
|
||||||
|
"description": "Replaces the share button with the Tidal Connect button from the top of the screen. (it's in the menu already)",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "MiniPlayerRedesign",
|
||||||
|
"order": 50,
|
||||||
|
"fileNames": [],
|
||||||
|
"title": "Redesigned Mini-Player",
|
||||||
|
"description": "Integrates the Seekbar into the control panel at the bottom of the screen.",
|
||||||
|
"default": false,
|
||||||
|
"defaultVariantIndex": 2,
|
||||||
|
"variants": [
|
||||||
|
{ "title": "Floating", "fileNames": [] },
|
||||||
|
{ "title": "Grey", "fileNames": ["mini-player-grey.patch"] },
|
||||||
|
{ "title": "Black", "fileNames": ["mini-player-black.patch"] }
|
||||||
|
],
|
||||||
|
"advancedOptions": [
|
||||||
|
{
|
||||||
|
"type": "toggle",
|
||||||
|
"key": "dynamic_bg",
|
||||||
|
"title": "Mini-Player Dynamic Background",
|
||||||
|
"description": "Colorizes the Mini-Player depending on the currently playing track.",
|
||||||
|
"default": false,
|
||||||
|
"inline": true,
|
||||||
|
"fileNames": ["mini-player-dynamic-bg.patch"],
|
||||||
|
"extensionFiles": ["radiant/MiniPlayerBackground.smali"],
|
||||||
|
"hidesVariants": [2],
|
||||||
|
"relabelVariants": { "1": "Legacy" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "toggle",
|
||||||
|
"key": "border",
|
||||||
|
"title": "Progress Border",
|
||||||
|
"description": "Draws an animated progress ring around the floating mini-player. Requires the Floating style.",
|
||||||
|
"default": false,
|
||||||
|
"requiresVariant": 0,
|
||||||
|
"fileNames": ["mini-player-floating-border.patch"],
|
||||||
|
"extensionFiles": ["radiant/MiniSeekerFloating.smali"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "toggle",
|
||||||
|
"key": "gestures",
|
||||||
|
"title": "Mini-Player Gestures",
|
||||||
|
"description": "Swipe up on the mini-player to open the full player.",
|
||||||
|
"default": true,
|
||||||
|
"fileNames": ["mini-player-gestures.patch"],
|
||||||
|
"extensionFiles": [
|
||||||
|
"radiant/MiniPlayerGestures.smali",
|
||||||
|
"radiant/MiniPlayerGestures$Gesture.smali",
|
||||||
|
"radiant/MiniPlayerGestures$RootGesture.smali",
|
||||||
|
"radiant/MiniPlayerGestures$ApplyPending.smali"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "toggle",
|
||||||
|
"key": "next_prev",
|
||||||
|
"title": "Next/Previous Gestures",
|
||||||
|
"description": "Swipe left or right on the mini-player to skip tracks.",
|
||||||
|
"default": false,
|
||||||
|
"requiresOption": "gestures",
|
||||||
|
"fileNames": ["mini-player-gestures-left-right.patch"],
|
||||||
|
"extensionFiles": [
|
||||||
|
"radiant/MiniPlayerTrackGestures.smali",
|
||||||
|
"radiant/MiniPlayerTrackGestures$Gesture.smali",
|
||||||
|
"radiant/MiniPlayerTrackGestures$OffsetLayer.smali",
|
||||||
|
"radiant/MiniPlayerTrackGestures$ResetAnimator.smali",
|
||||||
|
"radiant/MiniPlayerTrackGestures$TextDraw.smali",
|
||||||
|
"com/tidal/android/feature/appscaffold/ui/q$c.smali"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "LyricsKeepControlsVisible",
|
||||||
|
"order": 60,
|
||||||
|
"fileNames": ["lyrics-keep-controls-visible.patch"],
|
||||||
|
"title": "Keep Controls Visible",
|
||||||
|
"description": "Inverts the auto-hide behavior on the lyrics screen, playback controls stay visible by default and only hide when you tap them off.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "DebugMenuUnlock",
|
||||||
|
"order": 100,
|
||||||
|
"fileNames": ["debug-menu-unlock.patch"],
|
||||||
|
"title": "Unlock Debug Menu",
|
||||||
|
"description": "Reveals TIDAL's hidden Debug Menu in Settings. (Unlocks Feature Flags & the legacy Debug Options)",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
--- a/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||||
|
+++ b/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||||
|
@@ -2819,4 +2819,10 @@
|
||||||
|
.line 147
|
||||||
|
+ const/4 v8, 0x0
|
||||||
|
+
|
||||||
|
+ invoke-static {v10, v11, v15, v8}, Lradiant/MiniPlayerBackground;->color(JLandroidx/compose/runtime/Composer;I)J # dynamic bg or fallback
|
||||||
|
+
|
||||||
|
+ move-result-wide v10
|
||||||
|
+
|
||||||
|
sget-object v8, Lcom/tidal/android/feature/appscaffold/ui/composable/a;->c:Landroidx/compose/foundation/shape/RoundedCornerShape;
|
||||||
|
|
||||||
|
.line 148
|
||||||
|
--- a/com/tidal/android/feature/playerscreen/ui/PlayerViewModel.smali
|
||||||
|
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerViewModel.smali
|
||||||
|
@@ -1047,9 +1047,13 @@
|
||||||
|
invoke-static {v2, v1, v4, v3}, Lkotlinx/coroutines/flow/FlowKt;->stateIn(Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/SharingStarted;Ljava/lang/Object;)Lkotlinx/coroutines/flow/StateFlow;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
iput-object v1, v0, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->U:Lkotlinx/coroutines/flow/StateFlow;
|
||||||
|
|
||||||
|
+ iget-object v1, v0, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->O:Lkotlin/h;
|
||||||
|
+
|
||||||
|
+ invoke-interface {v1}, Lkotlin/h;->getValue()Ljava/lang/Object; # init player bg color
|
||||||
|
+
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
@@ -1607,6 +1611,8 @@
|
||||||
|
:goto_6
|
||||||
|
invoke-interface {v9, v10}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
|
||||||
|
|
||||||
|
+ invoke-static {v10}, Lradiant/MiniPlayerBackground;->setHex(Ljava/lang/String;)V # store player bg hex
|
||||||
|
+
|
||||||
|
.line 263
|
||||||
|
.line 264
|
||||||
|
.line 265
|
||||||
|
@@ -2306,6 +2312,8 @@
|
||||||
|
.line 598
|
||||||
|
invoke-interface {v1, v5}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
|
||||||
|
|
||||||
|
+ invoke-static {v5}, Lradiant/MiniPlayerBackground;->setHex(Ljava/lang/String;)V # clear bg color
|
||||||
|
+
|
||||||
|
.line 599
|
||||||
|
.line 600
|
||||||
|
.line 601
|
||||||
|
@@ -2523,6 +2531,8 @@
|
||||||
|
.line 704
|
||||||
|
invoke-interface {v1, v5}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
|
||||||
|
|
||||||
|
+ invoke-static {v5}, Lradiant/MiniPlayerBackground;->setHex(Ljava/lang/String;)V # clear bg color
|
||||||
|
+
|
||||||
|
.line 705
|
||||||
|
.line 706
|
||||||
|
.line 707
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
--- a/com/tidal/android/feature/appscaffold/ui/composable/MiniPlayerKt.smali
|
||||||
|
+++ b/com/tidal/android/feature/appscaffold/ui/composable/MiniPlayerKt.smali
|
||||||
|
@@ -1746,6 +1746,10 @@
|
||||||
|
invoke-static {v9, v10, v15, v6, v8}, Landroidx/compose/foundation/layout/PaddingKt;->padding-qDBjuR0(Landroidx/compose/ui/Modifier;FFFF)Landroidx/compose/ui/Modifier;
|
||||||
|
|
||||||
|
move-result-object v6
|
||||||
|
+
|
||||||
|
+ invoke-static {v6, v1}, Lradiant/MiniPlayerTrackGestures;->trackModifier(Landroidx/compose/ui/Modifier;Lyl0/l;)Landroidx/compose/ui/Modifier; # attach horizontal swipe listener
|
||||||
|
+
|
||||||
|
+ move-result-object v6
|
||||||
|
|
||||||
|
.line 38
|
||||||
|
sget-object v21, Landroidx/compose/ui/Alignment;->Companion:Landroidx/compose/ui/Alignment$Companion;
|
||||||
|
@@ -1884,5 +1888,5 @@
|
||||||
|
.line 56
|
||||||
|
iget-object v6, v0, Lcom/tidal/android/feature/appscaffold/ui/s;->b:Lcom/tidal/android/feature/appscaffold/ui/p;
|
||||||
|
-
|
||||||
|
+ invoke-static {v6}, Lradiant/MiniPlayerTrackGestures;->onRenderedItem(Ljava/lang/Object;)V # reset stale drag offset if track metadata changes mid-gesture
|
||||||
|
.line 57
|
||||||
|
invoke-virtual/range {v16 .. v16}, Lcom/squareup/ui/market/core/theme/MarketStylesheet;->getBorderRadii()Lcom/squareup/ui/market/core/theme/MarketStylesheet$b;
|
||||||
|
@@ -1919,5 +1923,6 @@
|
||||||
|
invoke-static {v5, v4}, Landroidx/compose/foundation/layout/SizeKt;->size-3ABfNKs(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier;
|
||||||
|
-
|
||||||
|
move-result-object v4
|
||||||
|
-
|
||||||
|
+ invoke-static {v4}, Lradiant/MiniPlayerTrackGestures;->feedbackModifier(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier; # move the whole cover container with horizontal swipe
|
||||||
|
+ move-result-object v4
|
||||||
|
+
|
||||||
|
.line 63
|
||||||
|
@@ -2032,3 +2038,5 @@
|
||||||
|
move-result-object v4
|
||||||
|
-
|
||||||
|
+ invoke-static {v4}, Lradiant/MiniPlayerTrackGestures;->textFeedbackModifier(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier; # move title/artist
|
||||||
|
+ move-result-object v4
|
||||||
|
+
|
||||||
|
.line 72
|
||||||
|
--- a/com/tidal/android/feature/appscaffold/ui/MiniPlayerViewModel.smali
|
||||||
|
+++ b/com/tidal/android/feature/appscaffold/ui/MiniPlayerViewModel.smali
|
||||||
|
@@ -816,10 +816,10 @@
|
||||||
|
.line 111
|
||||||
|
.line 112
|
||||||
|
.line 113
|
||||||
|
- move-result p1
|
||||||
|
+ move-result v0
|
||||||
|
|
||||||
|
.line 114
|
||||||
|
- if-eqz p1, :cond_f
|
||||||
|
+ if-eqz v0, :cond_f
|
||||||
|
|
||||||
|
.line 115
|
||||||
|
.line 116
|
||||||
|
@@ -997,11 +997,32 @@
|
||||||
|
:cond_e
|
||||||
|
return-void
|
||||||
|
|
||||||
|
- .line 195
|
||||||
|
:cond_f
|
||||||
|
+ sget-object v0, Lcom/tidal/android/feature/appscaffold/ui/q$c;->a:Lcom/tidal/android/feature/appscaffold/ui/q$c;
|
||||||
|
+
|
||||||
|
+ invoke-virtual {p1, v0}, Ljava/lang/Object;->equals(Ljava/lang/Object;)Z
|
||||||
|
+
|
||||||
|
+ move-result p1
|
||||||
|
+
|
||||||
|
+ if-eqz p1, :unknown_event
|
||||||
|
+
|
||||||
|
+ invoke-interface {v3}, Lei/e;->canSkipToPreviousOrRewind()Z
|
||||||
|
+
|
||||||
|
+ move-result p1
|
||||||
|
+
|
||||||
|
+ if-eqz p1, :previous_done
|
||||||
|
+
|
||||||
|
+ const/4 p1, 0x0
|
||||||
|
+
|
||||||
|
+ invoke-interface {v3, p1}, Lei/e;->h(Z)V # false = previous/rewind
|
||||||
|
+
|
||||||
|
+ :previous_done
|
||||||
|
+ return-void
|
||||||
|
+
|
||||||
|
+ :unknown_event
|
||||||
|
invoke-static {}, Landroidx/compose/ui/graphics/y;->b()V
|
||||||
|
|
||||||
|
.line 196
|
||||||
|
.line 197
|
||||||
|
.line 198
|
||||||
|
return-void
|
||||||
|
--- a/com/tidal/android/feature/appscaffold/ui/composable/e.smali
|
||||||
|
+++ b/com/tidal/android/feature/appscaffold/ui/composable/e.smali
|
||||||
|
@@ -75,6 +75,18 @@
|
||||||
|
.line 22
|
||||||
|
:pswitch_0
|
||||||
|
+ invoke-static {}, Lradiant/MiniPlayerTrackGestures;->consumeTapOpenSuppression()Z # prevent tap open after horizontal swipe
|
||||||
|
+
|
||||||
|
+ move-result v0
|
||||||
|
+
|
||||||
|
+ if-eqz v0, :continue_open
|
||||||
|
+
|
||||||
|
+ sget-object v0, Lkotlin/u;->a:Lkotlin/u;
|
||||||
|
+
|
||||||
|
+ return-object v0
|
||||||
|
+
|
||||||
|
+ :continue_open
|
||||||
|
+
|
||||||
|
iget-object v0, p0, Lcom/tidal/android/feature/appscaffold/ui/composable/e;->b:Ljava/lang/Object;
|
||||||
|
|
||||||
|
.line 23
|
||||||
|
.line 24
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
--- a/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||||
|
+++ b/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||||
|
@@ -2832,27 +2832,5 @@
|
||||||
|
- .line 150
|
||||||
|
- sget-object v8, Lkotlin/u;->a:Lkotlin/u;
|
||||||
|
-
|
||||||
|
- .line 151
|
||||||
|
- invoke-interface {v15}, Landroidx/compose/runtime/Composer;->rememberedValue()Ljava/lang/Object;
|
||||||
|
-
|
||||||
|
- move-result-object v10
|
||||||
|
-
|
||||||
|
- .line 152
|
||||||
|
- invoke-virtual/range {v22 .. v22}, Landroidx/compose/runtime/Composer$Companion;->getEmpty()Ljava/lang/Object;
|
||||||
|
-
|
||||||
|
- move-result-object v11
|
||||||
|
-
|
||||||
|
- if-ne v10, v11, :cond_48
|
||||||
|
-
|
||||||
|
- .line 153
|
||||||
|
- sget-object v10, Lcom/tidal/android/feature/appscaffold/ui/composable/AppScaffoldKt$AppScaffold$3$2$1$1;->a:Lcom/tidal/android/feature/appscaffold/ui/composable/AppScaffoldKt$AppScaffold$3$2$1$1;
|
||||||
|
-
|
||||||
|
- invoke-interface {v15, v10}, Landroidx/compose/runtime/Composer;->updateRememberedValue(Ljava/lang/Object;)V
|
||||||
|
-
|
||||||
|
- .line 154
|
||||||
|
- :cond_48
|
||||||
|
- check-cast v10, Landroidx/compose/ui/input/pointer/PointerInputEventHandler;
|
||||||
|
-
|
||||||
|
- invoke-static {v0, v8, v10}, Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt;->pointerInput(Landroidx/compose/ui/Modifier;Ljava/lang/Object;Landroidx/compose/ui/input/pointer/PointerInputEventHandler;)Landroidx/compose/ui/Modifier;
|
||||||
|
+ move-object/from16 v8, v25 # SheetState from AppScaffold
|
||||||
|
+
|
||||||
|
+ invoke-static {v0, v6, v8}, Lradiant/MiniPlayerGestures;->modifier(Landroidx/compose/ui/Modifier;Lyl0/l;Landroidx/compose/material3/SheetState;)Landroidx/compose/ui/Modifier; # attach swipe up listener
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
@@ -2427,6 +2406,12 @@
|
||||||
|
move-object/from16 v16, v9
|
||||||
|
|
||||||
|
.line 99
|
||||||
|
+ move-object/from16 v9, v25 # same SheetState used by the modal sheet
|
||||||
|
+
|
||||||
|
+ invoke-static {v5, v6, v9}, Lradiant/MiniPlayerGestures;->rootModifier(Landroidx/compose/ui/Modifier;Lyl0/l;Landroidx/compose/material3/SheetState;)Landroidx/compose/ui/Modifier; # continue drag after player recomposition
|
||||||
|
+
|
||||||
|
+ move-result-object v5
|
||||||
|
+
|
||||||
|
invoke-static {v15, v5}, Landroidx/compose/ui/ComposedModifierKt;->materializeModifier(Landroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
|
||||||
|
|
||||||
|
move-result-object v9
|
||||||
|
|
||||||
|
--- a/com/tidal/android/feature/appscaffold/ui/composable/MiniPlayerKt.smali
|
||||||
|
+++ b/com/tidal/android/feature/appscaffold/ui/composable/MiniPlayerKt.smali
|
||||||
|
@@ -1243,5 +1243,21 @@
|
||||||
|
invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
|
||||||
|
|
||||||
|
iget-object v3, v0, Lcom/tidal/android/feature/appscaffold/ui/s;->c:Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$PlayState;
|
||||||
|
+
|
||||||
|
+ iget-object v5, v0, Lcom/tidal/android/feature/appscaffold/ui/s;->a:Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$ItemType; # currently rendered mini player media type
|
||||||
|
+
|
||||||
|
+ sget-object v6, Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$ItemType;->Unknown:Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$ItemType; # Unknown = no media playing. other media types : Track, Video.
|
||||||
|
+
|
||||||
|
+ if-ne v5, v6, :rl_has_media # used to prevent swipe up when nothing is playing
|
||||||
|
+
|
||||||
|
+ const/4 v5, 0x0
|
||||||
|
+
|
||||||
|
+ goto :rl_set_media
|
||||||
|
+
|
||||||
|
+ :rl_has_media
|
||||||
|
+ const/4 v5, 0x1
|
||||||
|
+
|
||||||
|
+ :rl_set_media
|
||||||
|
+ invoke-static {v5}, Lradiant/MiniPlayerGestures;->setHasMiniPlayerMedia(Z)V
|
||||||
|
|
||||||
|
invoke-virtual {v1}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
|
||||||
|
|
||||||
|
--- a/androidx/compose/material3/ModalBottomSheetKt$ModalBottomSheet$4$1.smali
|
||||||
|
+++ b/androidx/compose/material3/ModalBottomSheetKt$ModalBottomSheet$4$1.smali
|
||||||
|
@@ -210,8 +210,10 @@
|
||||||
|
.line 22
|
||||||
|
.line 23
|
||||||
|
.line 24
|
||||||
|
iget-object p1, p0, Landroidx/compose/material3/ModalBottomSheetKt$ModalBottomSheet$4$1;->$sheetState:Landroidx/compose/material3/SheetState;
|
||||||
|
-
|
||||||
|
+ invoke-static {p1}, Lradiant/MiniPlayerGestures;->suppressAutoShow(Landroidx/compose/material3/SheetState;)Z # avoid Material3 show() taking over manual drag
|
||||||
|
+ move-result v1
|
||||||
|
+ if-nez v1, :skip_show
|
||||||
|
.line 25
|
||||||
|
.line 26
|
||||||
|
iput v2, p0, Landroidx/compose/material3/ModalBottomSheetKt$ModalBottomSheet$4$1;->label:I
|
||||||
|
@@ -231,7 +233,7 @@
|
||||||
|
.line 33
|
||||||
|
.line 34
|
||||||
|
return-object v0
|
||||||
|
-
|
||||||
|
+ :skip_show
|
||||||
|
.line 35
|
||||||
|
:cond_2
|
||||||
|
:goto_0
|
||||||
|
--- a/androidx/compose/material3/ModalBottomSheetKt.smali
|
||||||
|
+++ b/androidx/compose/material3/ModalBottomSheetKt.smali
|
||||||
|
@@ -1370,3 +1370,6 @@
|
||||||
|
.line 34
|
||||||
|
:cond_47
|
||||||
|
+ move-object v14, v12
|
||||||
|
+ check-cast v14, Lyl0/l;
|
||||||
|
+ invoke-static {v13, v14}, Lradiant/MiniPlayerGestures;->setSettleCallback(Landroidx/compose/material3/SheetState;Lyl0/l;)V # reuse native settle animation on release
|
||||||
|
move-object/from16 v32, v12
|
||||||
|
--- a/androidx/compose/material3/SheetDefaultsKt.smali
|
||||||
|
+++ b/androidx/compose/material3/SheetDefaultsKt.smali
|
||||||
|
@@ -100,6 +100,5 @@
|
||||||
|
.line 16
|
||||||
|
- const/16 v3, 0x12c
|
||||||
|
-
|
||||||
|
+ const/16 v3, 0x64 # 100ms
|
||||||
|
.line 17
|
||||||
|
.line 18
|
||||||
|
const/4 v4, 0x0
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
+
|
+
|
||||||
+ move-result-object v5 # filled modifier
|
+ move-result-object v5 # filled modifier
|
||||||
+
|
+
|
||||||
+ const/high16 v7, 0x42b40000 # 90f blur dp
|
+ const v7, __RL_BLUR_BITS__ # blur dp bits (Advanced Options: Blur Strength)
|
||||||
+
|
+
|
||||||
+ invoke-static {v7}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F # to Dp
|
+ invoke-static {v7}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F # to Dp
|
||||||
+
|
+
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
+
|
+
|
||||||
+ move-result-object v3 # fullscreen modifier
|
+ move-result-object v3 # fullscreen modifier
|
||||||
+
|
+
|
||||||
+ const v6, -0x80000000 # 50% black ARGB
|
+ const v6, __RL_SCRIM_ARGB__ # scrim ARGB (Advanced Options: Backdrop Dimming)
|
||||||
+
|
+
|
||||||
+ invoke-static {v6}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack color long
|
+ invoke-static {v6}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack color long
|
||||||
+
|
+
|
||||||
|
|||||||
Reference in New Issue
Block a user