From 208c1b45b9c6413e04282bbc571280368a4ebe0d Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 27 Dec 2024 16:42:05 +0400 Subject: [PATCH] feat(YouTube): Add in app option to select a preferred language for ReVanced specific text --- .../app/revanced/extension/shared/Utils.java | 12 ++ .../AppLanguage.java} | 16 +- .../shared/settings/BaseSettings.java | 5 +- .../shared/spoof/requests/PlayerRoutes.java | 6 +- .../youtube/settings/LicenseActivityHook.java | 17 +++ .../youtube/misc/settings/SettingsPatch.kt | 35 +++++ .../misc/spoof/SpoofVideoStreamsPatch.kt | 7 +- .../resources/addresources/values/arrays.xml | 142 +++++++++--------- .../resources/addresources/values/strings.xml | 109 +++++++------- 9 files changed, 217 insertions(+), 132 deletions(-) rename extensions/shared/library/src/main/java/app/revanced/extension/shared/{spoof/AudioStreamLanguage.java => settings/AppLanguage.java} (79%) diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java index c7eb05699..6412df333 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java @@ -40,6 +40,8 @@ import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import app.revanced.extension.shared.settings.AppLanguage; +import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BooleanSetting; import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference; @@ -360,7 +362,17 @@ public class Utils { } public static void setContext(Context appContext) { + // Must initially set context as the language settings needs it. context = appContext; + + AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get(); + if (language != AppLanguage.DEFAULT) { + // Create a new context with the desired language. + Configuration config = appContext.getResources().getConfiguration(); + config.setLocale(language.getLocale()); + context = appContext.createConfigurationContext(config); + } + // In some apps like TikTok, the Setting classes can load in weird orders due to cyclic class dependencies. // Calling the regular printDebug method here can cause a Settings context null pointer exception, // even though the context is already set before the call. diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/AppLanguage.java similarity index 79% rename from extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java rename to extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/AppLanguage.java index d94b0069f..7f8d3783a 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/AppLanguage.java @@ -1,8 +1,8 @@ -package app.revanced.extension.shared.spoof; +package app.revanced.extension.shared.settings; import java.util.Locale; -public enum AudioStreamLanguage { +public enum AppLanguage { /** * The current app language. */ @@ -34,7 +34,7 @@ public enum AudioStreamLanguage { GL, GU, HI, - HE, // App uses obsolete 'IW' and 'HE' is modern ISO code. + HE, // App uses obsolete 'IW' and not the modern 'HE' ISO code. HR, HU, HY, @@ -87,7 +87,7 @@ public enum AudioStreamLanguage { private final String language; - AudioStreamLanguage() { + AppLanguage() { language = name().toLowerCase(Locale.US); } @@ -103,4 +103,12 @@ public enum AudioStreamLanguage { return language; } + + public Locale getLocale() { + if (this == DEFAULT) { + return Locale.getDefault(); + } + + return Locale.forLanguageTag(getLanguage()); + } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java index 136d1d468..62aba6110 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java @@ -6,7 +6,6 @@ import static app.revanced.extension.shared.settings.Setting.parent; import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability; import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability; -import app.revanced.extension.shared.spoof.AudioStreamLanguage; import app.revanced.extension.shared.spoof.ClientType; /** @@ -22,8 +21,10 @@ public class BaseSettings { public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false); + public static final EnumSetting REVANCED_LANGUAGE = new EnumSetting<>("revanced_language", AppLanguage.DEFAULT, true); + public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message"); - public static final EnumSetting SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability()); + public static final EnumSetting SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability()); public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS)); public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true, "revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability()); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java index 9974064e9..0f51009d8 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java @@ -10,7 +10,7 @@ import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.requests.Requester; import app.revanced.extension.shared.requests.Route; import app.revanced.extension.shared.settings.BaseSettings; -import app.revanced.extension.shared.spoof.AudioStreamLanguage; +import app.revanced.extension.shared.settings.AppLanguage; import app.revanced.extension.shared.spoof.ClientType; final class PlayerRoutes { @@ -42,9 +42,9 @@ final class PlayerRoutes { // but if this is a fall over client it will set the language even though // the audio language is not selectable in the UI. ClientType userSelectedClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get(); - AudioStreamLanguage language = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH + AppLanguage language = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH ? BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get() - : AudioStreamLanguage.DEFAULT; + : AppLanguage.DEFAULT; JSONObject client = new JSONObject(); client.put("hl", language.getLanguage()); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java index 1ca0f5c19..1812ca5c5 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java @@ -2,11 +2,15 @@ package app.revanced.extension.youtube.settings; import android.annotation.SuppressLint; import android.app.Activity; +import android.content.Context; import android.preference.PreferenceFragment; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.TextView; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.Utils; +import app.revanced.extension.shared.settings.AppLanguage; +import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.youtube.ThemeHelper; import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment; import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment; @@ -25,6 +29,19 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier; @SuppressWarnings("unused") public class LicenseActivityHook { + /** + * Injection point. + * Overrides the ReVanced settings language. + */ + public static Context getAttachBaseContext(Context original) { + AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get(); + if (language == AppLanguage.DEFAULT) { + return original; + } + + return Utils.getContext(); + } + /** * Injection point. *

diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt index 5eb29385e..68c058b7b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt @@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -20,8 +21,12 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.fix.cairo.disableCairoSettingsPatch import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch import app.revanced.util.* +import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter import com.android.tools.smali.dexlib2.util.MethodUtil // Used by a fingerprint() from SettingsPatch. @@ -150,6 +155,10 @@ val settingsPatch = bytecodePatch( inputType = InputType.TEXT_MULTI_LINE, tag = "app.revanced.extension.shared.settings.preference.ImportExportPreference", ), + ListPreference( + key = "revanced_language", + summaryKey = null + ) ) setThemeFingerprint.method.let { setThemeMethod -> @@ -189,6 +198,32 @@ val settingsPatch = bytecodePatch( licenseActivityOnCreateFingerprint.classDef.apply { methods.removeIf { it.name != "onCreate" && !MethodUtil.isConstructor(it) } } + + // Add context override to force a specific settings language. + licenseActivityOnCreateFingerprint.classDef.apply { + val attachBaseContext = ImmutableMethod( + type, + "attachBaseContext", + listOf(ImmutableMethodParameter("Landroid/content/Context;", null, null)), + "V", + AccessFlags.PROTECTED.value, + null, + null, + MutableMethodImplementation(3), + ).toMutable().apply { + addInstructions( + """ + invoke-static { p1 }, $activityHookClassDescriptor->getAttachBaseContext(Landroid/content/Context;)Landroid/content/Context; + move-result-object p1 + invoke-super { p0, p1 }, $superclass->attachBaseContext(Landroid/content/Context;)V + return-void + """ + ) + } + + methods.add(attachBaseContext) + } + } finalize { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt index a408815ca..fec8800cf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt @@ -47,8 +47,11 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({ tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference" ), ListPreference( - "revanced_spoof_video_streams_language", - summaryKey = null + key = "revanced_spoof_video_streams_language", + summaryKey = null, + // Language strings are declared in Setting patch. + entriesKey = "revanced_language_entries", + entryValuesKey = "revanced_language_entry_values" ), SwitchPreference("revanced_spoof_video_streams_ios_force_avc"), SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"), diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml index 670b043b4..a8bf565d9 100644 --- a/patches/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -1,74 +1,61 @@ - - - - Android VR - @string/revanced_spoof_video_streams_client_type_android_vr_no_auth - Android TV - iOS TV + + + + @string/revanced_language_DEFAULT + @string/revanced_language_AR + @string/revanced_language_AZ + @string/revanced_language_BG + @string/revanced_language_BN + @string/revanced_language_CA + @string/revanced_language_CS + @string/revanced_language_DA + @string/revanced_language_DE + @string/revanced_language_EL + @string/revanced_language_EN + @string/revanced_language_ES + @string/revanced_language_ET + @string/revanced_language_FA + @string/revanced_language_FI + @string/revanced_language_FR + @string/revanced_language_GU + @string/revanced_language_HI + @string/revanced_language_HR + @string/revanced_language_HU + @string/revanced_language_ID + @string/revanced_language_IT + @string/revanced_language_JA + @string/revanced_language_KK + @string/revanced_language_KO + @string/revanced_language_LT + @string/revanced_language_LV + @string/revanced_language_MK + @string/revanced_language_MN + @string/revanced_language_MR + @string/revanced_language_MS + @string/revanced_language_MY + @string/revanced_language_NL + @string/revanced_language_OR + @string/revanced_language_PA + @string/revanced_language_PL + @string/revanced_language_PT + @string/revanced_language_RO + @string/revanced_language_RU + @string/revanced_language_SK + @string/revanced_language_SL + @string/revanced_language_SR + @string/revanced_language_SV + @string/revanced_language_SW + @string/revanced_language_TA + @string/revanced_language_TE + @string/revanced_language_TH + @string/revanced_language_TR + @string/revanced_language_UK + @string/revanced_language_UR + @string/revanced_language_VI + @string/revanced_language_ZH - - - ANDROID_VR - ANDROID_VR_NO_AUTH - ANDROID_UNPLUGGED - IOS_UNPLUGGED - - - @string/revanced_spoof_video_streams_language_DEFAULT - @string/revanced_spoof_video_streams_language_AR - @string/revanced_spoof_video_streams_language_AZ - @string/revanced_spoof_video_streams_language_BG - @string/revanced_spoof_video_streams_language_BN - @string/revanced_spoof_video_streams_language_CA - @string/revanced_spoof_video_streams_language_CS - @string/revanced_spoof_video_streams_language_DA - @string/revanced_spoof_video_streams_language_DE - @string/revanced_spoof_video_streams_language_EL - @string/revanced_spoof_video_streams_language_EN - @string/revanced_spoof_video_streams_language_ES - @string/revanced_spoof_video_streams_language_ET - @string/revanced_spoof_video_streams_language_FA - @string/revanced_spoof_video_streams_language_FI - @string/revanced_spoof_video_streams_language_FR - @string/revanced_spoof_video_streams_language_GU - @string/revanced_spoof_video_streams_language_HI - @string/revanced_spoof_video_streams_language_HR - @string/revanced_spoof_video_streams_language_HU - @string/revanced_spoof_video_streams_language_ID - @string/revanced_spoof_video_streams_language_IT - @string/revanced_spoof_video_streams_language_JA - @string/revanced_spoof_video_streams_language_KK - @string/revanced_spoof_video_streams_language_KO - @string/revanced_spoof_video_streams_language_LT - @string/revanced_spoof_video_streams_language_LV - @string/revanced_spoof_video_streams_language_MK - @string/revanced_spoof_video_streams_language_MN - @string/revanced_spoof_video_streams_language_MR - @string/revanced_spoof_video_streams_language_MS - @string/revanced_spoof_video_streams_language_MY - @string/revanced_spoof_video_streams_language_NL - @string/revanced_spoof_video_streams_language_OR - @string/revanced_spoof_video_streams_language_PA - @string/revanced_spoof_video_streams_language_PL - @string/revanced_spoof_video_streams_language_PT - @string/revanced_spoof_video_streams_language_RO - @string/revanced_spoof_video_streams_language_RU - @string/revanced_spoof_video_streams_language_SK - @string/revanced_spoof_video_streams_language_SL - @string/revanced_spoof_video_streams_language_SR - @string/revanced_spoof_video_streams_language_SV - @string/revanced_spoof_video_streams_language_SW - @string/revanced_spoof_video_streams_language_TA - @string/revanced_spoof_video_streams_language_TE - @string/revanced_spoof_video_streams_language_TH - @string/revanced_spoof_video_streams_language_TR - @string/revanced_spoof_video_streams_language_UK - @string/revanced_spoof_video_streams_language_UR - @string/revanced_spoof_video_streams_language_VI - @string/revanced_spoof_video_streams_language_ZH - - + DEFAULT AR AZ @@ -123,6 +110,23 @@ ZH + + + + + Android VR + @string/revanced_spoof_video_streams_client_type_android_vr_no_auth + Android TV + iOS TV + + + + ANDROID_VR + ANDROID_VR_NO_AUTH + ANDROID_UNPLUGGED + IOS_UNPLUGGED + + @string/revanced_spoof_app_version_target_entry_1 diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index 6e4bceb3b..e7f5830d6 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -42,6 +42,63 @@ Second \"item\" text" ReVanced settings reset to default Imported %d settings Import failed: %s + + ReVanced language + App language + Arabic + Azerbaijani + Bulgarian + Bengali + Catalan + Czech + Danish + German + Greek + English + Spanish + Estonian + Persian + Finnish + French + Gujarati + Hindi + Croatian + Hungarian + Indonesian + Italian + Japanese + Kazakh + Korean + Lithuanian + Latvian + Macedonian + Mongolian + Marathi + Malay + Burmese + Dutch + Odia + Punjabi + Polish + Portuguese + Romanian + Russian + Slovak + Slovene + Serbian + Swedish + Swahili + Tamil + Telugu + Thai + Turkish + Ukrainian + Urdu + Vietnamese + Chinese + + + Import / Export Import / Export ReVanced settings @@ -1343,58 +1400,6 @@ AVC has a maximum resolution of 1080p, Opus audio codec is not available, and vi Client type is shown in Stats for nerds Client is hidden in Stats for nerds VR default audio stream language - App language - Arabic - Azerbaijani - Bulgarian - Bengali - Catalan - Czech - Danish - German - Greek - English - Spanish - Estonian - Persian - Finnish - French - Gujarati - Hindi - Croatian - Hungarian - Indonesian - Italian - Japanese - Kazakh - Korean - Lithuanian - Latvian - Macedonian - Mongolian - Marathi - Malay - Burmese - Dutch - Odia - Punjabi - Polish - Portuguese - Romanian - Russian - Slovak - Slovene - Serbian - Swedish - Swahili - Tamil - Telugu - Thai - Turkish - Ukrainian - Urdu - Vietnamese - Chinese