Skip to content

Commit 3932af3

Browse files
feat(YouTube): Add in app option to select a preferred language for ReVanced specific text (#4231)
1 parent 8aa25c3 commit 3932af3

File tree

11 files changed

+235
-141
lines changed

11 files changed

+235
-141
lines changed

extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import java.util.concurrent.ThreadPoolExecutor;
4141
import java.util.concurrent.TimeUnit;
4242

43+
import app.revanced.extension.shared.settings.AppLanguage;
44+
import app.revanced.extension.shared.settings.BaseSettings;
4345
import app.revanced.extension.shared.settings.BooleanSetting;
4446
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
4547

@@ -360,7 +362,17 @@ public static Context getContext() {
360362
}
361363

362364
public static void setContext(Context appContext) {
365+
// Must initially set context as the language settings needs it.
363366
context = appContext;
367+
368+
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
369+
if (language != AppLanguage.DEFAULT) {
370+
// Create a new context with the desired language.
371+
Configuration config = appContext.getResources().getConfiguration();
372+
config.setLocale(language.getLocale());
373+
context = appContext.createConfigurationContext(config);
374+
}
375+
364376
// In some apps like TikTok, the Setting classes can load in weird orders due to cyclic class dependencies.
365377
// Calling the regular printDebug method here can cause a Settings context null pointer exception,
366378
// even though the context is already set before the call.
@@ -765,17 +777,17 @@ public static void setPreferenceTitlesToMultiLineIfNeeded(PreferenceGroup group)
765777
return;
766778
}
767779

768-
String deviceLanguage = Utils.getContext().getResources().getConfiguration().locale.getLanguage();
769-
if (deviceLanguage.equals("en")) {
780+
String revancedLocale = Utils.getContext().getResources().getConfiguration().locale.getLanguage();
781+
if (revancedLocale.equals(Locale.ENGLISH.getLanguage())) {
770782
return;
771783
}
772784

773785
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
774786
Preference pref = group.getPreference(i);
775787
pref.setSingleLineTitle(false);
776788

777-
if (pref instanceof PreferenceGroup) {
778-
setPreferenceTitlesToMultiLineIfNeeded((PreferenceGroup) pref);
789+
if (pref instanceof PreferenceGroup subGroup) {
790+
setPreferenceTitlesToMultiLineIfNeeded(subGroup);
779791
}
780792
}
781793
}
Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
package app.revanced.extension.shared.spoof;
1+
package app.revanced.extension.shared.settings;
22

33
import java.util.Locale;
44

5-
public enum AudioStreamLanguage {
5+
public enum AppLanguage {
66
/**
77
* The current app language.
88
*/
@@ -34,7 +34,7 @@ public enum AudioStreamLanguage {
3434
GL,
3535
GU,
3636
HI,
37-
HE, // App uses obsolete 'IW' and 'HE' is modern ISO code.
37+
HE, // App uses obsolete 'IW' and not the modern 'HE' ISO code.
3838
HR,
3939
HU,
4040
HY,
@@ -87,7 +87,7 @@ public enum AudioStreamLanguage {
8787

8888
private final String language;
8989

90-
AudioStreamLanguage() {
90+
AppLanguage() {
9191
language = name().toLowerCase(Locale.US);
9292
}
9393

@@ -103,4 +103,12 @@ public String getLanguage() {
103103

104104
return language;
105105
}
106+
107+
public Locale getLocale() {
108+
if (this == DEFAULT) {
109+
return Locale.getDefault();
110+
}
111+
112+
return Locale.forLanguageTag(language);
113+
}
106114
}

extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability;
77
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
88

9-
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
109
import app.revanced.extension.shared.spoof.ClientType;
1110

1211
/**
@@ -22,8 +21,10 @@ public class BaseSettings {
2221

2322
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
2423

24+
public static final EnumSetting<AppLanguage> REVANCED_LANGUAGE = new EnumSetting<>("revanced_language", AppLanguage.DEFAULT, true, "revanced_language_user_dialog_message");
25+
2526
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
26-
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability());
27+
public static final EnumSetting<AppLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability());
2728
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
2829
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
2930
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());

extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import app.revanced.extension.shared.requests.Requester;
1111
import app.revanced.extension.shared.requests.Route;
1212
import app.revanced.extension.shared.settings.BaseSettings;
13-
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
13+
import app.revanced.extension.shared.settings.AppLanguage;
1414
import app.revanced.extension.shared.spoof.ClientType;
1515

1616
final class PlayerRoutes {
@@ -42,9 +42,9 @@ static String createInnertubeBody(ClientType clientType) {
4242
// but if this is a fall over client it will set the language even though
4343
// the audio language is not selectable in the UI.
4444
ClientType userSelectedClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
45-
AudioStreamLanguage language = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH
45+
AppLanguage language = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH
4646
? BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get()
47-
: AudioStreamLanguage.DEFAULT;
47+
: AppLanguage.DEFAULT;
4848

4949
JSONObject client = new JSONObject();
5050
client.put("hl", language.getLanguage());

extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ExitFullscreenPatch.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ public static void endOfVideoReached() {
4949
Logger.printDebug(() -> "Fullscreen button is null, cannot click");
5050
} else {
5151
Logger.printDebug(() -> "Clicking fullscreen button");
52+
final boolean soundEffectsEnabled = button.isSoundEffectsEnabled();
53+
button.setSoundEffectsEnabled(false);
5254
button.performClick();
55+
button.setSoundEffectsEnabled(soundEffectsEnabled);
5356
}
5457
});
5558
}

extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
import android.annotation.SuppressLint;
44
import android.app.Activity;
5+
import android.content.Context;
56
import android.preference.PreferenceFragment;
67
import android.view.ViewGroup;
78
import android.widget.ImageButton;
89
import android.widget.TextView;
910
import app.revanced.extension.shared.Logger;
11+
import app.revanced.extension.shared.Utils;
12+
import app.revanced.extension.shared.settings.AppLanguage;
13+
import app.revanced.extension.shared.settings.BaseSettings;
1014
import app.revanced.extension.youtube.ThemeHelper;
1115
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
1216
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
@@ -25,6 +29,19 @@
2529
@SuppressWarnings("unused")
2630
public class LicenseActivityHook {
2731

32+
/**
33+
* Injection point.
34+
* Overrides the ReVanced settings language.
35+
*/
36+
public static Context getAttachBaseContext(Context original) {
37+
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
38+
if (language == AppLanguage.DEFAULT) {
39+
return original;
40+
}
41+
42+
return Utils.getContext();
43+
}
44+
2845
/**
2946
* Injection point.
3047
* <p>

extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
import app.revanced.extension.shared.Logger;
2727
import app.revanced.extension.shared.Utils;
28+
import app.revanced.extension.shared.settings.BaseSettings;
29+
import app.revanced.extension.shared.settings.EnumSetting;
2830
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
2931
import app.revanced.extension.youtube.ThemeHelper;
3032
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
@@ -109,15 +111,20 @@ protected void initialize() {
109111
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
110112
}
111113

112-
preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key);
113-
if (preference instanceof ListPreference languagePreference) {
114-
sortListPreferenceByValues(languagePreference, 1);
115-
}
114+
sortPreferenceListMenu(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE);
115+
sortPreferenceListMenu(BaseSettings.REVANCED_LANGUAGE);
116116
} catch (Exception ex) {
117117
Logger.printException(() -> "initialize failure", ex);
118118
}
119119
}
120120

121+
private void sortPreferenceListMenu(EnumSetting<?> setting) {
122+
Preference preference = findPreference(setting.key);
123+
if (preference instanceof ListPreference languagePreference) {
124+
sortListPreferenceByValues(languagePreference, 1);
125+
}
126+
}
127+
121128
private void setPreferenceScreenToolbar(PreferenceScreen parentScreen) {
122129
for (int i = 0, preferenceCount = parentScreen.getPreferenceCount(); i < preferenceCount; i++) {
123130
Preference childPreference = parentScreen.getPreference(i);

patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
66
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
77
import app.revanced.patcher.patch.bytecodePatch
88
import app.revanced.patcher.patch.resourcePatch
9+
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
910
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
1011
import app.revanced.patches.all.misc.resources.addResources
1112
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -20,8 +21,12 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
2021
import app.revanced.patches.youtube.misc.fix.cairo.disableCairoSettingsPatch
2122
import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch
2223
import app.revanced.util.*
24+
import com.android.tools.smali.dexlib2.AccessFlags
2325
import com.android.tools.smali.dexlib2.Opcode
26+
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
2427
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
28+
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
29+
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
2530
import com.android.tools.smali.dexlib2.util.MethodUtil
2631

2732
// Used by a fingerprint() from SettingsPatch.
@@ -150,6 +155,10 @@ val settingsPatch = bytecodePatch(
150155
inputType = InputType.TEXT_MULTI_LINE,
151156
tag = "app.revanced.extension.shared.settings.preference.ImportExportPreference",
152157
),
158+
ListPreference(
159+
key = "revanced_language",
160+
summaryKey = null
161+
)
153162
)
154163

155164
setThemeFingerprint.method.let { setThemeMethod ->
@@ -189,6 +198,32 @@ val settingsPatch = bytecodePatch(
189198
licenseActivityOnCreateFingerprint.classDef.apply {
190199
methods.removeIf { it.name != "onCreate" && !MethodUtil.isConstructor(it) }
191200
}
201+
202+
// Add context override to force a specific settings language.
203+
licenseActivityOnCreateFingerprint.classDef.apply {
204+
val attachBaseContext = ImmutableMethod(
205+
type,
206+
"attachBaseContext",
207+
listOf(ImmutableMethodParameter("Landroid/content/Context;", null, null)),
208+
"V",
209+
AccessFlags.PROTECTED.value,
210+
null,
211+
null,
212+
MutableMethodImplementation(3),
213+
).toMutable().apply {
214+
addInstructions(
215+
"""
216+
invoke-static { p1 }, $activityHookClassDescriptor->getAttachBaseContext(Landroid/content/Context;)Landroid/content/Context;
217+
move-result-object p1
218+
invoke-super { p0, p1 }, $superclass->attachBaseContext(Landroid/content/Context;)V
219+
return-void
220+
"""
221+
)
222+
}
223+
224+
methods.add(attachBaseContext)
225+
}
226+
192227
}
193228

194229
finalize {

patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,11 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
4747
tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference"
4848
),
4949
ListPreference(
50-
"revanced_spoof_video_streams_language",
51-
summaryKey = null
50+
key = "revanced_spoof_video_streams_language",
51+
summaryKey = null,
52+
// Language strings are declared in Setting patch.
53+
entriesKey = "revanced_language_entries",
54+
entryValuesKey = "revanced_language_entry_values"
5255
),
5356
SwitchPreference("revanced_spoof_video_streams_ios_force_avc"),
5457
SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"),

0 commit comments

Comments
 (0)