diff --git a/app/src/main/java/app/revanced/integrations/patches/ReturnYouTubeDislikePatch.java b/app/src/main/java/app/revanced/integrations/patches/ReturnYouTubeDislikePatch.java index dd1d6e37..bbadf90d 100644 --- a/app/src/main/java/app/revanced/integrations/patches/ReturnYouTubeDislikePatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/ReturnYouTubeDislikePatch.java @@ -1,48 +1,108 @@ package app.revanced.integrations.patches; +import static app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike.Vote; + +import android.text.SpannableString; import android.text.Spanned; -import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike; + +import androidx.annotation.NonNull; import java.util.concurrent.atomic.AtomicReference; -/** - * TODO: delete this empty class, and point the patch to {@link ReturnYouTubeDislike} - */ +import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike; +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.LogHelper; + public class ReturnYouTubeDislikePatch { /** - * Injection point + * Injection point. */ public static void newVideoLoaded(String videoId) { - ReturnYouTubeDislike.newVideoLoaded(videoId); + try { + if (!SettingsEnum.RYD_ENABLED.getBoolean()) return; + ReturnYouTubeDislike.newVideoLoaded(videoId); + } catch (Exception ex) { + LogHelper.printException(() -> "newVideoLoaded failure", ex); + } } /** - * Injection point + * Injection point. * - * Called when a litho text component is created + * Called when a litho text component is initially created, + * and also when a Span is later reused again (such as scrolling off/on screen). + * + * This method is sometimes called on the main thread, but it usually is called _off_ the main thread. + * This method can be called multiple times for the same UI element (including after dislikes was added). + * + * @param textRef Cache reference to the like/dislike char sequence, + * which may or may not be the same as the original span parameter. + * If dislikes are added, the atomic reference must be set to the replacement span. + * @param original Original span that was created or reused by Litho. + * @return The original span (if nothing should change), or a replacement span that contains dislikes. */ - public static void onComponentCreated(Object conversionContext, AtomicReference textRef) { - ReturnYouTubeDislike.onComponentCreated(conversionContext, textRef); + @NonNull + public static CharSequence onLithoTextLoaded(@NonNull Object conversionContext, + @NonNull AtomicReference textRef, + @NonNull CharSequence original) { + try { + if (!SettingsEnum.RYD_ENABLED.getBoolean()) { + return original; + } + SpannableString replacement = ReturnYouTubeDislike.getDislikeSpanForContext(conversionContext, original); + if (replacement != null) { + textRef.set(replacement); + return replacement; + } + } catch (Exception ex) { + LogHelper.printException(() -> "onComponentCreated AtomicReference failure", ex); + } + return original; } /** - * Injection point + * Injection point. * - * Called when a Shorts dislike Spannable is created + * Called when a Shorts dislike Spanned is created. */ - public static Spanned onShortsComponentCreated(Spanned dislike) { - return ReturnYouTubeDislike.onShortsComponentCreated(dislike); + public static Spanned onShortsComponentCreated(Spanned original) { + try { + if (!SettingsEnum.RYD_ENABLED.getBoolean()) { + return original; + } + SpannableString replacement = ReturnYouTubeDislike.getDislikeSpanForShort(original); + if (replacement != null) { + return replacement; + } + } catch (Exception ex) { + LogHelper.printException(() -> "onShortsComponentCreated failure", ex); + } + return original; } /** - * Injection point + * Injection point. * - * Called when the like/dislike button is clicked + * Called when the user likes or dislikes. * - * @param vote -1 (dislike), 0 (none) or 1 (like) + * @param vote int that matches {@link ReturnYouTubeDislike.Vote#value} */ public static void sendVote(int vote) { - ReturnYouTubeDislike.sendVote(vote); + try { + if (!SettingsEnum.RYD_ENABLED.getBoolean()) { + return; + } + + for (Vote v : Vote.values()) { + if (v.value == vote) { + ReturnYouTubeDislike.sendVote(v); + return; + } + } + LogHelper.printException(() -> "Unknown vote type: " + vote); + } catch (Exception ex) { + LogHelper.printException(() -> "sendVote failure", ex); + } } } diff --git a/app/src/main/java/app/revanced/integrations/returnyoutubedislike/ReturnYouTubeDislike.java b/app/src/main/java/app/revanced/integrations/returnyoutubedislike/ReturnYouTubeDislike.java index d441c2b1..b256964b 100644 --- a/app/src/main/java/app/revanced/integrations/returnyoutubedislike/ReturnYouTubeDislike.java +++ b/app/src/main/java/app/revanced/integrations/returnyoutubedislike/ReturnYouTubeDislike.java @@ -31,7 +31,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReference; import app.revanced.integrations.returnyoutubedislike.requests.RYDVoteData; import app.revanced.integrations.returnyoutubedislike.requests.ReturnYouTubeDislikeApi; @@ -41,10 +40,13 @@ import app.revanced.integrations.utils.LogHelper; import app.revanced.integrations.utils.ReVancedUtils; import app.revanced.integrations.utils.ThemeHelper; +/** + * Because Litho creates spans using multiple threads, this entire class supports multithreading as well. + */ public class ReturnYouTubeDislike { /** * Maximum amount of time to block the UI from updates while waiting for network call to complete. - *

+ * * Must be less than 5 seconds, as per: * https://developer.android.com/topic/performance/vitals/anr */ @@ -52,18 +54,17 @@ public class ReturnYouTubeDislike { /** * Unique placeholder character, used to detect if a segmented span already has dislikes added to it. - * Can be any almost any non-visible character + * Can be any almost any non-visible character. */ private static final char MIDDLE_SEPARATOR_CHARACTER = '\u2009'; // 'narrow space' character /** - * Used to send votes, one by one, in the same order the user created them + * Used to send votes, one by one, in the same order the user created them. */ private static final ExecutorService voteSerialExecutor = Executors.newSingleThreadExecutor(); /** - * Used to guard {@link #currentVideoId} and {@link #voteFetchFuture}, - * as multiple threads access this class. + * Used to guard {@link #currentVideoId} and {@link #voteFetchFuture}. */ private static final Object videoIdLockObject = new Object(); @@ -71,14 +72,13 @@ public class ReturnYouTubeDislike { @GuardedBy("videoIdLockObject") private static String currentVideoId; - /** - * If {@link #currentVideoId} and the RYD data is for the last shorts loaded + * If {@link #currentVideoId} and the RYD data is for the last shorts loaded. */ private static volatile boolean lastVideoLoadedWasShort; /** - * Stores the results of the vote api fetch, and used as a barrier to wait until fetch completes + * Stores the results of the vote api fetch, and used as a barrier to wait until fetch completes. */ @Nullable @GuardedBy("videoIdLockObject") @@ -86,19 +86,31 @@ public class ReturnYouTubeDislike { /** * Original dislike span, before modifications. - * Required for segmented layout */ @Nullable @GuardedBy("videoIdLockObject") private static Spanned originalDislikeSpan; /** - * Replacement like/dislike span that includes formatted dislikes and is ready to display + * Replacement like/dislike span that includes formatted dislikes. + * Used to prevent recreating the same span multiple times. */ @Nullable @GuardedBy("videoIdLockObject") private static SpannableString replacementLikeDislikeSpan; + /** + * For formatting dislikes as number. + */ + @GuardedBy("ReturnYouTubeDislike.class") // not thread safe + private static CompactDecimalFormat dislikeCountFormatter; + + /** + * For formatting dislikes as percentage. + */ + @GuardedBy("ReturnYouTubeDislike.class") + private static NumberFormat dislikePercentageFormatter; + public enum Vote { LIKE(1), DISLIKE(-1), @@ -114,18 +126,6 @@ public class ReturnYouTubeDislike { private ReturnYouTubeDislike() { } // only static methods - /** - * Used to format like/dislike count. - */ - @GuardedBy("ReturnYouTubeDislike.class") // not thread safe - private static CompactDecimalFormat dislikeCountFormatter; - - /** - * Used to format like/dislike count. - */ - @GuardedBy("ReturnYouTubeDislike.class") - private static NumberFormat dislikePercentageFormatter; - public static void onEnabledChange(boolean enabled) { if (!enabled) { // Must clear old values, to protect against using stale data @@ -148,7 +148,7 @@ public class ReturnYouTubeDislike { } /** - * Should be called if user changes settings for dislikes appearance. + * Should be called after a user dislikes, or if the user changes settings for dislikes appearance. */ public static void clearCache() { synchronized (videoIdLockObject) { @@ -174,146 +174,113 @@ public class ReturnYouTubeDislike { } public static void newVideoLoaded(@NonNull String videoId) { - if (!SettingsEnum.RYD_ENABLED.getBoolean()) return; + Objects.requireNonNull(videoId); - try { - Objects.requireNonNull(videoId); - - PlayerType currentPlayerType = PlayerType.getCurrent(); - if (currentPlayerType == PlayerType.INLINE_MINIMAL) { - LogHelper.printDebug(() -> "Ignoring inline playback of video: " + videoId); + PlayerType currentPlayerType = PlayerType.getCurrent(); + if (currentPlayerType == PlayerType.INLINE_MINIMAL) { + LogHelper.printDebug(() -> "Ignoring inline playback of video: " + videoId); + setCurrentVideoId(null); + return; + } + synchronized (videoIdLockObject) { + if (videoId.equals(currentVideoId)) { + return; // already loaded + } + if (!ReVancedUtils.isNetworkConnected()) { // must do network check after verifying it's a new video id + LogHelper.printDebug(() -> "Network not connected, ignoring video: " + videoId); setCurrentVideoId(null); return; } - synchronized (videoIdLockObject) { - if (videoId.equals(currentVideoId)) { - return; // already loaded - } - if (!ReVancedUtils.isNetworkConnected()) { // must do network check after verifying it's a new video id - LogHelper.printDebug(() -> "Network not connected, ignoring video: " + videoId); - setCurrentVideoId(null); - return; - } - LogHelper.printDebug(() -> "New video loaded: " + videoId + " playerType: " + currentPlayerType); - setCurrentVideoId(videoId); + LogHelper.printDebug(() -> "New video loaded: " + videoId + " playerType: " + currentPlayerType); + setCurrentVideoId(videoId); - // If a Short is opened while a regular video is on screen, this will incorrectly set this as false. - // But this check is needed to fix unusual situations of opening/closing the app - // while both a regular video and a short are on screen. - lastVideoLoadedWasShort = PlayerType.getCurrent().isNoneOrHidden(); + // If a Short is opened while a regular video is on screen, this will incorrectly set this as false. + // But this check is needed to fix unusual situations of opening/closing the app + // while both a regular video and a short are on screen. + lastVideoLoadedWasShort = PlayerType.getCurrent().isNoneOrHidden(); - // no need to wrap the call in a try/catch, - // as any exceptions are propagated out in the later Future#Get call - voteFetchFuture = ReVancedUtils.submitOnBackgroundThread(() -> ReturnYouTubeDislikeApi.fetchVotes(videoId)); - } - } catch (Exception ex) { - LogHelper.printException(() -> "Failed to load new video: " + videoId, ex); + // No need to wrap the call in a try/catch, + // as any exceptions are propagated out in the later Future#Get call. + voteFetchFuture = ReVancedUtils.submitOnBackgroundThread(() -> ReturnYouTubeDislikeApi.fetchVotes(videoId)); } } /** - * Called when a litho text component is created. - * - * This method is sometimes called on the main thread, but it usually is called _off_ the main thread. - * This method can be called multiple times for the same UI element (including after dislikes was added) - * - * @param textRef atomic reference should always be non null, but the spanned reference inside can be null. + * @return NULL if the span does not need changing or if RYD is not available. */ - public static void onComponentCreated(@NonNull Object conversionContext, @NonNull AtomicReference textRef) { - try { - if (!SettingsEnum.RYD_ENABLED.getBoolean()) return; - - if (PlayerType.getCurrent().isNoneOrHidden()) { - return; - } - - String conversionContextString = conversionContext.toString(); - final boolean isSegmentedButton; - if (conversionContextString.contains("|segmented_like_dislike_button.eml|")) { - isSegmentedButton = true; - } else if (conversionContextString.contains("|dislike_button.eml|")) { - isSegmentedButton = false; - } else { - return; - } - - if (lastVideoLoadedWasShort) { - // user: - // 1, opened a video - // 2. opened a short (without closing the regular video) - // 3. closed the short - // 4. regular video is now present, but the videoId and RYD data is still for the short - LogHelper.printDebug(() -> "Ignoring onComponentCreated(), as data loaded is is for prior short"); - return; - } - - Spanned replacement = waitForFetchAndUpdateReplacementSpan((Spanned) textRef.get(), isSegmentedButton); - if (replacement != null) { - textRef.set(replacement); - } - } catch (Exception ex) { - LogHelper.printException(() -> "onComponentCreated failure", ex); + @Nullable + public static SpannableString getDislikeSpanForContext(@NonNull Object conversionContext, @NonNull CharSequence original) { + if (PlayerType.getCurrent().isNoneOrHidden()) { + return null; } + String conversionContextString = conversionContext.toString(); + final boolean isSegmentedButton; + if (conversionContextString.contains("|segmented_like_dislike_button.eml|")) { + isSegmentedButton = true; + } else if (conversionContextString.contains("|dislike_button.eml|")) { + isSegmentedButton = false; + } else { + return null; + } + + if (lastVideoLoadedWasShort) { + // user: + // 1, opened a video + // 2. opened a short (without closing the regular video) + // 3. closed the short + // 4. regular video is now present, but the videoId and RYD data is still for the short + LogHelper.printDebug(() -> "Ignoring getDislikeSpanForContext(), as data loaded is for prior short"); + return null; + } + + return waitForFetchAndUpdateReplacementSpan((Spannable) original, isSegmentedButton); } /** * Called when a Shorts dislike Spannable is created. */ - public static Spanned onShortsComponentCreated(Spanned original) { - try { - if (SettingsEnum.RYD_ENABLED.getBoolean()) { - lastVideoLoadedWasShort = true; // it's now certain the video and data are a short - Spanned replacement = waitForFetchAndUpdateReplacementSpan(original, false); - if (replacement != null) { - return replacement; - } - } - } catch (Exception ex) { - LogHelper.printException(() -> "onShortsComponentCreated failure", ex); - } - return original; + public static SpannableString getDislikeSpanForShort(@NonNull Spanned original) { + lastVideoLoadedWasShort = true; // it's now certain the video and data are a short + return waitForFetchAndUpdateReplacementSpan(original, false); } - // alternatively, this could check if the span contains one of the custom created spans, but this is simple and quick + // Alternatively, this could check if the span contains one of the custom created spans, but this is simple and quick. private static boolean isPreviouslyCreatedSegmentedSpan(@NonNull Spanned span) { return span.toString().indexOf(MIDDLE_SEPARATOR_CHARACTER) != -1; } /** - * @return NULL if the span does not need changing or if RYD is not available + * @return NULL if the span does not need changing or if RYD is not available. */ @Nullable - private static SpannableString waitForFetchAndUpdateReplacementSpan(@Nullable Spanned oldSpannable, boolean isSegmentedButton) { - if (oldSpannable == null) { - LogHelper.printDebug(() -> "Cannot add dislikes (injection code was called with null Span)"); - return null; - } + private static SpannableString waitForFetchAndUpdateReplacementSpan(@NonNull Spanned oldSpannable, boolean isSegmentedButton) { try { synchronized (videoIdLockObject) { - if (oldSpannable.equals(replacementLikeDislikeSpan)) { - LogHelper.printDebug(() -> "Ignoring span that already contains dislikes"); - return null; - } if (replacementLikeDislikeSpan != null) { - LogHelper.printDebug(() -> "Using previously created dislike span"); - return replacementLikeDislikeSpan; - } - if (isSegmentedButton) { - if (isPreviouslyCreatedSegmentedSpan(oldSpannable)) { - // need to recreate using original, as oldSpannable has prior outdated dislike values - oldSpannable = originalDislikeSpan; - if (oldSpannable == null) { - LogHelper.printDebug(() -> "Cannot add dislikes - original span is null"); // should never happen - return null; - } - } else { - originalDislikeSpan = oldSpannable; // most up to date original + String oldSpannableString = oldSpannable.toString(); + if (replacementLikeDislikeSpan.toString().equals(oldSpannableString)) { + LogHelper.printDebug(() -> "Ignoring previously created dislikes span"); + return null; } + if (originalDislikeSpan.toString().equals(oldSpannableString)) { + LogHelper.printDebug(() -> "Replacing span with previously created dislike span"); + return replacementLikeDislikeSpan; + } + } + if (isSegmentedButton && isPreviouslyCreatedSegmentedSpan(oldSpannable)) { + // need to recreate using original, as oldSpannable has prior outdated dislike values + oldSpannable = originalDislikeSpan; + if (oldSpannable == null) { + LogHelper.printDebug(() -> "Cannot add dislikes - original span is null"); // should never happen + return null; + } + } else { + originalDislikeSpan = oldSpannable; // most up to date original } } - // Must block the current thread until fetching is done - // There's no known way to edit the text after creation yet + // Must block the current thread until fetching is done. + // There's no known way to edit the text after creation yet. Future fetchFuture = getVoteFetchFuture(); if (fetchFuture == null) { LogHelper.printDebug(() -> "fetch future not available (user enabled RYD while video was playing?)"); @@ -340,37 +307,16 @@ public class ReturnYouTubeDislike { return null; } - /** - * Called when the like/dislike button is clicked. - * - * @param vote int that matches {@link Vote#value} - */ - public static void sendVote(int vote) { - if (!SettingsEnum.RYD_ENABLED.getBoolean()) return; - - try { - for (Vote v : Vote.values()) { - if (v.value == vote) { - sendVote(v); - return; - } - } - LogHelper.printException(() -> "Unknown vote type: " + vote); - } catch (Exception ex) { - LogHelper.printException(() -> "sendVote failure", ex); - } - } - - private static void sendVote(@NonNull Vote vote) { + public static void sendVote(@NonNull Vote vote) { ReVancedUtils.verifyOnMainThread(); Objects.requireNonNull(vote); try { - // Must make a local copy of videoId, since it may change between now and when the vote thread runs + // Must make a local copy of videoId, since it may change between now and when the vote thread runs. String videoIdToVoteFor = getCurrentVideoId(); if (videoIdToVoteFor == null || lastVideoLoadedWasShort != PlayerType.getCurrent().isNoneOrHidden()) { // User enabled RYD after starting playback of a video. // Or shorts was loaded with regular video present, then shorts was closed, - // and then user voted on the now visible original video + // and then user voted on the now visible original video. // Cannot send a vote, because the loaded videoId is for the wrong video. ReVancedUtils.showToastLong(str("revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted")); return; @@ -387,15 +333,15 @@ public class ReturnYouTubeDislike { } }); - clearCache(); // ui values need updating + clearCache(); // UI needs updating - // update the downloaded vote data + // Update the downloaded vote data. Future future = getVoteFetchFuture(); if (future == null) { LogHelper.printException(() -> "Cannot update UI dislike count - vote fetch is null"); return; } - // the future should always be completed before user can like/dislike, but use a timeout just in case + // The future should always be completed before user can like/dislike, but use a timeout just in case. RYDVoteData voteData = future.get(MAX_MILLISECONDS_TO_BLOCK_UI_WHILE_WAITING_FOR_FETCH_VOTES_TO_COMPLETE, TimeUnit.MILLISECONDS); if (voteData == null) { // RYD fetch failed @@ -409,10 +355,10 @@ public class ReturnYouTubeDislike { } /** - * Must call off main thread, as this will make a network call if user is not yet registered + * Must call off main thread, as this will make a network call if user is not yet registered. * * @return ReturnYouTubeDislike user ID. If user registration has never happened - * and the network call fails, this returns NULL + * and the network call fails, this returns NULL. */ @Nullable private static String getUserId() { @@ -423,7 +369,7 @@ public class ReturnYouTubeDislike { return userId; } - userId = ReturnYouTubeDislikeApi.registerAsNewUser(); // blocks until network call is completed + userId = ReturnYouTubeDislikeApi.registerAsNewUser(); if (userId != null) { SettingsEnum.RYD_USER_ID.saveValue(userId); } @@ -431,25 +377,25 @@ public class ReturnYouTubeDislike { } /** - * @param isSegmentedButton if UI is using the segmented single UI component for both like and dislike + * @param isSegmentedButton If UI is using the segmented single UI component for both like and dislike. */ private static SpannableString createDislikeSpan(@NonNull Spanned oldSpannable, boolean isSegmentedButton, @NonNull RYDVoteData voteData) { if (!isSegmentedButton) { - // simple replacement of 'dislike' with a number/percentage + // Simple replacement of 'dislike' with a number/percentage. return newSpannableWithDislikes(oldSpannable, voteData); } - // note: some locales use right to left layout (arabic, hebrew, etc), - // and care must be taken to retain the existing RTL encoding character on the likes string - // otherwise text will incorrectly show as left to right - // if making changes to this code, change device settings to a RTL language and verify layout is correct + // Note: Some locales use right to left layout (arabic, hebrew, etc), + // and care must be taken to retain the existing RTL encoding character on the likes string, + // otherwise text will incorrectly show as left to right. + // If making changes to this code, change device settings to a RTL language and verify layout is correct. String oldLikesString = oldSpannable.toString(); // YouTube creators can hide the like count on a video, - // and the like count appears as a device language specific string that says 'Like' - // check if the string contains any numbers + // and the like count appears as a device language specific string that says 'Like'. + // Check if the string contains any numbers. if (!stringContainsNumber(oldLikesString)) { - // likes are hidden. + // Likes are hidden. // RYD does not provide usable data for these types of videos, // and the API returns bogus data (zero likes and zero dislikes) // discussion about this: https://github.com/Anarios/return-youtube-dislike/discussions/530 @@ -457,7 +403,7 @@ public class ReturnYouTubeDislike { // example video: https://www.youtube.com/watch?v=UnrU5vxCHxw // RYD data: https://returnyoutubedislikeapi.com/votes?videoId=UnrU5vxCHxw // - // Change the "Likes" string to show that likes and dislikes are hidden + // Change the "Likes" string to show that likes and dislikes are hidden. String hiddenMessageString = str("revanced_ryd_video_likes_hidden_by_video_owner"); return newSpanUsingStylingOfAnotherSpan(oldSpannable, hiddenMessageString); } @@ -467,7 +413,7 @@ public class ReturnYouTubeDislike { final int separatorColor = ThemeHelper.isDarkTheme() ? 0x29AAAAAA // transparent dark gray : 0xFFD9D9D9; // light gray - DisplayMetrics dp = ReVancedUtils.getContext().getResources().getDisplayMetrics(); + DisplayMetrics dp = Objects.requireNonNull(ReVancedUtils.getContext()).getResources().getDisplayMetrics(); if (!compactLayout) { // left separator @@ -511,9 +457,9 @@ public class ReturnYouTubeDislike { } /** - * Correctly handles any unicode numbers (such as Arabic numbers) + * Correctly handles any unicode numbers (such as Arabic numbers). * - * @return if the string contains at least 1 number + * @return if the string contains at least 1 number. */ private static boolean stringContainsNumber(@NonNull String text) { for (int index = 0, length = text.length(); index < length; index++) { @@ -545,10 +491,10 @@ public class ReturnYouTubeDislike { synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize if (dislikeCountFormatter == null) { // Note: Java number formatters will use the locale specific number characters. - // such as Arabic which formats "1.2" into "١٫٢" + // such as Arabic which formats "1.234" into "۱,۲۳٤" // But YouTube disregards locale specific number characters // and instead shows english number characters everywhere. - Locale locale = ReVancedUtils.getContext().getResources().getConfiguration().locale; + Locale locale = Objects.requireNonNull(ReVancedUtils.getContext()).getResources().getConfiguration().locale; LogHelper.printDebug(() -> "Locale: " + locale); dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT); } @@ -563,7 +509,7 @@ public class ReturnYouTubeDislike { private static String formatDislikePercentage(float dislikePercentage) { synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize if (dislikePercentageFormatter == null) { - Locale locale = ReVancedUtils.getContext().getResources().getConfiguration().locale; + Locale locale = Objects.requireNonNull(ReVancedUtils.getContext()).getResources().getConfiguration().locale; LogHelper.printDebug(() -> "Locale: " + locale); dislikePercentageFormatter = NumberFormat.getPercentInstance(locale); } diff --git a/app/src/main/java/app/revanced/integrations/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java b/app/src/main/java/app/revanced/integrations/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java index ae154cfe..970f3e48 100644 --- a/app/src/main/java/app/revanced/integrations/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java +++ b/app/src/main/java/app/revanced/integrations/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java @@ -203,7 +203,7 @@ public class ReturnYouTubeDislikeApi { @SuppressWarnings("NonAtomicOperationOnVolatileField") // do not want to pay performance cost of full synchronization for debug fields that are only estimates anyways private static void updateStatistics(long timeNetworkCallStarted, long timeNetworkCallEnded, boolean connectionError, boolean rateLimitHit) { if (connectionError && rateLimitHit) { - throw new IllegalArgumentException("both connection error and rate limit parameter were true"); + throw new IllegalArgumentException(); } final long responseTimeOfFetchCall = timeNetworkCallEnded - timeNetworkCallStarted; fetchCallResponseTimeTotal += responseTimeOfFetchCall; @@ -320,7 +320,7 @@ public class ReturnYouTubeDislikeApi { return confirmRegistration(userId, solution); } LogHelper.printException(() -> "Failed to register new user: " + userId - + " response code was: " + responseCode); + + " response code was: " + responseCode); // failed attempt, and ok to log userId connection.disconnect(); } catch (Exception ex) { LogHelper.printException(() -> "Failed to register user", ex); @@ -337,7 +337,7 @@ public class ReturnYouTubeDislikeApi { if (checkIfRateLimitInEffect("confirmRegistration")) { return null; } - LogHelper.printDebug(() -> "Trying to confirm registration for user: " + userId + " with solution: " + solution); + LogHelper.printDebug(() -> "Trying to confirm registration with solution: " + solution); HttpURLConnection connection = getRYDConnectionFromRoute(ReturnYouTubeDislikeRoutes.CONFIRM_REGISTRATION, userId); applyCommonPostRequestSettings(connection); @@ -355,7 +355,7 @@ public class ReturnYouTubeDislikeApi { if (responseCode == HTTP_STATUS_CODE_SUCCESS) { String result = Requester.parseJson(connection); if (result.equalsIgnoreCase("true")) { - LogHelper.printDebug(() -> "Registration confirmation successful for user: " + userId); + LogHelper.printDebug(() -> "Registration confirmation successful"); return userId; } LogHelper.printException(() -> "Failed to confirm registration for user: " + userId @@ -382,8 +382,7 @@ public class ReturnYouTubeDislikeApi { if (checkIfRateLimitInEffect("sendVote")) { return false; } - LogHelper.printDebug(() -> "Trying to vote for video: " - + videoId + " with vote: " + vote + " user: " + userId); + LogHelper.printDebug(() -> "Trying to vote for video: " + videoId + " with vote: " + vote); HttpURLConnection connection = getRYDConnectionFromRoute(ReturnYouTubeDislikeRoutes.SEND_VOTE); applyCommonPostRequestSettings(connection); @@ -408,11 +407,10 @@ public class ReturnYouTubeDislikeApi { return confirmVote(videoId, userId, solution); } LogHelper.printException(() -> "Failed to send vote for video: " + videoId - + " userId: " + userId + " vote: " + vote + " response code was: " + responseCode); + + " vote: " + vote + " response code was: " + responseCode); connection.disconnect(); // something went wrong, might as well disconnect } catch (Exception ex) { - LogHelper.printException(() -> "Failed to send vote for video: " + videoId - + " user: " + userId + " vote: " + vote, ex); + LogHelper.printException(() -> "Failed to send vote for video: " + videoId + " vote: " + vote, ex); } return false; } @@ -427,8 +425,7 @@ public class ReturnYouTubeDislikeApi { if (checkIfRateLimitInEffect("confirmVote")) { return false; } - LogHelper.printDebug(() -> "Trying to confirm vote for video: " - + videoId + " user: " + userId + " solution: " + solution); + LogHelper.printDebug(() -> "Trying to confirm vote for video: " + videoId + " solution: " + solution); HttpURLConnection connection = getRYDConnectionFromRoute(ReturnYouTubeDislikeRoutes.CONFIRM_VOTE); applyCommonPostRequestSettings(connection); @@ -450,15 +447,15 @@ public class ReturnYouTubeDislikeApi { return true; } LogHelper.printException(() -> "Failed to confirm vote for video: " + videoId - + " user: " + userId + " solution: " + solution + " response string was: " + result); + + " solution: " + solution + " response string was: " + result); } else { LogHelper.printException(() -> "Failed to confirm vote for video: " + videoId - + " user: " + userId + " solution: " + solution + " response code was: " + responseCode); + + " solution: " + solution + " response code was: " + responseCode); } connection.disconnect(); // something went wrong, might as well disconnect } catch (Exception ex) { LogHelper.printException(() -> "Failed to confirm vote for video: " + videoId - + " user: " + userId + " solution: " + solution, ex); + + " solution: " + solution, ex); } return false; }