diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ads/VideoAds.java b/app/src/main/java/fi/vanced/libraries/youtube/ads/VideoAds.java index 58dec2fb..161e26df 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ads/VideoAds.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ads/VideoAds.java @@ -25,10 +25,12 @@ import fi.razerman.youtube.XGlobals; import fi.vanced.libraries.youtube.player.ChannelModel; import fi.vanced.libraries.youtube.player.VideoInformation; import fi.vanced.utils.ObjectSerializer; +import fi.vanced.utils.SharedPrefUtils; public class VideoAds { public static final String TAG = "VI - VideoAds"; public static final String PREFERENCES_NAME = "channel-whitelist"; + public static boolean isEnabled; private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1"; private static final String YT_API_KEY = "replaceMeWithTheYouTubeAPIKey"; private static ArrayList whiteList; @@ -36,6 +38,7 @@ public class VideoAds { static { whiteList = parseWhitelist(YouTubeTikTokRoot_Application.getAppContext()); + isEnabled = SharedPrefUtils.getBoolean(YouTubeTikTokRoot_Application.getAppContext(), "youtube", "vanced_videoadwhitelisting_enabled", false); } // Call to this needs to be injected in YT code @@ -45,6 +48,8 @@ public class VideoAds { } VideoInformation.channelName = channelName; + if (!isEnabled) return; + if (adBlockButton != null) { adBlockButton.changeEnabled(getShouldShowAds()); } @@ -108,6 +113,8 @@ public class VideoAds { } public static boolean getShouldShowAds() { + if (!isEnabled) return false; + if (channelName == null || channelName.isEmpty() || channelName.trim().isEmpty()) { if (XGlobals.debug) { Log.d(TAG, "getShouldShowAds skipped because channelId was null"); @@ -131,6 +138,23 @@ public class VideoAds { public static boolean addToWhitelist(Context context, String channelName, String channelId) { try { + // Check that the channel doesn't exist already (can happen if for example the channel changes the name) + // If it exists, remove it + Iterator iterator = whiteList.iterator(); + while(iterator.hasNext()) + { + ChannelModel value = iterator.next(); + if (value.getChannelId().equals(channelId)) + { + if (XGlobals.debug) { + Log.d(TAG, String.format("Tried whitelisting an existing channel again. Old info (%1$s | %2$s) - New info (%3$s | %4$s)", + value.getAuthor(), value.getChannelId(), channelName, channelId)); + } + iterator.remove(); + break; + } + } + whiteList.add(new ChannelModel(channelName, channelId)); updateWhitelist(context); return true; diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDFragment.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDFragment.java new file mode 100644 index 00000000..93b1cc90 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDFragment.java @@ -0,0 +1,81 @@ +package fi.vanced.libraries.youtube.ryd; + +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_KEY_RYD_ENABLED; +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_NAME; +import static pl.jakubweg.StringRef.str; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; + +import fi.vanced.utils.SharedPrefUtils; + +public class RYDFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getPreferenceManager().setSharedPreferencesName(PREFERENCES_NAME); + + final Activity context = this.getActivity(); + + PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(context); + setPreferenceScreen(preferenceScreen); + + // RYD enable toggle + { + SwitchPreference preference = new SwitchPreference(context); + preferenceScreen.addPreference(preference); + preference.setKey(PREFERENCES_KEY_RYD_ENABLED); + preference.setDefaultValue(false); + preference.setChecked(SharedPrefUtils.getBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_ENABLED)); + preference.setTitle(str("vanced_ryd_title")); + preference.setSummary(str("vanced_ryd_summary")); + preference.setOnPreferenceChangeListener((pref, newValue) -> { + final boolean value = (Boolean) newValue; + ReturnYouTubeDislikes.onEnabledChange(value); + return true; + }); + } + + // About category + addAboutCategory(context, preferenceScreen); + } + + private void addAboutCategory(Context context, PreferenceScreen screen) { + PreferenceCategory category = new PreferenceCategory(context); + screen.addPreference(category); + category.setTitle(str("about")); + + { + Preference preference = new Preference(context); + screen.addPreference(preference); + preference.setTitle(str("vanced_ryd_attribution_title")); + preference.setSummary(str("vanced_ryd_attribution_summary")); + preference.setOnPreferenceClickListener(pref -> { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://returnyoutubedislike.com")); + pref.getContext().startActivity(i); + return false; + }); + } + + { + Preference preference = new Preference(context); + screen.addPreference(preference); + preference.setTitle("GitHub"); + preference.setOnPreferenceClickListener(pref -> { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/Anarios/return-youtube-dislike")); + pref.getContext().startActivity(i); + return false; + }); + } + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDSettings.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDSettings.java new file mode 100644 index 00000000..fda4808e --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDSettings.java @@ -0,0 +1,7 @@ +package fi.vanced.libraries.youtube.ryd; + +public class RYDSettings { + public static final String PREFERENCES_NAME = "ryd"; + public static final String PREFERENCES_KEY_USERID = "userId"; + public static final String PREFERENCES_KEY_RYD_ENABLED = "ryd-enabled"; +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/Registration.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Registration.java index 263e649c..9f2edd2b 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ryd/Registration.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Registration.java @@ -1,6 +1,8 @@ package fi.vanced.libraries.youtube.ryd; import static fi.razerman.youtube.XGlobals.debug; +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_KEY_USERID; +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_NAME; import static fi.vanced.utils.VancedUtils.getPreferences; import static fi.vanced.utils.VancedUtils.parseJson; import static fi.vanced.utils.VancedUtils.randomString; @@ -18,7 +20,6 @@ import java.nio.charset.StandardCharsets; public class Registration { private static final String TAG = "VI - RYD - Registration"; - public static final String PREFERENCES_NAME = "ryd"; private String userId; private Context context; @@ -36,7 +37,7 @@ public class Registration { if (this.context == null) throw new Exception("Unable to fetch userId because context was null"); SharedPreferences preferences = getPreferences(context, PREFERENCES_NAME); - this.userId = preferences.getString("userId", null); + this.userId = preferences.getString(PREFERENCES_KEY_USERID, null); if (this.userId == null) { this.userId = register(); @@ -55,7 +56,7 @@ public class Registration { SharedPreferences preferences = getPreferences(context, PREFERENCES_NAME); SharedPreferences.Editor editor = preferences.edit(); - editor.putString("userId", userId).apply(); + editor.putString(PREFERENCES_KEY_USERID, userId).apply(); } catch (Exception ex) { Log.e(TAG, "Unable to save the userId in shared preferences", ex); diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/ReturnYouTubeDislikes.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/ReturnYouTubeDislikes.java index 79d5a484..363b4519 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ryd/ReturnYouTubeDislikes.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/ReturnYouTubeDislikes.java @@ -2,6 +2,8 @@ package fi.vanced.libraries.youtube.ryd; import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_KEY_RYD_ENABLED; +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_NAME; import static fi.vanced.utils.VancedUtils.getIdentifier; import static fi.vanced.utils.VancedUtils.parseJson; @@ -21,8 +23,11 @@ import java.net.URL; import static fi.vanced.libraries.youtube.player.VideoInformation.dislikeCount; +import fi.vanced.utils.SharedPrefUtils; + public class ReturnYouTubeDislikes { public static final String RYD_API_URL = "https://returnyoutubedislikeapi.com"; + public static boolean isEnabled; private static final String TAG = "VI - RYD"; private static View _dislikeView = null; private static Thread _dislikeFetchThread = null; @@ -34,8 +39,21 @@ public class ReturnYouTubeDislikes { private static int votingValue = 0; // 1 = like, -1 = dislike, 0 = no vote static { - registration = new Registration(YouTubeTikTokRoot_Application.getAppContext()); - voting = new Voting(YouTubeTikTokRoot_Application.getAppContext(), registration); + isEnabled = SharedPrefUtils.getBoolean(YouTubeTikTokRoot_Application.getAppContext(), PREFERENCES_NAME, PREFERENCES_KEY_RYD_ENABLED, false); + if (isEnabled) { + registration = new Registration(YouTubeTikTokRoot_Application.getAppContext()); + voting = new Voting(YouTubeTikTokRoot_Application.getAppContext(), registration); + } + } + + public static void onEnabledChange(boolean enabled) { + isEnabled = enabled; + if (registration == null) { + registration = new Registration(YouTubeTikTokRoot_Application.getAppContext()); + } + if (voting == null) { + voting = new Voting(YouTubeTikTokRoot_Application.getAppContext(), registration); + } } public static void newVideoLoaded(String videoId) { @@ -43,6 +61,9 @@ public class ReturnYouTubeDislikes { Log.d(TAG, "newVideoLoaded - " + videoId); } + dislikeCount = null; + if (!isEnabled) return; + try { if (_dislikeFetchThread != null && _dislikeFetchThread.getState() != Thread.State.TERMINATED) { if (debug) { @@ -93,10 +114,14 @@ public class ReturnYouTubeDislikes { // Call to this needs to be injected in YT code public static void setLikeTag(View view) { + if (!isEnabled) return; + setTag(view, "like"); } public static void setLikeTag(View view, boolean active) { + if (!isEnabled) return; + likeActive = active; if (likeActive) { votingValue = 1; @@ -109,11 +134,15 @@ public class ReturnYouTubeDislikes { // Call to this needs to be injected in YT code public static void setDislikeTag(View view) { + if (!isEnabled) return; + _dislikeView = view; setTag(view, "dislike"); } public static void setDislikeTag(View view, boolean active) { + if (!isEnabled) return; + dislikeActive = active; if (dislikeActive) { votingValue = -1; @@ -127,15 +156,20 @@ public class ReturnYouTubeDislikes { // Call to this needs to be injected in YT code public static CharSequence onSetText(View view, CharSequence originalText) { + if (!isEnabled) return originalText; return handleOnSetText(view, originalText); } // Call to this needs to be injected in YT code public static void onClick(View view, boolean inactive) { + if (!isEnabled) return; + handleOnClick(view, inactive); } private static CharSequence handleOnSetText(View view, CharSequence originalText) { + if (!isEnabled) return originalText; + try { CharSequence tag = (CharSequence) view.getTag(); if (debug) { @@ -158,6 +192,8 @@ public class ReturnYouTubeDislikes { } private static void trySetDislikes(String dislikeCount) { + if (!isEnabled) return; + try { // Try to set normal video dislike count if (_dislikeView == null) { @@ -184,6 +220,8 @@ public class ReturnYouTubeDislikes { } private static void handleOnClick(View view, boolean previousState) { + if (!isEnabled) return; + try { String tag = (String) view.getTag(); if (debug) { @@ -232,6 +270,8 @@ public class ReturnYouTubeDislikes { } private static void sendVote(int vote) { + if (!isEnabled) return; + if (debug) { Log.d(TAG, "sending vote - " + vote + " for video " + currentVideoId); } @@ -264,6 +304,8 @@ public class ReturnYouTubeDislikes { } private static void setTag(View view, String tag) { + if (!isEnabled) return; + try { if (view == null) { if (debug) { diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/AdBlock.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/AdBlock.java index 58700fb8..94fdac54 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/AdBlock.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/AdBlock.java @@ -24,6 +24,7 @@ import java.net.URL; import fi.vanced.libraries.youtube.ads.VideoAds; import fi.vanced.libraries.youtube.player.ChannelModel; import fi.vanced.libraries.youtube.player.VideoInformation; +import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; public class AdBlock extends SlimButton { @@ -32,7 +33,7 @@ public class AdBlock extends SlimButton { private static final String YT_API_KEY = "replaceMeWithTheYouTubeAPIKey"; public AdBlock(Context context, ViewGroup container) { - super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, true); + super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, SharedPrefUtils.getBoolean(context, "youtube", "vanced_videoadwhitelisting_enabled", false)); initialize(); } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/ButtonVisibility.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/ButtonVisibility.java new file mode 100644 index 00000000..3f0ed23b --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/ButtonVisibility.java @@ -0,0 +1,36 @@ +package fi.vanced.libraries.youtube.ui; + +import android.content.Context; + +import fi.vanced.utils.SharedPrefUtils; + +public class ButtonVisibility { + public static Visibility getButtonVisibility(Context context, String key) { + return getButtonVisibility(context, key, "youtube"); + } + + public static Visibility getButtonVisibility(Context context, String key, String preferenceName) { + String value = SharedPrefUtils.getString(context, preferenceName, key, null); + + if (value == null || value.isEmpty()) return Visibility.NONE; + + switch (value.toUpperCase()) { + case "PLAYER": return Visibility.PLAYER; + case "BUTTON_CONTAINER": return Visibility.BUTTON_CONTAINER; + case "BOTH": return Visibility.BOTH; + default: return Visibility.NONE; + } + } + + public static boolean isVisibleInContainer(Context context, String key) { + return isVisibleInContainer(getButtonVisibility(context, key)); + } + + public static boolean isVisibleInContainer(Context context, String key, String preferenceName) { + return isVisibleInContainer(getButtonVisibility(context, key, preferenceName)); + } + + public static boolean isVisibleInContainer(Visibility visibility) { + return visibility == Visibility.BOTH || visibility == Visibility.BUTTON_CONTAINER; + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyButton.java index 00e45e75..bdd02945 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyButton.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyButton.java @@ -7,12 +7,11 @@ import android.view.View; import android.view.ViewGroup; import fi.vanced.libraries.youtube.player.VideoHelpers; -import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; public class CopyButton extends SlimButton { public CopyButton(Context context, ViewGroup container) { - super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, SharedPrefUtils.getBoolean(context, "youtube", "pref_copy_video_url_button", false)); + super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, ButtonVisibility.isVisibleInContainer(context, "pref_copy_video_url_button_list")); initialize(); } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyWithTimestamp.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyWithTimestamp.java index 1a05dab1..e9f8f9cb 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyWithTimestamp.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyWithTimestamp.java @@ -5,14 +5,13 @@ import android.view.View; import android.view.ViewGroup; import fi.vanced.libraries.youtube.player.VideoHelpers; -import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; import static pl.jakubweg.StringRef.str; public class CopyWithTimestamp extends SlimButton { public CopyWithTimestamp(Context context, ViewGroup container) { - super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, SharedPrefUtils.getBoolean(context, "youtube", "pref_copy_video_url_timestamp_button", false)); + super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, ButtonVisibility.isVisibleInContainer(context, "pref_copy_video_url_timestamp_button_list")); initialize(); } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButtonContainer.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButtonContainer.java index a316c3b9..5b8781da 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButtonContainer.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButtonContainer.java @@ -1,12 +1,17 @@ package fi.vanced.libraries.youtube.ui; +import static fi.razerman.youtube.XGlobals.debug; + import android.content.Context; +import android.content.SharedPreferences; import android.util.AttributeSet; import android.util.Log; import android.view.ViewGroup; import com.google.android.apps.youtube.app.ui.SlimMetadataScrollableButtonContainerLayout; +import fi.vanced.libraries.youtube.ads.VideoAds; +import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLayout { @@ -15,19 +20,24 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa private CopyButton copyButton; private CopyWithTimestamp copyWithTimestampButton; public static AdBlock adBlockButton; + private final Context context; + SharedPreferences.OnSharedPreferenceChangeListener listener; public SlimButtonContainer(Context context) { super(context); + this.context = context; this.initialize(context); } public SlimButtonContainer(Context context, AttributeSet attrs) { super(context, attrs); + this.context = context; this.initialize(context); } public SlimButtonContainer(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + this.context = context; this.initialize(context); } @@ -41,9 +51,40 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa adBlockButton = new AdBlock(context, this); new SponsorBlock(context, this); new SponsorBlockVoting(context, this); + + addSharedPrefsChangeListener(); } catch (Exception ex) { Log.e(TAG, "Unable to initialize the button container", ex); } } + + private void addSharedPrefsChangeListener() { + listener = (sharedPreferences, key) -> { + try { + if (debug) { + Log.d(TAG, String.format("SharedPreference changed with key %s", key)); + } + if ("pref_copy_video_url_button_list".equals(key) && copyButton != null) { + copyButton.setVisible(ButtonVisibility.isVisibleInContainer(context, "pref_copy_video_url_button_list")); + return; + } + if ("pref_copy_video_url_timestamp_button_list".equals(key) && copyWithTimestampButton != null) { + copyWithTimestampButton.setVisible(ButtonVisibility.isVisibleInContainer(context, "pref_copy_video_url_timestamp_button_list")); + return; + } + if ("vanced_videoadwhitelisting_enabled".equals(key) && adBlockButton != null) { + VideoAds.isEnabled = SharedPrefUtils.getBoolean(context, "youtube", "vanced_videoadwhitelisting_enabled", false); + adBlockButton.setVisible(VideoAds.isEnabled); + return; + } + } + catch (Exception ex) { + Log.e(TAG, "Error handling shared preference change", ex); + } + }; + + context.getSharedPreferences("youtube", Context.MODE_PRIVATE) + .registerOnSharedPreferenceChangeListener(listener); + } } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/Visibility.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/Visibility.java new file mode 100644 index 00000000..7e01a413 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/Visibility.java @@ -0,0 +1,8 @@ +package fi.vanced.libraries.youtube.ui; + +public enum Visibility { + NONE, + PLAYER, + BUTTON_CONTAINER, + BOTH, +} diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 00000000..2c2d2057 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,15 @@ + + + + @string/vanced_button_location_entry_none + @string/vanced_button_location_entry_player + @string/vanced_button_location_entry_buttoncontainer + @string/vanced_button_location_entry_both + + + NONE + PLAYER + BUTTON_CONTAINER + BOTH + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d8835b25..e19765aa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -337,4 +337,19 @@ Copy TCopy Ads + + Video ad settings + Video ad whitelisting + Video ad whitelisting is turned off + Video ad whitelisting is turned on. Use the ad button under the player to whitelist a channel + Hidden + In player + Under player + Both + RYD settings + Uses the RYD API + Enable RYD + Switch this on to see the dislike counts again + Return YouTube Dislike Integration + This integration uses the RYD API from https://returnyoutubedislike.com. Tap to learn more