fix(youtube/return-youtube-dislike): support old UI layouts (#378)

This commit is contained in:
LisoUseInAIKyrios 2023-04-30 23:41:13 +04:00 committed by GitHub
parent ff0d64287c
commit d3f8fb9739
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 118 additions and 28 deletions

View File

@ -2,23 +2,114 @@ package app.revanced.integrations.patches;
import static app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike.Vote;
import android.text.Editable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextWatcher;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicReference;
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
public class ReturnYouTubeDislikePatch {
/**
* Resource identifier of old UI dislike button.
*/
private static final int OLD_UI_DISLIKE_BUTTON_RESOURCE_ID
= ReVancedUtils.getResourceIdentifier("dislike_button", "id");
/**
* Dislikes text label used by old UI.
*/
@NonNull
private static WeakReference<TextView> oldUITextViewRef = new WeakReference<>(null);
/**
* Original old UI 'Dislikes' text before patch modifications.
* Required to reset the dislikes when changing videos and RYD is not available.
* Set only once during the first load.
*/
private static Spanned oldUIOriginalSpan;
/**
* Replacement span that contains dislike value. Used by {@link #oldUiTextWatcher}.
*/
@Nullable
private static Spanned oldUIReplacementSpan;
/**
* Old UI dislikes can be set multiple times by YouTube.
* To prevent it from reverting changes made here, this listener overrides any future changes YouTube makes.
*/
private static final TextWatcher oldUiTextWatcher = new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void afterTextChanged(Editable s) {
if (oldUIReplacementSpan == null || oldUIReplacementSpan.toString().equals(s.toString())) {
return;
}
s.replace(0, s.length(), oldUIReplacementSpan);
}
};
private static void updateOldUIDislikesTextView() {
TextView oldUITextView = oldUITextViewRef.get();
if (oldUITextView == null) {
return;
}
Spanned dislikes = ReturnYouTubeDislike.getDislikesSpanForRegularVideo(oldUIOriginalSpan, false);
if (dislikes == null) { // Dislikes not available.
// Must reset text back to original as the TextView may contain dislikes of a prior video.
dislikes = oldUIOriginalSpan;
}
oldUIReplacementSpan = dislikes;
if (!dislikes.equals(oldUITextView.getText())) {
oldUITextView.setText(dislikes);
}
}
/**
* Injection point. Called on main thread.
*
* Used when spoofing the older app versions of {@link SpoofAppVersionPatch}.
*/
public static void setOldUILayoutDislikes(int buttonViewResourceId, @NonNull TextView textView) {
try {
if (!SettingsEnum.RYD_ENABLED.getBoolean()
|| buttonViewResourceId != OLD_UI_DISLIKE_BUTTON_RESOURCE_ID) {
return;
}
if (oldUIOriginalSpan == null) {
// Use value of the first instance, as it appears TextViews can be recycled
// and might contain dislikes previously added by the patch.
oldUIOriginalSpan = (Spanned) textView.getText();
}
oldUITextViewRef = new WeakReference<>(textView);
// No way to check if a listener is already attached, so remove and add again.
textView.removeTextChangedListener(oldUiTextWatcher);
textView.addTextChangedListener(oldUiTextWatcher);
updateOldUIDislikesTextView();
} catch (Exception ex) {
LogHelper.printException(() -> "setOldUILayoutDislikes failure", ex);
}
}
/**
* Injection point.
*/
public static void newVideoLoaded(String videoId) {
public static void newVideoLoaded(@NonNull String videoId) {
try {
if (!SettingsEnum.RYD_ENABLED.getBoolean()) return;
ReturnYouTubeDislike.newVideoLoaded(videoId);
@ -97,6 +188,7 @@ public class ReturnYouTubeDislikePatch {
for (Vote v : Vote.values()) {
if (v.value == vote) {
ReturnYouTubeDislike.sendVote(v);
updateOldUIDislikesTextView();
return;
}
}

View File

@ -76,7 +76,7 @@ public class ReturnYouTubeDislike {
/**
* If {@link #currentVideoId} and the RYD data is for the last shorts loaded.
*/
private static volatile boolean lastVideoLoadedWasShort;
private static volatile boolean dislikeDataIsShort;
/**
* Stores the results of the vote api fetch, and used as a barrier to wait until fetch completes.
@ -141,7 +141,7 @@ public class ReturnYouTubeDislike {
LogHelper.printDebug(() -> "Clearing data");
}
currentVideoId = videoId;
lastVideoLoadedWasShort = false;
dislikeDataIsShort = false;
voteFetchFuture = null;
originalDislikeSpan = null;
replacementLikeDislikeSpan = null;
@ -198,7 +198,7 @@ public class ReturnYouTubeDislike {
// 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();
dislikeDataIsShort = 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.
@ -224,7 +224,15 @@ public class ReturnYouTubeDislike {
return null;
}
if (lastVideoLoadedWasShort) {
return getDislikesSpanForRegularVideo((Spannable) original, isSegmentedButton);
}
/**
* @return NULL if the span does not need changing or if RYD is not available.
*/
@Nullable
public static SpannableString getDislikesSpanForRegularVideo(@NonNull Spanned original, boolean isSegmentedButton) {
if (dislikeDataIsShort) {
// user:
// 1, opened a video
// 2. opened a short (without closing the regular video)
@ -234,14 +242,14 @@ public class ReturnYouTubeDislike {
return null;
}
return waitForFetchAndUpdateReplacementSpan((Spannable) original, isSegmentedButton);
return waitForFetchAndUpdateReplacementSpan(original, isSegmentedButton);
}
/**
* Called when a Shorts dislike Spannable is created.
*/
public static SpannableString getDislikeSpanForShort(@NonNull Spanned original) {
lastVideoLoadedWasShort = true; // it's now certain the video and data are a short
dislikeDataIsShort = true; // it's now certain the video and data are a short
return waitForFetchAndUpdateReplacementSpan(original, false);
}
@ -313,7 +321,7 @@ public class ReturnYouTubeDislike {
try {
// 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()) {
if (videoIdToVoteFor == null || dislikeDataIsShort != 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.

View File

@ -20,33 +20,17 @@ import app.revanced.integrations.settings.SharedPrefCategory;
public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
/**
* If ReturnYouTubeDislike is enabled
*/
private SwitchPreference enabledPreference;
/**
* If dislikes are shown as percentage
* If dislikes are shown as percentage.
*/
private SwitchPreference percentagePreference;
/**
* If segmented like/dislike button uses smaller compact layout
* If segmented like/dislike button uses smaller compact layout.
*/
private SwitchPreference compactLayoutPreference;
private void updateUIState() {
enabledPreference.setSummary(SettingsEnum.RYD_ENABLED.getBoolean()
? str("revanced_ryd_enable_summary_on")
: str("revanced_ryd_enable_summary_off"));
percentagePreference.setSummary(SettingsEnum.RYD_SHOW_DISLIKE_PERCENTAGE.getBoolean()
? str("revanced_ryd_dislike_percentage_summary_on")
: str("revanced_ryd_dislike_percentage_summary_off"));
percentagePreference.setEnabled(SettingsEnum.RYD_SHOW_DISLIKE_PERCENTAGE.isAvailable());
compactLayoutPreference.setSummary(SettingsEnum.RYD_USE_COMPACT_LAYOUT.getBoolean()
? str("revanced_ryd_compact_layout_summary_on")
: str("revanced_ryd_compact_layout_summary_off"));
compactLayoutPreference.setEnabled(SettingsEnum.RYD_USE_COMPACT_LAYOUT.isAvailable());
}
@ -59,9 +43,11 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(context);
setPreferenceScreen(preferenceScreen);
enabledPreference = new SwitchPreference(context);
SwitchPreference enabledPreference = new SwitchPreference(context);
enabledPreference.setChecked(SettingsEnum.RYD_ENABLED.getBoolean());
enabledPreference.setTitle(str("revanced_ryd_enable_title"));
enabledPreference.setSummaryOn(str("revanced_ryd_enable_summary_on"));
enabledPreference.setSummaryOff(str("revanced_ryd_enable_summary_off"));
enabledPreference.setOnPreferenceChangeListener((pref, newValue) -> {
final boolean rydIsEnabled = (Boolean) newValue;
SettingsEnum.RYD_ENABLED.saveValue(rydIsEnabled);
@ -75,6 +61,8 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
percentagePreference = new SwitchPreference(context);
percentagePreference.setChecked(SettingsEnum.RYD_SHOW_DISLIKE_PERCENTAGE.getBoolean());
percentagePreference.setTitle(str("revanced_ryd_dislike_percentage_title"));
percentagePreference.setSummaryOn(str("revanced_ryd_dislike_percentage_summary_on"));
percentagePreference.setSummaryOff(str("revanced_ryd_dislike_percentage_summary_off"));
percentagePreference.setOnPreferenceChangeListener((pref, newValue) -> {
SettingsEnum.RYD_SHOW_DISLIKE_PERCENTAGE.saveValue(newValue);
ReturnYouTubeDislike.clearCache();
@ -86,6 +74,8 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
compactLayoutPreference = new SwitchPreference(context);
compactLayoutPreference.setChecked(SettingsEnum.RYD_USE_COMPACT_LAYOUT.getBoolean());
compactLayoutPreference.setTitle(str("revanced_ryd_compact_layout_title"));
compactLayoutPreference.setSummaryOn(str("revanced_ryd_compact_layout_summary_on"));
compactLayoutPreference.setSummaryOff(str("revanced_ryd_compact_layout_summary_off"));
compactLayoutPreference.setOnPreferenceChangeListener((pref, newValue) -> {
SettingsEnum.RYD_USE_COMPACT_LAYOUT.saveValue(newValue);
ReturnYouTubeDislike.clearCache();
@ -185,7 +175,7 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
}
}
private String createSummaryText(int value, String summaryStringZeroKey, String summaryStringOneOrMoreKey) {
private static String createSummaryText(int value, String summaryStringZeroKey, String summaryStringOneOrMoreKey) {
if (value == 0) {
return str(summaryStringZeroKey);
}