Skip to content
This repository was archived by the owner on Dec 11, 2024. It is now read-only.

fix(YouTube - Return YouTube Dislike): Disabling Show dislikes in Shorts disabled dislikes everywhere #11

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
@GuardedBy("itself")
private static final Map<String, Boolean> lastVideoIds = new LinkedHashMap<>() {
/**
* Number of video id's to keep track of for searching thru the buffer.
* Number of video id's to keep track of for searching through the buffer.
* A minimum value of 3 should be sufficient, but check a few more just in case.
*/
private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 5;
Expand Down Expand Up @@ -64,6 +64,7 @@ public ReturnYouTubeDislikeFilterPatch() {
/**
* Injection point.
*/
@SuppressWarnings("unused")
public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOpeningOrPlaying) {
try {
if (!isShortAndOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean()) {
Expand Down Expand Up @@ -110,7 +111,7 @@ boolean isFiltered(String path, @Nullable String identifier, String allValue, by
String matchedVideoId = findVideoId(protobufBufferArray);
// Matched video will be null if in incognito mode.
// Must pass a null id to correctly clear out the current video data.
// Otherwise if a Short is opened in non-incognito, then incognito is enabled and another Short is opened,
// Otherwise, if a Short is opened in non-incognito, then incognito is enabled and another Short is opened,
// the new incognito Short will show the old prior data.
ReturnYouTubeDislikePatch.setLastLithoShortsVideoId(matchedVideoId);
}
Expand All @@ -129,4 +130,4 @@ private String findVideoId(byte[] protobufBufferArray) {
return null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.text.Editable;
import android.text.Spannable;
import android.text.SpannableString;
Expand All @@ -25,6 +26,7 @@
import app.revanced.integrations.youtube.patches.components.ReturnYouTubeDislikeFilterPatch;
import app.revanced.integrations.youtube.patches.video.VideoInformation;
import app.revanced.integrations.youtube.returnyoutubedislike.ReturnYouTubeDislike;
import app.revanced.integrations.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
import app.revanced.integrations.youtube.settings.SettingsEnum;
import app.revanced.integrations.youtube.shared.PlayerType;
import app.revanced.integrations.youtube.utils.LogHelper;
Expand All @@ -41,7 +43,7 @@
* <p>
* A (yet to be implemented) solution that fixes this problem. Any one of:
* - Modify patch to hook onto the Shorts Litho TextView, and update the dislikes text asynchronously.
* - Find a way to force Litho to rebuild it's component tree,
* - Find a way to force Litho to rebuild its component tree,
* and use that hook to force the shorts dislikes to update after the fetch is completed.
* - Hook into the dislikes button image view, and replace the dislikes thumb down image with a
* generated image of the number of dislikes, then update the image asynchronously. This Could
Expand Down Expand Up @@ -81,17 +83,16 @@ public class ReturnYouTubeDislikePatch {
private static volatile boolean lithoShortsShouldUseCurrentData;

/**
* Last video id prefetched. Field is prevent prefetching the same video id multiple times in a row.
* Last video id prefetched. Field is to prevent prefetching the same video id multiple times in a row.
*/
@Nullable
private static volatile String lastPrefetchedVideoId;

public static void onRYDStatusChange(boolean rydEnabled) {
if (!rydEnabled) {
// Must remove all values to protect against using stale data
// if the user enables RYD while a video is on screen.
clearData();
}
ReturnYouTubeDislikeApi.resetRateLimits();
// Must remove all values to protect against using stale data
// if the user enables RYD while a video is on screen.
clearData();
}

private static void clearData() {
Expand Down Expand Up @@ -220,7 +221,7 @@ public static CharSequence onLithoTextLoaded(@NonNull Object conversionContext,
* 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 original Original char sequence was created or reused by Litho.
* @param original Original char sequence was created or reused by Litho.
* @param isRollingNumber If the span is for a Rolling Number.
* @return The original char sequence (if nothing should change), or a replacement char sequence that contains dislikes.
*/
Expand Down Expand Up @@ -312,18 +313,14 @@ public static CharSequence onCharSequenceLoaded(@NonNull Object conversionContex
return original;
}
if (!SettingsEnum.RYD_SHORTS.getBoolean()) {
// Must clear the current video here, otherwise if the user opens a regular video
// then opens a litho short (while keeping the regular video on screen), then closes the short,
// the original video may show the incorrect dislike value.
clearData();
return original;
}

final boolean fetchDislikeIncognito =
conversionContextString.contains("|shorts_dislike_button.eml|")
&& isIncognito;
final boolean fetchDislikeLiveStream =
conversionContextString.contains("immersive_live_video_action_bar.eml")
conversionContextString.contains("|immersive_live_video_action_bar.eml|")
&& conversionContextString.contains("|dislike_button.eml|");

if (fetchDislikeIncognito) {
Expand Down Expand Up @@ -364,9 +361,10 @@ public static String onRollingNumberLoaded(@NonNull Object conversionContext,
@NonNull String original) {
try {
CharSequence replacement = onLithoTextLoaded(conversionContext, original, true);
if (!replacement.toString().equals(original)) {
String replacementString = replacement.toString();
if (!replacementString.equals(original)) {
rollingNumberSpan = replacement;
return replacement.toString();
return replacementString;
} // Else, the text was not a likes count but instead the view count or something else.
} catch (Exception ex) {
LogHelper.printException(() -> "onRollingNumberLoaded failure", ex);
Expand Down Expand Up @@ -400,6 +398,7 @@ public static float onRollingNumberMeasured(String text, float measuredTextWidth
} catch (Exception ex) {
LogHelper.printException(() -> "onRollingNumberMeasured failure", ex);
}

return measuredTextWidth;
}

Expand All @@ -417,11 +416,12 @@ private static void addRollingNumberPatchChanges(TextView view) {
} else {
view.setCompoundDrawables(separator, null, null, null);
}
// Liking/disliking can cause the span to grow in size,
// which is ok and is laid out correctly,
// but if the user then undoes their action the layout will not remove the extra padding.

// Disliking can cause the span to grow in size, which is ok and is laid out correctly,
// but if the user then removes their dislike the layout will not adjust to the new shorter width.
// Use a center alignment to take up any extra space.
view.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);

// Single line mode does not clip words if the span is larger than the view bounds.
// The styled span applied to the view should always have the same bounds,
// but use this feature just in case the measurements are somehow off by a few pixels.
Expand Down Expand Up @@ -490,7 +490,7 @@ public static CharSequence updateRollingNumber(TextView view, CharSequence origi
}

//
// Non litho Shorts player.
// Non-litho Shorts player.
//

/**
Expand All @@ -507,7 +507,9 @@ public static CharSequence updateRollingNumber(TextView view, CharSequence origi
private static final List<WeakReference<TextView>> shortsTextViewRefs = new ArrayList<>();

private static void clearRemovedShortsTextViews() {
shortsTextViewRefs.removeIf(ref -> ref.get() == null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // YouTube requires Android N or greater
shortsTextViewRefs.removeIf(ref -> ref.get() == null);
}
}

/**
Expand Down Expand Up @@ -621,7 +623,7 @@ private static boolean isShortTextViewOnScreen(@NonNull View view) {


//
// Video Id and voting hooks (all players).
// Video ID and voting hooks (all players).
//

private static volatile boolean lastPlayerResponseWasShort;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.graphics.drawable.shapes.OvalShape;
import android.graphics.drawable.shapes.RectShape;
import android.icu.text.CompactDecimalFormat;
import android.os.Build;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
Expand Down Expand Up @@ -150,7 +151,7 @@ public enum Vote {
private final Future<RYDVoteData> future;

/**
* Time this instance and the future was created.
* Time this instance and the fetch future was created.
*/
private final long timeFetched;

Expand Down Expand Up @@ -184,12 +185,12 @@ public enum Vote {

/**
* Color of the left and middle separator, based on the color of the right separator.
* It's unknown where YT gets the color from, and the colors here are approximated by hand.
* Ideally, the color here would be the actual color YT uses at runtime.
* It's unknown where YT gets the color from, and the values here are approximated by hand.
* Ideally, this would be the actual color YT uses at runtime.
*
* Older versions before the 'Me' library tab use a slightly different color.
* If spoofing was previously used and is now turned off,
* or an old version was recently upgraded then the old colors are sometimes used.
* or an old version was recently upgraded then the old colors are sometimes still used.
*/
private static int getSeparatorColor() {
if (IS_SPOOFING_TO_OLD_SEPARATOR_COLOR) {
Expand Down Expand Up @@ -250,7 +251,7 @@ private static SpannableString createDislikeSpan(@NonNull Spanned oldSpannable,
: "\u200E"; // u200E = left to right character
final Spannable leftSeparatorSpan;
if (isRollingNumber) {
leftSeparatorSpan = new SpannableString(leftSeparatorString);
leftSeparatorSpan = new SpannableString(leftSeparatorString);
} else {
leftSeparatorString += " ";
leftSeparatorSpan = new SpannableString(leftSeparatorString);
Expand Down Expand Up @@ -350,18 +351,23 @@ private static SpannableString newSpanUsingStylingOfAnotherSpan(@NonNull Spanned
}

private static String formatDislikeCount(long dislikeCount) {
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.234" into "۱,۲۳٤"
// But YouTube disregards locale specific number characters
// and instead shows english number characters everywhere.
Locale locale = Objects.requireNonNull(ReVancedUtils.getContext()).getResources().getConfiguration().locale;
LogHelper.printDebug(() -> "Locale: " + locale);
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
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.234" into "۱,۲۳٤"
// But YouTube disregards locale specific number characters
// and instead shows english number characters everywhere.
Locale locale = Objects.requireNonNull(ReVancedUtils.getContext()).getResources().getConfiguration().locale;
LogHelper.printDebug(() -> "Locale: " + locale);
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
}
return dislikeCountFormatter.format(dislikeCount);
}
return dislikeCountFormatter.format(dislikeCount);
}

// Will never be reached, as the oldest supported YouTube app requires Android N or greater.
return String.valueOf(dislikeCount);
}

private static String formatDislikePercentage(float dislikePercentage) {
Expand All @@ -385,13 +391,15 @@ public static ReturnYouTubeDislike getFetchForVideoId(@Nullable String videoId)
Objects.requireNonNull(videoId);
synchronized (fetchCache) {
// Remove any expired entries.
final long now = System.currentTimeMillis();
fetchCache.values().removeIf(value -> {
final boolean expired = value.isExpired(now);
if (expired)
LogHelper.printDebug(() -> "Removing expired fetch: " + value.videoId);
return expired;
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
final long now = System.currentTimeMillis();
fetchCache.values().removeIf(value -> {
final boolean expired = value.isExpired(now);
if (expired)
LogHelper.printDebug(() -> "Removing expired fetch: " + value.videoId);
return expired;
});
}

ReturnYouTubeDislike fetch = fetchCache.get(videoId);
if (fetch == null) {
Expand All @@ -403,7 +411,7 @@ public static ReturnYouTubeDislike getFetchForVideoId(@Nullable String videoId)
}

/**
* Should be called if the user changes settings for dislikes appearance.
* Should be called if the user changes dislikes appearance settings.
*/
public static void clearAllUICaches() {
synchronized (fetchCache) {
Expand Down Expand Up @@ -645,8 +653,8 @@ class VerticallyCenteredImageSpan extends ImageSpan {

/**
* @param useOriginalWidth Use the original layout width of the text this span is applied to,
* and not the bounds of the Drawable. Drawable is always displayed using it's own bounds,
* and this setting only affects the layout width of the entire span.
* and not the bounds of the Drawable. Drawable is always displayed using it's own bounds,
* and this setting only affects the layout width of the entire span.
*/
public VerticallyCenteredImageSpan(Drawable drawable, boolean useOriginalWidth) {
super(drawable);
Expand Down
Loading