2023-09-20 05:33:02 +02:00
|
|
|
package app.revanced.patches.twitch.misc.settings
|
2022-11-28 23:50:04 +01:00
|
|
|
|
|
|
|
import app.revanced.patcher.data.BytecodeContext
|
2023-06-07 03:46:13 +02:00
|
|
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
|
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
|
|
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
2023-05-06 23:10:01 +02:00
|
|
|
import app.revanced.patcher.extensions.or
|
2023-10-22 23:31:50 +02:00
|
|
|
import app.revanced.patcher.fingerprint.MethodFingerprintResult
|
2022-11-28 23:50:04 +01:00
|
|
|
import app.revanced.patcher.patch.BytecodePatch
|
2023-09-20 05:33:02 +02:00
|
|
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
|
|
|
import app.revanced.patcher.patch.annotation.Patch
|
2022-11-28 23:50:04 +01:00
|
|
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
|
|
|
import app.revanced.patcher.util.smali.ExternalLabel
|
|
|
|
import app.revanced.patches.shared.settings.preference.impl.PreferenceCategory
|
|
|
|
import app.revanced.patches.shared.settings.preference.impl.StringResource
|
2024-01-01 16:33:07 +01:00
|
|
|
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
|
2022-11-28 23:50:04 +01:00
|
|
|
import app.revanced.patches.shared.settings.util.AbstractPreferenceScreen
|
2023-09-20 05:33:02 +02:00
|
|
|
import app.revanced.patches.twitch.misc.integrations.IntegrationsPatch
|
2023-05-06 23:10:01 +02:00
|
|
|
import app.revanced.patches.twitch.misc.settings.fingerprints.MenuGroupsOnClickFingerprint
|
|
|
|
import app.revanced.patches.twitch.misc.settings.fingerprints.MenuGroupsUpdatedFingerprint
|
|
|
|
import app.revanced.patches.twitch.misc.settings.fingerprints.SettingsActivityOnCreateFingerprint
|
|
|
|
import app.revanced.patches.twitch.misc.settings.fingerprints.SettingsMenuItemEnumFingerprint
|
2024-01-01 16:33:07 +01:00
|
|
|
import app.revanced.util.exception
|
2023-08-27 21:40:49 +02:00
|
|
|
import com.android.tools.smali.dexlib2.AccessFlags
|
|
|
|
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
2023-06-27 04:10:33 +02:00
|
|
|
import java.io.Closeable
|
2022-11-28 23:50:04 +01:00
|
|
|
|
2023-09-20 05:33:02 +02:00
|
|
|
|
|
|
|
@Patch(
|
|
|
|
name = "Settings",
|
|
|
|
description = "Adds settings menu to Twitch.",
|
|
|
|
dependencies = [IntegrationsPatch::class, SettingsResourcePatch::class],
|
|
|
|
compatiblePackages = [
|
2023-12-05 16:41:22 +01:00
|
|
|
CompatiblePackage("tv.twitch.android.app", ["15.4.1", "16.1.0", "16.9.1"])
|
2023-09-20 05:33:02 +02:00
|
|
|
]
|
|
|
|
)
|
|
|
|
object SettingsPatch : BytecodePatch(
|
|
|
|
setOf(
|
2022-11-28 23:50:04 +01:00
|
|
|
SettingsActivityOnCreateFingerprint,
|
|
|
|
SettingsMenuItemEnumFingerprint,
|
|
|
|
MenuGroupsUpdatedFingerprint,
|
|
|
|
MenuGroupsOnClickFingerprint
|
|
|
|
)
|
2023-06-27 04:10:33 +02:00
|
|
|
), Closeable {
|
2023-09-20 05:33:02 +02:00
|
|
|
private const val REVANCED_SETTINGS_MENU_ITEM_NAME = "RevancedSettings"
|
|
|
|
private const val REVANCED_SETTINGS_MENU_ITEM_ID = 0x7
|
|
|
|
private const val REVANCED_SETTINGS_MENU_ITEM_TITLE_RES = "revanced_settings"
|
|
|
|
private const val REVANCED_SETTINGS_MENU_ITEM_ICON_RES = "ic_settings"
|
|
|
|
|
2024-01-01 16:33:07 +01:00
|
|
|
private const val MENU_ITEM_ENUM_CLASS_DESCRIPTOR = "Ltv/twitch/android/feature/settings/menu/SettingsMenuItem;"
|
|
|
|
private const val MENU_DISMISS_EVENT_CLASS_DESCRIPTOR = "Ltv/twitch/android/feature/settings/menu/SettingsMenuViewDelegate\$Event\$OnDismissClicked;"
|
2023-09-20 05:33:02 +02:00
|
|
|
|
2024-01-01 16:33:07 +01:00
|
|
|
private const val INTEGRATIONS_PACKAGE = "app/revanced/integrations/twitch"
|
|
|
|
private const val ACTIVITY_HOOKS_CLASS_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/settings/AppCompatActivityHook;"
|
|
|
|
private const val UTILS_CLASS_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/Utils;"
|
2023-09-20 05:33:02 +02:00
|
|
|
|
2023-08-27 21:40:49 +02:00
|
|
|
override fun execute(context: BytecodeContext) {
|
2022-11-28 23:50:04 +01:00
|
|
|
// Hook onCreate to handle fragment creation
|
2023-06-21 20:04:00 +02:00
|
|
|
SettingsActivityOnCreateFingerprint.result?.apply {
|
2022-11-28 23:50:04 +01:00
|
|
|
val insertIndex = mutableMethod.implementation!!.instructions.size - 2
|
2023-06-07 03:46:13 +02:00
|
|
|
mutableMethod.addInstructionsWithLabels(
|
2022-11-28 23:50:04 +01:00
|
|
|
insertIndex,
|
|
|
|
"""
|
2024-01-01 16:33:07 +01:00
|
|
|
invoke-static {p0}, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingsCreation(Landroidx/appcompat/app/AppCompatActivity;)Z
|
2023-06-07 03:46:13 +02:00
|
|
|
move-result v0
|
|
|
|
if-eqz v0, :no_rv_settings_init
|
|
|
|
return-void
|
2022-11-28 23:50:04 +01:00
|
|
|
""",
|
2023-06-07 03:46:13 +02:00
|
|
|
ExternalLabel("no_rv_settings_init", mutableMethod.getInstruction(insertIndex))
|
2022-11-28 23:50:04 +01:00
|
|
|
)
|
2023-08-27 21:40:49 +02:00
|
|
|
} ?: throw SettingsActivityOnCreateFingerprint.exception
|
2022-11-28 23:50:04 +01:00
|
|
|
|
|
|
|
// Create new menu item for settings menu
|
2023-06-21 20:04:00 +02:00
|
|
|
SettingsMenuItemEnumFingerprint.result?.apply {
|
2022-11-28 23:50:04 +01:00
|
|
|
injectMenuItem(
|
|
|
|
REVANCED_SETTINGS_MENU_ITEM_NAME,
|
|
|
|
REVANCED_SETTINGS_MENU_ITEM_ID,
|
|
|
|
REVANCED_SETTINGS_MENU_ITEM_TITLE_RES,
|
|
|
|
REVANCED_SETTINGS_MENU_ITEM_ICON_RES
|
|
|
|
)
|
2023-08-27 21:40:49 +02:00
|
|
|
} ?: throw SettingsMenuItemEnumFingerprint.exception
|
2022-11-28 23:50:04 +01:00
|
|
|
|
|
|
|
// Intercept settings menu creation and add new menu item
|
2023-06-21 20:04:00 +02:00
|
|
|
MenuGroupsUpdatedFingerprint.result?.apply {
|
2022-11-28 23:50:04 +01:00
|
|
|
mutableMethod.addInstructions(
|
|
|
|
0,
|
|
|
|
"""
|
2024-01-01 16:33:07 +01:00
|
|
|
sget-object v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->$REVANCED_SETTINGS_MENU_ITEM_NAME:$MENU_ITEM_ENUM_CLASS_DESCRIPTOR
|
|
|
|
invoke-static {p1, v0}, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingMenuCreation(Ljava/util/List;Ljava/lang/Object;)Ljava/util/List;
|
2022-11-28 23:50:04 +01:00
|
|
|
move-result-object p1
|
|
|
|
"""
|
|
|
|
)
|
2023-08-27 21:40:49 +02:00
|
|
|
} ?: throw MenuGroupsUpdatedFingerprint.exception
|
2022-11-28 23:50:04 +01:00
|
|
|
|
|
|
|
// Intercept onclick events for the settings menu
|
2023-06-21 20:04:00 +02:00
|
|
|
MenuGroupsOnClickFingerprint.result?.apply {
|
2022-11-28 23:50:04 +01:00
|
|
|
val insertIndex = 0
|
2023-06-07 03:46:13 +02:00
|
|
|
mutableMethod.addInstructionsWithLabels(
|
2022-11-28 23:50:04 +01:00
|
|
|
insertIndex,
|
|
|
|
"""
|
2024-01-01 16:33:07 +01:00
|
|
|
invoke-static {p1}, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingMenuOnClick(Ljava/lang/Enum;)Z
|
2022-11-28 23:50:04 +01:00
|
|
|
move-result p2
|
|
|
|
if-eqz p2, :no_rv_settings_onclick
|
2024-01-01 16:33:07 +01:00
|
|
|
sget-object p1, $MENU_DISMISS_EVENT_CLASS_DESCRIPTOR->INSTANCE:$MENU_DISMISS_EVENT_CLASS_DESCRIPTOR
|
2022-11-28 23:50:04 +01:00
|
|
|
invoke-virtual {p0, p1}, Ltv/twitch/android/core/mvp/viewdelegate/RxViewDelegate;->pushEvent(Ltv/twitch/android/core/mvp/viewdelegate/ViewDelegateEvent;)V
|
|
|
|
return-void
|
|
|
|
""",
|
2023-06-07 03:46:13 +02:00
|
|
|
ExternalLabel("no_rv_settings_onclick", mutableMethod.getInstruction(insertIndex))
|
2022-11-28 23:50:04 +01:00
|
|
|
)
|
2023-08-27 21:40:49 +02:00
|
|
|
} ?: throw MenuGroupsOnClickFingerprint.exception
|
2022-11-28 23:50:04 +01:00
|
|
|
|
|
|
|
addString("revanced_settings", "ReVanced Settings", false)
|
2024-01-01 16:33:07 +01:00
|
|
|
|
|
|
|
PreferenceScreen.MISC.OTHER.addPreferences(
|
|
|
|
SwitchPreference(
|
|
|
|
"revanced_debug",
|
|
|
|
StringResource("revanced_debug_title", "Debug logging"),
|
|
|
|
StringResource("revanced_debug_summary_on", "Debug logs are enabled"),
|
|
|
|
StringResource("revanced_debug_summary_off", "Debug logs are disabled")
|
|
|
|
),
|
|
|
|
)
|
2022-11-28 23:50:04 +01:00
|
|
|
}
|
|
|
|
|
2023-09-20 05:33:02 +02:00
|
|
|
fun addString(identifier: String, value: String, formatted: Boolean = true) =
|
|
|
|
SettingsResourcePatch.addString(identifier, value, formatted)
|
2022-11-28 23:50:04 +01:00
|
|
|
|
2023-09-20 05:33:02 +02:00
|
|
|
fun addPreferenceScreen(preferenceScreen: app.revanced.patches.shared.settings.preference.impl.PreferenceScreen) =
|
|
|
|
SettingsResourcePatch.addPreferenceScreen(preferenceScreen)
|
|
|
|
|
|
|
|
private fun MethodFingerprintResult.injectMenuItem(
|
|
|
|
name: String,
|
|
|
|
value: Int,
|
|
|
|
titleResourceName: String,
|
|
|
|
iconResourceName: String
|
|
|
|
) {
|
|
|
|
// Add new static enum member field
|
|
|
|
mutableClass.staticFields.add(
|
|
|
|
ImmutableField(
|
|
|
|
mutableMethod.definingClass,
|
|
|
|
name,
|
2024-01-01 16:33:07 +01:00
|
|
|
MENU_ITEM_ENUM_CLASS_DESCRIPTOR,
|
2023-09-20 05:33:02 +02:00
|
|
|
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.ENUM or AccessFlags.STATIC,
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
null
|
|
|
|
).toMutable()
|
|
|
|
)
|
|
|
|
|
|
|
|
// Add initializer for the new enum member
|
|
|
|
mutableMethod.addInstructions(
|
|
|
|
mutableMethod.implementation!!.instructions.size - 4,
|
|
|
|
"""
|
2024-01-01 16:33:07 +01:00
|
|
|
new-instance v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR
|
2022-11-28 23:50:04 +01:00
|
|
|
const-string v1, "$titleResourceName"
|
2024-01-01 16:33:07 +01:00
|
|
|
invoke-static {v1}, $UTILS_CLASS_DESCRIPTOR->getStringId(Ljava/lang/String;)I
|
2022-11-28 23:50:04 +01:00
|
|
|
move-result v1
|
|
|
|
const-string v3, "$iconResourceName"
|
2024-01-01 16:33:07 +01:00
|
|
|
invoke-static {v3}, $UTILS_CLASS_DESCRIPTOR->getDrawableId(Ljava/lang/String;)I
|
2022-11-28 23:50:04 +01:00
|
|
|
move-result v3
|
|
|
|
const-string v4, "$name"
|
|
|
|
const/4 v5, $value
|
2024-01-01 16:33:07 +01:00
|
|
|
invoke-direct {v0, v4, v5, v1, v3}, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR-><init>(Ljava/lang/String;III)V
|
|
|
|
sput-object v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->$name:$MENU_ITEM_ENUM_CLASS_DESCRIPTOR
|
2022-11-28 23:50:04 +01:00
|
|
|
"""
|
2023-09-20 05:33:02 +02:00
|
|
|
)
|
2022-11-28 23:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Preference screens patches should add their settings to.
|
|
|
|
*/
|
|
|
|
internal object PreferenceScreen : AbstractPreferenceScreen() {
|
|
|
|
val ADS = CustomScreen("ads", "Ads", "Ad blocking settings")
|
|
|
|
val CHAT = CustomScreen("chat", "Chat", "Chat settings")
|
|
|
|
val MISC = CustomScreen("misc", "Misc", "Miscellaneous patches")
|
|
|
|
|
|
|
|
internal class CustomScreen(key: String, title: String, summary: String) : Screen(key, title, summary) {
|
|
|
|
/* Categories */
|
|
|
|
val GENERAL = CustomCategory("general", "General settings")
|
|
|
|
val OTHER = CustomCategory("other", "Other settings")
|
|
|
|
val CLIENT_SIDE = CustomCategory("client_ads", "Client-side ads")
|
2022-12-05 23:46:29 +01:00
|
|
|
val SURESTREAM = CustomCategory("surestream_ads", "Server-side surestream ads")
|
2022-11-28 23:50:04 +01:00
|
|
|
|
|
|
|
internal inner class CustomCategory(key: String, title: String) : Screen.Category(key, title) {
|
|
|
|
/* For Twitch, we need to load our CustomPreferenceCategory class instead of the default one. */
|
|
|
|
override fun transform(): PreferenceCategory {
|
2023-05-15 09:51:43 +02:00
|
|
|
return PreferenceCategory(
|
2022-11-28 23:50:04 +01:00
|
|
|
key,
|
|
|
|
StringResource("${key}_title", title),
|
2023-05-15 09:51:43 +02:00
|
|
|
preferences.sortedBy { it.title.value },
|
2024-01-01 16:33:07 +01:00
|
|
|
"app.revanced.integrations.twitch.settings.preference.CustomPreferenceCategory"
|
2022-11-28 23:50:04 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun commit(screen: app.revanced.patches.shared.settings.preference.impl.PreferenceScreen) {
|
|
|
|
addPreferenceScreen(screen)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun close() = PreferenceScreen.close()
|
|
|
|
}
|