fix(YouTube): Patched app crashes after first launch or clearing data

This commit is contained in:
inotia00 2025-01-04 19:12:22 +09:00
parent 6a9ad25d30
commit b69a041d5d
7 changed files with 89 additions and 46 deletions

View File

@ -1,5 +1,6 @@
package app.revanced.extension.shared.utils; package app.revanced.extension.shared.utils;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.view.animation.Animation; import android.view.animation.Animation;
@ -16,12 +17,25 @@ public class ResourceUtils extends Utils {
} // utility class } // utility class
public static int getIdentifier(@NonNull String str, @NonNull ResourceType resourceType) { public static int getIdentifier(@NonNull String str, @NonNull ResourceType resourceType) {
return getIdentifier(str, resourceType, getContext()); Activity mActivity = getActivity();
Context mContext = mActivity != null
? mActivity
: getContext();
if (mContext == null) {
handleException(str, resourceType);
return 0;
}
return getIdentifier(str, resourceType, mContext);
} }
public static int getIdentifier(@NonNull String str, @NonNull ResourceType resourceType, public static int getIdentifier(@NonNull String str, @NonNull ResourceType resourceType,
@NonNull Context context) { @NonNull Context context) {
return getResources().getIdentifier(str, resourceType.getType(), context.getPackageName()); try {
return context.getResources().getIdentifier(str, resourceType.getType(), context.getPackageName());
} catch (Exception ex) {
handleException(str, resourceType);
}
return 0;
} }
public static int getAnimIdentifier(@NonNull String str) { public static int getAnimIdentifier(@NonNull String str) {

View File

@ -1,6 +1,7 @@
package app.revanced.extension.shared.utils; package app.revanced.extension.shared.utils;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
@ -104,7 +105,10 @@ public class StringRef extends Utils {
public String toString() { public String toString() {
if (!resolved) { if (!resolved) {
try { try {
Context context = getContext(); Activity mActivity = getActivity();
Context context = mActivity != null
? mActivity
: getContext();
if (resources == null) { if (resources == null) {
resources = getResources(); resources = getResources();
} }

View File

@ -54,8 +54,6 @@ public class Utils {
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
public static Context context; public static Context context;
private static Resources resources;
protected Utils() { protected Utils() {
} // utility class } // utility class
@ -282,11 +280,15 @@ public class Utils {
} }
public static Resources getResources() { public static Resources getResources() {
if (resources == null) { Activity mActivity = activityRef.get();
return getLocalizedContextAndSetResources(getContext()).getResources(); if (mActivity != null) {
} else { return mActivity.getResources();
return resources;
} }
Context mContext = getContext();
if (mContext != null) {
return mContext.getResources();
}
throw new IllegalStateException("Get resources failed");
} }
/** /**
@ -304,6 +306,9 @@ public class Utils {
if (mActivity == null) { if (mActivity == null) {
return mContext; return mContext;
} }
if (mContext == null) {
return null;
}
// Locale of MainActivity. // Locale of MainActivity.
Locale applicationLocale; Locale applicationLocale;
@ -321,7 +326,6 @@ public class Utils {
// If they are identical, no need to override them. // If they are identical, no need to override them.
if (applicationLocale == contextLocale) { if (applicationLocale == contextLocale) {
resources = mActivity.getResources();
return mContext; return mContext;
} }
@ -329,9 +333,7 @@ public class Utils {
Locale.setDefault(applicationLocale); Locale.setDefault(applicationLocale);
Configuration configuration = new Configuration(mContext.getResources().getConfiguration()); Configuration configuration = new Configuration(mContext.getResources().getConfiguration());
configuration.setLocale(applicationLocale); configuration.setLocale(applicationLocale);
Context localizedContext = mContext.createConfigurationContext(configuration); return mContext.createConfigurationContext(configuration);
resources = localizedContext.getResources();
return localizedContext;
} }
public static void setActivity(Activity mainActivity) { public static void setActivity(Activity mainActivity) {

View File

@ -40,7 +40,6 @@ import java.util.EnumMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.ResourceUtils; import app.revanced.extension.shared.utils.ResourceUtils;
import app.revanced.extension.shared.utils.Utils; import app.revanced.extension.shared.utils.Utils;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
@ -109,12 +108,7 @@ public class GeneralPatch {
// region [Disable splash animation] patch // region [Disable splash animation] patch
public static boolean disableSplashAnimation(boolean original) { public static boolean disableSplashAnimation(boolean original) {
try {
return !Settings.DISABLE_SPLASH_ANIMATION.get() && original; return !Settings.DISABLE_SPLASH_ANIMATION.get() && original;
} catch (Exception ex) {
Logger.printException(() -> "Failed to load disableSplashAnimation", ex);
}
return original;
} }
// endregion // endregion
@ -130,12 +124,7 @@ public class GeneralPatch {
// region [Hide layout components] patch // region [Hide layout components] patch
public static boolean disableTranslucentStatusBar(boolean original) { public static boolean disableTranslucentStatusBar(boolean original) {
try {
return !Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get() && original; return !Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get() && original;
} catch (Exception ex) {
Logger.printException(() -> "Failed to load disableTranslucentStatusBar", ex);
}
return original;
} }
private static String[] accountMenuBlockList; private static String[] accountMenuBlockList;
@ -240,12 +229,7 @@ public class GeneralPatch {
} }
public static boolean switchCreateWithNotificationButton(boolean original) { public static boolean switchCreateWithNotificationButton(boolean original) {
try {
return Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get() || original; return Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get() || original;
} catch (Exception ex) {
Logger.printException(() -> "switchCreateWithNotificationButton Failed", ex);
}
return original;
} }
public static void navigationTabCreated(NavigationButton button, View tabView) { public static void navigationTabCreated(NavigationButton button, View tabView) {
@ -327,6 +311,9 @@ public class GeneralPatch {
// region [Toolbar components] patch // region [Toolbar components] patch
private static final int generalHeaderAttributeId = ResourceUtils.getAttrIdentifier("ytWordmarkHeader");
private static final int premiumHeaderAttributeId = ResourceUtils.getAttrIdentifier("ytPremiumWordmarkHeader");
public static void setDrawerNavigationHeader(View lithoView) { public static void setDrawerNavigationHeader(View lithoView) {
final int headerAttributeId = getHeaderAttributeId(); final int headerAttributeId = getHeaderAttributeId();
@ -344,8 +331,8 @@ public class GeneralPatch {
public static int getHeaderAttributeId() { public static int getHeaderAttributeId() {
return Settings.CHANGE_YOUTUBE_HEADER.get() return Settings.CHANGE_YOUTUBE_HEADER.get()
? ResourceUtils.getAttrIdentifier("ytPremiumWordmarkHeader") ? premiumHeaderAttributeId
: ResourceUtils.getAttrIdentifier("ytWordmarkHeader"); : generalHeaderAttributeId;
} }
public static boolean overridePremiumHeader() { public static boolean overridePremiumHeader() {
@ -357,6 +344,11 @@ public class GeneralPatch {
return ResourceUtils.getDrawable(""); return ResourceUtils.getDrawable("");
} }
private static final int searchBarId = ResourceUtils.getIdIdentifier("search_bar");
private static final int youtubeTextId = ResourceUtils.getIdIdentifier("youtube_text");
private static final int searchBoxId = ResourceUtils.getIdIdentifier("search_box");
private static final int searchIconId = ResourceUtils.getIdIdentifier("search_icon");
private static final boolean wideSearchbarEnabled = Settings.ENABLE_WIDE_SEARCH_BAR.get(); private static final boolean wideSearchbarEnabled = Settings.ENABLE_WIDE_SEARCH_BAR.get();
// Loads the search bar deprecated by Google. // Loads the search bar deprecated by Google.
private static final boolean wideSearchbarWithHeaderEnabled = Settings.ENABLE_WIDE_SEARCH_BAR_WITH_HEADER.get(); private static final boolean wideSearchbarWithHeaderEnabled = Settings.ENABLE_WIDE_SEARCH_BAR_WITH_HEADER.get();
@ -396,17 +388,12 @@ public class GeneralPatch {
if (!wideSearchbarEnabled) if (!wideSearchbarEnabled)
return; return;
final int searchBarId = ResourceUtils.getIdIdentifier("search_bar");
if (!(view.findViewById(searchBarId) instanceof RelativeLayout searchBarView)) if (!(view.findViewById(searchBarId) instanceof RelativeLayout searchBarView))
return; return;
// When the deprecated search bar is loaded, two search bars overlap. // When the deprecated search bar is loaded, two search bars overlap.
// Manually hides another search bar. // Manually hides another search bar.
if (wideSearchbarWithHeaderEnabled) { if (wideSearchbarWithHeaderEnabled) {
final int youtubeTextId = ResourceUtils.getIdIdentifier("youtube_text");
final int searchBoxId = ResourceUtils.getIdIdentifier("search_box");
final int searchIconId = ResourceUtils.getIdIdentifier("search_icon");
final View searchIconView = searchBarView.findViewById(searchIconId); final View searchIconView = searchBarView.findViewById(searchIconId);
final View searchBoxView = searchBarView.findViewById(searchBoxId); final View searchBoxView = searchBarView.findViewById(searchBoxId);
final View textView = searchBarView.findViewById(youtubeTextId); final View textView = searchBarView.findViewById(youtubeTextId);
@ -521,14 +508,16 @@ public class GeneralPatch {
imageView.setImageDrawable(drawable); imageView.setImageDrawable(drawable);
} }
private static final int settingsDrawableId =
ResourceUtils.getDrawableIdentifier("yt_outline_gear_black_24");
private static final int settingsCairoDrawableId =
ResourceUtils.getDrawableIdentifier("yt_outline_gear_cairo_black_24");
public static int getCreateButtonDrawableId(int original) { public static int getCreateButtonDrawableId(int original) {
if (!Settings.REPLACE_TOOLBAR_CREATE_BUTTON.get()) { if (!Settings.REPLACE_TOOLBAR_CREATE_BUTTON.get()) {
return original; return original;
} }
final int settingsDrawableId =
ResourceUtils.getDrawableIdentifier("yt_outline_gear_black_24");
if (settingsDrawableId == 0) { if (settingsDrawableId == 0) {
return original; return original;
} }
@ -539,9 +528,6 @@ public class GeneralPatch {
return settingsDrawableId; return settingsDrawableId;
} }
final int settingsCairoDrawableId =
ResourceUtils.getDrawableIdentifier("yt_outline_gear_cairo_black_24");
return settingsCairoDrawableId == 0 return settingsCairoDrawableId == 0
? settingsDrawableId ? settingsDrawableId
: settingsCairoDrawableId; : settingsCairoDrawableId;

View File

@ -2,8 +2,10 @@ package app.revanced.patches.youtube.utils.extension
import app.revanced.patches.shared.extension.sharedExtensionPatch import app.revanced.patches.shared.extension.sharedExtensionPatch
import app.revanced.patches.youtube.utils.extension.hooks.applicationInitHook import app.revanced.patches.youtube.utils.extension.hooks.applicationInitHook
import app.revanced.patches.youtube.utils.extension.hooks.mainActivityBaseContextHook
// TODO: Move this to a "Hook.kt" file. Same for other extension hook patches. // TODO: Move this to a "Hook.kt" file. Same for other extension hook patches.
val sharedExtensionPatch = sharedExtensionPatch( val sharedExtensionPatch = sharedExtensionPatch(
applicationInitHook, applicationInitHook,
mainActivityBaseContextHook,
) )

View File

@ -0,0 +1,35 @@
package app.revanced.patches.youtube.utils.extension.hooks
import app.revanced.patches.shared.extension.extensionHook
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private var attachBaseContextIndex = -1
internal val mainActivityBaseContextHook = extensionHook(
insertIndexResolver = { method ->
attachBaseContextIndex = method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "attachBaseContext"
}
attachBaseContextIndex + 1
},
contextRegisterResolver = { method ->
val overrideInstruction = method.implementation!!.instructions.elementAt(attachBaseContextIndex)
as FiveRegisterInstruction
"v${overrideInstruction.registerD}"
},
) {
returns("V")
parameters("Landroid/content/Context;")
custom { method, classDef ->
method.name == "attachBaseContext" &&
(
classDef.endsWith("/MainActivity;") ||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
classDef.endsWith("/WatchWhileActivity;")
)
}
}