fix(YouTube - Overlay buttons): App crashes due to incorrect Context access https://github.com/inotia00/ReVanced_Extended/issues/2831

This commit is contained in:
inotia00 2025-03-14 18:14:56 +09:00
parent 699cd055ac
commit 806976b6d8
5 changed files with 82 additions and 48 deletions

View File

@ -7,6 +7,7 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@SuppressWarnings({"unused", "deprecation", "DiscouragedApi"}) @SuppressWarnings({"unused", "deprecation", "DiscouragedApi"})
public class ResourceUtils extends Utils { public class ResourceUtils extends Utils {
@ -100,13 +101,19 @@ public class ResourceUtils extends Utils {
return getIdentifier(str, ResourceType.XML); return getIdentifier(str, ResourceType.XML);
} }
@Nullable
public static Animation getAnimation(@NonNull String str) { public static Animation getAnimation(@NonNull String str) {
int identifier = getAnimIdentifier(str); try {
if (identifier == 0) { int identifier = getAnimIdentifier(str);
if (identifier == 0) {
handleException(str, ResourceType.ANIM);
identifier = android.R.anim.fade_in;
}
return AnimationUtils.loadAnimation(getContext(), identifier);
} catch (Exception ex) {
handleException(str, ResourceType.ANIM); handleException(str, ResourceType.ANIM);
identifier = android.R.anim.fade_in;
} }
return AnimationUtils.loadAnimation(getContext(), identifier); return null;
} }
public static int getColor(@NonNull String str) { public static int getColor(@NonNull String str) {

View File

@ -53,7 +53,8 @@ import kotlin.text.Regex;
public class Utils { public class Utils {
private static WeakReference<Activity> activityRef = new WeakReference<>(null); private static WeakReference<Activity> activityRef = new WeakReference<>(null);
private static WeakReference<Context> contextRef = new WeakReference<>(null); @SuppressLint("StaticFieldLeak")
private static volatile Context context;
protected Utils() { protected Utils() {
} // utility class } // utility class
@ -274,17 +275,15 @@ public class Utils {
} }
public static Context getContext() { public static Context getContext() {
Context mContext = contextRef.get(); if (context == null) {
if (mContext == null) {
Logger.initializationException(Utils.class, "Context is null, returning null!", null); Logger.initializationException(Utils.class, "Context is null, returning null!", null);
} }
return mContext; return context;
} }
public static Resources getResources() { public static Resources getResources() {
Context mContext = contextRef.get(); if (context != null) {
if (mContext != null) { return context.getResources();
return mContext.getResources();
} }
Activity mActivity = activityRef.get(); Activity mActivity = activityRef.get();
if (mActivity != null) { if (mActivity != null) {
@ -347,7 +346,7 @@ public class Utils {
} }
// Must initially set context to check the app language. // Must initially set context to check the app language.
contextRef = new WeakReference<>(appContext); context = appContext;
Logger.initializationInfo(Utils.class, "Set context: " + appContext); Logger.initializationInfo(Utils.class, "Set context: " + appContext);
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get(); AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
@ -355,7 +354,7 @@ public class Utils {
// Create a new context with the desired language. // Create a new context with the desired language.
Configuration config = appContext.getResources().getConfiguration(); Configuration config = appContext.getResources().getConfiguration();
config.setLocale(language.getLocale()); config.setLocale(language.getLocale());
contextRef = new WeakReference<>(appContext.createConfigurationContext(config)); context = appContext.createConfigurationContext(config);
} }
} }
@ -364,8 +363,7 @@ public class Utils {
} }
public static void setClipboard(@NonNull String text, @Nullable String toastMessage) { public static void setClipboard(@NonNull String text, @Nullable String toastMessage) {
Context mContext = contextRef.get(); if (context != null && context.getSystemService(Context.CLIPBOARD_SERVICE) instanceof ClipboardManager clipboardManager) {
if (mContext != null && mContext.getSystemService(Context.CLIPBOARD_SERVICE) instanceof ClipboardManager clipboardManager) {
android.content.ClipData clip = android.content.ClipData.newPlainText("ReVanced", text); android.content.ClipData clip = android.content.ClipData.newPlainText("ReVanced", text);
clipboardManager.setPrimaryClip(clip); clipboardManager.setPrimaryClip(clip);
@ -516,20 +514,18 @@ public class Utils {
} }
public static int dpToPx(float dp) { public static int dpToPx(float dp) {
Context mContext = contextRef.get(); if (context == null) {
if (mContext == null) {
return (int) dp; return (int) dp;
} else { } else {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mContext.getResources().getDisplayMetrics()); return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
} }
} }
public static int dpToPx(int dp) { public static int dpToPx(int dp) {
Context mContext = contextRef.get(); if (context == null) {
if (mContext == null) {
return dp; return dp;
} else { } else {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mContext.getResources().getDisplayMetrics()); return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
} }
} }
@ -550,20 +546,18 @@ public class Utils {
private static void showToast(@NonNull String messageToToast, int toastDuration) { private static void showToast(@NonNull String messageToToast, int toastDuration) {
Objects.requireNonNull(messageToToast); Objects.requireNonNull(messageToToast);
runOnMainThreadNowOrLater(() -> { runOnMainThreadNowOrLater(() -> {
Context mContext = contextRef.get(); if (context == null) {
if (mContext == null) {
Logger.initializationException(Utils.class, "Cannot show toast (context is null): " + messageToToast, null); Logger.initializationException(Utils.class, "Cannot show toast (context is null): " + messageToToast, null);
} else { } else {
Logger.printDebug(() -> "Showing toast: " + messageToToast); Logger.printDebug(() -> "Showing toast: " + messageToToast);
Toast.makeText(mContext, messageToToast, toastDuration).show(); Toast.makeText(context, messageToToast, toastDuration).show();
} }
}); });
} }
public static boolean isLandscapeOrientation() { public static boolean isLandscapeOrientation() {
Context mContext = contextRef.get(); if (context == null) return false;
if (mContext == null) return false; final int orientation = context.getResources().getConfiguration().orientation;
final int orientation = mContext.getResources().getConfiguration().orientation;
return orientation == Configuration.ORIENTATION_LANDSCAPE; return orientation == Configuration.ORIENTATION_LANDSCAPE;
} }
@ -654,8 +648,7 @@ public class Utils {
@SuppressLint("MissingPermission") // permission already included in YouTube @SuppressLint("MissingPermission") // permission already included in YouTube
public static NetworkType getNetworkType() { public static NetworkType getNetworkType() {
Context mContext = contextRef.get(); if (context == null || !(context.getSystemService(Context.CONNECTIVITY_SERVICE) instanceof ConnectivityManager cm))
if (mContext == null || !(mContext.getSystemService(Context.CONNECTIVITY_SERVICE) instanceof ConnectivityManager cm))
return NetworkType.NONE; return NetworkType.NONE;
final NetworkInfo networkInfo = cm.getActiveNetworkInfo(); final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
@ -762,7 +755,6 @@ public class Utils {
* If a preference has no key or no {@link Sort} suffix, * If a preference has no key or no {@link Sort} suffix,
* then the preferences are left unsorted. * then the preferences are left unsorted.
*/ */
@SuppressWarnings("deprecation")
public static void sortPreferenceGroups(@NonNull PreferenceGroup group) { public static void sortPreferenceGroups(@NonNull PreferenceGroup group) {
Sort groupSort = Sort.fromKey(group.getKey(), Sort.UNSORTED); Sort groupSort = Sort.fromKey(group.getKey(), Sort.UNSORTED);
SortedMap<String, Preference> preferences = new TreeMap<>(); SortedMap<String, Preference> preferences = new TreeMap<>();

View File

@ -25,8 +25,11 @@ import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.Utils; import app.revanced.extension.shared.utils.Utils;
public abstract class BottomControlButton { public abstract class BottomControlButton {
@Nullable
private static final Animation fadeIn; private static final Animation fadeIn;
@Nullable
private static final Animation fadeOut; private static final Animation fadeOut;
@Nullable
private static final Animation fadeOutImmediate; private static final Animation fadeOutImmediate;
private final ColorFilter cf = private final ColorFilter cf =
@ -40,29 +43,35 @@ public abstract class BottomControlButton {
static { static {
fadeIn = getAnimation("fade_in"); fadeIn = getAnimation("fade_in");
// android.R.integer.config_shortAnimTime, 200 if (fadeIn != null) {
fadeIn.setDuration(getInteger("fade_duration_fast")); // android.R.integer.config_shortAnimTime, 200
fadeIn.setDuration(getInteger("fade_duration_fast"));
}
fadeOut = getAnimation("fade_out"); fadeOut = getAnimation("fade_out");
// android.R.integer.config_mediumAnimTime, 400 if (fadeOut != null) {
fadeOut.setDuration(getInteger("fade_overlay_fade_duration")); // android.R.integer.config_mediumAnimTime, 400
fadeOut.setDuration(getInteger("fade_overlay_fade_duration"));
}
fadeOutImmediate = getAnimation("abc_fade_out"); fadeOutImmediate = getAnimation("abc_fade_out");
// android.R.integer.config_shortAnimTime, 200 if (fadeOutImmediate != null) {
fadeOutImmediate.setDuration(getInteger("fade_duration_fast")); // android.R.integer.config_shortAnimTime, 200
fadeOutImmediate.setDuration(getInteger("fade_duration_fast"));
}
} }
@NonNull @Nullable
public static Animation getButtonFadeIn() { public static Animation getButtonFadeIn() {
return fadeIn; return fadeIn;
} }
@NonNull @Nullable
public static Animation getButtonFadeOut() { public static Animation getButtonFadeOut() {
return fadeOut; return fadeOut;
} }
@NonNull @Nullable
public static Animation getButtonFadeOutImmediate() { public static Animation getButtonFadeOutImmediate() {
return fadeOutImmediate; return fadeOutImmediate;
} }
@ -153,11 +162,15 @@ public abstract class BottomControlButton {
imageView.clearAnimation(); imageView.clearAnimation();
if (visible && setting.get()) { if (visible && setting.get()) {
imageView.setVisibility(View.VISIBLE); imageView.setVisibility(View.VISIBLE);
if (animation) imageView.startAnimation(fadeIn); if (animation && fadeIn != null) {
imageView.startAnimation(fadeIn);
}
return; return;
} }
if (imageView.getVisibility() == View.VISIBLE) { if (imageView.getVisibility() == View.VISIBLE) {
if (animation) imageView.startAnimation(fadeOut); if (animation && fadeOut != null) {
imageView.startAnimation(fadeOut);
}
imageView.setVisibility(View.GONE); imageView.setVisibility(View.GONE);
} }
} }
@ -168,7 +181,9 @@ public abstract class BottomControlButton {
if (!setting.get()) return; if (!setting.get()) return;
imageView.clearAnimation(); imageView.clearAnimation();
imageView.startAnimation(fadeOutImmediate); if (fadeOutImmediate != null) {
imageView.startAnimation(fadeOutImmediate);
}
imageView.setVisibility(View.GONE); imageView.setVisibility(View.GONE);
} }
} }

View File

@ -3,6 +3,7 @@ package app.revanced.extension.youtube.sponsorblock.ui;
import static app.revanced.extension.shared.utils.Utils.getChildView; import static app.revanced.extension.shared.utils.Utils.getChildView;
import android.view.View; import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView; import android.widget.ImageView;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -45,7 +46,10 @@ public class CreateSegmentButtonController {
return; return;
} }
if (animation) { if (animation) {
imageView.startAnimation(BottomControlButton.getButtonFadeIn()); Animation fadeIn = BottomControlButton.getButtonFadeIn();
if (fadeIn != null) {
imageView.startAnimation(fadeIn);
}
} }
imageView.setVisibility(View.VISIBLE); imageView.setVisibility(View.VISIBLE);
return; return;
@ -53,7 +57,10 @@ public class CreateSegmentButtonController {
if (imageView.getVisibility() == View.VISIBLE) { if (imageView.getVisibility() == View.VISIBLE) {
imageView.clearAnimation(); imageView.clearAnimation();
if (animation) { if (animation) {
imageView.startAnimation(BottomControlButton.getButtonFadeOut()); Animation fadeOut = BottomControlButton.getButtonFadeOut();
if (fadeOut != null) {
imageView.startAnimation(fadeOut);
}
} }
imageView.setVisibility(View.GONE); imageView.setVisibility(View.GONE);
} }
@ -65,7 +72,10 @@ public class CreateSegmentButtonController {
if (!shouldBeShown()) return; if (!shouldBeShown()) return;
imageView.clearAnimation(); imageView.clearAnimation();
imageView.startAnimation(BottomControlButton.getButtonFadeOutImmediate()); Animation fadeOutImmediate = BottomControlButton.getButtonFadeOutImmediate();
if (fadeOutImmediate != null) {
imageView.startAnimation(fadeOutImmediate);
}
imageView.setVisibility(View.GONE); imageView.setVisibility(View.GONE);
} }

View File

@ -4,6 +4,7 @@ import static app.revanced.extension.shared.utils.Utils.getChildView;
import static app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController.videoHasSegments; import static app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController.videoHasSegments;
import android.view.View; import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView; import android.widget.ImageView;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -47,7 +48,10 @@ public class VotingButtonController {
return; return;
} }
if (animation) { if (animation) {
imageView.startAnimation(BottomControlButton.getButtonFadeIn()); Animation fadeIn = BottomControlButton.getButtonFadeIn();
if (fadeIn != null) {
imageView.startAnimation(fadeIn);
}
} }
imageView.setVisibility(View.VISIBLE); imageView.setVisibility(View.VISIBLE);
return; return;
@ -55,7 +59,10 @@ public class VotingButtonController {
if (imageView.getVisibility() == View.VISIBLE) { if (imageView.getVisibility() == View.VISIBLE) {
imageView.clearAnimation(); imageView.clearAnimation();
if (animation) { if (animation) {
imageView.startAnimation(BottomControlButton.getButtonFadeOut()); Animation fadeOut = BottomControlButton.getButtonFadeOut();
if (fadeOut != null) {
imageView.startAnimation(fadeOut);
}
} }
imageView.setVisibility(View.GONE); imageView.setVisibility(View.GONE);
} }
@ -68,7 +75,10 @@ public class VotingButtonController {
imageView.clearAnimation(); imageView.clearAnimation();
imageView.startAnimation(BottomControlButton.getButtonFadeOutImmediate()); Animation fadeOutImmediate = BottomControlButton.getButtonFadeOutImmediate();
if (fadeOutImmediate != null) {
imageView.startAnimation(fadeOutImmediate);
}
imageView.setVisibility(View.GONE); imageView.setVisibility(View.GONE);
} }