mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-08-26 22:17:44 +10:00
Merge pull request #63 from Se7en-dev/feat/waze-support-patch
[PATCH] Waze Integration
This commit is contained in:
+5
@@ -6,6 +6,7 @@ import com.meowarex.rlmobile.patcher.steps.StepGroup
|
|||||||
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.util.ManifestPatcher
|
import com.meowarex.rlmobile.patcher.util.ManifestPatcher
|
||||||
|
import com.meowarex.rlmobile.ui.screens.patchopts.KnownPatch
|
||||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
||||||
import com.github.diamondminer88.zip.ZipReader
|
import com.github.diamondminer88.zip.ZipReader
|
||||||
import com.github.diamondminer88.zip.ZipWriter
|
import com.github.diamondminer88.zip.ZipWriter
|
||||||
@@ -26,11 +27,15 @@ class PatchManifestStep(private val options: PatchOptions) : Step() {
|
|||||||
?: throw IllegalArgumentException("No manifest found in APK")
|
?: throw IllegalArgumentException("No manifest found in APK")
|
||||||
|
|
||||||
container.log("Patching manifest")
|
container.log("Patching manifest")
|
||||||
|
val enableWazeIntegration =
|
||||||
|
options.patchStates[KnownPatch.WazeIntegration.name] ?: KnownPatch.WazeIntegration.default.isEnabled
|
||||||
|
|
||||||
val patchedManifest = ManifestPatcher.patchManifest(
|
val patchedManifest = ManifestPatcher.patchManifest(
|
||||||
manifestBytes = manifest,
|
manifestBytes = manifest,
|
||||||
packageName = options.packageName,
|
packageName = options.packageName,
|
||||||
appName = options.appName,
|
appName = options.appName,
|
||||||
debuggable = options.debuggable,
|
debuggable = options.debuggable,
|
||||||
|
enableWazeIntegration = enableWazeIntegration,
|
||||||
)
|
)
|
||||||
|
|
||||||
container.log("Repacking apk with patched manifest")
|
container.log("Repacking apk with patched manifest")
|
||||||
|
|||||||
+13
-1
@@ -95,7 +95,19 @@ class SmaliPatchStep(
|
|||||||
val entry = zip.openEntry(patchFile)
|
val entry = zip.openEntry(patchFile)
|
||||||
?: throw FileNotFoundException("Missing zip entry: $patchFile")
|
?: throw FileNotFoundException("Missing zip entry: $patchFile")
|
||||||
out.canonicalFile.parentFile?.mkdirs()
|
out.canonicalFile.parentFile?.mkdirs()
|
||||||
out.writeBytes(entry.read())
|
var smaliText = entry.read()
|
||||||
|
.decodeToString()
|
||||||
|
.replace("\r\n", "\n")
|
||||||
|
for ((token, value) in substitutions) {
|
||||||
|
if (smaliText.contains(token)) {
|
||||||
|
smaliText = smaliText.replace(token, value)
|
||||||
|
container.log("Applied substitution $token -> $value in $patchFile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UNRESOLVED_TOKEN.find(smaliText)?.let { match ->
|
||||||
|
throw Error("Unresolved option placeholder ${match.value} in $patchFile")
|
||||||
|
}
|
||||||
|
out.writeText(smaliText)
|
||||||
container.log("Extracted extension smali: $relative")
|
container.log("Extracted extension smali: $relative")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,16 @@ object ManifestPatcher {
|
|||||||
private const val IS_SPLIT_REQUIRED = "isSplitRequired"
|
private const val IS_SPLIT_REQUIRED = "isSplitRequired"
|
||||||
private const val REQUIRED_SPLIT_TYPES = "requiredSplitTypes"
|
private const val REQUIRED_SPLIT_TYPES = "requiredSplitTypes"
|
||||||
private const val SPLIT_TYPES = "splitTypes"
|
private const val SPLIT_TYPES = "splitTypes"
|
||||||
|
private const val WAZE_PACKAGE = "com.waze"
|
||||||
|
private const val WAZE_INIT_RECEIVER = "radiant.WazeInitReceiver"
|
||||||
|
private const val WAZE_ACTION_INIT = "com.waze.sdk.audio.ACTION_INIT"
|
||||||
|
|
||||||
fun patchManifest(
|
fun patchManifest(
|
||||||
manifestBytes: ByteArray,
|
manifestBytes: ByteArray,
|
||||||
packageName: String,
|
packageName: String,
|
||||||
appName: String,
|
appName: String,
|
||||||
debuggable: Boolean,
|
debuggable: Boolean,
|
||||||
|
enableWazeIntegration: Boolean,
|
||||||
): ByteArray {
|
): ByteArray {
|
||||||
// Extract original package name to rewrite every reference to it
|
// Extract original package name to rewrite every reference to it
|
||||||
// (permissions, provider authorities) to the new packageName.
|
// (permissions, provider authorities) to the new packageName.
|
||||||
@@ -84,6 +88,49 @@ object ManifestPatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return when (name) {
|
return when (name) {
|
||||||
|
"queries" -> object : NodeVisitor(nv) {
|
||||||
|
private var hasWazePackage = false
|
||||||
|
|
||||||
|
override fun child(ns: String?, name: String): NodeVisitor {
|
||||||
|
val visitor = super.child(ns, name)
|
||||||
|
|
||||||
|
return if (enableWazeIntegration && name == "package") {
|
||||||
|
object : NodeVisitor(visitor) {
|
||||||
|
override fun attr(
|
||||||
|
ns: String?,
|
||||||
|
name: String?,
|
||||||
|
resourceId: Int,
|
||||||
|
type: Int,
|
||||||
|
value: Any?,
|
||||||
|
) {
|
||||||
|
if (name == "name" && value == WAZE_PACKAGE) {
|
||||||
|
hasWazePackage = true
|
||||||
|
}
|
||||||
|
super.attr(ns, name, resourceId, type, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
visitor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun end() {
|
||||||
|
if (enableWazeIntegration && !hasWazePackage) {
|
||||||
|
super.child(null, "package").apply {
|
||||||
|
attr(
|
||||||
|
ANDROID_NAMESPACE,
|
||||||
|
"name",
|
||||||
|
android.R.attr.name,
|
||||||
|
TYPE_STRING,
|
||||||
|
WAZE_PACKAGE,
|
||||||
|
)
|
||||||
|
end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"uses-permission" -> object : NodeVisitor(nv) {
|
"uses-permission" -> object : NodeVisitor(nv) {
|
||||||
override fun attr(ns: String?, name: String?, resourceId: Int, type: Int, value: Any?) {
|
override fun attr(ns: String?, name: String?, resourceId: Int, type: Int, value: Any?) {
|
||||||
if (name != "maxSdkVersion") {
|
if (name != "maxSdkVersion") {
|
||||||
@@ -132,6 +179,7 @@ object ManifestPatcher {
|
|||||||
private var addUseEmbeddedDex = true
|
private var addUseEmbeddedDex = true
|
||||||
private var addExtractNativeLibs = true
|
private var addExtractNativeLibs = true
|
||||||
private var addMetadata = true
|
private var addMetadata = true
|
||||||
|
private var hasWazeInitReceiver = false
|
||||||
|
|
||||||
override fun attr(ns: String?, name: String, resourceId: Int, type: Int, value: Any?) {
|
override fun attr(ns: String?, name: String, resourceId: Int, type: Int, value: Any?) {
|
||||||
if (name == REQUEST_LEGACY_EXTERNAL_STORAGE) addLegacyStorage = false
|
if (name == REQUEST_LEGACY_EXTERNAL_STORAGE) addLegacyStorage = false
|
||||||
@@ -161,6 +209,22 @@ object ManifestPatcher {
|
|||||||
|
|
||||||
return when (name) {
|
return when (name) {
|
||||||
"activity" -> ReplaceAttrsVisitor(visitor, mapOf("label" to appName))
|
"activity" -> ReplaceAttrsVisitor(visitor, mapOf("label" to appName))
|
||||||
|
|
||||||
|
"receiver" -> object : NodeVisitor(visitor) {
|
||||||
|
override fun attr(
|
||||||
|
ns: String?,
|
||||||
|
name: String,
|
||||||
|
resourceId: Int,
|
||||||
|
type: Int,
|
||||||
|
value: Any?
|
||||||
|
) {
|
||||||
|
if (name == "name" && value == WAZE_INIT_RECEIVER) {
|
||||||
|
hasWazeInitReceiver = true
|
||||||
|
}
|
||||||
|
super.attr(ns, name, resourceId, type, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"provider" -> object : NodeVisitor(visitor) {
|
"provider" -> object : NodeVisitor(visitor) {
|
||||||
override fun attr(
|
override fun attr(
|
||||||
ns: String?,
|
ns: String?,
|
||||||
@@ -219,6 +283,39 @@ object ManifestPatcher {
|
|||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (enableWazeIntegration && !hasWazeInitReceiver) {
|
||||||
|
super.child(null, "receiver").apply {
|
||||||
|
attr(
|
||||||
|
ANDROID_NAMESPACE,
|
||||||
|
"name",
|
||||||
|
android.R.attr.name,
|
||||||
|
TYPE_STRING,
|
||||||
|
WAZE_INIT_RECEIVER,
|
||||||
|
)
|
||||||
|
attr(
|
||||||
|
ANDROID_NAMESPACE,
|
||||||
|
"exported",
|
||||||
|
android.R.attr.exported,
|
||||||
|
TYPE_INT_BOOLEAN,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
child(null, "intent-filter").apply {
|
||||||
|
child(null, "action").apply {
|
||||||
|
attr(
|
||||||
|
ANDROID_NAMESPACE,
|
||||||
|
"name",
|
||||||
|
android.R.attr.name,
|
||||||
|
TYPE_STRING,
|
||||||
|
WAZE_ACTION_INIT,
|
||||||
|
)
|
||||||
|
end()
|
||||||
|
}
|
||||||
|
end()
|
||||||
|
}
|
||||||
|
end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.end()
|
super.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,39 @@ enum class KnownPatch(
|
|||||||
descRes = R.string.patch_debug_menu_unlock_desc,
|
descRes = R.string.patch_debug_menu_unlock_desc,
|
||||||
default = Disabled,
|
default = Disabled,
|
||||||
),
|
),
|
||||||
|
WazeIntegration(
|
||||||
|
order = 90,
|
||||||
|
fileNames = listOf("waze-media-browser.patch"),
|
||||||
|
extensionFiles = listOf(
|
||||||
|
"radiant/WazeInitReceiver.smali",
|
||||||
|
"radiant/WazeServiceConnection.smali",
|
||||||
|
),
|
||||||
|
titleRes = R.string.patch_waze_integration_title,
|
||||||
|
descRes = R.string.patch_waze_integration_desc,
|
||||||
|
default = Disabled,
|
||||||
|
advancedOptions = listOf(
|
||||||
|
PatchOption.Choice(
|
||||||
|
key = "browse_root",
|
||||||
|
titleRes = R.string.patch_waze_browse_root_title,
|
||||||
|
entries = listOf(
|
||||||
|
ChoiceEntry(R.string.patch_waze_root_auto, value = "ROOT_AUTO"),
|
||||||
|
ChoiceEntry(R.string.patch_waze_root_home, value = "HOME_V2::home_page_v2_id"),
|
||||||
|
ChoiceEntry(
|
||||||
|
R.string.patch_waze_root_recents,
|
||||||
|
value = "RECENTLY_PLAYED::home/pages/CONTINUE_LISTEN_TO/view-all",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
defaultIndex = 0,
|
||||||
|
token = "RL_WAZE_ROOT_ID",
|
||||||
|
),
|
||||||
|
PatchOption.Color(
|
||||||
|
key = "accent_color",
|
||||||
|
titleRes = R.string.patch_waze_accent_color_title,
|
||||||
|
default = -16747037, // Waze default #ff0075e3
|
||||||
|
token = "RL_WAZE_THEME_COLOR",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
LyricsProgressPill(
|
LyricsProgressPill(
|
||||||
order = 40,
|
order = 40,
|
||||||
fileNames = listOf(
|
fileNames = listOf(
|
||||||
|
|||||||
+9
-1
@@ -59,12 +59,20 @@ sealed interface PatchOption {
|
|||||||
data class Choice(
|
data class Choice(
|
||||||
override val key: String,
|
override val key: String,
|
||||||
@StringRes override val titleRes: Int,
|
@StringRes override val titleRes: Int,
|
||||||
@StringRes override val descRes: Int,
|
@StringRes override val descRes: Int = 0,
|
||||||
val entries: List<ChoiceEntry>,
|
val entries: List<ChoiceEntry>,
|
||||||
val defaultIndex: Int = 0,
|
val defaultIndex: Int = 0,
|
||||||
val requiresOption: String? = null,
|
val requiresOption: String? = null,
|
||||||
val token: String? = null,
|
val token: String? = null,
|
||||||
) : PatchOption
|
) : PatchOption
|
||||||
|
|
||||||
|
data class Color(
|
||||||
|
override val key: String,
|
||||||
|
@StringRes override val titleRes: Int,
|
||||||
|
@StringRes override val descRes: Int = 0,
|
||||||
|
val default: Int,
|
||||||
|
val token: String? = null,
|
||||||
|
) : PatchOption
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ChoiceEntry(
|
data class ChoiceEntry(
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ data class PatchOptions(
|
|||||||
(optionInts["${spec.id}/${option.key}"] ?: option.defaultIndex)
|
(optionInts["${spec.id}/${option.key}"] ?: option.defaultIndex)
|
||||||
.coerceIn(0, option.entries.lastIndex.coerceAtLeast(0))
|
.coerceIn(0, option.entries.lastIndex.coerceAtLeast(0))
|
||||||
|
|
||||||
|
fun colorValue(spec: PatchSpec, option: OptionSpec.Color): Int =
|
||||||
|
optionInts["${spec.id}/${option.key}"] ?: option.default
|
||||||
|
|
||||||
fun isToggleOn(spec: PatchSpec, option: OptionSpec.Toggle): Boolean =
|
fun isToggleOn(spec: PatchSpec, option: OptionSpec.Toggle): Boolean =
|
||||||
optionBools["${spec.id}/${option.key}"] ?: option.default
|
optionBools["${spec.id}/${option.key}"] ?: option.default
|
||||||
|
|
||||||
@@ -135,6 +138,10 @@ data class PatchOptions(
|
|||||||
val index = if (gatedOff) 0 else choiceIndex(spec, option)
|
val index = if (gatedOff) 0 else choiceIndex(spec, option)
|
||||||
put("__${token}__", option.values.getOrNull(index) ?: continue)
|
put("__${token}__", option.values.getOrNull(index) ?: continue)
|
||||||
}
|
}
|
||||||
|
is OptionSpec.Color -> {
|
||||||
|
val token = option.token ?: continue
|
||||||
|
put("__${token}__", colorValue(spec, option).toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -153,11 +153,19 @@ class PatchOptionsModel(
|
|||||||
optionInts = optionInts + (keyOf(spec, option) to index)
|
optionInts = optionInts + (keyOf(spec, option) to index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun colorValue(spec: PatchSpec, option: OptionSpec.Color): Int =
|
||||||
|
optionInts[keyOf(spec, option)] ?: option.default
|
||||||
|
|
||||||
|
fun setColorValue(spec: PatchSpec, option: OptionSpec.Color, value: Int) {
|
||||||
|
optionInts = optionInts + (keyOf(spec, option) to value)
|
||||||
|
}
|
||||||
|
|
||||||
fun isAdvancedModified(spec: PatchSpec): Boolean = spec.advancedOptions.any { option ->
|
fun isAdvancedModified(spec: PatchSpec): Boolean = spec.advancedOptions.any { option ->
|
||||||
when (option) {
|
when (option) {
|
||||||
is OptionSpec.Toggle -> toggleValue(spec, option) != option.default
|
is OptionSpec.Toggle -> toggleValue(spec, option) != option.default
|
||||||
is OptionSpec.Slider -> sliderValue(spec, option) != option.default
|
is OptionSpec.Slider -> sliderValue(spec, option) != option.default
|
||||||
is OptionSpec.Choice -> choiceValue(spec, option) != option.defaultIndex
|
is OptionSpec.Choice -> choiceValue(spec, option) != option.defaultIndex
|
||||||
|
is OptionSpec.Color -> colorValue(spec, option) != option.default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,6 +183,8 @@ class PatchOptionsModel(
|
|||||||
setSlider = ::setSliderValue,
|
setSlider = ::setSliderValue,
|
||||||
choice = ::choiceValue,
|
choice = ::choiceValue,
|
||||||
setChoice = ::setChoiceValue,
|
setChoice = ::setChoiceValue,
|
||||||
|
color = ::colorValue,
|
||||||
|
setColor = ::setColorValue,
|
||||||
isModified = ::isAdvancedModified,
|
isModified = ::isAdvancedModified,
|
||||||
reset = ::resetAdvanced,
|
reset = ::resetAdvanced,
|
||||||
)
|
)
|
||||||
|
|||||||
+25
-1
@@ -106,6 +106,18 @@ sealed interface OptionSpec {
|
|||||||
val requiresOption: String? = null,
|
val requiresOption: String? = null,
|
||||||
val token: String? = null,
|
val token: String? = null,
|
||||||
) : OptionSpec
|
) : OptionSpec
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@Serializable
|
||||||
|
@SerialName("color")
|
||||||
|
data class Color(
|
||||||
|
override val key: String,
|
||||||
|
override val title: String = "",
|
||||||
|
override val description: String = "",
|
||||||
|
val default: Int = 0,
|
||||||
|
/** Placeholder name (without the surrounding `__`) baked into the `.patch`/extension files. */
|
||||||
|
val token: String? = null,
|
||||||
|
) : OptionSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@@ -187,13 +199,21 @@ private fun PatchOption.toSpec(resolve: (Int) -> String): OptionSpec = when (thi
|
|||||||
is PatchOption.Choice -> OptionSpec.Choice(
|
is PatchOption.Choice -> OptionSpec.Choice(
|
||||||
key = key,
|
key = key,
|
||||||
title = resolve(titleRes),
|
title = resolve(titleRes),
|
||||||
description = resolve(descRes),
|
description = if (descRes != 0) resolve(descRes) else "",
|
||||||
entries = entries.map { resolve(it.labelRes) },
|
entries = entries.map { resolve(it.labelRes) },
|
||||||
defaultIndex = defaultIndex,
|
defaultIndex = defaultIndex,
|
||||||
values = entries.map { it.value ?: "" },
|
values = entries.map { it.value ?: "" },
|
||||||
requiresOption = requiresOption,
|
requiresOption = requiresOption,
|
||||||
token = token,
|
token = token,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is PatchOption.Color -> OptionSpec.Color(
|
||||||
|
key = key,
|
||||||
|
title = resolve(titleRes),
|
||||||
|
description = if (descRes != 0) resolve(descRes) else "",
|
||||||
|
default = default,
|
||||||
|
token = token,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -258,6 +278,8 @@ class PatchOptionState(
|
|||||||
val setSlider: (PatchSpec, OptionSpec.Slider, Float) -> Unit,
|
val setSlider: (PatchSpec, OptionSpec.Slider, Float) -> Unit,
|
||||||
val choice: (PatchSpec, OptionSpec.Choice) -> Int,
|
val choice: (PatchSpec, OptionSpec.Choice) -> Int,
|
||||||
val setChoice: (PatchSpec, OptionSpec.Choice, Int) -> Unit,
|
val setChoice: (PatchSpec, OptionSpec.Choice, Int) -> Unit,
|
||||||
|
val color: (PatchSpec, OptionSpec.Color) -> Int,
|
||||||
|
val setColor: (PatchSpec, OptionSpec.Color, Int) -> Unit,
|
||||||
val isModified: (PatchSpec) -> Boolean,
|
val isModified: (PatchSpec) -> Boolean,
|
||||||
val reset: (PatchSpec) -> Unit,
|
val reset: (PatchSpec) -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -270,6 +292,8 @@ class PatchOptionState(
|
|||||||
setSlider = { _, _, _ -> },
|
setSlider = { _, _, _ -> },
|
||||||
choice = { _, option -> option.defaultIndex },
|
choice = { _, option -> option.defaultIndex },
|
||||||
setChoice = { _, _, _ -> },
|
setChoice = { _, _, _ -> },
|
||||||
|
color = { _, option -> option.default },
|
||||||
|
setColor = { _, _, _ -> },
|
||||||
isModified = { false },
|
isModified = { false },
|
||||||
reset = {},
|
reset = {},
|
||||||
)
|
)
|
||||||
|
|||||||
+209
@@ -7,6 +7,8 @@ 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.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
@@ -15,11 +17,15 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.fromHtml
|
import androidx.compose.ui.text.fromHtml
|
||||||
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
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 androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -196,6 +202,12 @@ fun PatchAdvancedOptionsSheet(
|
|||||||
onSelect = { state.setChoice(patch, option, it) },
|
onSelect = { state.setChoice(patch, option, it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is OptionSpec.Color -> ColorOptionRow(
|
||||||
|
title = option.title,
|
||||||
|
color = state.color(patch, option),
|
||||||
|
onColorChange = { state.setColor(patch, option, it) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,6 +390,39 @@ private fun ChoiceOptionRow(
|
|||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (entries.size > 3) {
|
||||||
|
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||||
|
val selectedLabel = entries.getOrNull(selectedIndex).orEmpty()
|
||||||
|
|
||||||
|
Box(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = { expanded = true },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = selectedLabel,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { expanded = false },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
entries.forEachIndexed { index, entry ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(entry) },
|
||||||
|
onClick = {
|
||||||
|
onSelect(index)
|
||||||
|
expanded = false
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@Column
|
||||||
|
}
|
||||||
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
||||||
entries.forEachIndexed { index, entry ->
|
entries.forEachIndexed { index, entry ->
|
||||||
SegmentedButton(
|
SegmentedButton(
|
||||||
@@ -400,6 +445,150 @@ private fun ChoiceOptionRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ColorOptionRow(
|
||||||
|
title: String,
|
||||||
|
color: Int,
|
||||||
|
onColorChange: (Int) -> Unit,
|
||||||
|
) {
|
||||||
|
var showPicker by rememberSaveable { mutableStateOf(false) }
|
||||||
|
val selectedColor = color.withOpaqueAlpha()
|
||||||
|
val materialYouColor = MaterialTheme.colorScheme.primary.toArgb().withOpaqueAlpha()
|
||||||
|
val presets = listOf(
|
||||||
|
stringResource(R.string.patch_waze_color_waze_default),
|
||||||
|
stringResource(R.string.patch_waze_color_tidal_cyan),
|
||||||
|
stringResource(R.string.patch_waze_color_material_you),
|
||||||
|
)
|
||||||
|
val presetColors = listOf(WAZE_DEFAULT, TIDAL_CYAN, materialYouColor)
|
||||||
|
val selectedPreset = presetColors.indexOf(selectedColor)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(MaterialTheme.shapes.medium)
|
||||||
|
.clickable { showPicker = true }
|
||||||
|
.padding(vertical = 4.dp),
|
||||||
|
) {
|
||||||
|
ColorSwatch(color = selectedColor)
|
||||||
|
Text(
|
||||||
|
text = selectedColor.toRgbHex(),
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
presets.forEachIndexed { index, label ->
|
||||||
|
SegmentedButton(
|
||||||
|
selected = index == selectedPreset,
|
||||||
|
onClick = { onColorChange(presetColors[index]) },
|
||||||
|
shape = SegmentedButtonDefaults.itemShape(index = index, count = presets.size),
|
||||||
|
icon = {},
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
text = label,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showPicker) {
|
||||||
|
ColorPickerDialog(
|
||||||
|
initialColor = selectedColor,
|
||||||
|
onDismiss = { showPicker = false },
|
||||||
|
onColorSelected = {
|
||||||
|
onColorChange(it)
|
||||||
|
showPicker = false
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ColorSwatch(color: Int, modifier: Modifier = Modifier) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.size(32.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(Color(color))
|
||||||
|
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, CircleShape),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ColorPickerDialog(
|
||||||
|
initialColor: Int,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onColorSelected: (Int) -> Unit,
|
||||||
|
) {
|
||||||
|
var color by rememberSaveable(initialColor) { mutableIntStateOf(initialColor.withOpaqueAlpha()) }
|
||||||
|
var hex by rememberSaveable(initialColor) { mutableStateOf(color.toRgbHex()) }
|
||||||
|
val parsedHex = parseRgbHex(hex)
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
title = { Text(stringResource(R.string.patch_color_picker_title)) },
|
||||||
|
text = {
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
) {
|
||||||
|
ColorSwatch(color = parsedHex ?: color, modifier = Modifier.size(40.dp))
|
||||||
|
OutlinedTextField(
|
||||||
|
value = hex,
|
||||||
|
onValueChange = { value ->
|
||||||
|
hex = value
|
||||||
|
parseRgbHex(value)?.let { color = it }
|
||||||
|
},
|
||||||
|
label = { Text(stringResource(R.string.patch_color_picker_hex)) },
|
||||||
|
singleLine = true,
|
||||||
|
isError = parsedHex == null,
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
capitalization = KeyboardCapitalization.Characters,
|
||||||
|
keyboardType = KeyboardType.Ascii,
|
||||||
|
),
|
||||||
|
supportingText = if (parsedHex == null) {
|
||||||
|
{ Text(stringResource(R.string.patch_color_picker_hex_error)) }
|
||||||
|
} else null,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
enabled = parsedHex != null,
|
||||||
|
onClick = { parsedHex?.let(onColorSelected) },
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.action_done))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text(stringResource(R.string.action_cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun OptionText(
|
private fun OptionText(
|
||||||
title: String,
|
title: String,
|
||||||
@@ -432,6 +621,26 @@ private fun formatSliderValue(option: OptionSpec.Slider, value: Float): String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val OPAQUE_ALPHA = -0x1000000
|
||||||
|
private const val WAZE_DEFAULT = -16747037 // #ff0075e3
|
||||||
|
private const val TIDAL_CYAN = -14549268 // #ff21feec
|
||||||
|
|
||||||
|
private fun Int.withOpaqueAlpha(): Int = (this and 0x00FFFFFF) or OPAQUE_ALPHA
|
||||||
|
|
||||||
|
private fun Int.toRgbHex(): String = "#" +
|
||||||
|
(this and 0x00FFFFFF).toString(16).uppercase().padStart(6, '0')
|
||||||
|
|
||||||
|
private fun parseRgbHex(value: String): Int? {
|
||||||
|
val clean = value.trim()
|
||||||
|
.removePrefix("#")
|
||||||
|
.removePrefix("0x")
|
||||||
|
.removePrefix("0X")
|
||||||
|
if (clean.length != 6 && clean.length != 8) return null
|
||||||
|
val parsed = clean.toLongOrNull(16) ?: return null
|
||||||
|
val rgb = if (clean.length == 8) parsed and 0x00FFFFFFL else parsed
|
||||||
|
return (0xFF000000L or rgb).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun OptionLockDialog(lock: OptionLock, onDismiss: () -> Unit) {
|
private fun OptionLockDialog(lock: OptionLock, onDismiss: () -> Unit) {
|
||||||
|
|||||||
@@ -286,6 +286,19 @@
|
|||||||
<string
|
<string
|
||||||
name="patch_debug_menu_unlock_desc"
|
name="patch_debug_menu_unlock_desc"
|
||||||
>Reveals TIDAL\'s hidden Debug Menu in Settings. (Unlocks Feature Flags & the legacy Debug Options)</string>
|
>Reveals TIDAL\'s hidden Debug Menu in Settings. (Unlocks Feature Flags & the legacy Debug Options)</string>
|
||||||
|
<string name="patch_waze_integration_title">Waze Integration</string>
|
||||||
|
<string name="patch_waze_integration_desc">Control TIDAL playback from the Waze app</string>
|
||||||
|
<string name="patch_waze_browse_root_title">Media Menu</string>
|
||||||
|
<string name="patch_waze_accent_color_title">Accent Color</string>
|
||||||
|
<string name="patch_waze_color_waze_default">Waze Default</string>
|
||||||
|
<string name="patch_waze_color_tidal_cyan">TIDAL Cyan</string>
|
||||||
|
<string name="patch_waze_color_material_you">Material You</string>
|
||||||
|
<string name="patch_waze_root_auto">Auto Menu</string>
|
||||||
|
<string name="patch_waze_root_home">Home</string>
|
||||||
|
<string name="patch_waze_root_recents">Recents</string>
|
||||||
|
<string name="patch_color_picker_title">Custom color</string>
|
||||||
|
<string name="patch_color_picker_hex">Hex color</string>
|
||||||
|
<string name="patch_color_picker_hex_error">Use #RRGGBB or #AARRGGBB.</string>
|
||||||
<string name="patch_enable_legacy_ui_title">Enable Legacy UI</string>
|
<string name="patch_enable_legacy_ui_title">Enable Legacy UI</string>
|
||||||
<string
|
<string
|
||||||
name="patch_enable_legacy_ui_desc"
|
name="patch_enable_legacy_ui_desc"
|
||||||
|
|||||||
+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.14"
|
"patchesVersion": "0.9.15"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
.class public final Lradiant/WazeInitReceiver;
|
||||||
|
.super Landroid/content/BroadcastReceiver;
|
||||||
|
.source "WazeInitReceiver.smali"
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method public constructor <init>()V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Landroid/content/BroadcastReceiver;-><init>()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
.method public onReceive(Landroid/content/Context;Landroid/content/Intent;)V
|
||||||
|
.locals 5
|
||||||
|
|
||||||
|
if-eqz p1, :return
|
||||||
|
|
||||||
|
if-eqz p2, :return
|
||||||
|
|
||||||
|
const-string v0, "com.waze.sdk.audio.ACTION_INIT"
|
||||||
|
|
||||||
|
invoke-virtual {p2}, Landroid/content/Intent;->getAction()Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
|
||||||
|
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
if-eqz v0, :return
|
||||||
|
|
||||||
|
const-string v0, "token"
|
||||||
|
|
||||||
|
invoke-virtual {p2, v0}, Landroid/content/Intent;->getStringExtra(Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
if-eqz v0, :return
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Ljava/lang/String;->length()I
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
if-lez v1, :return
|
||||||
|
|
||||||
|
invoke-virtual {p1}, Landroid/content/Context;->getApplicationContext()Landroid/content/Context;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
if-eqz v1, :context_ready
|
||||||
|
|
||||||
|
move-object p1, v1
|
||||||
|
|
||||||
|
:context_ready
|
||||||
|
new-instance v1, Landroid/content/Intent;
|
||||||
|
|
||||||
|
invoke-direct {v1}, Landroid/content/Intent;-><init>()V
|
||||||
|
|
||||||
|
const-string v2, "com.waze"
|
||||||
|
|
||||||
|
const-string v3, "com.waze.sdk.SdkService"
|
||||||
|
|
||||||
|
invoke-virtual {v1, v2, v3}, Landroid/content/Intent;->setClassName(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
|
||||||
|
|
||||||
|
new-instance v2, Lradiant/WazeServiceConnection;
|
||||||
|
|
||||||
|
invoke-direct {v2, p1, v0}, Lradiant/WazeServiceConnection;-><init>(Landroid/content/Context;Ljava/lang/String;)V
|
||||||
|
|
||||||
|
:try_start_0
|
||||||
|
const/4 v3, 0x1
|
||||||
|
|
||||||
|
invoke-virtual {p1, v1, v2, v3}, Landroid/content/Context;->bindService(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z
|
||||||
|
:try_end_0
|
||||||
|
.catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0
|
||||||
|
|
||||||
|
goto :return
|
||||||
|
|
||||||
|
:catch_0
|
||||||
|
move-exception v4
|
||||||
|
|
||||||
|
:return
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
.class public final Lradiant/WazeServiceConnection;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.source "WazeServiceConnection.smali"
|
||||||
|
|
||||||
|
# interfaces
|
||||||
|
.implements Landroid/content/ServiceConnection;
|
||||||
|
|
||||||
|
|
||||||
|
# instance fields
|
||||||
|
.field private final context:Landroid/content/Context;
|
||||||
|
|
||||||
|
.field private final token:Ljava/lang/String;
|
||||||
|
|
||||||
|
|
||||||
|
# direct methods
|
||||||
|
.method public constructor <init>(Landroid/content/Context;Ljava/lang/String;)V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
iput-object p1, p0, Lradiant/WazeServiceConnection;->context:Landroid/content/Context;
|
||||||
|
|
||||||
|
iput-object p2, p0, Lradiant/WazeServiceConnection;->token:Ljava/lang/String;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private addOpenMeIntent(Landroid/os/Bundle;)V
|
||||||
|
.locals 4
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/WazeServiceConnection;->context:Landroid/content/Context;
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroid/content/Context;->getPackageManager()Landroid/content/pm/PackageManager;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroid/content/Context;->getPackageName()Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
invoke-virtual {v1, v2}, Landroid/content/pm/PackageManager;->getLaunchIntentForPackage(Ljava/lang/String;)Landroid/content/Intent;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
if-eqz v1, :return
|
||||||
|
|
||||||
|
const/high16 v2, 0x10000000
|
||||||
|
|
||||||
|
invoke-virtual {v1, v2}, Landroid/content/Intent;->addFlags(I)Landroid/content/Intent;
|
||||||
|
|
||||||
|
const v2, 0xc000000
|
||||||
|
|
||||||
|
const/4 v3, 0x0
|
||||||
|
|
||||||
|
invoke-static {v0, v3, v1, v2}, Landroid/app/PendingIntent;->getActivity(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
const-string v2, "openMeIntent"
|
||||||
|
|
||||||
|
invoke-virtual {p1, v2, v1}, Landroid/os/Bundle;->putParcelable(Ljava/lang/String;Landroid/os/Parcelable;)V
|
||||||
|
|
||||||
|
:return
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private createClientMessenger()Landroid/os/Messenger;
|
||||||
|
.locals 3
|
||||||
|
|
||||||
|
new-instance v0, Landroid/os/Handler;
|
||||||
|
|
||||||
|
invoke-static {}, Landroid/os/Looper;->getMainLooper()Landroid/os/Looper;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
invoke-direct {v0, v1}, Landroid/os/Handler;-><init>(Landroid/os/Looper;)V
|
||||||
|
|
||||||
|
new-instance v2, Landroid/os/Messenger;
|
||||||
|
|
||||||
|
invoke-direct {v2, v0}, Landroid/os/Messenger;-><init>(Landroid/os/Handler;)V
|
||||||
|
|
||||||
|
return-object v2
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private createConfigBundle()Landroid/os/Bundle;
|
||||||
|
.locals 3
|
||||||
|
|
||||||
|
new-instance v0, Landroid/os/Bundle;
|
||||||
|
|
||||||
|
invoke-direct {v0}, Landroid/os/Bundle;-><init>()V
|
||||||
|
|
||||||
|
const-string v1, "versionCode"
|
||||||
|
|
||||||
|
const/16 v2, 0xa
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1, v2}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V
|
||||||
|
|
||||||
|
const-string v1, "themeColor"
|
||||||
|
|
||||||
|
const v2, __RL_WAZE_THEME_COLOR__ # Waze SDK themeColor
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1, v2}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V
|
||||||
|
|
||||||
|
invoke-direct {p0, v0}, Lradiant/WazeServiceConnection;->addOpenMeIntent(Landroid/os/Bundle;)V
|
||||||
|
|
||||||
|
return-object v0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private register(Landroid/os/IBinder;)V
|
||||||
|
.locals 5
|
||||||
|
|
||||||
|
if-eqz p1, :return
|
||||||
|
|
||||||
|
invoke-static {}, Landroid/os/Parcel;->obtain()Landroid/os/Parcel;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
invoke-static {}, Landroid/os/Parcel;->obtain()Landroid/os/Parcel;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
const-string v2, "com.waze.sdk.ISdkService"
|
||||||
|
|
||||||
|
invoke-virtual {v0, v2}, Landroid/os/Parcel;->writeInterfaceToken(Ljava/lang/String;)V
|
||||||
|
|
||||||
|
iget-object v2, p0, Lradiant/WazeServiceConnection;->token:Ljava/lang/String;
|
||||||
|
|
||||||
|
invoke-virtual {v0, v2}, Landroid/os/Parcel;->writeString(Ljava/lang/String;)V
|
||||||
|
|
||||||
|
invoke-direct {p0}, Lradiant/WazeServiceConnection;->createConfigBundle()Landroid/os/Bundle;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
invoke-static {v0, v2}, Lradiant/WazeServiceConnection;->writeParcelable(Landroid/os/Parcel;Landroid/os/Parcelable;)V
|
||||||
|
|
||||||
|
invoke-direct {p0}, Lradiant/WazeServiceConnection;->createClientMessenger()Landroid/os/Messenger;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
invoke-static {v0, v2}, Lradiant/WazeServiceConnection;->writeParcelable(Landroid/os/Parcel;Landroid/os/Parcelable;)V
|
||||||
|
|
||||||
|
const/4 v2, 0x1
|
||||||
|
|
||||||
|
const/4 v3, 0x0
|
||||||
|
|
||||||
|
invoke-interface {p1, v2, v0, v1, v3}, Landroid/os/IBinder;->transact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
|
||||||
|
|
||||||
|
move-result v4
|
||||||
|
|
||||||
|
if-eqz v4, :cleanup
|
||||||
|
|
||||||
|
invoke-virtual {v1}, Landroid/os/Parcel;->readException()V
|
||||||
|
|
||||||
|
:cleanup
|
||||||
|
invoke-virtual {v1}, Landroid/os/Parcel;->recycle()V
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Landroid/os/Parcel;->recycle()V
|
||||||
|
|
||||||
|
:return
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private safeUnbind()V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
:try_start_0
|
||||||
|
iget-object v0, p0, Lradiant/WazeServiceConnection;->context:Landroid/content/Context;
|
||||||
|
|
||||||
|
invoke-virtual {v0, p0}, Landroid/content/Context;->unbindService(Landroid/content/ServiceConnection;)V
|
||||||
|
:try_end_0
|
||||||
|
.catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0
|
||||||
|
|
||||||
|
goto :return
|
||||||
|
|
||||||
|
:catch_0
|
||||||
|
move-exception v0
|
||||||
|
|
||||||
|
:return
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private static writeParcelable(Landroid/os/Parcel;Landroid/os/Parcelable;)V
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
if-eqz p1, :null_value
|
||||||
|
|
||||||
|
const/4 v1, 0x1
|
||||||
|
|
||||||
|
invoke-virtual {p0, v1}, Landroid/os/Parcel;->writeInt(I)V
|
||||||
|
|
||||||
|
invoke-interface {p1, p0, v0}, Landroid/os/Parcelable;->writeToParcel(Landroid/os/Parcel;I)V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
|
||||||
|
:null_value
|
||||||
|
invoke-virtual {p0, v0}, Landroid/os/Parcel;->writeInt(I)V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
.method public onServiceConnected(Landroid/content/ComponentName;Landroid/os/IBinder;)V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
:try_start_0
|
||||||
|
invoke-direct {p0, p2}, Lradiant/WazeServiceConnection;->register(Landroid/os/IBinder;)V
|
||||||
|
:try_end_0
|
||||||
|
.catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0
|
||||||
|
|
||||||
|
goto :unbind
|
||||||
|
|
||||||
|
:catch_0
|
||||||
|
move-exception v0
|
||||||
|
|
||||||
|
:unbind
|
||||||
|
invoke-direct {p0}, Lradiant/WazeServiceConnection;->safeUnbind()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public onServiceDisconnected(Landroid/content/ComponentName;)V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
@@ -212,6 +212,37 @@
|
|||||||
"description": "Inverts the auto-hide behavior on the lyrics screen, playback controls stay visible by default and only hide when you tap them off.",
|
"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
|
"default": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "WazeIntegration",
|
||||||
|
"order": 90,
|
||||||
|
"fileNames": ["waze-media-browser.patch"],
|
||||||
|
"extensionFiles": ["radiant/WazeInitReceiver.smali", "radiant/WazeServiceConnection.smali"],
|
||||||
|
"title": "Waze Integration",
|
||||||
|
"description": "Control TIDAL playback from the Waze app",
|
||||||
|
"default": true,
|
||||||
|
"advancedOptions": [
|
||||||
|
{
|
||||||
|
"type": "choice",
|
||||||
|
"key": "browse_root",
|
||||||
|
"title": "Media Menu",
|
||||||
|
"entries": ["Auto Menu", "Home", "Recents"],
|
||||||
|
"values": [
|
||||||
|
"ROOT_AUTO",
|
||||||
|
"HOME_V2::home_page_v2_id",
|
||||||
|
"RECENTLY_PLAYED::home/pages/CONTINUE_LISTEN_TO/view-all"
|
||||||
|
],
|
||||||
|
"defaultIndex": 0,
|
||||||
|
"token": "RL_WAZE_ROOT_ID"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "color",
|
||||||
|
"key": "accent_color",
|
||||||
|
"title": "Accent Color",
|
||||||
|
"default": -16747037,
|
||||||
|
"token": "RL_WAZE_THEME_COLOR"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "DebugMenuUnlock",
|
"id": "DebugMenuUnlock",
|
||||||
"order": 100,
|
"order": 100,
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
--- a/pb/c.smali
|
||||||
|
+++ b/pb/c.smali
|
||||||
|
@@ -801,6 +801,26 @@
|
||||||
|
-
|
||||||
|
+
|
||||||
|
.line 236
|
||||||
|
:goto_8
|
||||||
|
+ const-string/jumbo v5, "com.waze"
|
||||||
|
+
|
||||||
|
+ invoke-static {v3, v5}, Lkotlin/jvm/internal/n;->b(Ljava/lang/Object;Ljava/lang/Object;)Z # package match
|
||||||
|
+
|
||||||
|
+ move-result v5
|
||||||
|
+
|
||||||
|
+ if-eqz v5, :rl_waze_done
|
||||||
|
+
|
||||||
|
+ const-string/jumbo v5, "03:63:7f:6c:5d:8f:60:4e:6f:db:79:a6:ff:bf:a5:78:de:4e:31:8f:8d:a2:2f:c6:10:66:65:24:7f:88:07:d7" # Waze cert
|
||||||
|
+
|
||||||
|
+ invoke-static {v2, v5}, Lkotlin/jvm/internal/n;->b(Ljava/lang/Object;Ljava/lang/Object;)Z # cert match
|
||||||
|
+
|
||||||
|
+ move-result v5
|
||||||
|
+
|
||||||
|
+ if-eqz v5, :rl_waze_done
|
||||||
|
+
|
||||||
|
+ goto :goto_9 # trust Waze
|
||||||
|
+
|
||||||
|
+ :rl_waze_done
|
||||||
|
+
|
||||||
|
invoke-static {}, Landroid/os/Process;->myUid()I
|
||||||
|
-
|
||||||
|
+
|
||||||
|
.line 237
|
||||||
|
@@ -957,6 +977,26 @@
|
||||||
|
:goto_b
|
||||||
|
if-eqz v2, :cond_14
|
||||||
|
-
|
||||||
|
+
|
||||||
|
+ const-string/jumbo v1, "com.waze"
|
||||||
|
+
|
||||||
|
+ invoke-static {v3, v1}, Lkotlin/jvm/internal/n;->b(Ljava/lang/Object;Ljava/lang/Object;)Z # package match
|
||||||
|
+
|
||||||
|
+ move-result v1
|
||||||
|
+
|
||||||
|
+ if-eqz v1, :rl_waze_recent_root_done_2
|
||||||
|
+
|
||||||
|
+ new-instance v1, Landroidx/media/MediaBrowserServiceCompat$BrowserRoot;
|
||||||
|
+
|
||||||
|
+ const-string/jumbo v4, "__RL_WAZE_ROOT_ID__"
|
||||||
|
+
|
||||||
|
+ const/4 v5, 0x0
|
||||||
|
+
|
||||||
|
+ invoke-direct {v1, v4, v5}, Landroidx/media/MediaBrowserServiceCompat$BrowserRoot;-><init>(Ljava/lang/String;Landroid/os/Bundle;)V
|
||||||
|
+
|
||||||
|
+ return-object v1
|
||||||
|
+
|
||||||
|
+ :rl_waze_recent_root_done_2
|
||||||
|
+
|
||||||
|
.line 306
|
||||||
|
.line 307
|
||||||
|
iget-object v1, v0, Lpb/c;->c:Ljava/util/Map;
|
||||||
Reference in New Issue
Block a user