From 306457ef84239da7ae156f06d8ac6bc394796a2a Mon Sep 17 00:00:00 2001 From: VancedOfficial Date: Mon, 10 Jan 2022 22:58:25 +0200 Subject: [PATCH 01/46] RYD and Video ad whitelisting base --- ...tadataScrollableButtonContainerLayout.java | 25 ++ .../libraries/youtube/ads/VideoAds.java | 213 +++++++++++++ .../youtube/player/ChannelModel.java | 29 ++ .../youtube/player/VideoHelpers.java | 62 ++++ .../youtube/player/VideoInformation.java | 39 +++ .../libraries/youtube/ryd/Registration.java | 149 +++++++++ .../youtube/ryd/ReturnYouTubeDislikes.java | 285 ++++++++++++++++++ .../vanced/libraries/youtube/ryd/Utils.java | 64 ++++ .../vanced/libraries/youtube/ryd/Voting.java | 120 ++++++++ .../vanced/libraries/youtube/ui/AdBlock.java | 135 +++++++++ .../libraries/youtube/ui/CopyButton.java | 29 ++ .../youtube/ui/CopyWithTimestamp.java | 29 ++ .../libraries/youtube/ui/SlimButton.java | 61 ++++ .../youtube/ui/SlimButtonContainer.java | 49 +++ .../libraries/youtube/ui/SponsorBlock.java | 31 ++ .../youtube/ui/SponsorBlockVoting.java | 28 ++ .../fi/vanced/utils/ObjectSerializer.java | 83 +++++ .../java/fi/vanced/utils/SharedPrefUtils.java | 43 +++ .../java/fi/vanced/utils/VancedUtils.java | 49 +++ .../java/pl/jakubweg/PlayerController.java | 2 - app/src/main/res/values/strings.xml | 4 + 21 files changed, 1527 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/google/android/apps/youtube/app/ui/SlimMetadataScrollableButtonContainerLayout.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ads/VideoAds.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/player/ChannelModel.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/player/VideoHelpers.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ryd/Registration.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ryd/ReturnYouTubeDislikes.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ryd/Utils.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ryd/Voting.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/AdBlock.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/CopyButton.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/CopyWithTimestamp.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButton.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButtonContainer.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlock.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlockVoting.java create mode 100644 app/src/main/java/fi/vanced/utils/ObjectSerializer.java create mode 100644 app/src/main/java/fi/vanced/utils/SharedPrefUtils.java create mode 100644 app/src/main/java/fi/vanced/utils/VancedUtils.java diff --git a/app/src/main/java/com/google/android/apps/youtube/app/ui/SlimMetadataScrollableButtonContainerLayout.java b/app/src/main/java/com/google/android/apps/youtube/app/ui/SlimMetadataScrollableButtonContainerLayout.java new file mode 100644 index 00000000..9b314f99 --- /dev/null +++ b/app/src/main/java/com/google/android/apps/youtube/app/ui/SlimMetadataScrollableButtonContainerLayout.java @@ -0,0 +1,25 @@ +package com.google.android.apps.youtube.app.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.ViewGroup; + +public class SlimMetadataScrollableButtonContainerLayout extends ViewGroup { + + public SlimMetadataScrollableButtonContainerLayout(Context context) { + super(context); + } + + public SlimMetadataScrollableButtonContainerLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public SlimMetadataScrollableButtonContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onLayout(boolean b, int i, int i1, int i2, int i3) { + + } +} 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 new file mode 100644 index 00000000..58dec2fb --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ads/VideoAds.java @@ -0,0 +1,213 @@ +package fi.vanced.libraries.youtube.ads; + +import static fi.razerman.youtube.XGlobals.debug; +import static fi.vanced.libraries.youtube.player.VideoInformation.channelName; +import static fi.vanced.libraries.youtube.ui.SlimButtonContainer.adBlockButton; +import static fi.vanced.utils.VancedUtils.getPreferences; +import static fi.vanced.utils.VancedUtils.parseJson; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; + +import org.json.JSONObject; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; + +import fi.razerman.youtube.XGlobals; +import fi.vanced.libraries.youtube.player.ChannelModel; +import fi.vanced.libraries.youtube.player.VideoInformation; +import fi.vanced.utils.ObjectSerializer; + +public class VideoAds { + public static final String TAG = "VI - VideoAds"; + public static final String PREFERENCES_NAME = "channel-whitelist"; + 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; + private static Thread fetchThread = null; + + static { + whiteList = parseWhitelist(YouTubeTikTokRoot_Application.getAppContext()); + } + + // Call to this needs to be injected in YT code + public static void setChannelName(String channelName) { + if (debug) { + Log.d(TAG, "channel name set to " + channelName); + } + VideoInformation.channelName = channelName; + + if (adBlockButton != null) { + adBlockButton.changeEnabled(getShouldShowAds()); + } + } + + // Call to this needs to be injected in YT code (CURRENTLY NOT USED) + public static void newVideoLoaded(String videoId) { + if (debug) { + Log.d(TAG, "newVideoLoaded - " + videoId); + } + + try { + if (fetchThread != null && fetchThread.getState() != Thread.State.TERMINATED) { + if (debug) { + Log.d(TAG, "Interrupting the thread. Current state " + fetchThread.getState()); + } + fetchThread.interrupt(); + } + } + catch (Exception ex) { + Log.e(TAG, "Error in the fetch thread", ex); + } + + fetchThread = new Thread(() -> { + try { + if (debug) { + Log.d(TAG, "Fetching channelId for " + videoId); + } + HttpURLConnection connection = (HttpURLConnection) new URL(YT_API_URL + "/player?key=" + YT_API_KEY).openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json; utf-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setDoOutput(true); + connection.setConnectTimeout(2 * 1000); + + // TODO: Actually fetch the version + String jsonInputString = "{\"context\": {\"client\": { \"clientName\": \"Android\", \"clientVersion\": \"16.49.37\" } }, \"videoId\": \"" + videoId + "\"}"; + try(OutputStream os = connection.getOutputStream()) { + byte[] input = jsonInputString.getBytes("utf-8"); + os.write(input, 0, input.length); + } + if (connection.getResponseCode() == 200) { + JSONObject json = new JSONObject(parseJson(connection)); + JSONObject videoInfo = json.getJSONObject("videoDetails"); + ChannelModel channelModel = new ChannelModel(videoInfo.getString("author"), videoInfo.getString("channelId")); + if (debug) { + Log.d(TAG, "channelId " + channelModel.getChannelId() + " fetched for author " + channelModel.getAuthor()); + } + } + else if (debug) { + Log.d(TAG, "player fetch response was " + connection.getResponseCode()); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to fetch channelId", ex); + return; + } + }); + + fetchThread.start(); + } + + public static boolean getShouldShowAds() { + if (channelName == null || channelName.isEmpty() || channelName.trim().isEmpty()) { + if (XGlobals.debug) { + Log.d(TAG, "getShouldShowAds skipped because channelId was null"); + } + + return false; + } + + for (ChannelModel channelModel: whiteList) { + if (channelModel.getAuthor().equals(channelName)) { + if (XGlobals.debug) { + Log.d(TAG, "Video ad whitelist for " + channelName); + } + + return true; + } + } + + return false; + } + + public static boolean addToWhitelist(Context context, String channelName, String channelId) { + try { + whiteList.add(new ChannelModel(channelName, channelId)); + updateWhitelist(context); + return true; + } + catch (Exception ex) { + Log.d(TAG, "Unable to add " + channelName + " with id " + channelId + " to whitelist"); + } + + return false; + } + + public static boolean removeFromWhitelist(Context context, String channelName) { + try { + //whiteList.removeIf(x -> x.getAuthor().equals(channelName)); // Requires Android N + + Iterator iterator = whiteList.iterator(); + while(iterator.hasNext()) + { + ChannelModel value = iterator.next(); + if (value.getAuthor().equals(channelName)) + { + iterator.remove(); + break; + } + } + updateWhitelist(context); + return true; + } + catch (Exception ex) { + Log.d(TAG, "Unable to remove " + channelName + " from whitelist"); + } + + return false; + } + + private static void updateWhitelist(Context context) { + if (context == null) return; + + SharedPreferences preferences = getPreferences(context, PREFERENCES_NAME); + SharedPreferences.Editor editor = preferences.edit(); + + try { + editor.putString("channels", ObjectSerializer.serialize(whiteList)); + } catch (IOException e) { + e.printStackTrace(); + } + + editor.apply(); + } + + private static ArrayList parseWhitelist(Context context) { + if (context == null) return new ArrayList<>(); + + SharedPreferences preferences = getPreferences(context, PREFERENCES_NAME); + try { + String channels = preferences.getString("channels", null); + if (channels == null) { + if (debug) { + Log.d(TAG, "channels string was null for ad whitelisting"); + } + + return new ArrayList<>(); + } + + ArrayList channelModels = (ArrayList) ObjectSerializer.deserialize(channels); + if (debug) { + Log.d(TAG, channels); + for (ChannelModel channelModel: channelModels) { + Log.d(TAG, "Ad whitelisted " + channelModel.getAuthor() + " with id of " + channelModel.getChannelId()); + } + } + + return channelModels; + } catch (IOException e) { + e.printStackTrace(); + } + + return new ArrayList<>(); + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/player/ChannelModel.java b/app/src/main/java/fi/vanced/libraries/youtube/player/ChannelModel.java new file mode 100644 index 00000000..3ad86fe0 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/player/ChannelModel.java @@ -0,0 +1,29 @@ +package fi.vanced.libraries.youtube.player; + +import java.io.Serializable; + +public class ChannelModel implements Serializable { + private String author; + private String channelId; + + public ChannelModel(String author, String channelId) { + this.author = author; + this.channelId = channelId; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/player/VideoHelpers.java b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoHelpers.java new file mode 100644 index 00000000..93e921f3 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoHelpers.java @@ -0,0 +1,62 @@ +package fi.vanced.libraries.youtube.player; + +import android.content.Context; +import android.util.Log; +import android.widget.Toast; + +import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; + +import static fi.razerman.youtube.XGlobals.debug; +import static pl.jakubweg.StringRef.str; + +public class VideoHelpers { + public static final String TAG = "VideoHelpers"; + + public static void copyVideoUrlToClipboard() { + generateVideoUrl(false); + } + + public static void copyVideoUrlWithTimeStampToClipboard() { + generateVideoUrl(true); + } + + private static void generateVideoUrl(boolean appendTimeStamp) { + try { + String videoId = VideoInformation.currentVideoId; + if (videoId == null || videoId.isEmpty()) { + if (debug) { + Log.d(TAG, "VideoId was empty"); + } + return; + } + + String videoUrl = String.format("https://youtu.be/%s", videoId); + if (appendTimeStamp) { + long videoTime = VideoInformation.lastKnownVideoTime; + videoUrl += String.format("?t=%s", (videoTime / 1000)); + } + + if (debug) { + Log.d(TAG, "Video URL: " + videoUrl); + } + + setClipboard(YouTubeTikTokRoot_Application.getAppContext(), videoUrl); + + Toast.makeText(YouTubeTikTokRoot_Application.getAppContext(), str("share_copy_url_success"), Toast.LENGTH_SHORT).show(); + } + catch (Exception ex) { + Log.e(TAG, "Couldn't generate video url", ex); + } + } + + private static void setClipboard(Context context, String text) { + if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) { + android.text.ClipboardManager clipboard = (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setText(text); + } else { + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText("link", text); + clipboard.setPrimaryClip(clip); + } + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java index a7411180..968055b8 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java @@ -1,6 +1,45 @@ package fi.vanced.libraries.youtube.player; +import static fi.razerman.youtube.XGlobals.debug; + +import android.util.Log; + +import fi.vanced.libraries.youtube.ryd.ReturnYouTubeDislikes; + public class VideoInformation { + private static final String TAG = "VI - VideoInfo"; + public static String currentVideoId; + public static Integer dislikeCount = null; + public static String channelName = null; public static long lastKnownVideoTime = -1L; + + // Call hook in the YT code when the video changes + public static void setCurrentVideoId(final String videoId) { + if (videoId == null) { + if (debug) { + Log.d(TAG, "setCurrentVideoId - new id was null - currentVideoId was" + currentVideoId); + } + currentVideoId = null; + dislikeCount = null; + channelName = null; + return; + } + + if (videoId.equals(currentVideoId)) { + if (debug) { + Log.d(TAG, "setCurrentVideoId - new and current video were equal - " + videoId); + } + return; + } + + if (debug) { + Log.d(TAG, "setCurrentVideoId - video id updated from " + currentVideoId + " to " + videoId); + } + + currentVideoId = videoId; + + // New video + ReturnYouTubeDislikes.newVideoLoaded(videoId); + } } 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 new file mode 100644 index 00000000..263e649c --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Registration.java @@ -0,0 +1,149 @@ +package fi.vanced.libraries.youtube.ryd; + +import static fi.razerman.youtube.XGlobals.debug; +import static fi.vanced.utils.VancedUtils.getPreferences; +import static fi.vanced.utils.VancedUtils.parseJson; +import static fi.vanced.utils.VancedUtils.randomString; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import org.json.JSONObject; + +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +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; + + public Registration(Context context) { + this.context = context; + } + + public String getUserId() { + return userId != null ? userId : fetchUserId(); + } + + private String fetchUserId() { + try { + 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); + + if (this.userId == null) { + this.userId = register(); + } + } + catch (Exception ex) { + Log.e(TAG, "Unable to fetch the userId from shared preferences", ex); + } + + return this.userId; + } + + private void saveUserId(String userId) { + try { + if (this.context == null) throw new Exception("Unable to save userId because context was null"); + + SharedPreferences preferences = getPreferences(context, PREFERENCES_NAME); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString("userId", userId).apply(); + } + catch (Exception ex) { + Log.e(TAG, "Unable to save the userId in shared preferences", ex); + } + } + + private String register() { + try { + // Generate a new userId + String userId = randomString(36); + if (debug) { + Log.d(TAG, "Trying to register the following userId: " + userId); + } + + // Get the registration challenge + HttpURLConnection connection = (HttpURLConnection) new URL(ReturnYouTubeDislikes.RYD_API_URL + "/puzzle/registration?userId=" + userId).openConnection(); + connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); + connection.setConnectTimeout(5 * 1000); + if (connection.getResponseCode() == 200) { + JSONObject json = new JSONObject(parseJson(connection)); + String challenge = json.getString("challenge"); + int difficulty = json.getInt("difficulty"); + if (debug) { + Log.d(TAG, "Registration challenge - " + challenge + " with difficulty of " + difficulty); + } + + // Solve the puzzle + String solution = Utils.solvePuzzle(challenge, difficulty); + if (debug) { + Log.d(TAG, "Registration confirmation solution is " + solution); + } + + return confirmRegistration(userId, solution); + } + else if (debug) { + Log.d(TAG, "Registration response was " + connection.getResponseCode()); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to register userId", ex); + } + + return null; + } + + public String confirmRegistration(String userId, String solution) { + try { + if (debug) { + Log.d(TAG, "Trying to confirm registration for the following userId: " + userId + " with solution: " + solution); + } + + // Confirm registration + HttpURLConnection confirmationCon = (HttpURLConnection) new URL(ReturnYouTubeDislikes.RYD_API_URL + "/puzzle/registration?userId=" + userId).openConnection(); + confirmationCon.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); + confirmationCon.setRequestMethod("POST"); + confirmationCon.setRequestProperty("Content-Type", "application/json"); + confirmationCon.setRequestProperty("Accept", "application/json"); + confirmationCon.setDoOutput(true); + confirmationCon.setConnectTimeout(5 * 1000); + + String jsonInputString = "{\"solution\": \"" + solution + "\"}"; + try(OutputStream os = confirmationCon.getOutputStream()) { + byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + if (confirmationCon.getResponseCode() == 200) { + String result = parseJson(confirmationCon); + if (debug) { + Log.d(TAG, "Registration confirmation result was " + result); + } + + if (result.equalsIgnoreCase("true")) { + saveUserId(userId); + if (debug) { + Log.d(TAG, "Registration was successful for user " + userId); + } + + return userId; + } + } + else if (debug) { + Log.d(TAG, "Registration confirmation response was " + confirmationCon.getResponseCode()); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to confirm registration", ex); + } + + return null; + } +} 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 new file mode 100644 index 00000000..79d5a484 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/ReturnYouTubeDislikes.java @@ -0,0 +1,285 @@ +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.utils.VancedUtils.getIdentifier; +import static fi.vanced.utils.VancedUtils.parseJson; + +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; + +import org.json.JSONObject; + +import java.net.HttpURLConnection; +import java.net.URL; + +import static fi.vanced.libraries.youtube.player.VideoInformation.dislikeCount; + +public class ReturnYouTubeDislikes { + public static final String RYD_API_URL = "https://returnyoutubedislikeapi.com"; + private static final String TAG = "VI - RYD"; + private static View _dislikeView = null; + private static Thread _dislikeFetchThread = null; + private static Thread _votingThread = null; + private static Registration registration; + private static Voting voting; + private static boolean likeActive; + private static boolean dislikeActive; + 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); + } + + public static void newVideoLoaded(String videoId) { + if (debug) { + Log.d(TAG, "newVideoLoaded - " + videoId); + } + + try { + if (_dislikeFetchThread != null && _dislikeFetchThread.getState() != Thread.State.TERMINATED) { + if (debug) { + Log.d(TAG, "Interrupting the thread. Current state " + _dislikeFetchThread.getState()); + } + _dislikeFetchThread.interrupt(); + } + } + catch (Exception ex) { + Log.e(TAG, "Error in the dislike fetch thread", ex); + } + + _dislikeFetchThread = new Thread(() -> { + try { + if (debug) { + Log.d(TAG, "Fetching dislikes for " + videoId); + } + HttpURLConnection connection = (HttpURLConnection) new URL(RYD_API_URL + "/votes?videoId=" + videoId).openConnection(); + connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); + connection.setConnectTimeout(5 * 1000); + if (connection.getResponseCode() == 200) { + JSONObject json = new JSONObject(parseJson(connection)); + dislikeCount = json.getInt("dislikes"); + if (debug) { + Log.d(TAG, "dislikes fetched - " + dislikeCount); + } + + // Set the dislikes + new Handler(Looper.getMainLooper()).post(new Runnable () { + @Override + public void run () { + trySetDislikes(String.valueOf(dislikeCount)); + } + }); + } + else if (debug) { + Log.d(TAG, "dislikes fetch response was " + connection.getResponseCode()); + } + } + catch (Exception ex) { + dislikeCount = null; + Log.e(TAG, "Failed to fetch dislikes", ex); + return; + } + }); + _dislikeFetchThread.start(); + } + + // Call to this needs to be injected in YT code + public static void setLikeTag(View view) { + setTag(view, "like"); + } + + public static void setLikeTag(View view, boolean active) { + likeActive = active; + if (likeActive) { + votingValue = 1; + } + if (debug) { + Log.d(TAG, "Like tag active " + likeActive); + } + setTag(view, "like"); + } + + // Call to this needs to be injected in YT code + public static void setDislikeTag(View view) { + _dislikeView = view; + setTag(view, "dislike"); + } + + public static void setDislikeTag(View view, boolean active) { + dislikeActive = active; + if (dislikeActive) { + votingValue = -1; + } + _dislikeView = view; + if (debug) { + Log.d(TAG, "Dislike tag active " + dislikeActive); + } + setTag(view, "dislike"); + } + + // Call to this needs to be injected in YT code + public static CharSequence onSetText(View view, CharSequence originalText) { + return handleOnSetText(view, originalText); + } + + // Call to this needs to be injected in YT code + public static void onClick(View view, boolean inactive) { + handleOnClick(view, inactive); + } + + private static CharSequence handleOnSetText(View view, CharSequence originalText) { + try { + CharSequence tag = (CharSequence) view.getTag(); + if (debug) { + Log.d(TAG, "handleOnSetText - " + tag + " - original text - " + originalText); + } + if (tag == null) return originalText; + + if (tag == "like") { + return originalText; + } + else if (tag == "dislike") { + return dislikeCount != null ? String.valueOf(dislikeCount) : originalText; + } + } + catch (Exception ex) { + Log.e(TAG, "Error while handling the setText", ex); + } + + return originalText; + } + + private static void trySetDislikes(String dislikeCount) { + try { + // Try to set normal video dislike count + if (_dislikeView == null) { + if (debug) { Log.d(TAG, "_dislikeView was null"); } + return; + } + + View buttonView = _dislikeView.findViewById(getIdentifier("button_text", "id")); + if (buttonView == null) { + if (debug) { Log.d(TAG, "buttonView was null"); } + return; + } + TextView button = (TextView) buttonView; + button.setText(dislikeCount); + if (debug) { + Log.d(TAG, "trySetDislikes - " + dislikeCount); + } + } + catch (Exception ex) { + if (debug) { + Log.e(TAG, "Error while trying to set dislikes text", ex); + } + } + } + + private static void handleOnClick(View view, boolean previousState) { + try { + String tag = (String) view.getTag(); + if (debug) { + Log.d(TAG, "handleOnClick - " + tag + " - previousState - " + previousState); + } + if (tag == null) return; + + // If active status was removed, vote should be none + if (previousState) { votingValue = 0; } + if (tag == "like") { + dislikeActive = false; + + // Like was activated + if (!previousState) { votingValue = 1; likeActive = true; } + else { likeActive = false; } + + // Like was activated and dislike was previously activated + if (!previousState && dislikeActive) { dislikeCount--; trySetDislikes(String.valueOf(dislikeCount)); } + } + else if (tag == "dislike") { + likeActive = false; + + // Dislike was activated + if (!previousState) { votingValue = -1; dislikeActive = true; dislikeCount++; } + // Dislike was removed + else { dislikeActive = false; dislikeCount--; } + trySetDislikes(String.valueOf(dislikeCount)); + } + else { + // Unknown tag + return; + } + + if (debug) { + Log.d(TAG, "New vote status - " + votingValue); + Log.d(TAG, "Like button " + likeActive + " | Dislike button " + dislikeActive); + } + + Toast.makeText(YouTubeTikTokRoot_Application.getAppContext(), "Voting value: " + votingValue, Toast.LENGTH_SHORT).show(); + + sendVote(votingValue); + } + catch (Exception ex) { + Log.e(TAG, "Error while handling the onClick", ex); + } + } + + private static void sendVote(int vote) { + if (debug) { + Log.d(TAG, "sending vote - " + vote + " for video " + currentVideoId); + } + + try { + if (_votingThread != null && _votingThread.getState() != Thread.State.TERMINATED) { + if (debug) { + Log.d(TAG, "Interrupting the thread. Current state " + _votingThread.getState()); + } + _votingThread.interrupt(); + } + } + catch (Exception ex) { + Log.e(TAG, "Error in the voting thread", ex); + } + + _votingThread = new Thread(() -> { + try { + boolean result = voting.sendVote(currentVideoId, vote); + if (debug) { + Log.d(TAG, "sendVote status " + result); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to send vote", ex); + return; + } + }); + _votingThread.start(); + } + + private static void setTag(View view, String tag) { + try { + if (view == null) { + if (debug) { + Log.d(TAG, "View was empty"); + } + return; + } + + if (debug) { + Log.d(TAG, "setTag - " + tag); + } + + view.setTag(tag); + } + catch (Exception ex) { + Log.e(TAG, "Error while trying to set tag to view", ex); + } + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/Utils.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Utils.java new file mode 100644 index 00000000..9ee92c28 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Utils.java @@ -0,0 +1,64 @@ +package fi.vanced.libraries.youtube.ryd; + +import android.util.Log; +import android.util.Base64; +import java.security.MessageDigest; + +public class Utils { + private static final String TAG = "VI - RYD - Utils"; + + static String solvePuzzle(String challenge, int difficulty) { + byte[] decodedChallenge = Base64.decode(challenge, Base64.NO_WRAP); + + byte[] buffer = new byte[20]; + for (int i = 4; i < 20; i++) { + buffer[i] = decodedChallenge[i - 4]; + } + + try { + int maxCount = (int) (Math.pow(2, difficulty + 1) * 5); + MessageDigest md = MessageDigest.getInstance("SHA-512"); + for (int i = 0; i < maxCount; i++) { + buffer[0] = (byte)i; + buffer[1] = (byte)(i >> 8); + buffer[2] = (byte)(i >> 16); + buffer[3] = (byte)(i >> 24); + byte[] messageDigest = md.digest(buffer); + + if (countLeadingZeroes(messageDigest) >= difficulty) { + String encode = Base64.encodeToString(new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, Base64.NO_WRAP); + return encode; + } + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to solve puzzle", ex); + } + + return null; + } + + static int countLeadingZeroes(byte[] uInt8View) { + int zeroes = 0; + int value = 0; + for (int i = 0; i < uInt8View.length; i++) { + value = uInt8View[i] & 0xFF; + if (value == 0) { + zeroes += 8; + } else { + int count = 1; + if (value >>> 4 == 0) { + count += 4; + value <<= 4; + } + if (value >>> 6 == 0) { + count += 2; + value <<= 2; + } + zeroes += count - (value >>> 7); + break; + } + } + return zeroes; + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/Voting.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Voting.java new file mode 100644 index 00000000..fd53bf55 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Voting.java @@ -0,0 +1,120 @@ +package fi.vanced.libraries.youtube.ryd; + +import static fi.razerman.youtube.XGlobals.debug; +import static fi.vanced.utils.VancedUtils.parseJson; + +import android.content.Context; +import android.util.Log; + +import org.json.JSONObject; + +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +public class Voting { + private static final String TAG = "VI - RYD - Voting"; + + private Registration registration; + private Context context; + + public Voting(Context context, Registration registration) { + this.context = context; + this.registration = registration; + } + + public boolean sendVote(String videoId, int vote) { + try { + String userId = registration.getUserId(); + if (debug) { + Log.d(TAG, "Trying to vote the following video: " + videoId + " with vote " + vote + " and userId: " + userId); + } + + // Send the vote + HttpURLConnection connection = (HttpURLConnection) new URL(ReturnYouTubeDislikes.RYD_API_URL + "/interact/vote").openConnection(); + connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("Accept", "application/json"); + connection.setDoOutput(true); + connection.setConnectTimeout(5 * 1000); + String voteJsonString = "{\"userId\": \"" + userId + "\", \"videoId\": \"" + videoId + "\", \"value\": \"" + vote + "\"}"; + try(OutputStream os = connection.getOutputStream()) { + byte[] input = voteJsonString.getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + + if (connection.getResponseCode() == 200) { + JSONObject json = new JSONObject(parseJson(connection)); + String challenge = json.getString("challenge"); + int difficulty = json.getInt("difficulty"); + if (debug) { + Log.d(TAG, "Vote challenge - " + challenge + " with difficulty of " + difficulty); + } + + // Solve the puzzle + String solution = Utils.solvePuzzle(challenge, difficulty); + if (debug) { + Log.d(TAG, "Vote confirmation solution is " + solution); + } + + // Confirm vote + return confirmVote(userId, videoId, solution); + } + else if (debug) { + Log.d(TAG, "Vote response was " + connection.getResponseCode()); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to send vote", ex); + } + + return false; + } + + public boolean confirmVote(String userId, String videoId, String solution) { + try { + if (debug) { + Log.d(TAG, "Trying to confirm vote for video: " + videoId + " with solution " + solution + " and userId: " + userId); + } + + // Confirm vote + HttpURLConnection confirmationCon = (HttpURLConnection) new URL(ReturnYouTubeDislikes.RYD_API_URL + "/interact/confirmVote").openConnection(); + confirmationCon.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); + confirmationCon.setRequestMethod("POST"); + confirmationCon.setRequestProperty("Content-Type", "application/json"); + confirmationCon.setRequestProperty("Accept", "application/json"); + confirmationCon.setDoOutput(true); + confirmationCon.setConnectTimeout(5 * 1000); + + String jsonInputString = "{\"userId\": \"" + userId + "\", \"videoId\": \"" + videoId + "\", \"solution\": \"" + solution + "\"}"; + try(OutputStream os = confirmationCon.getOutputStream()) { + byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + if (confirmationCon.getResponseCode() == 200) { + String result = parseJson(confirmationCon); + if (debug) { + Log.d(TAG, "Vote confirmation result was " + result); + } + + if (result.equalsIgnoreCase("true")) { + if (debug) { + Log.d(TAG, "Vote was successful for user " + userId); + } + + return true; + } + } + else if (debug) { + Log.d(TAG, "Vote confirmation response was " + confirmationCon.getResponseCode()); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to send vote", ex); + } + + return false; + } +} 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 new file mode 100644 index 00000000..58700fb8 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/AdBlock.java @@ -0,0 +1,135 @@ +package fi.vanced.libraries.youtube.ui; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.Toast; + +import static fi.razerman.youtube.XGlobals.debug; +import static fi.vanced.libraries.youtube.ads.VideoAds.getShouldShowAds; +import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; +import static fi.vanced.utils.VancedUtils.parseJson; +import static pl.jakubweg.StringRef.str; + +import org.json.JSONObject; + +import java.io.OutputStream; +import java.net.HttpURLConnection; +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.VancedUtils; + +public class AdBlock extends SlimButton { + private static final String TAG = "VI - AdBlock - Button"; + private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1"; + private static final String YT_API_KEY = "replaceMeWithTheYouTubeAPIKey"; + + public AdBlock(Context context, ViewGroup container) { + super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, true); + + initialize(); + } + + private void initialize() { + this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_yt_ad_button", "drawable")); + this.button_text.setText(str("action_ads")); + changeEnabled(getShouldShowAds()); + } + + public void changeEnabled(boolean enabled) { + if (debug) { + Log.d(TAG, "changeEnabled " + enabled); + } + this.button_icon.setEnabled(enabled); + } + + @Override + public void onClick(View view) { + this.view.setEnabled(false); + if (this.button_icon.isEnabled()) { + removeFromWhitelist(); + return; + } + //this.button_icon.setEnabled(!this.button_icon.isEnabled()); + + addToWhiteList(this.view, this.button_icon); + } + + private void removeFromWhitelist() { + try { + VideoAds.removeFromWhitelist(this.context, VideoInformation.channelName); + this.button_icon.setEnabled(false); + } + catch (Exception ex) { + Log.e(TAG, "Failed to remove from whitelist", ex); + return; + } + + this.view.setEnabled(true); + } + + private void addToWhiteList(View view, ImageView buttonIcon) { + new Thread(() -> { + try { + if (debug) { + Log.d(TAG, "Fetching channelId for " + currentVideoId); + } + HttpURLConnection connection = (HttpURLConnection) new URL(YT_API_URL + "/player?key=" + YT_API_KEY).openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json; utf-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setDoOutput(true); + connection.setConnectTimeout(2 * 1000); + + // TODO: Actually fetch the version + String jsonInputString = "{\"context\": {\"client\": { \"clientName\": \"Android\", \"clientVersion\": \"16.49.37\" } }, \"videoId\": \"" + currentVideoId + "\"}"; + try(OutputStream os = connection.getOutputStream()) { + byte[] input = jsonInputString.getBytes("utf-8"); + os.write(input, 0, input.length); + } + if (connection.getResponseCode() == 200) { + JSONObject json = new JSONObject(parseJson(connection)); + JSONObject videoInfo = json.getJSONObject("videoDetails"); + ChannelModel channelModel = new ChannelModel(videoInfo.getString("author"), videoInfo.getString("channelId")); + if (debug) { + Log.d(TAG, "channelId " + channelModel.getChannelId() + " fetched for author " + channelModel.getAuthor()); + } + + boolean success = VideoAds.addToWhitelist(this.context, channelModel.getAuthor(), channelModel.getChannelId()); + new Handler(Looper.getMainLooper()).post(() -> { + if (success) { + buttonIcon.setEnabled(true); + Toast.makeText(context, "Channel " + channelModel.getAuthor() + " whitelisted", Toast.LENGTH_SHORT).show(); + } + else { + buttonIcon.setEnabled(false); + Toast.makeText(context, "Channel " + channelModel.getAuthor() + " failed to whitelist", Toast.LENGTH_SHORT).show(); + } + + view.setEnabled(true); + }); + } + else { + if (debug) { + Log.d(TAG, "player fetch response was " + connection.getResponseCode()); + } + + buttonIcon.setEnabled(false); + this.view.setEnabled(true); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to fetch channelId", ex); + this.view.setEnabled(true); + return; + } + }).start(); + } +} 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 new file mode 100644 index 00000000..00e45e75 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyButton.java @@ -0,0 +1,29 @@ +package fi.vanced.libraries.youtube.ui; + +import static pl.jakubweg.StringRef.str; + +import android.content.Context; +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)); + + initialize(); + } + + private void initialize() { + this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_yt_copy_icon", "drawable")); + this.button_text.setText(str("action_copy")); + } + + @Override + public void onClick(View view) { + VideoHelpers.copyVideoUrlToClipboard(); + } +} 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 new file mode 100644 index 00000000..1a05dab1 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/CopyWithTimestamp.java @@ -0,0 +1,29 @@ +package fi.vanced.libraries.youtube.ui; + +import android.content.Context; +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)); + + initialize(); + } + + private void initialize() { + this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_yt_copy_icon_with_time", "drawable")); + this.button_text.setText(str("action_tcopy")); + } + + @Override + public void onClick(View view) { + VideoHelpers.copyVideoUrlWithTimeStampToClipboard(); + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButton.java new file mode 100644 index 00000000..41164ff3 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButton.java @@ -0,0 +1,61 @@ +package fi.vanced.libraries.youtube.ui; + +import static fi.razerman.youtube.XGlobals.debug; + +import android.content.Context; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import fi.vanced.utils.VancedUtils; + +public abstract class SlimButton implements View.OnClickListener { + private static final String TAG = "VI - Slim - Button"; + public static int SLIM_METADATA_BUTTON_ID; + public final View view; + public final Context context; + private final ViewGroup container; + protected final ImageView button_icon; + protected final TextView button_text; + + static { + SLIM_METADATA_BUTTON_ID = VancedUtils.getIdentifier("slim_metadata_button", "layout"); + } + + public SlimButton(Context context, ViewGroup container, int id, boolean visible) { + if (debug) { + Log.d(TAG, "Adding button with id " + id + " and visibility of " + visible); + } + this.context = context; + this.container = container; + view = LayoutInflater.from(context).inflate(id, container, false); + button_icon = (ImageView)view.findViewById(VancedUtils.getIdentifier("button_icon", "id")); + button_text = (TextView)view.findViewById(VancedUtils.getIdentifier("button_text", "id")); + + view.setOnClickListener(this); + setVisible(visible); + + container.addView(view); + } + + public void setVisible(boolean visible) { + view.setVisibility(visible ? View.VISIBLE : View.GONE); + setContainerVisibility(); + } + + private void setContainerVisibility() { + if (container == null) return; + + for (int i = 0; i < container.getChildCount(); i++) { + if (container.getChildAt(i).getVisibility() == View.VISIBLE) { + container.setVisibility(View.VISIBLE); + return; + } + } + + container.setVisibility(View.GONE); + } +} 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 new file mode 100644 index 00000000..a316c3b9 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButtonContainer.java @@ -0,0 +1,49 @@ +package fi.vanced.libraries.youtube.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ViewGroup; + +import com.google.android.apps.youtube.app.ui.SlimMetadataScrollableButtonContainerLayout; + +import fi.vanced.utils.VancedUtils; + +public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLayout { + private static final String TAG = "VI - Slim - Container"; + private ViewGroup container; + private CopyButton copyButton; + private CopyWithTimestamp copyWithTimestampButton; + public static AdBlock adBlockButton; + + public SlimButtonContainer(Context context) { + super(context); + this.initialize(context); + } + + public SlimButtonContainer(Context context, AttributeSet attrs) { + super(context, attrs); + this.initialize(context); + } + + public SlimButtonContainer(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.initialize(context); + } + + public void initialize(Context context) { + try { + container = this.findViewById(VancedUtils.getIdentifier("button_container_vanced", "id")); + if (container == null) throw new Exception("Unable to initialize the button container because the button_container_vanced couldn't be found"); + + copyButton = new CopyButton(context, this); + copyWithTimestampButton = new CopyWithTimestamp(context, this); + adBlockButton = new AdBlock(context, this); + new SponsorBlock(context, this); + new SponsorBlockVoting(context, this); + } + catch (Exception ex) { + Log.e(TAG, "Unable to initialize the button container", ex); + } + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlock.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlock.java new file mode 100644 index 00000000..47132d58 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlock.java @@ -0,0 +1,31 @@ +package fi.vanced.libraries.youtube.ui; + +import static pl.jakubweg.StringRef.str; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; + +import fi.vanced.libraries.youtube.player.VideoHelpers; +import fi.vanced.utils.VancedUtils; + +public class SponsorBlock extends SlimButton { + public SponsorBlock(Context context, ViewGroup container) { + super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, false); + + initialize(); + } + + private void initialize() { + this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_sb_logo", "drawable")); + this.button_text.setText("SB"); + } + + @Override + public void onClick(View view) { + Toast.makeText(YouTubeTikTokRoot_Application.getAppContext(), "Nothing atm", Toast.LENGTH_SHORT).show(); + } +} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlockVoting.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlockVoting.java new file mode 100644 index 00000000..283b6509 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlockVoting.java @@ -0,0 +1,28 @@ +package fi.vanced.libraries.youtube.ui; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; + +import fi.vanced.utils.VancedUtils; + +public class SponsorBlockVoting extends SlimButton { + public SponsorBlockVoting(Context context, ViewGroup container) { + super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, false); + + initialize(); + } + + private void initialize() { + this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_sb_voting", "drawable")); + this.button_text.setText("SB Voting"); + } + + @Override + public void onClick(View view) { + Toast.makeText(YouTubeTikTokRoot_Application.getAppContext(), "Nothing atm", Toast.LENGTH_SHORT).show(); + } +} diff --git a/app/src/main/java/fi/vanced/utils/ObjectSerializer.java b/app/src/main/java/fi/vanced/utils/ObjectSerializer.java new file mode 100644 index 00000000..1bd3290f --- /dev/null +++ b/app/src/main/java/fi/vanced/utils/ObjectSerializer.java @@ -0,0 +1,83 @@ +package fi.vanced.utils; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Modifications copyright (C) 2022 Vanced + */ + +import android.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +public class ObjectSerializer { + private static final String TAG = "VI - ObjectSerializer"; + + public static String serialize(Serializable obj) throws IOException { + if (obj == null) return ""; + try { + ByteArrayOutputStream serialObj = new ByteArrayOutputStream(); + ObjectOutputStream objStream = new ObjectOutputStream(serialObj); + objStream.writeObject(obj); + objStream.close(); + return encodeBytes(serialObj.toByteArray()); + } catch (Exception e) { + Log.e(TAG, "Serialization error: " + e.getMessage(), e); + throw new IOException(e); + } + } + + public static Object deserialize(String str) throws IOException { + if (str == null || str.length() == 0) return null; + try { + ByteArrayInputStream serialObj = new ByteArrayInputStream(decodeBytes(str)); + ObjectInputStream objStream = new ObjectInputStream(serialObj); + return objStream.readObject(); + } catch (Exception e) { + Log.e(TAG, "Deserialization error: " + e.getMessage(), e); + throw new IOException(e); + } + } + + public static String encodeBytes(byte[] bytes) { + StringBuffer strBuf = new StringBuffer(); + + for (int i = 0; i < bytes.length; i++) { + strBuf.append((char) (((bytes[i] >> 4) & 0xF) + ((int) 'a'))); + strBuf.append((char) (((bytes[i]) & 0xF) + ((int) 'a'))); + } + + return strBuf.toString(); + } + + public static byte[] decodeBytes(String str) { + byte[] bytes = new byte[str.length() / 2]; + for (int i = 0; i < str.length(); i+=2) { + char c = str.charAt(i); + bytes[i/2] = (byte) ((c - 'a') << 4); + c = str.charAt(i+1); + bytes[i/2] += (c - 'a'); + } + return bytes; + } + +} \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/utils/SharedPrefUtils.java b/app/src/main/java/fi/vanced/utils/SharedPrefUtils.java new file mode 100644 index 00000000..cc3277b0 --- /dev/null +++ b/app/src/main/java/fi/vanced/utils/SharedPrefUtils.java @@ -0,0 +1,43 @@ +package fi.vanced.utils; + +import android.content.Context; +import android.content.SharedPreferences; + +public class SharedPrefUtils { + public static void saveString(Context context, String preferenceName, String key, String value){ + SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE); + sharedPreferences.edit().putString(key, value).apply(); + } + public static void saveBoolean(Context context, String preferenceName, String key, Boolean value){ + SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE); + sharedPreferences.edit().putBoolean(key, value).apply(); + } + public static void saveInt(Context context, String preferenceName, String key, Integer value){ + SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE); + sharedPreferences.edit().putInt(key, value).apply(); + } + + public static String getString(Context context, String preferenceName, String key){ + return getString(context, preferenceName, key, null); + } + public static String getString(Context context, String preferenceName, String key, String _default){ + SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE); + return (sharedPreferences.getString(key, _default)); + } + + public static Boolean getBoolean(Context context, String preferenceName, String key){ + return getBoolean(context, preferenceName, key, false); + } + public static Boolean getBoolean(Context context, String preferenceName, String key, Boolean _default){ + SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE); + return (sharedPreferences.getBoolean(key, _default)); + } + + public static Integer getInt(Context context, String preferenceName, String key){ + return getInt(context, preferenceName, key, -1); + } + public static Integer getInt(Context context, String preferenceName, String key, Integer _default){ + SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE); + return (sharedPreferences.getInt(key, _default)); + } +} diff --git a/app/src/main/java/fi/vanced/utils/VancedUtils.java b/app/src/main/java/fi/vanced/utils/VancedUtils.java new file mode 100644 index 00000000..e9b0c785 --- /dev/null +++ b/app/src/main/java/fi/vanced/utils/VancedUtils.java @@ -0,0 +1,49 @@ +package fi.vanced.utils; + +import android.content.Context; +import android.content.SharedPreferences; + +import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.security.SecureRandom; + +public class VancedUtils { + + public static SharedPreferences getPreferences(Context context, String preferencesName) { + if (context == null) return null; + return context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE); + } + + public static String parseJson(HttpURLConnection connection) throws IOException { + StringBuilder jsonBuilder = new StringBuilder(); + InputStream inputStream = connection.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + String line; + while ((line = reader.readLine()) != null) { + jsonBuilder.append(line); + } + inputStream.close(); + return jsonBuilder.toString(); + } + + public static int getIdentifier(String name, String defType) { + Context context = YouTubeTikTokRoot_Application.getAppContext(); + return context.getResources().getIdentifier(name, defType, context.getPackageName()); + } + + // https://stackoverflow.com/a/157202 + static final String AB = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + static SecureRandom rnd = new SecureRandom(); + + public static String randomString(int len){ + StringBuilder sb = new StringBuilder(len); + for(int i = 0; i < len; i++) + sb.append(AB.charAt(rnd.nextInt(AB.length()))); + return sb.toString(); + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/jakubweg/PlayerController.java b/app/src/main/java/pl/jakubweg/PlayerController.java index e884f575..6039bc8e 100644 --- a/app/src/main/java/pl/jakubweg/PlayerController.java +++ b/app/src/main/java/pl/jakubweg/PlayerController.java @@ -64,8 +64,6 @@ public class PlayerController { return; } - VideoInformation.currentVideoId = videoId; - Context context = YouTubeTikTokRoot_Application.getAppContext(); if(context == null){ Log.e(TAG, "context is null"); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 129c4ab9..d8835b25 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -333,4 +333,8 @@ Invalid hex code Change Reset + + Copy + TCopy + Ads From e48e16ae699673d07f63df5af768bc70836db0c2 Mon Sep 17 00:00:00 2001 From: VancedOfficial Date: Sun, 16 Jan 2022 18:19:59 +0200 Subject: [PATCH 02/46] Settings added plus some fixes --- .../libraries/youtube/ads/VideoAds.java | 24 ++++++ .../libraries/youtube/ryd/RYDFragment.java | 81 +++++++++++++++++++ .../libraries/youtube/ryd/RYDSettings.java | 7 ++ .../libraries/youtube/ryd/Registration.java | 7 +- .../youtube/ryd/ReturnYouTubeDislikes.java | 46 ++++++++++- .../vanced/libraries/youtube/ui/AdBlock.java | 3 +- .../youtube/ui/ButtonVisibility.java | 36 +++++++++ .../libraries/youtube/ui/CopyButton.java | 3 +- .../youtube/ui/CopyWithTimestamp.java | 3 +- .../youtube/ui/SlimButtonContainer.java | 41 ++++++++++ .../libraries/youtube/ui/Visibility.java | 8 ++ app/src/main/res/values/arrays.xml | 15 ++++ app/src/main/res/values/strings.xml | 15 ++++ 13 files changed, 279 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDFragment.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDSettings.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/ButtonVisibility.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/Visibility.java create mode 100644 app/src/main/res/values/arrays.xml 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 From 88a7838cd81103e8c3d3e88d025e3f61d11e6d73 Mon Sep 17 00:00:00 2001 From: VancedOfficial Date: Sun, 16 Jan 2022 21:07:34 +0200 Subject: [PATCH 03/46] Added dislike formatting --- .../youtube/ryd/ReturnYouTubeDislikes.java | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) 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 363b4519..e182953d 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 @@ -7,6 +7,9 @@ import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_NAME; import static fi.vanced.utils.VancedUtils.getIdentifier; import static fi.vanced.utils.VancedUtils.parseJson; +import android.content.Context; +import android.icu.text.CompactDecimalFormat; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.util.Log; @@ -20,6 +23,7 @@ import org.json.JSONObject; import java.net.HttpURLConnection; import java.net.URL; +import java.util.Locale; import static fi.vanced.libraries.youtube.player.VideoInformation.dislikeCount; @@ -37,12 +41,28 @@ public class ReturnYouTubeDislikes { private static boolean likeActive; private static boolean dislikeActive; private static int votingValue = 0; // 1 = like, -1 = dislike, 0 = no vote + private static CompactDecimalFormat compactNumberFormatter; static { - isEnabled = SharedPrefUtils.getBoolean(YouTubeTikTokRoot_Application.getAppContext(), PREFERENCES_NAME, PREFERENCES_KEY_RYD_ENABLED, false); + Context context = YouTubeTikTokRoot_Application.getAppContext(); + isEnabled = SharedPrefUtils.getBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_ENABLED, false); if (isEnabled) { - registration = new Registration(YouTubeTikTokRoot_Application.getAppContext()); - voting = new Voting(YouTubeTikTokRoot_Application.getAppContext(), registration); + registration = new Registration(context); + voting = new Voting(context, registration); + } + + Locale locale = context.getResources().getConfiguration().locale; + if (debug) { + Log.d(TAG, "locale - " + locale); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + compactNumberFormatter = CompactDecimalFormat.getInstance( + locale, + CompactDecimalFormat.CompactStyle.SHORT + ); + compactNumberFormatter.setMaximumIntegerDigits(3); + compactNumberFormatter.setMaximumFractionDigits(1); + compactNumberFormatter.setSignificantDigitsUsed(false); } } @@ -95,7 +115,7 @@ public class ReturnYouTubeDislikes { new Handler(Looper.getMainLooper()).post(new Runnable () { @Override public void run () { - trySetDislikes(String.valueOf(dislikeCount)); + trySetDislikes(formatDislikes(dislikeCount)); } }); } @@ -181,7 +201,7 @@ public class ReturnYouTubeDislikes { return originalText; } else if (tag == "dislike") { - return dislikeCount != null ? String.valueOf(dislikeCount) : originalText; + return dislikeCount != null ? formatDislikes(dislikeCount) : originalText; } } catch (Exception ex) { @@ -239,7 +259,7 @@ public class ReturnYouTubeDislikes { else { likeActive = false; } // Like was activated and dislike was previously activated - if (!previousState && dislikeActive) { dislikeCount--; trySetDislikes(String.valueOf(dislikeCount)); } + if (!previousState && dislikeActive) { dislikeCount--; trySetDislikes(formatDislikes(dislikeCount)); } } else if (tag == "dislike") { likeActive = false; @@ -248,7 +268,7 @@ public class ReturnYouTubeDislikes { if (!previousState) { votingValue = -1; dislikeActive = true; dislikeCount++; } // Dislike was removed else { dislikeActive = false; dislikeCount--; } - trySetDislikes(String.valueOf(dislikeCount)); + trySetDislikes(formatDislikes(dislikeCount)); } else { // Unknown tag @@ -324,4 +344,20 @@ public class ReturnYouTubeDislikes { Log.e(TAG, "Error while trying to set tag to view", ex); } } + + private static String formatDislikes(int dislikes) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && compactNumberFormatter != null) { + final String formatted = compactNumberFormatter.format(dislikes); + if (debug) { + Log.d(TAG, "Formatting dislikes - " + dislikes + " - " + formatted); + } + + return formatted; + } + + if (debug) { + Log.d(TAG, "Couldn't format dislikes, using the unformatted count - " + dislikes); + } + return String.valueOf(dislikes); + } } From b0d6658741363d4390a9ac8a2a2799685a56488f Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 17 Jan 2022 14:54:11 +0100 Subject: [PATCH 04/46] start refactoring the integration impl --- .../libraries/youtube/ads/AdsRequester.java | 91 ++++++++ .../libraries/youtube/ads/AdsRoutes.java | 11 + .../libraries/youtube/ads/VideoAds.java | 63 ----- .../libraries/youtube/ryd/Registration.java | 95 +------- .../youtube/ryd/ReturnYouTubeDislikes.java | 52 +---- .../vanced/libraries/youtube/ryd/Utils.java | 5 +- .../vanced/libraries/youtube/ryd/Voting.java | 102 +------- .../youtube/ryd/requests/RYDRequester.java | 221 ++++++++++++++++++ .../youtube/ryd/requests/RYDRoutes.java | 16 ++ .../vanced/libraries/youtube/ui/AdBlock.java | 84 +------ .../youtube/whitelisting/Whitelist.java | 101 ++++++++ .../youtube/whitelisting/WhitelistType.java | 22 ++ .../java/fi/vanced/utils/VancedUtils.java | 28 +-- .../fi/vanced/utils/requests/Requester.java | 48 ++++ .../java/fi/vanced/utils/requests/Route.java | 59 +++++ .../java/pl/jakubweg/PlayerController.java | 12 +- .../SponsorBlockPreferenceFragment.java | 4 +- .../java/pl/jakubweg/SponsorBlockUtils.java | 17 +- .../main/java/pl/jakubweg/requests/Route.java | 70 ------ .../{Requester.java => SBRequester.java} | 50 ++-- .../java/pl/jakubweg/requests/SBRoutes.java | 18 ++ 21 files changed, 658 insertions(+), 511 deletions(-) create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRoutes.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRoutes.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java create mode 100644 app/src/main/java/fi/vanced/utils/requests/Requester.java create mode 100644 app/src/main/java/fi/vanced/utils/requests/Route.java delete mode 100644 app/src/main/java/pl/jakubweg/requests/Route.java rename app/src/main/java/pl/jakubweg/requests/{Requester.java => SBRequester.java} (78%) create mode 100644 app/src/main/java/pl/jakubweg/requests/SBRoutes.java diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java new file mode 100644 index 00000000..3fb82856 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java @@ -0,0 +1,91 @@ +package fi.vanced.libraries.youtube.ads; + +import static fi.razerman.youtube.XGlobals.debug; +import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; +import static fi.vanced.libraries.youtube.ui.AdBlock.TAG; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; +import android.widget.Toast; + +import org.json.JSONObject; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.nio.charset.StandardCharsets; + +import fi.vanced.libraries.youtube.player.ChannelModel; +import fi.vanced.utils.requests.Requester; +import fi.vanced.utils.requests.Route; + +public class AdsRequester { + private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1"; + private static final String YT_API_KEY = "replaceMeWithTheYouTubeAPIKey"; + + public static void retrieveChannelDetails(View view, ImageView buttonIcon, Context context) { + try { + HttpURLConnection connection = getConnectionFromRoute(AdsRoutes.GET_CHANNEL_DETAILS, YT_API_KEY); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json; utf-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setDoOutput(true); + connection.setConnectTimeout(2 * 1000); + + // TODO: Actually fetch the version + String jsonInputString = "{\"context\": {\"client\": { \"clientName\": \"Android\", \"clientVersion\": \"16.49.37\" } }, \"videoId\": \"" + currentVideoId + "\"}"; + try(OutputStream os = connection.getOutputStream()) { + byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + if (connection.getResponseCode() == 200) { + JSONObject json = getJSONObject(connection); + JSONObject videoInfo = json.getJSONObject("videoDetails"); + ChannelModel channelModel = new ChannelModel(videoInfo.getString("author"), videoInfo.getString("channelId")); + if (debug) { + Log.d(TAG, "channelId " + channelModel.getChannelId() + " fetched for author " + channelModel.getAuthor()); + } + + boolean success = VideoAds.addToWhitelist(context, channelModel.getAuthor(), channelModel.getChannelId()); + new Handler(Looper.getMainLooper()).post(() -> { + if (success) { + buttonIcon.setEnabled(true); + Toast.makeText(context, "Channel " + channelModel.getAuthor() + " whitelisted", Toast.LENGTH_SHORT).show(); + } + else { + buttonIcon.setEnabled(false); + Toast.makeText(context, "Channel " + channelModel.getAuthor() + " failed to whitelist", Toast.LENGTH_SHORT).show(); + } + + view.setEnabled(true); + }); + } + else { + if (debug) { + Log.d(TAG, "player fetch response was " + connection.getResponseCode()); + } + + buttonIcon.setEnabled(false); + view.setEnabled(true); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to fetch channelId", ex); + view.setEnabled(true); + } + } + + // helpers + + private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { + return Requester.getConnectionFromRoute(YT_API_URL, route, params); + } + + private static JSONObject getJSONObject(HttpURLConnection connection) throws Exception { + return Requester.getJSONObject(connection); + } +} \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRoutes.java b/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRoutes.java new file mode 100644 index 00000000..b885798f --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRoutes.java @@ -0,0 +1,11 @@ +package fi.vanced.libraries.youtube.ads; + +import static fi.vanced.utils.requests.Route.Method.GET; + +import fi.vanced.utils.requests.Route; + +public class AdsRoutes { + public static final Route GET_CHANNEL_DETAILS = new Route(GET, "player?key={api_key}"); + + private AdsRoutes() {} +} \ No newline at end of file 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 161e26df..b023790b 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 @@ -4,7 +4,6 @@ import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.player.VideoInformation.channelName; import static fi.vanced.libraries.youtube.ui.SlimButtonContainer.adBlockButton; import static fi.vanced.utils.VancedUtils.getPreferences; -import static fi.vanced.utils.VancedUtils.parseJson; import android.content.Context; import android.content.SharedPreferences; @@ -12,12 +11,7 @@ import android.util.Log; import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; -import org.json.JSONObject; - import java.io.IOException; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; import java.util.ArrayList; import java.util.Iterator; @@ -55,63 +49,6 @@ public class VideoAds { } } - // Call to this needs to be injected in YT code (CURRENTLY NOT USED) - public static void newVideoLoaded(String videoId) { - if (debug) { - Log.d(TAG, "newVideoLoaded - " + videoId); - } - - try { - if (fetchThread != null && fetchThread.getState() != Thread.State.TERMINATED) { - if (debug) { - Log.d(TAG, "Interrupting the thread. Current state " + fetchThread.getState()); - } - fetchThread.interrupt(); - } - } - catch (Exception ex) { - Log.e(TAG, "Error in the fetch thread", ex); - } - - fetchThread = new Thread(() -> { - try { - if (debug) { - Log.d(TAG, "Fetching channelId for " + videoId); - } - HttpURLConnection connection = (HttpURLConnection) new URL(YT_API_URL + "/player?key=" + YT_API_KEY).openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json; utf-8"); - connection.setRequestProperty("Accept", "application/json"); - connection.setDoOutput(true); - connection.setConnectTimeout(2 * 1000); - - // TODO: Actually fetch the version - String jsonInputString = "{\"context\": {\"client\": { \"clientName\": \"Android\", \"clientVersion\": \"16.49.37\" } }, \"videoId\": \"" + videoId + "\"}"; - try(OutputStream os = connection.getOutputStream()) { - byte[] input = jsonInputString.getBytes("utf-8"); - os.write(input, 0, input.length); - } - if (connection.getResponseCode() == 200) { - JSONObject json = new JSONObject(parseJson(connection)); - JSONObject videoInfo = json.getJSONObject("videoDetails"); - ChannelModel channelModel = new ChannelModel(videoInfo.getString("author"), videoInfo.getString("channelId")); - if (debug) { - Log.d(TAG, "channelId " + channelModel.getChannelId() + " fetched for author " + channelModel.getAuthor()); - } - } - else if (debug) { - Log.d(TAG, "player fetch response was " + connection.getResponseCode()); - } - } - catch (Exception ex) { - Log.e(TAG, "Failed to fetch channelId", ex); - return; - } - }); - - fetchThread.start(); - } - public static boolean getShouldShowAds() { if (!isEnabled) return false; 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 9f2edd2b..7d408354 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 @@ -4,19 +4,13 @@ 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; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; -import org.json.JSONObject; - -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.StandardCharsets; +import fi.vanced.libraries.youtube.ryd.requests.RYDRequester; public class Registration { private static final String TAG = "VI - RYD - Registration"; @@ -50,7 +44,7 @@ public class Registration { return this.userId; } - private void saveUserId(String userId) { + public void saveUserId(String userId) { try { if (this.context == null) throw new Exception("Unable to save userId because context was null"); @@ -64,87 +58,10 @@ public class Registration { } private String register() { - try { - // Generate a new userId - String userId = randomString(36); - if (debug) { - Log.d(TAG, "Trying to register the following userId: " + userId); - } - - // Get the registration challenge - HttpURLConnection connection = (HttpURLConnection) new URL(ReturnYouTubeDislikes.RYD_API_URL + "/puzzle/registration?userId=" + userId).openConnection(); - connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); - connection.setConnectTimeout(5 * 1000); - if (connection.getResponseCode() == 200) { - JSONObject json = new JSONObject(parseJson(connection)); - String challenge = json.getString("challenge"); - int difficulty = json.getInt("difficulty"); - if (debug) { - Log.d(TAG, "Registration challenge - " + challenge + " with difficulty of " + difficulty); - } - - // Solve the puzzle - String solution = Utils.solvePuzzle(challenge, difficulty); - if (debug) { - Log.d(TAG, "Registration confirmation solution is " + solution); - } - - return confirmRegistration(userId, solution); - } - else if (debug) { - Log.d(TAG, "Registration response was " + connection.getResponseCode()); - } + String userId = randomString(36); + if (debug) { + Log.d(TAG, "Trying to register the following userId: " + userId); } - catch (Exception ex) { - Log.e(TAG, "Failed to register userId", ex); - } - - return null; - } - - public String confirmRegistration(String userId, String solution) { - try { - if (debug) { - Log.d(TAG, "Trying to confirm registration for the following userId: " + userId + " with solution: " + solution); - } - - // Confirm registration - HttpURLConnection confirmationCon = (HttpURLConnection) new URL(ReturnYouTubeDislikes.RYD_API_URL + "/puzzle/registration?userId=" + userId).openConnection(); - confirmationCon.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); - confirmationCon.setRequestMethod("POST"); - confirmationCon.setRequestProperty("Content-Type", "application/json"); - confirmationCon.setRequestProperty("Accept", "application/json"); - confirmationCon.setDoOutput(true); - confirmationCon.setConnectTimeout(5 * 1000); - - String jsonInputString = "{\"solution\": \"" + solution + "\"}"; - try(OutputStream os = confirmationCon.getOutputStream()) { - byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); - os.write(input, 0, input.length); - } - if (confirmationCon.getResponseCode() == 200) { - String result = parseJson(confirmationCon); - if (debug) { - Log.d(TAG, "Registration confirmation result was " + result); - } - - if (result.equalsIgnoreCase("true")) { - saveUserId(userId); - if (debug) { - Log.d(TAG, "Registration was successful for user " + userId); - } - - return userId; - } - } - else if (debug) { - Log.d(TAG, "Registration confirmation response was " + confirmationCon.getResponseCode()); - } - } - catch (Exception ex) { - Log.e(TAG, "Failed to confirm registration", ex); - } - - return null; + return RYDRequester.register(userId, this); } } 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 e182953d..e03c3b65 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,16 +2,14 @@ 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.player.VideoInformation.dislikeCount; 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; import android.content.Context; import android.icu.text.CompactDecimalFormat; import android.os.Build; -import android.os.Handler; -import android.os.Looper; import android.util.Log; import android.view.View; import android.widget.TextView; @@ -19,20 +17,14 @@ import android.widget.Toast; import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; -import org.json.JSONObject; - -import java.net.HttpURLConnection; -import java.net.URL; import java.util.Locale; -import static fi.vanced.libraries.youtube.player.VideoInformation.dislikeCount; - +import fi.vanced.libraries.youtube.ryd.requests.RYDRequester; 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"; + public static final String TAG = "VI - RYD"; private static View _dislikeView = null; private static Thread _dislikeFetchThread = null; private static Thread _votingThread = null; @@ -96,39 +88,7 @@ public class ReturnYouTubeDislikes { Log.e(TAG, "Error in the dislike fetch thread", ex); } - _dislikeFetchThread = new Thread(() -> { - try { - if (debug) { - Log.d(TAG, "Fetching dislikes for " + videoId); - } - HttpURLConnection connection = (HttpURLConnection) new URL(RYD_API_URL + "/votes?videoId=" + videoId).openConnection(); - connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); - connection.setConnectTimeout(5 * 1000); - if (connection.getResponseCode() == 200) { - JSONObject json = new JSONObject(parseJson(connection)); - dislikeCount = json.getInt("dislikes"); - if (debug) { - Log.d(TAG, "dislikes fetched - " + dislikeCount); - } - - // Set the dislikes - new Handler(Looper.getMainLooper()).post(new Runnable () { - @Override - public void run () { - trySetDislikes(formatDislikes(dislikeCount)); - } - }); - } - else if (debug) { - Log.d(TAG, "dislikes fetch response was " + connection.getResponseCode()); - } - } - catch (Exception ex) { - dislikeCount = null; - Log.e(TAG, "Failed to fetch dislikes", ex); - return; - } - }); + _dislikeFetchThread = new Thread(() -> RYDRequester.fetchDislikes(videoId)); _dislikeFetchThread.start(); } @@ -211,7 +171,7 @@ public class ReturnYouTubeDislikes { return originalText; } - private static void trySetDislikes(String dislikeCount) { + public static void trySetDislikes(String dislikeCount) { if (!isEnabled) return; try { @@ -345,7 +305,7 @@ public class ReturnYouTubeDislikes { } } - private static String formatDislikes(int dislikes) { + public static String formatDislikes(int dislikes) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && compactNumberFormatter != null) { final String formatted = compactNumberFormatter.format(dislikes); if (debug) { diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/Utils.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Utils.java index 9ee92c28..955d9b3b 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ryd/Utils.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Utils.java @@ -1,13 +1,14 @@ package fi.vanced.libraries.youtube.ryd; -import android.util.Log; import android.util.Base64; +import android.util.Log; + import java.security.MessageDigest; public class Utils { private static final String TAG = "VI - RYD - Utils"; - static String solvePuzzle(String challenge, int difficulty) { + public static String solvePuzzle(String challenge, int difficulty) { byte[] decodedChallenge = Base64.decode(challenge, Base64.NO_WRAP); byte[] buffer = new byte[20]; diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/Voting.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Voting.java index fd53bf55..f023b42d 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ryd/Voting.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/Voting.java @@ -1,17 +1,11 @@ package fi.vanced.libraries.youtube.ryd; import static fi.razerman.youtube.XGlobals.debug; -import static fi.vanced.utils.VancedUtils.parseJson; import android.content.Context; import android.util.Log; -import org.json.JSONObject; - -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.StandardCharsets; +import fi.vanced.libraries.youtube.ryd.requests.RYDRequester; public class Voting { private static final String TAG = "VI - RYD - Voting"; @@ -25,96 +19,10 @@ public class Voting { } public boolean sendVote(String videoId, int vote) { - try { - String userId = registration.getUserId(); - if (debug) { - Log.d(TAG, "Trying to vote the following video: " + videoId + " with vote " + vote + " and userId: " + userId); - } - - // Send the vote - HttpURLConnection connection = (HttpURLConnection) new URL(ReturnYouTubeDislikes.RYD_API_URL + "/interact/vote").openConnection(); - connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setRequestProperty("Accept", "application/json"); - connection.setDoOutput(true); - connection.setConnectTimeout(5 * 1000); - String voteJsonString = "{\"userId\": \"" + userId + "\", \"videoId\": \"" + videoId + "\", \"value\": \"" + vote + "\"}"; - try(OutputStream os = connection.getOutputStream()) { - byte[] input = voteJsonString.getBytes(StandardCharsets.UTF_8); - os.write(input, 0, input.length); - } - - if (connection.getResponseCode() == 200) { - JSONObject json = new JSONObject(parseJson(connection)); - String challenge = json.getString("challenge"); - int difficulty = json.getInt("difficulty"); - if (debug) { - Log.d(TAG, "Vote challenge - " + challenge + " with difficulty of " + difficulty); - } - - // Solve the puzzle - String solution = Utils.solvePuzzle(challenge, difficulty); - if (debug) { - Log.d(TAG, "Vote confirmation solution is " + solution); - } - - // Confirm vote - return confirmVote(userId, videoId, solution); - } - else if (debug) { - Log.d(TAG, "Vote response was " + connection.getResponseCode()); - } + String userId = registration.getUserId(); + if (debug) { + Log.d(TAG, "Trying to vote the following video: " + videoId + " with vote " + vote + " and userId: " + userId); } - catch (Exception ex) { - Log.e(TAG, "Failed to send vote", ex); - } - - return false; - } - - public boolean confirmVote(String userId, String videoId, String solution) { - try { - if (debug) { - Log.d(TAG, "Trying to confirm vote for video: " + videoId + " with solution " + solution + " and userId: " + userId); - } - - // Confirm vote - HttpURLConnection confirmationCon = (HttpURLConnection) new URL(ReturnYouTubeDislikes.RYD_API_URL + "/interact/confirmVote").openConnection(); - confirmationCon.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); - confirmationCon.setRequestMethod("POST"); - confirmationCon.setRequestProperty("Content-Type", "application/json"); - confirmationCon.setRequestProperty("Accept", "application/json"); - confirmationCon.setDoOutput(true); - confirmationCon.setConnectTimeout(5 * 1000); - - String jsonInputString = "{\"userId\": \"" + userId + "\", \"videoId\": \"" + videoId + "\", \"solution\": \"" + solution + "\"}"; - try(OutputStream os = confirmationCon.getOutputStream()) { - byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); - os.write(input, 0, input.length); - } - if (confirmationCon.getResponseCode() == 200) { - String result = parseJson(confirmationCon); - if (debug) { - Log.d(TAG, "Vote confirmation result was " + result); - } - - if (result.equalsIgnoreCase("true")) { - if (debug) { - Log.d(TAG, "Vote was successful for user " + userId); - } - - return true; - } - } - else if (debug) { - Log.d(TAG, "Vote confirmation response was " + confirmationCon.getResponseCode()); - } - } - catch (Exception ex) { - Log.e(TAG, "Failed to send vote", ex); - } - - return false; + return RYDRequester.sendVote(videoId, userId, vote); } } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java new file mode 100644 index 00000000..da6832a4 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java @@ -0,0 +1,221 @@ +package fi.vanced.libraries.youtube.ryd.requests; + +import static fi.razerman.youtube.XGlobals.debug; +import static fi.vanced.libraries.youtube.player.VideoInformation.dislikeCount; +import static fi.vanced.libraries.youtube.ryd.ReturnYouTubeDislikes.TAG; +import static fi.vanced.utils.requests.Requester.parseJson; + +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import org.json.JSONObject; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.nio.charset.StandardCharsets; + +import fi.vanced.libraries.youtube.ryd.Registration; +import fi.vanced.libraries.youtube.ryd.ReturnYouTubeDislikes; +import fi.vanced.libraries.youtube.ryd.Utils; +import fi.vanced.utils.requests.Requester; +import fi.vanced.utils.requests.Route; + +public class RYDRequester { + private static final String RYD_API_URL = "https://returnyoutubedislikeapi.com/"; + + private RYDRequester() {} + + public static void fetchDislikes(String videoId) { + try { + if (debug) { + Log.d(TAG, "Fetching dislikes for " + videoId); + } + HttpURLConnection connection = getConnectionFromRoute(RYDRoutes.GET_DISLIKES, videoId); + connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); + connection.setConnectTimeout(5 * 1000); + if (connection.getResponseCode() == 200) { + JSONObject json = getJSONObject(connection); + dislikeCount = json.getInt("dislikes"); + if (debug) { + Log.d(TAG, "dislikes fetched - " + dislikeCount); + } + + // Set the dislikes + new Handler(Looper.getMainLooper()).post(() -> ReturnYouTubeDislikes.trySetDislikes(ReturnYouTubeDislikes.formatDislikes(dislikeCount))); + } + else if (debug) { + Log.d(TAG, "dislikes fetch response was " + connection.getResponseCode()); + } + } + catch (Exception ex) { + dislikeCount = null; + Log.e(TAG, "Failed to fetch dislikes", ex); + } + } + + public static String register(String userId, Registration registration) { + try { + HttpURLConnection connection = getConnectionFromRoute(RYDRoutes.GET_REGISTRATION, userId); + connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); + connection.setConnectTimeout(5 * 1000); + if (connection.getResponseCode() == 200) { + JSONObject json = getJSONObject(connection); + String challenge = json.getString("challenge"); + int difficulty = json.getInt("difficulty"); + if (debug) { + Log.d(TAG, "Registration challenge - " + challenge + " with difficulty of " + difficulty); + } + + // Solve the puzzle + String solution = Utils.solvePuzzle(challenge, difficulty); + if (debug) { + Log.d(TAG, "Registration confirmation solution is " + solution); + } + + return confirmRegistration(userId, solution, registration); + } + else if (debug) { + Log.d(TAG, "Registration response was " + connection.getResponseCode()); + } + } + catch (Exception ex){ + Log.e(TAG, "Failed to register userId", ex); + } + return null; + } + + private static String confirmRegistration(String userId, String solution, Registration registration) { + try { + if (debug) { + Log.d(TAG, "Trying to confirm registration for the following userId: " + userId + " with solution: " + solution); + } + + HttpURLConnection connection = getConnectionFromRoute(RYDRoutes.CONFIRM_REGISTRATION, userId); + applyCommonRequestSettings(connection); + + String jsonInputString = "{\"solution\": \"" + solution + "\"}"; + try(OutputStream os = connection.getOutputStream()) { + byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + if (connection.getResponseCode() == 200) { + String result = parseJson(connection); + if (debug) { + Log.d(TAG, "Registration confirmation result was " + result); + } + + if (result.equalsIgnoreCase("true")) { + registration.saveUserId(userId); + if (debug) { + Log.d(TAG, "Registration was successful for user " + userId); + } + + return userId; + } + } + else if (debug) { + Log.d(TAG, "Registration confirmation response was " + connection.getResponseCode()); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to confirm registration", ex); + } + + return null; + } + + public static boolean sendVote(String videoId, String userId, int vote) { + try { + HttpURLConnection connection = getConnectionFromRoute(RYDRoutes.SEND_VOTE); + applyCommonRequestSettings(connection); + + String voteJsonString = "{\"userId\": \"" + userId + "\", \"videoId\": \"" + videoId + "\", \"value\": \"" + vote + "\"}"; + try(OutputStream os = connection.getOutputStream()) { + byte[] input = voteJsonString.getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + + if (connection.getResponseCode() == 200) { + JSONObject json = getJSONObject(connection); + String challenge = json.getString("challenge"); + int difficulty = json.getInt("difficulty"); + if (debug) { + Log.d(TAG, "Vote challenge - " + challenge + " with difficulty of " + difficulty); + } + + // Solve the puzzle + String solution = Utils.solvePuzzle(challenge, difficulty); + if (debug) { + Log.d(TAG, "Vote confirmation solution is " + solution); + } + + // Confirm vote + return confirmVote(videoId, userId, solution); + } + else if (debug) { + Log.d(TAG, "Vote response was " + connection.getResponseCode()); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to send vote", ex); + } + return false; + } + + private static boolean confirmVote(String videoId, String userId, String solution) { + try { + HttpURLConnection connection = getConnectionFromRoute(RYDRoutes.CONFIRM_VOTE); + applyCommonRequestSettings(connection); + + String jsonInputString = "{\"userId\": \"" + userId + "\", \"videoId\": \"" + videoId + "\", \"solution\": \"" + solution + "\"}"; + try(OutputStream os = connection.getOutputStream()) { + byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + if (connection.getResponseCode() == 200) { + String result = parseJson(connection); + if (debug) { + Log.d(TAG, "Vote confirmation result was " + result); + } + + if (result.equalsIgnoreCase("true")) { + if (debug) { + Log.d(TAG, "Vote was successful for user " + userId); + } + + return true; + } + } + else if (debug) { + Log.d(TAG, "Vote confirmation response was " + connection.getResponseCode()); + } + } + catch (Exception ex) { + Log.e(TAG, "Failed to confirm vote", ex); + } + return false; + } + + // utils + + private static void applyCommonRequestSettings(HttpURLConnection connection) throws Exception { + connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";vanced"); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("Accept", "application/json"); + connection.setDoOutput(true); + connection.setConnectTimeout(5 * 1000); + } + + // helpers + + private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { + return Requester.getConnectionFromRoute(RYD_API_URL, route, params); + } + + private static JSONObject getJSONObject(HttpURLConnection connection) throws Exception { + return Requester.getJSONObject(connection); + } +} \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRoutes.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRoutes.java new file mode 100644 index 00000000..27378e7c --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRoutes.java @@ -0,0 +1,16 @@ +package fi.vanced.libraries.youtube.ryd.requests; + +import static fi.vanced.utils.requests.Route.Method.GET; +import static fi.vanced.utils.requests.Route.Method.POST; + +import fi.vanced.utils.requests.Route; + +public class RYDRoutes { + public static final Route SEND_VOTE = new Route(POST,"interact/vote"); + public static final Route CONFIRM_VOTE = new Route(POST,"interact/confirmVote"); + public static final Route GET_DISLIKES = new Route(GET, "votes?videoId={video_id}"); + public static final Route GET_REGISTRATION = new Route(GET, "puzzle/registration?userId={user_id}"); + public static final Route CONFIRM_REGISTRATION = new Route(POST,"puzzle/registration?userId={user_id}"); + + private RYDRoutes() {} +} \ No newline at end of file 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 94fdac54..e6f0461c 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 @@ -1,36 +1,23 @@ package fi.vanced.libraries.youtube.ui; -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.Toast; - import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.ads.VideoAds.getShouldShowAds; import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; -import static fi.vanced.utils.VancedUtils.parseJson; import static pl.jakubweg.StringRef.str; -import org.json.JSONObject; - -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; +import android.content.Context; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import fi.vanced.libraries.youtube.ads.AdsRequester; 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 { - private static final String TAG = "VI - AdBlock - Button"; - private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1"; - private static final String YT_API_KEY = "replaceMeWithTheYouTubeAPIKey"; + public static final String TAG = "VI - AdBlock - Button"; public AdBlock(Context context, ViewGroup container) { super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, SharedPrefUtils.getBoolean(context, "youtube", "vanced_videoadwhitelisting_enabled", false)); @@ -60,7 +47,7 @@ public class AdBlock extends SlimButton { } //this.button_icon.setEnabled(!this.button_icon.isEnabled()); - addToWhiteList(this.view, this.button_icon); + addToWhiteList(); } private void removeFromWhitelist() { @@ -76,61 +63,12 @@ public class AdBlock extends SlimButton { this.view.setEnabled(true); } - private void addToWhiteList(View view, ImageView buttonIcon) { + private void addToWhiteList() { new Thread(() -> { - try { - if (debug) { - Log.d(TAG, "Fetching channelId for " + currentVideoId); - } - HttpURLConnection connection = (HttpURLConnection) new URL(YT_API_URL + "/player?key=" + YT_API_KEY).openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json; utf-8"); - connection.setRequestProperty("Accept", "application/json"); - connection.setDoOutput(true); - connection.setConnectTimeout(2 * 1000); - - // TODO: Actually fetch the version - String jsonInputString = "{\"context\": {\"client\": { \"clientName\": \"Android\", \"clientVersion\": \"16.49.37\" } }, \"videoId\": \"" + currentVideoId + "\"}"; - try(OutputStream os = connection.getOutputStream()) { - byte[] input = jsonInputString.getBytes("utf-8"); - os.write(input, 0, input.length); - } - if (connection.getResponseCode() == 200) { - JSONObject json = new JSONObject(parseJson(connection)); - JSONObject videoInfo = json.getJSONObject("videoDetails"); - ChannelModel channelModel = new ChannelModel(videoInfo.getString("author"), videoInfo.getString("channelId")); - if (debug) { - Log.d(TAG, "channelId " + channelModel.getChannelId() + " fetched for author " + channelModel.getAuthor()); - } - - boolean success = VideoAds.addToWhitelist(this.context, channelModel.getAuthor(), channelModel.getChannelId()); - new Handler(Looper.getMainLooper()).post(() -> { - if (success) { - buttonIcon.setEnabled(true); - Toast.makeText(context, "Channel " + channelModel.getAuthor() + " whitelisted", Toast.LENGTH_SHORT).show(); - } - else { - buttonIcon.setEnabled(false); - Toast.makeText(context, "Channel " + channelModel.getAuthor() + " failed to whitelist", Toast.LENGTH_SHORT).show(); - } - - view.setEnabled(true); - }); - } - else { - if (debug) { - Log.d(TAG, "player fetch response was " + connection.getResponseCode()); - } - - buttonIcon.setEnabled(false); - this.view.setEnabled(true); - } - } - catch (Exception ex) { - Log.e(TAG, "Failed to fetch channelId", ex); - this.view.setEnabled(true); - return; + if (debug) { + Log.d(TAG, "Fetching channelId for " + currentVideoId); } + AdsRequester.retrieveChannelDetails(this.view, this.button_icon, this.context); }).start(); } } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java new file mode 100644 index 00000000..63c127a0 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java @@ -0,0 +1,101 @@ +package fi.vanced.libraries.youtube.whitelisting; + +import static fi.razerman.youtube.XGlobals.debug; +import static fi.vanced.libraries.youtube.player.VideoInformation.channelName; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import fi.vanced.libraries.youtube.player.ChannelModel; +import fi.vanced.utils.ObjectSerializer; +import fi.vanced.utils.SharedPrefUtils; +import fi.vanced.utils.VancedUtils; + +public class Whitelist { + private static final String TAG = "VI - Whitelisting"; + private static final Map> whitelistMap = parseWhitelist(YouTubeTikTokRoot_Application.getAppContext()); + private static final Map enabledMap = parseEnabledMap(YouTubeTikTokRoot_Application.getAppContext()); + + private Whitelist() {} + + public static boolean shouldShowAds() { + return isWhitelisted(WhitelistType.ADS); + } + + public static boolean shouldShowSegments() { + return !isWhitelisted(WhitelistType.SPONSORBLOCK); + } + + private static Map> parseWhitelist(Context context) { + if (context == null) { + return Collections.emptyMap(); + } + WhitelistType[] whitelistTypes = WhitelistType.values(); + Map> whitelistMap = new HashMap<>(whitelistTypes.length); + + for (WhitelistType whitelistType : whitelistTypes) { + SharedPreferences preferences = VancedUtils.getPreferences(context, whitelistType.getPreferencesName()); + String serializedChannels = preferences.getString("channels", null); + if (serializedChannels == null) { + if (debug) { + Log.d(TAG, String.format("channels string was null for %s whitelisting", whitelistType)); + } + return Collections.emptyMap(); + } + try { + List deserializedChannels = (List) ObjectSerializer.deserialize(serializedChannels); + if (debug) { + Log.d(TAG, serializedChannels); + for (ChannelModel channel : deserializedChannels) { + Log.d(TAG, String.format("Whitelisted channel %s (%s) for type %s", channel.getAuthor(), channel.getChannelId(), whitelistType)); + } + } + whitelistMap.put(whitelistType, deserializedChannels); + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + return whitelistMap; + } + + private static Map parseEnabledMap(Context context) { + WhitelistType[] whitelistTypes = WhitelistType.values(); + Map enabledMap = new HashMap<>(whitelistTypes.length); + for (WhitelistType whitelistType : whitelistTypes) { + enabledMap.put(whitelistType, SharedPrefUtils.getBoolean(context, "youtube", whitelistType.getPreferenceEnabledName())); + } + return enabledMap; + } + + private static boolean isWhitelisted(WhitelistType whitelistType) { + boolean isEnabled = enabledMap.get(whitelistType); + if (!isEnabled) { + return false; + } + if (channelName == null || channelName.trim().isEmpty()) { + if (debug) { + Log.d(TAG, String.format("Can't check whitelist status for %s because channel name was missing", whitelistType)); + } + return false; + } + List whitelistedChannels = whitelistMap.get(whitelistType); + for (ChannelModel channel : whitelistedChannels) { + if (channel.getAuthor().equals(channelName)) { + if (debug) { + Log.d(TAG, String.format("Whitelist for channel %s for type %s", channelName, whitelistType)); + } + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java new file mode 100644 index 00000000..bdeec3a8 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java @@ -0,0 +1,22 @@ +package fi.vanced.libraries.youtube.whitelisting; + +public enum WhitelistType { + ADS("vanced_whitelist_ads_enabled"), + SPONSORBLOCK("vanced_whitelist_sb_enabled"); + + private final String preferencesName; + private final String preferenceEnabledName; + + WhitelistType(String preferenceEnabledName) { + this.preferencesName = "whitelist_" + name(); + this.preferenceEnabledName = preferenceEnabledName; + } + + public String getPreferencesName() { + return preferencesName; + } + + public String getPreferenceEnabledName() { + return preferenceEnabledName; + } +} \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/utils/VancedUtils.java b/app/src/main/java/fi/vanced/utils/VancedUtils.java index e9b0c785..aced35ab 100644 --- a/app/src/main/java/fi/vanced/utils/VancedUtils.java +++ b/app/src/main/java/fi/vanced/utils/VancedUtils.java @@ -5,32 +5,17 @@ import android.content.SharedPreferences; import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; import java.security.SecureRandom; public class VancedUtils { + private VancedUtils() {} + public static SharedPreferences getPreferences(Context context, String preferencesName) { if (context == null) return null; return context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE); } - public static String parseJson(HttpURLConnection connection) throws IOException { - StringBuilder jsonBuilder = new StringBuilder(); - InputStream inputStream = connection.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - String line; - while ((line = reader.readLine()) != null) { - jsonBuilder.append(line); - } - inputStream.close(); - return jsonBuilder.toString(); - } - public static int getIdentifier(String name, String defType) { Context context = YouTubeTikTokRoot_Application.getAppContext(); return context.getResources().getIdentifier(name, defType, context.getPackageName()); @@ -46,4 +31,13 @@ public class VancedUtils { sb.append(AB.charAt(rnd.nextInt(AB.length()))); return sb.toString(); } + + public static int countMatches(CharSequence seq, char c) { + int count = 0; + for (int i = 0; i < seq.length(); i++) { + if (seq.charAt(i) == c) + count++; + } + return count; + } } \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/utils/requests/Requester.java b/app/src/main/java/fi/vanced/utils/requests/Requester.java new file mode 100644 index 00000000..a39b28a3 --- /dev/null +++ b/app/src/main/java/fi/vanced/utils/requests/Requester.java @@ -0,0 +1,48 @@ +package fi.vanced.utils.requests; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +public class Requester { + private Requester() {} + + public static HttpURLConnection getConnectionFromRoute(String apiUrl, Route route, String... params) throws IOException { + String url = apiUrl + route.compile(params).getCompiledRoute(); + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod(route.getMethod().name()); + return connection; + } + + public static String parseJson(HttpURLConnection connection) throws IOException { + StringBuilder jsonBuilder = new StringBuilder(); + InputStream inputStream = connection.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + String line; + while ((line = reader.readLine()) != null) { + jsonBuilder.append(line); + } + inputStream.close(); + return jsonBuilder.toString(); + } + + public static JSONObject getJSONObject(HttpURLConnection connection) throws Exception { + return new JSONObject(parseJsonAndDisconnect(connection)); + } + + public static JSONArray getJSONArray(HttpURLConnection connection) throws Exception { + return new JSONArray(parseJsonAndDisconnect(connection)); + } + + private static String parseJsonAndDisconnect(HttpURLConnection connection) throws IOException { + String json = parseJson(connection); + connection.disconnect(); + return json; + } +} \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/utils/requests/Route.java b/app/src/main/java/fi/vanced/utils/requests/Route.java new file mode 100644 index 00000000..ff82722a --- /dev/null +++ b/app/src/main/java/fi/vanced/utils/requests/Route.java @@ -0,0 +1,59 @@ +package fi.vanced.utils.requests; + +import fi.vanced.utils.VancedUtils; + +public class Route { + private final String route; + private final Route.Method method; + private final int paramCount; + + public Route(Route.Method method, String route) { + this.method = method; + this.route = route; + this.paramCount = VancedUtils.countMatches(route, '{'); + + if (paramCount != VancedUtils.countMatches(route, '}')) + throw new IllegalArgumentException("Not enough parameters"); + } + + public Route.Method getMethod() { + return method; + } + + public Route.CompiledRoute compile(String... params) { + if (params.length != paramCount) + throw new IllegalArgumentException("Error compiling route [" + route + "], incorrect amount of parameters provided. " + + "Expected: " + paramCount + ", provided: " + params.length); + + StringBuilder compiledRoute = new StringBuilder(route); + for (int i = 0; i < paramCount; i++) { + int paramStart = compiledRoute.indexOf("{"); + int paramEnd = compiledRoute.indexOf("}"); + compiledRoute.replace(paramStart, paramEnd + 1, params[i]); + } + return new Route.CompiledRoute(this, compiledRoute.toString()); + } + + public static class CompiledRoute { + private final Route baseRoute; + private final String compiledRoute; + + private CompiledRoute(Route baseRoute, String compiledRoute) { + this.baseRoute = baseRoute; + this.compiledRoute = compiledRoute; + } + + public String getCompiledRoute() { + return compiledRoute; + } + + public Route.Method getMethod() { + return baseRoute.method; + } + } + + public enum Method { + GET, + POST + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/jakubweg/PlayerController.java b/app/src/main/java/pl/jakubweg/PlayerController.java index 6039bc8e..d618351a 100644 --- a/app/src/main/java/pl/jakubweg/PlayerController.java +++ b/app/src/main/java/pl/jakubweg/PlayerController.java @@ -1,5 +1,8 @@ package pl.jakubweg; +import static pl.jakubweg.SponsorBlockSettings.skippedSegments; +import static pl.jakubweg.SponsorBlockSettings.skippedTime; + import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; @@ -23,10 +26,7 @@ import java.util.TimerTask; import fi.vanced.libraries.youtube.player.VideoInformation; import pl.jakubweg.objects.SponsorSegment; -import pl.jakubweg.requests.Requester; - -import static pl.jakubweg.SponsorBlockSettings.skippedSegments; -import static pl.jakubweg.SponsorBlockSettings.skippedTime; +import pl.jakubweg.requests.SBRequester; @SuppressLint({"LongLogTag"}) public class PlayerController { @@ -123,7 +123,7 @@ public class PlayerController { } public static void executeDownloadSegments(String videoId) { - SponsorSegment[] segments = Requester.getSegments(videoId); + SponsorSegment[] segments = SBRequester.getSegments(videoId); Arrays.sort(segments); if (VERBOSE) @@ -281,7 +281,7 @@ public class PlayerController { segment.category != SponsorBlockSettings.SegmentInfo.UNSUBMITTED && millis - segment.start < 2000) { // Only skips from the start should count as a view - Requester.sendViewCountRequest(segment); + SBRequester.sendViewCountRequest(segment); } }).start(); } diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index 5f42a490..1662dc27 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -38,7 +38,7 @@ import android.widget.Toast; import java.text.DecimalFormat; import java.util.ArrayList; -import pl.jakubweg.requests.Requester; +import pl.jakubweg.requests.SBRequester; @SuppressWarnings({"unused", "deprecation"}) // injected public class SponsorBlockPreferenceFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -197,7 +197,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement category.addPreference(preference); preference.setTitle(str("stats_loading")); - Requester.retrieveUserStats(category, preference); + SBRequester.retrieveUserStats(category, preference); } } diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java index 8ac1e0a6..52488782 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java @@ -24,7 +24,7 @@ import static pl.jakubweg.SponsorBlockSettings.skippedSegments; import static pl.jakubweg.SponsorBlockSettings.skippedTime; import static pl.jakubweg.SponsorBlockSettings.uuid; import static pl.jakubweg.StringRef.str; -import static pl.jakubweg.requests.Requester.voteForSegment; +import static pl.jakubweg.requests.SBRequester.voteForSegment; import android.annotation.SuppressLint; import android.app.AlertDialog; @@ -61,7 +61,7 @@ import java.util.TimeZone; import pl.jakubweg.objects.SponsorSegment; import pl.jakubweg.objects.UserStats; -import pl.jakubweg.requests.Requester; +import pl.jakubweg.requests.SBRequester; @SuppressWarnings({"LongLogTag"}) public abstract class SponsorBlockUtils { @@ -281,7 +281,7 @@ public abstract class SponsorBlockUtils { Log.e(TAG, "Unable to submit times, invalid parameters"); return; } - Requester.submitSegments(videoId, uuid, ((float) start) / 1000f, ((float) end) / 1000f, segmentType.key, toastRunnable); + SBRequester.submitSegments(videoId, uuid, ((float) start) / 1000f, ((float) end) / 1000f, segmentType.key, toastRunnable); newSponsorSegmentEndMillis = newSponsorSegmentStartMillis = -1; } catch (Exception e) { Log.e(TAG, "Unable to submit segment", e); @@ -485,15 +485,6 @@ public abstract class SponsorBlockUtils { } } - public static int countMatches(CharSequence seq, char c) { - int count = 0; - for (int i = 0; i < seq.length(); i++) { - if (seq.charAt(i) == c) - count++; - } - return count; - } - public static String formatColorString(int color) { return String.format("#%06X", color); } @@ -514,7 +505,7 @@ public abstract class SponsorBlockUtils { preference.setText(userName); preference.setOnPreferenceChangeListener((preference1, newUsername) -> { appContext = new WeakReference<>(context.getApplicationContext()); - Requester.setUsername((String) newUsername, toastRunnable); + SBRequester.setUsername((String) newUsername, toastRunnable); return false; }); } diff --git a/app/src/main/java/pl/jakubweg/requests/Route.java b/app/src/main/java/pl/jakubweg/requests/Route.java deleted file mode 100644 index ce86b955..00000000 --- a/app/src/main/java/pl/jakubweg/requests/Route.java +++ /dev/null @@ -1,70 +0,0 @@ -package pl.jakubweg.requests; - -import static pl.jakubweg.requests.Route.Method.GET; -import static pl.jakubweg.requests.Route.Method.POST; - -import pl.jakubweg.SponsorBlockUtils; - -public class Route { - public static final Route GET_SEGMENTS = new Route(GET, "skipSegments?videoID={video_id}&categories={categories}"); - public static final Route VIEWED_SEGMENT = new Route(POST, "viewedVideoSponsorTime?UUID={segment_id}"); - public static final Route GET_USER_STATS = new Route(GET, "userInfo?userID={user_id}&values=[\"userName\", \"minutesSaved\", \"segmentCount\", \"viewCount\"]"); - public static final Route CHANGE_USERNAME = new Route(POST, "setUsername?userID={user_id}&username={username}"); - public static final Route SUBMIT_SEGMENTS = new Route(POST, "skipSegments?videoID={video_id}&userID={user_id}&startTime={start_time}&endTime={end_time}&category={category}"); - public static final Route VOTE_ON_SEGMENT_QUALITY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&type={type}"); - public static final Route VOTE_ON_SEGMENT_CATEGORY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&category={category}"); - - private final String route; - private final Method method; - private final int paramCount; - - private Route(Method method, String route) { - this.method = method; - this.route = route; - this.paramCount = SponsorBlockUtils.countMatches(route, '{'); - - if (paramCount != SponsorBlockUtils.countMatches(route, '}')) - throw new IllegalArgumentException("Not enough parameters"); - } - - public Method getMethod() { - return method; - } - - public CompiledRoute compile(String... params) { - if (params.length != paramCount) - throw new IllegalArgumentException("Error compiling route [" + route + "], incorrect amount of parameters provided. " + - "Expected: " + paramCount + ", provided: " + params.length); - - StringBuilder compiledRoute = new StringBuilder(route); - for (int i = 0; i < paramCount; i++) { - int paramStart = compiledRoute.indexOf("{"); - int paramEnd = compiledRoute.indexOf("}"); - compiledRoute.replace(paramStart, paramEnd + 1, params[i]); - } - return new CompiledRoute(this, compiledRoute.toString()); - } - - public static class CompiledRoute { - private final Route baseRoute; - private final String compiledRoute; - - private CompiledRoute(Route baseRoute, String compiledRoute) { - this.baseRoute = baseRoute; - this.compiledRoute = compiledRoute; - } - - public String getCompiledRoute() { - return compiledRoute; - } - - public Method getMethod() { - return baseRoute.method; - } - } - - public enum Method { - GET, - POST - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/jakubweg/requests/Requester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java similarity index 78% rename from app/src/main/java/pl/jakubweg/requests/Requester.java rename to app/src/main/java/pl/jakubweg/requests/SBRequester.java index 2b7884c1..08850b1c 100644 --- a/app/src/main/java/pl/jakubweg/requests/Requester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -14,41 +14,39 @@ import android.widget.Toast; import org.json.JSONArray; import org.json.JSONObject; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.net.HttpURLConnection; -import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import fi.vanced.utils.requests.Requester; +import fi.vanced.utils.requests.Route; import pl.jakubweg.SponsorBlockSettings; import pl.jakubweg.SponsorBlockUtils; import pl.jakubweg.SponsorBlockUtils.VoteOption; import pl.jakubweg.objects.SponsorSegment; import pl.jakubweg.objects.UserStats; -public class Requester { - private static final String SPONSORBLOCK_API_URL = "https://sponsor.ajay.app/api/"; +public class SBRequester { + private static final String SPONSORLOCK_API_URL = "https://sponsor.ajay.app/api/"; private static final String TIME_TEMPLATE = "%.3f"; - private Requester() {} + private SBRequester() {} public static synchronized SponsorSegment[] getSegments(String videoId) { List segments = new ArrayList<>(); try { - HttpURLConnection connection = getConnectionFromRoute(Route.GET_SEGMENTS, videoId, SponsorBlockSettings.sponsorBlockUrlCategories); + HttpURLConnection connection = getConnectionFromRoute(SBRoutes.GET_SEGMENTS, videoId, SponsorBlockSettings.sponsorBlockUrlCategories); int responseCode = connection.getResponseCode(); videoHasSegments = false; timeWithoutSegments = ""; if (responseCode == 200) { - JSONArray responseArray = new JSONArray(parseJson(connection)); + JSONArray responseArray = Requester.getJSONArray(connection); int length = responseArray.length(); for (int i = 0; i < length; i++) { - JSONObject obj = ((JSONObject) responseArray.get(i)); + JSONObject obj = (JSONObject) responseArray.get(i); JSONArray segment = obj.getJSONArray("segment"); long start = (long) (segment.getDouble(0) * 1000); long end = (long) (segment.getDouble(1) * 1000); @@ -64,7 +62,6 @@ public class Requester { videoHasSegments = true; timeWithoutSegments = SponsorBlockUtils.getTimeWithoutSegments(segments.toArray(new SponsorSegment[0])); } - connection.disconnect(); } catch (Exception ex) { ex.printStackTrace(); @@ -76,7 +73,7 @@ public class Requester { try { String start = String.format(Locale.US, TIME_TEMPLATE, startTime); String end = String.format(Locale.US, TIME_TEMPLATE, endTime); - HttpURLConnection connection = getConnectionFromRoute(Route.SUBMIT_SEGMENTS, videoId, uuid, start, end, category); + HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, videoId, uuid, start, end, category); int responseCode = connection.getResponseCode(); switch (responseCode) { @@ -106,7 +103,7 @@ public class Requester { public static void sendViewCountRequest(SponsorSegment segment) { try { - HttpURLConnection connection = getConnectionFromRoute(Route.VIEWED_SEGMENT, segment.UUID); + HttpURLConnection connection = getConnectionFromRoute(SBRoutes.VIEWED_SEGMENT, segment.UUID); connection.disconnect(); } catch (Exception ex) { @@ -123,8 +120,8 @@ public class Requester { Toast.makeText(context, str("vote_started"), Toast.LENGTH_SHORT).show(); HttpURLConnection connection = voteOption == VoteOption.CATEGORY_CHANGE - ? getConnectionFromRoute(Route.VOTE_ON_SEGMENT_CATEGORY, segmentUuid, uuid, args[0]) - : getConnectionFromRoute(Route.VOTE_ON_SEGMENT_QUALITY, segmentUuid, uuid, vote); + ? getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_CATEGORY, segmentUuid, uuid, args[0]) + : getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_QUALITY, segmentUuid, uuid, vote); int responseCode = connection.getResponseCode(); switch (responseCode) { @@ -154,9 +151,7 @@ public class Requester { new Thread(() -> { try { - HttpURLConnection connection = getConnectionFromRoute(Route.GET_USER_STATS, SponsorBlockSettings.uuid); - JSONObject json = new JSONObject(parseJson(connection)); - connection.disconnect(); + JSONObject json = getJSONObject(SBRoutes.GET_USER_STATS, SponsorBlockSettings.uuid); UserStats stats = new UserStats(json.getString("userName"), json.getDouble("minutesSaved"), json.getInt("segmentCount"), json.getInt("viewCount")); SponsorBlockUtils.addUserStats(category, loadingPreference, stats); @@ -169,7 +164,7 @@ public class Requester { public static void setUsername(String username, Runnable toastRunnable) { try { - HttpURLConnection connection = getConnectionFromRoute(Route.CHANGE_USERNAME, SponsorBlockSettings.uuid, username); + HttpURLConnection connection = getConnectionFromRoute(SBRoutes.CHANGE_USERNAME, SponsorBlockSettings.uuid, username); int responseCode = connection.getResponseCode(); if (responseCode == 200) { @@ -187,21 +182,10 @@ public class Requester { } private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { - String url = SPONSORBLOCK_API_URL + route.compile(params).getCompiledRoute(); - HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - connection.setRequestMethod(route.getMethod().name()); - return connection; + return Requester.getConnectionFromRoute(SPONSORLOCK_API_URL, route, params); } - private static String parseJson(HttpURLConnection connection) throws IOException { - StringBuilder jsonBuilder = new StringBuilder(); - InputStream inputStream = connection.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - String line; - while ((line = reader.readLine()) != null) { - jsonBuilder.append(line); - } - inputStream.close(); - return jsonBuilder.toString(); + private static JSONObject getJSONObject(Route route, String... params) throws Exception { + return Requester.getJSONObject(getConnectionFromRoute(route, params)); } } \ No newline at end of file diff --git a/app/src/main/java/pl/jakubweg/requests/SBRoutes.java b/app/src/main/java/pl/jakubweg/requests/SBRoutes.java new file mode 100644 index 00000000..840e6b10 --- /dev/null +++ b/app/src/main/java/pl/jakubweg/requests/SBRoutes.java @@ -0,0 +1,18 @@ +package pl.jakubweg.requests; + +import static fi.vanced.utils.requests.Route.Method.GET; +import static fi.vanced.utils.requests.Route.Method.POST; + +import fi.vanced.utils.requests.Route; + +public class SBRoutes { + public static final Route GET_SEGMENTS = new Route(GET, "skipSegments?videoID={video_id}&categories={categories}"); + public static final Route VIEWED_SEGMENT = new Route(POST, "viewedVideoSponsorTime?UUID={segment_id}"); + public static final Route GET_USER_STATS = new Route(GET, "userInfo?userID={user_id}&values=[\"userName\", \"minutesSaved\", \"segmentCount\", \"viewCount\"]"); + public static final Route CHANGE_USERNAME = new Route(POST, "setUsername?userID={user_id}&username={username}"); + public static final Route SUBMIT_SEGMENTS = new Route(POST, "skipSegments?videoID={video_id}&userID={user_id}&startTime={start_time}&endTime={end_time}&category={category}"); + public static final Route VOTE_ON_SEGMENT_QUALITY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&type={type}"); + public static final Route VOTE_ON_SEGMENT_CATEGORY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&category={category}"); + + private SBRoutes() {} +} \ No newline at end of file From dd72d292a42549638049a706d8276e4062329fe0 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 17 Jan 2022 14:56:13 +0100 Subject: [PATCH 05/46] fix route --- .../main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java index 3fb82856..7120acb6 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java @@ -24,7 +24,7 @@ import fi.vanced.utils.requests.Requester; import fi.vanced.utils.requests.Route; public class AdsRequester { - private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1"; + private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1/"; private static final String YT_API_KEY = "replaceMeWithTheYouTubeAPIKey"; public static void retrieveChannelDetails(View view, ImageView buttonIcon, Context context) { From a8f6cca8520aeef5e9a6291a499795b9cf74a1d3 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 17 Jan 2022 18:12:09 +0100 Subject: [PATCH 06/46] move some requests and prepare for unifying --- .../vanced/libraries/youtube/ui/AdBlock.java | 5 +- .../youtube/whitelisting/Whitelist.java | 60 +++++++++++++++++-- .../requests/WhitelistRequester.java} | 12 ++-- .../requests/WhitelistRoutes.java} | 8 +-- 4 files changed, 69 insertions(+), 16 deletions(-) rename app/src/main/java/fi/vanced/libraries/youtube/{ads/AdsRequester.java => whitelisting/requests/WhitelistRequester.java} (87%) rename app/src/main/java/fi/vanced/libraries/youtube/{ads/AdsRoutes.java => whitelisting/requests/WhitelistRoutes.java} (52%) 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 e6f0461c..7204c9de 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 @@ -10,9 +10,10 @@ import android.util.Log; import android.view.View; import android.view.ViewGroup; -import fi.vanced.libraries.youtube.ads.AdsRequester; import fi.vanced.libraries.youtube.ads.VideoAds; import fi.vanced.libraries.youtube.player.VideoInformation; +import fi.vanced.libraries.youtube.whitelisting.WhitelistType; +import fi.vanced.libraries.youtube.whitelisting.requests.WhitelistRequester; import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; @@ -68,7 +69,7 @@ public class AdBlock extends SlimButton { if (debug) { Log.d(TAG, "Fetching channelId for " + currentVideoId); } - AdsRequester.retrieveChannelDetails(this.view, this.button_icon, this.context); + WhitelistRequester.addChannelToWhitelist(WhitelistType.ADS, this.view, this.button_icon, this.context); }).start(); } } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java index 63c127a0..16475b68 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java @@ -2,6 +2,7 @@ package fi.vanced.libraries.youtube.whitelisting; import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.player.VideoInformation.channelName; +import static fi.vanced.utils.VancedUtils.getPreferences; import android.content.Context; import android.content.SharedPreferences; @@ -9,8 +10,11 @@ import android.util.Log; import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; +import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -21,7 +25,7 @@ import fi.vanced.utils.VancedUtils; public class Whitelist { private static final String TAG = "VI - Whitelisting"; - private static final Map> whitelistMap = parseWhitelist(YouTubeTikTokRoot_Application.getAppContext()); + private static final Map> whitelistMap = parseWhitelist(YouTubeTikTokRoot_Application.getAppContext()); private static final Map enabledMap = parseEnabledMap(YouTubeTikTokRoot_Application.getAppContext()); private Whitelist() {} @@ -34,12 +38,12 @@ public class Whitelist { return !isWhitelisted(WhitelistType.SPONSORBLOCK); } - private static Map> parseWhitelist(Context context) { + private static Map> parseWhitelist(Context context) { if (context == null) { return Collections.emptyMap(); } WhitelistType[] whitelistTypes = WhitelistType.values(); - Map> whitelistMap = new HashMap<>(whitelistTypes.length); + Map> whitelistMap = new HashMap<>(whitelistTypes.length); for (WhitelistType whitelistType : whitelistTypes) { SharedPreferences preferences = VancedUtils.getPreferences(context, whitelistType.getPreferencesName()); @@ -48,10 +52,10 @@ public class Whitelist { if (debug) { Log.d(TAG, String.format("channels string was null for %s whitelisting", whitelistType)); } - return Collections.emptyMap(); + continue; } try { - List deserializedChannels = (List) ObjectSerializer.deserialize(serializedChannels); + ArrayList deserializedChannels = (ArrayList) ObjectSerializer.deserialize(serializedChannels); if (debug) { Log.d(TAG, serializedChannels); for (ChannelModel channel : deserializedChannels) { @@ -98,4 +102,50 @@ public class Whitelist { } return false; } + + public static boolean addToWhitelist(WhitelistType whitelistType, Context context, ChannelModel channel) { + ArrayList whitelisted = whitelistMap.get(whitelistType); + for (ChannelModel whitelistedChannel : whitelisted) { + String channelId = channel.getChannelId(); + if (whitelistedChannel.getChannelId().equals(channelId)) { + if (debug) { + Log.d(TAG, String.format("Tried whitelisting an existing channel again. Old info (%1$s | %2$s) - New info (%3$s | %4$s)", + whitelistedChannel.getAuthor(), channelId, channelName, channelId)); + } + return true; + } + } + whitelisted.add(channel); + return updateWhitelist(whitelistType, whitelisted, context); + } + + public static boolean removeFromWhitelist(WhitelistType whitelistType, Context context, String channelName) { + ArrayList channels = whitelistMap.get(whitelistType); + Iterator iterator = channels.iterator(); + while (iterator.hasNext()) { + ChannelModel channel = iterator.next(); + if (channel.getAuthor().equals(channelName)) { + iterator.remove(); + break; + } + } + return updateWhitelist(whitelistType, channels, context); + } + + private static boolean updateWhitelist(WhitelistType whitelistType, ArrayList channels, Context context) { + if (context == null) { + return false; + } + SharedPreferences preferences = getPreferences(context, whitelistType.getPreferencesName()); + SharedPreferences.Editor editor = preferences.edit(); + + try { + editor.putString("channels", ObjectSerializer.serialize(channels)); + editor.apply(); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } } \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java similarity index 87% rename from app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java rename to app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java index 7120acb6..b281c541 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java @@ -1,4 +1,4 @@ -package fi.vanced.libraries.youtube.ads; +package fi.vanced.libraries.youtube.whitelisting.requests; import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; @@ -20,16 +20,18 @@ import java.net.HttpURLConnection; import java.nio.charset.StandardCharsets; import fi.vanced.libraries.youtube.player.ChannelModel; +import fi.vanced.libraries.youtube.whitelisting.Whitelist; +import fi.vanced.libraries.youtube.whitelisting.WhitelistType; import fi.vanced.utils.requests.Requester; import fi.vanced.utils.requests.Route; -public class AdsRequester { +public class WhitelistRequester { private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1/"; private static final String YT_API_KEY = "replaceMeWithTheYouTubeAPIKey"; - public static void retrieveChannelDetails(View view, ImageView buttonIcon, Context context) { + public static void addChannelToWhitelist(WhitelistType whitelistType, View view, ImageView buttonIcon, Context context) { try { - HttpURLConnection connection = getConnectionFromRoute(AdsRoutes.GET_CHANNEL_DETAILS, YT_API_KEY); + HttpURLConnection connection = getConnectionFromRoute(WhitelistRoutes.GET_CHANNEL_DETAILS, YT_API_KEY); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json; utf-8"); connection.setRequestProperty("Accept", "application/json"); @@ -50,7 +52,7 @@ public class AdsRequester { Log.d(TAG, "channelId " + channelModel.getChannelId() + " fetched for author " + channelModel.getAuthor()); } - boolean success = VideoAds.addToWhitelist(context, channelModel.getAuthor(), channelModel.getChannelId()); + boolean success = Whitelist.addToWhitelist(whitelistType, context, channelModel); new Handler(Looper.getMainLooper()).post(() -> { if (success) { buttonIcon.setEnabled(true); diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRoutes.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java similarity index 52% rename from app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRoutes.java rename to app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java index b885798f..f71858e2 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ads/AdsRoutes.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java @@ -1,11 +1,11 @@ -package fi.vanced.libraries.youtube.ads; +package fi.vanced.libraries.youtube.whitelisting.requests; import static fi.vanced.utils.requests.Route.Method.GET; import fi.vanced.utils.requests.Route; -public class AdsRoutes { - public static final Route GET_CHANNEL_DETAILS = new Route(GET, "player?key={api_key}"); +public class WhitelistRoutes { + public static final Route GET_CHANNEL_DETAILS = new Route(GET, "player?key={api_key"); - private AdsRoutes() {} + private WhitelistRoutes() {} } \ No newline at end of file From f53b526957a38790cfc592beeafd951b120e8974 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 17 Jan 2022 20:15:43 +0100 Subject: [PATCH 07/46] unify whitelisting --- .../libraries/youtube/ads/VideoAds.java | 174 ------------------ .../ui/{AdBlock.java => AdButton.java} | 16 +- .../youtube/ui/SBWhitelistButton.java | 74 ++++++++ .../youtube/ui/SlimButtonContainer.java | 27 ++- .../libraries/youtube/ui/SponsorBlock.java | 31 ---- .../youtube/whitelisting/Whitelist.java | 25 +++ .../requests/WhitelistRequester.java | 2 +- .../requests/WhitelistRoutes.java | 2 +- 8 files changed, 129 insertions(+), 222 deletions(-) delete mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ads/VideoAds.java rename app/src/main/java/fi/vanced/libraries/youtube/ui/{AdBlock.java => AdButton.java} (79%) create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java delete mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlock.java 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 deleted file mode 100644 index b023790b..00000000 --- a/app/src/main/java/fi/vanced/libraries/youtube/ads/VideoAds.java +++ /dev/null @@ -1,174 +0,0 @@ -package fi.vanced.libraries.youtube.ads; - -import static fi.razerman.youtube.XGlobals.debug; -import static fi.vanced.libraries.youtube.player.VideoInformation.channelName; -import static fi.vanced.libraries.youtube.ui.SlimButtonContainer.adBlockButton; -import static fi.vanced.utils.VancedUtils.getPreferences; - -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; - -import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; - -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; - private static Thread fetchThread = null; - - 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 - public static void setChannelName(String channelName) { - if (debug) { - Log.d(TAG, "channel name set to " + channelName); - } - VideoInformation.channelName = channelName; - - if (!isEnabled) return; - - if (adBlockButton != null) { - adBlockButton.changeEnabled(getShouldShowAds()); - } - } - - 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"); - } - - return false; - } - - for (ChannelModel channelModel: whiteList) { - if (channelModel.getAuthor().equals(channelName)) { - if (XGlobals.debug) { - Log.d(TAG, "Video ad whitelist for " + channelName); - } - - return true; - } - } - - return false; - } - - 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; - } - catch (Exception ex) { - Log.d(TAG, "Unable to add " + channelName + " with id " + channelId + " to whitelist"); - } - - return false; - } - - public static boolean removeFromWhitelist(Context context, String channelName) { - try { - //whiteList.removeIf(x -> x.getAuthor().equals(channelName)); // Requires Android N - - Iterator iterator = whiteList.iterator(); - while(iterator.hasNext()) - { - ChannelModel value = iterator.next(); - if (value.getAuthor().equals(channelName)) - { - iterator.remove(); - break; - } - } - updateWhitelist(context); - return true; - } - catch (Exception ex) { - Log.d(TAG, "Unable to remove " + channelName + " from whitelist"); - } - - return false; - } - - private static void updateWhitelist(Context context) { - if (context == null) return; - - SharedPreferences preferences = getPreferences(context, PREFERENCES_NAME); - SharedPreferences.Editor editor = preferences.edit(); - - try { - editor.putString("channels", ObjectSerializer.serialize(whiteList)); - } catch (IOException e) { - e.printStackTrace(); - } - - editor.apply(); - } - - private static ArrayList parseWhitelist(Context context) { - if (context == null) return new ArrayList<>(); - - SharedPreferences preferences = getPreferences(context, PREFERENCES_NAME); - try { - String channels = preferences.getString("channels", null); - if (channels == null) { - if (debug) { - Log.d(TAG, "channels string was null for ad whitelisting"); - } - - return new ArrayList<>(); - } - - ArrayList channelModels = (ArrayList) ObjectSerializer.deserialize(channels); - if (debug) { - Log.d(TAG, channels); - for (ChannelModel channelModel: channelModels) { - Log.d(TAG, "Ad whitelisted " + channelModel.getAuthor() + " with id of " + channelModel.getChannelId()); - } - } - - return channelModels; - } catch (IOException e) { - e.printStackTrace(); - } - - return new ArrayList<>(); - } -} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/AdBlock.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java similarity index 79% rename from app/src/main/java/fi/vanced/libraries/youtube/ui/AdBlock.java rename to app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java index 7204c9de..3d536fa0 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/AdBlock.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java @@ -1,7 +1,6 @@ package fi.vanced.libraries.youtube.ui; import static fi.razerman.youtube.XGlobals.debug; -import static fi.vanced.libraries.youtube.ads.VideoAds.getShouldShowAds; import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; import static pl.jakubweg.StringRef.str; @@ -10,18 +9,19 @@ import android.util.Log; import android.view.View; import android.view.ViewGroup; -import fi.vanced.libraries.youtube.ads.VideoAds; import fi.vanced.libraries.youtube.player.VideoInformation; +import fi.vanced.libraries.youtube.whitelisting.Whitelist; import fi.vanced.libraries.youtube.whitelisting.WhitelistType; import fi.vanced.libraries.youtube.whitelisting.requests.WhitelistRequester; import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; -public class AdBlock extends SlimButton { - public static final String TAG = "VI - AdBlock - Button"; +public class AdButton extends SlimButton { + public static final String TAG = "VI - AdButton - Button"; - public AdBlock(Context context, ViewGroup container) { - super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, SharedPrefUtils.getBoolean(context, "youtube", "vanced_videoadwhitelisting_enabled", false)); + public AdButton(Context context, ViewGroup container) { + super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, + SharedPrefUtils.getBoolean(context, "youtube", WhitelistType.ADS.getPreferenceEnabledName(), false)); initialize(); } @@ -29,7 +29,7 @@ public class AdBlock extends SlimButton { private void initialize() { this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_yt_ad_button", "drawable")); this.button_text.setText(str("action_ads")); - changeEnabled(getShouldShowAds()); + changeEnabled(Whitelist.shouldShowAds()); } public void changeEnabled(boolean enabled) { @@ -53,7 +53,7 @@ public class AdBlock extends SlimButton { private void removeFromWhitelist() { try { - VideoAds.removeFromWhitelist(this.context, VideoInformation.channelName); + Whitelist.removeFromWhitelist(WhitelistType.ADS, this.context, VideoInformation.channelName); this.button_icon.setEnabled(false); } catch (Exception ex) { diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java new file mode 100644 index 00000000..22f08c6a --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java @@ -0,0 +1,74 @@ +package fi.vanced.libraries.youtube.ui; + +import static fi.razerman.youtube.XGlobals.debug; +import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; + +import android.content.Context; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import fi.vanced.libraries.youtube.player.VideoInformation; +import fi.vanced.libraries.youtube.whitelisting.Whitelist; +import fi.vanced.libraries.youtube.whitelisting.WhitelistType; +import fi.vanced.libraries.youtube.whitelisting.requests.WhitelistRequester; +import fi.vanced.utils.SharedPrefUtils; +import fi.vanced.utils.VancedUtils; + +public class SBWhitelistButton extends SlimButton { + public static final String TAG = "VI - SBWhitelistButton"; + + public SBWhitelistButton(Context context, ViewGroup container) { + super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, + SharedPrefUtils.getBoolean(context, "youtube", WhitelistType.SPONSORBLOCK.getPreferenceEnabledName(), false)); + + initialize(); + } + + private void initialize() { + this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_sb_logo", "drawable")); + this.button_text.setText("SB"); + changeEnabled(Whitelist.shouldShowSegments()); + } + + public void changeEnabled(boolean enabled) { + if (debug) { + Log.d(TAG, "changeEnabled " + enabled); + } + this.button_icon.setEnabled(enabled); + } + + @Override + public void onClick(View view) { + this.view.setEnabled(false); + if (this.button_icon.isEnabled()) { + removeFromWhitelist(); + return; + } + //this.button_icon.setEnabled(!this.button_icon.isEnabled()); + + addToWhiteList(); + } + + private void removeFromWhitelist() { + try { + Whitelist.removeFromWhitelist(WhitelistType.SPONSORBLOCK, this.context, VideoInformation.channelName); + this.button_icon.setEnabled(false); + } + catch (Exception ex) { + Log.e(TAG, "Failed to remove from whitelist", ex); + return; + } + + this.view.setEnabled(true); + } + + private void addToWhiteList() { + new Thread(() -> { + if (debug) { + Log.d(TAG, "Fetching channelId for " + currentVideoId); + } + WhitelistRequester.addChannelToWhitelist(WhitelistType.SPONSORBLOCK, this.view, this.button_icon, this.context); + }).start(); + } +} 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 5b8781da..bfceb36f 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 @@ -10,7 +10,8 @@ import android.view.ViewGroup; import com.google.android.apps.youtube.app.ui.SlimMetadataScrollableButtonContainerLayout; -import fi.vanced.libraries.youtube.ads.VideoAds; +import fi.vanced.libraries.youtube.whitelisting.Whitelist; +import fi.vanced.libraries.youtube.whitelisting.WhitelistType; import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; @@ -19,7 +20,8 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa private ViewGroup container; private CopyButton copyButton; private CopyWithTimestamp copyWithTimestampButton; - public static AdBlock adBlockButton; + public static AdButton adBlockButton; + public static SBWhitelistButton sbWhitelistButton; private final Context context; SharedPreferences.OnSharedPreferenceChangeListener listener; @@ -48,8 +50,8 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa copyButton = new CopyButton(context, this); copyWithTimestampButton = new CopyWithTimestamp(context, this); - adBlockButton = new AdBlock(context, this); - new SponsorBlock(context, this); + adBlockButton = new AdButton(context, this); + sbWhitelistButton = new SBWhitelistButton(context, this); new SponsorBlockVoting(context, this); addSharedPrefsChangeListener(); @@ -73,9 +75,20 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa 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); + WhitelistType whitelistAds = WhitelistType.ADS; + String adsEnabledPreferenceName = whitelistAds.getPreferenceEnabledName(); + if (adsEnabledPreferenceName.equals(key) && adBlockButton != null) { + boolean enabled = SharedPrefUtils.getBoolean(context, "youtube", adsEnabledPreferenceName, false); + Whitelist.setEnabled(whitelistAds, enabled); + adBlockButton.setVisible(enabled); + return; + } + WhitelistType whitelistSB = WhitelistType.SPONSORBLOCK; + String sbEnabledPreferenceName = whitelistSB.getPreferenceEnabledName(); + if (sbEnabledPreferenceName.equals(key) && sbWhitelistButton != null) { + boolean enabled = SharedPrefUtils.getBoolean(context, "youtube", sbEnabledPreferenceName, false); + Whitelist.setEnabled(whitelistSB, enabled); + sbWhitelistButton.setVisible(enabled); return; } } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlock.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlock.java deleted file mode 100644 index 47132d58..00000000 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/SponsorBlock.java +++ /dev/null @@ -1,31 +0,0 @@ -package fi.vanced.libraries.youtube.ui; - -import static pl.jakubweg.StringRef.str; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; - -import fi.vanced.libraries.youtube.player.VideoHelpers; -import fi.vanced.utils.VancedUtils; - -public class SponsorBlock extends SlimButton { - public SponsorBlock(Context context, ViewGroup container) { - super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, false); - - initialize(); - } - - private void initialize() { - this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_sb_logo", "drawable")); - this.button_text.setText("SB"); - } - - @Override - public void onClick(View view) { - Toast.makeText(YouTubeTikTokRoot_Application.getAppContext(), "Nothing atm", Toast.LENGTH_SHORT).show(); - } -} diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java index 16475b68..e6d0bf66 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java @@ -2,6 +2,8 @@ package fi.vanced.libraries.youtube.whitelisting; import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.player.VideoInformation.channelName; +import static fi.vanced.libraries.youtube.ui.SlimButtonContainer.adBlockButton; +import static fi.vanced.libraries.youtube.ui.SlimButtonContainer.sbWhitelistButton; import static fi.vanced.utils.VancedUtils.getPreferences; import android.content.Context; @@ -19,6 +21,7 @@ import java.util.List; import java.util.Map; import fi.vanced.libraries.youtube.player.ChannelModel; +import fi.vanced.libraries.youtube.player.VideoInformation; import fi.vanced.utils.ObjectSerializer; import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; @@ -30,10 +33,28 @@ public class Whitelist { private Whitelist() {} + // injected calls + public static boolean shouldShowAds() { return isWhitelisted(WhitelistType.ADS); } + public static void setChannelName(String channelName) { + if (debug) { + Log.d(TAG, "channel name set to " + channelName); + } + VideoInformation.channelName = channelName; + + if (enabledMap.get(WhitelistType.ADS) && adBlockButton != null) { + adBlockButton.changeEnabled(shouldShowAds()); + } + if (enabledMap.get(WhitelistType.SPONSORBLOCK) && sbWhitelistButton != null) { + sbWhitelistButton.changeEnabled(shouldShowSegments()); + } + } + + // the rest + public static boolean shouldShowSegments() { return !isWhitelisted(WhitelistType.SPONSORBLOCK); } @@ -148,4 +169,8 @@ public class Whitelist { return false; } } + + public static void setEnabled(WhitelistType whitelistType, boolean enabled) { + enabledMap.put(whitelistType, enabled); + } } \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java index b281c541..978127ba 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java @@ -2,7 +2,7 @@ package fi.vanced.libraries.youtube.whitelisting.requests; import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; -import static fi.vanced.libraries.youtube.ui.AdBlock.TAG; +import static fi.vanced.libraries.youtube.ui.AdButton.TAG; import android.content.Context; import android.os.Handler; diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java index f71858e2..41d2f11c 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java @@ -5,7 +5,7 @@ import static fi.vanced.utils.requests.Route.Method.GET; import fi.vanced.utils.requests.Route; public class WhitelistRoutes { - public static final Route GET_CHANNEL_DETAILS = new Route(GET, "player?key={api_key"); + public static final Route GET_CHANNEL_DETAILS = new Route(GET, "player?key={api_key}"); private WhitelistRoutes() {} } \ No newline at end of file From 2128c2d7d67cf6ffafcdcd321d4f87bb445ae150 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 17 Jan 2022 23:38:25 +0100 Subject: [PATCH 08/46] add sb whitelisting (TODO: fix checks) --- .../vanced/libraries/youtube/ui/AdButton.java | 7 +++--- .../youtube/ui/SBWhitelistButton.java | 14 ++++++----- .../youtube/whitelisting/Whitelist.java | 23 +++++++++++++------ .../youtube/whitelisting/WhitelistType.java | 12 +++++++--- .../requests/WhitelistRequester.java | 11 ++++++--- .../java/pl/jakubweg/PlayerController.java | 7 ++++++ .../SponsorBlockPreferenceFragment.java | 10 ++++++++ .../pl/jakubweg/requests/SBRequester.java | 2 -- app/src/main/res/values/strings.xml | 6 +++++ 9 files changed, 68 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java index 3d536fa0..f184a1d3 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java @@ -8,6 +8,7 @@ import android.content.Context; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import fi.vanced.libraries.youtube.player.VideoInformation; import fi.vanced.libraries.youtube.whitelisting.Whitelist; @@ -48,7 +49,7 @@ public class AdButton extends SlimButton { } //this.button_icon.setEnabled(!this.button_icon.isEnabled()); - addToWhiteList(); + addToWhiteList(this.view, this.button_icon); } private void removeFromWhitelist() { @@ -64,12 +65,12 @@ public class AdButton extends SlimButton { this.view.setEnabled(true); } - private void addToWhiteList() { + private void addToWhiteList(View view, ImageView buttonIcon) { new Thread(() -> { if (debug) { Log.d(TAG, "Fetching channelId for " + currentVideoId); } - WhitelistRequester.addChannelToWhitelist(WhitelistType.ADS, this.view, this.button_icon, this.context); + WhitelistRequester.addChannelToWhitelist(WhitelistType.ADS, view, buttonIcon, this.context); }).start(); } } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java index 22f08c6a..9d7f2e42 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java @@ -7,6 +7,7 @@ import android.content.Context; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import fi.vanced.libraries.youtube.player.VideoInformation; import fi.vanced.libraries.youtube.whitelisting.Whitelist; @@ -14,13 +15,14 @@ import fi.vanced.libraries.youtube.whitelisting.WhitelistType; import fi.vanced.libraries.youtube.whitelisting.requests.WhitelistRequester; import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; +import pl.jakubweg.SponsorBlockSettings; public class SBWhitelistButton extends SlimButton { public static final String TAG = "VI - SBWhitelistButton"; public SBWhitelistButton(Context context, ViewGroup container) { super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, - SharedPrefUtils.getBoolean(context, "youtube", WhitelistType.SPONSORBLOCK.getPreferenceEnabledName(), false)); + SharedPrefUtils.getBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, WhitelistType.SPONSORBLOCK.getPreferenceEnabledName(), false)); initialize(); } @@ -32,9 +34,9 @@ public class SBWhitelistButton extends SlimButton { } public void changeEnabled(boolean enabled) { - if (debug) { + //if (debug) { Log.d(TAG, "changeEnabled " + enabled); - } + //} this.button_icon.setEnabled(enabled); } @@ -47,7 +49,7 @@ public class SBWhitelistButton extends SlimButton { } //this.button_icon.setEnabled(!this.button_icon.isEnabled()); - addToWhiteList(); + addToWhiteList(this.view, this.button_icon); } private void removeFromWhitelist() { @@ -63,12 +65,12 @@ public class SBWhitelistButton extends SlimButton { this.view.setEnabled(true); } - private void addToWhiteList() { + private void addToWhiteList(View view, ImageView buttonIcon) { new Thread(() -> { if (debug) { Log.d(TAG, "Fetching channelId for " + currentVideoId); } - WhitelistRequester.addChannelToWhitelist(WhitelistType.SPONSORBLOCK, this.view, this.button_icon, this.context); + WhitelistRequester.addChannelToWhitelist(WhitelistType.SPONSORBLOCK, view, buttonIcon, this.context); }).start(); } } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java index e6d0bf66..553b8040 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java @@ -5,17 +5,19 @@ import static fi.vanced.libraries.youtube.player.VideoInformation.channelName; import static fi.vanced.libraries.youtube.ui.SlimButtonContainer.adBlockButton; import static fi.vanced.libraries.youtube.ui.SlimButtonContainer.sbWhitelistButton; import static fi.vanced.utils.VancedUtils.getPreferences; +import static pl.jakubweg.StringRef.str; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; +import android.widget.Toast; import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -64,7 +66,7 @@ public class Whitelist { return Collections.emptyMap(); } WhitelistType[] whitelistTypes = WhitelistType.values(); - Map> whitelistMap = new HashMap<>(whitelistTypes.length); + Map> whitelistMap = new EnumMap<>(WhitelistType.class); for (WhitelistType whitelistType : whitelistTypes) { SharedPreferences preferences = VancedUtils.getPreferences(context, whitelistType.getPreferencesName()); @@ -73,6 +75,7 @@ public class Whitelist { if (debug) { Log.d(TAG, String.format("channels string was null for %s whitelisting", whitelistType)); } + whitelistMap.put(whitelistType, new ArrayList<>()); continue; } try { @@ -93,9 +96,8 @@ public class Whitelist { } private static Map parseEnabledMap(Context context) { - WhitelistType[] whitelistTypes = WhitelistType.values(); - Map enabledMap = new HashMap<>(whitelistTypes.length); - for (WhitelistType whitelistType : whitelistTypes) { + Map enabledMap = new EnumMap<>(WhitelistType.class); + for (WhitelistType whitelistType : WhitelistType.values()) { enabledMap.put(whitelistType, SharedPrefUtils.getBoolean(context, "youtube", whitelistType.getPreferenceEnabledName())); } return enabledMap; @@ -140,7 +142,7 @@ public class Whitelist { return updateWhitelist(whitelistType, whitelisted, context); } - public static boolean removeFromWhitelist(WhitelistType whitelistType, Context context, String channelName) { + public static void removeFromWhitelist(WhitelistType whitelistType, Context context, String channelName) { ArrayList channels = whitelistMap.get(whitelistType); Iterator iterator = channels.iterator(); while (iterator.hasNext()) { @@ -150,7 +152,14 @@ public class Whitelist { break; } } - return updateWhitelist(whitelistType, channels, context); + boolean success = updateWhitelist(whitelistType, channels, context); + String friendlyName = whitelistType.getFriendlyName(); + if (success) { + Toast.makeText(context, str("vanced_whitelisting_removed", channelName, friendlyName), Toast.LENGTH_SHORT).show(); + } + else { + Toast.makeText(context, str("vanced_whitelisting_remove_failed", channelName, friendlyName), Toast.LENGTH_SHORT).show(); + } } private static boolean updateWhitelist(WhitelistType whitelistType, ArrayList channels, Context context) { diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java index bdeec3a8..0e9a7e56 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java @@ -1,17 +1,23 @@ package fi.vanced.libraries.youtube.whitelisting; public enum WhitelistType { - ADS("vanced_whitelist_ads_enabled"), - SPONSORBLOCK("vanced_whitelist_sb_enabled"); + ADS("Ads", "vanced_whitelist_ads_enabled"), + SPONSORBLOCK("SponsorBlock", "vanced_whitelist_sb_enabled"); + private final String friendlyName; private final String preferencesName; private final String preferenceEnabledName; - WhitelistType(String preferenceEnabledName) { + WhitelistType(String friendlyName, String preferenceEnabledName) { + this.friendlyName = friendlyName; this.preferencesName = "whitelist_" + name(); this.preferenceEnabledName = preferenceEnabledName; } + public String getFriendlyName() { + return friendlyName; + } + public String getPreferencesName() { return preferencesName; } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java index 978127ba..534d7402 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java @@ -3,6 +3,7 @@ package fi.vanced.libraries.youtube.whitelisting.requests; import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; import static fi.vanced.libraries.youtube.ui.AdButton.TAG; +import static pl.jakubweg.StringRef.str; import android.content.Context; import android.os.Handler; @@ -29,6 +30,8 @@ public class WhitelistRequester { private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1/"; private static final String YT_API_KEY = "replaceMeWithTheYouTubeAPIKey"; + private WhitelistRequester() {} + public static void addChannelToWhitelist(WhitelistType whitelistType, View view, ImageView buttonIcon, Context context) { try { HttpURLConnection connection = getConnectionFromRoute(WhitelistRoutes.GET_CHANNEL_DETAILS, YT_API_KEY); @@ -48,19 +51,21 @@ public class WhitelistRequester { JSONObject json = getJSONObject(connection); JSONObject videoInfo = json.getJSONObject("videoDetails"); ChannelModel channelModel = new ChannelModel(videoInfo.getString("author"), videoInfo.getString("channelId")); + String author = channelModel.getAuthor(); if (debug) { - Log.d(TAG, "channelId " + channelModel.getChannelId() + " fetched for author " + channelModel.getAuthor()); + Log.d(TAG, "channelId " + channelModel.getChannelId() + " fetched for author " + author); } boolean success = Whitelist.addToWhitelist(whitelistType, context, channelModel); + String whitelistTypeName = whitelistType.getFriendlyName(); new Handler(Looper.getMainLooper()).post(() -> { if (success) { buttonIcon.setEnabled(true); - Toast.makeText(context, "Channel " + channelModel.getAuthor() + " whitelisted", Toast.LENGTH_SHORT).show(); + Toast.makeText(context, str("vanced_whitelisting_added", author, whitelistTypeName), Toast.LENGTH_SHORT).show(); } else { buttonIcon.setEnabled(false); - Toast.makeText(context, "Channel " + channelModel.getAuthor() + " failed to whitelist", Toast.LENGTH_SHORT).show(); + Toast.makeText(context, str("vanced_whitelisting_add_failed", author, whitelistTypeName), Toast.LENGTH_SHORT).show(); } view.setEnabled(true); diff --git a/app/src/main/java/pl/jakubweg/PlayerController.java b/app/src/main/java/pl/jakubweg/PlayerController.java index d618351a..edbd27c8 100644 --- a/app/src/main/java/pl/jakubweg/PlayerController.java +++ b/app/src/main/java/pl/jakubweg/PlayerController.java @@ -2,6 +2,8 @@ package pl.jakubweg; import static pl.jakubweg.SponsorBlockSettings.skippedSegments; import static pl.jakubweg.SponsorBlockSettings.skippedTime; +import static pl.jakubweg.SponsorBlockUtils.timeWithoutSegments; +import static pl.jakubweg.SponsorBlockUtils.videoHasSegments; import android.annotation.SuppressLint; import android.app.Activity; @@ -25,6 +27,7 @@ import java.util.Timer; import java.util.TimerTask; import fi.vanced.libraries.youtube.player.VideoInformation; +import fi.vanced.libraries.youtube.whitelisting.Whitelist; import pl.jakubweg.objects.SponsorSegment; import pl.jakubweg.requests.SBRequester; @@ -123,6 +126,10 @@ public class PlayerController { } public static void executeDownloadSegments(String videoId) { + videoHasSegments = false; + timeWithoutSegments = ""; + if (!Whitelist.shouldShowSegments()) + return; SponsorSegment[] segments = SBRequester.getSegments(videoId); Arrays.sort(segments); diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index 1662dc27..4af3e7a3 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -38,6 +38,7 @@ import android.widget.Toast; import java.text.DecimalFormat; import java.util.ArrayList; +import fi.vanced.libraries.youtube.whitelisting.WhitelistType; import pl.jakubweg.requests.SBRequester; @SuppressWarnings({"unused", "deprecation"}) // injected @@ -278,6 +279,15 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement screen.addPreference(preference); } + { + Preference preference = new SwitchPreference(context); + preference.setTitle(str("general_whitelisting")); + preference.setSummary(str("general_whitelisting_sum")); + preference.setKey(WhitelistType.SPONSORBLOCK.getPreferenceEnabledName()); + preferencesToDisableWhenSBDisabled.add(preference); + screen.addPreference(preference); + } + { EditTextPreference preference = new EditTextPreference(context); preference.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED); diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index 08850b1c..e1cb1922 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -39,8 +39,6 @@ public class SBRequester { try { HttpURLConnection connection = getConnectionFromRoute(SBRoutes.GET_SEGMENTS, videoId, SponsorBlockSettings.sponsorBlockUrlCategories); int responseCode = connection.getResponseCode(); - videoHasSegments = false; - timeWithoutSegments = ""; if (responseCode == 200) { JSONArray responseArray = Requester.getJSONArray(connection); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e19765aa..7c91628e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -311,6 +311,8 @@ Show time without segments This time appears in brackets next to the current time. This shows the total video duration minus any segments. + Channel whitelisting + Use the whitelist button under the player to whitelist a channel Preview/Recap Quick recap of previous episodes, or a preview of what\'s coming up later in the current video. Meant for edited together clips, not for spoken summaries. Stats @@ -342,6 +344,10 @@ 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 + Channel %s was added to the %s whitelist + Channel %s was removed from the %s whitelist + Failed to add channel %s to the %s whitelist + Failed to remove channel %s from the %s whitelist Hidden In player Under player From d04c8f99ab31a0f347b50cafc451b29d2780ffab Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 17 Jan 2022 23:40:02 +0100 Subject: [PATCH 09/46] uncomment debug call --- .../fi/vanced/libraries/youtube/ui/SBWhitelistButton.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java index 9d7f2e42..19a25f56 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java @@ -34,9 +34,9 @@ public class SBWhitelistButton extends SlimButton { } public void changeEnabled(boolean enabled) { - //if (debug) { + if (debug) { Log.d(TAG, "changeEnabled " + enabled); - //} + } this.button_icon.setEnabled(enabled); } From 16073a7f74155d23fb12b870285a7abe7f3204f0 Mon Sep 17 00:00:00 2001 From: caneleex Date: Sun, 23 Jan 2022 22:36:01 +0100 Subject: [PATCH 10/46] final touches --- .../youtube/ryd/ReturnYouTubeDislikes.java | 3 --- .../vanced/libraries/youtube/ui/AdButton.java | 4 +-- .../youtube/ui/SBWhitelistButton.java | 16 ++++++------ .../youtube/ui/SlimButtonContainer.java | 8 +++--- .../youtube/whitelisting/Whitelist.java | 8 +++--- .../youtube/whitelisting/WhitelistType.java | 14 +++++++--- .../requests/WhitelistRequester.java | 26 ++++++++++++------- .../java/pl/jakubweg/PlayerController.java | 2 +- app/src/main/res/drawable/vanced_yt_sb.xml | 14 ++++++++++ .../res/drawable/vanced_yt_sb_blocked.xml | 14 ++++++++++ .../main/res/drawable/vanced_yt_sb_button.xml | 6 +++++ app/src/main/res/values/strings.xml | 2 ++ 12 files changed, 83 insertions(+), 34 deletions(-) create mode 100644 app/src/main/res/drawable/vanced_yt_sb.xml create mode 100644 app/src/main/res/drawable/vanced_yt_sb_blocked.xml create mode 100644 app/src/main/res/drawable/vanced_yt_sb_button.xml 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 e03c3b65..d8f2a1ee 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 @@ -13,7 +13,6 @@ import android.os.Build; import android.util.Log; import android.view.View; import android.widget.TextView; -import android.widget.Toast; import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; @@ -240,8 +239,6 @@ public class ReturnYouTubeDislikes { Log.d(TAG, "Like button " + likeActive + " | Dislike button " + dislikeActive); } - Toast.makeText(YouTubeTikTokRoot_Application.getAppContext(), "Voting value: " + votingValue, Toast.LENGTH_SHORT).show(); - sendVote(votingValue); } catch (Exception ex) { diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java index f184a1d3..1e3a5e90 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/AdButton.java @@ -22,7 +22,7 @@ public class AdButton extends SlimButton { public AdButton(Context context, ViewGroup container) { super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, - SharedPrefUtils.getBoolean(context, "youtube", WhitelistType.ADS.getPreferenceEnabledName(), false)); + SharedPrefUtils.getBoolean(context, WhitelistType.ADS.getSharedPreferencesName(), WhitelistType.ADS.getPreferenceEnabledName(), false)); initialize(); } @@ -55,7 +55,7 @@ public class AdButton extends SlimButton { private void removeFromWhitelist() { try { Whitelist.removeFromWhitelist(WhitelistType.ADS, this.context, VideoInformation.channelName); - this.button_icon.setEnabled(false); + changeEnabled(false); } catch (Exception ex) { Log.e(TAG, "Failed to remove from whitelist", ex); diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java index 19a25f56..c759ee63 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java @@ -2,6 +2,7 @@ package fi.vanced.libraries.youtube.ui; import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; +import static pl.jakubweg.StringRef.str; import android.content.Context; import android.util.Log; @@ -15,35 +16,34 @@ import fi.vanced.libraries.youtube.whitelisting.WhitelistType; import fi.vanced.libraries.youtube.whitelisting.requests.WhitelistRequester; import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; -import pl.jakubweg.SponsorBlockSettings; public class SBWhitelistButton extends SlimButton { public static final String TAG = "VI - SBWhitelistButton"; public SBWhitelistButton(Context context, ViewGroup container) { super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, - SharedPrefUtils.getBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, WhitelistType.SPONSORBLOCK.getPreferenceEnabledName(), false)); + SharedPrefUtils.getBoolean(context, WhitelistType.SPONSORBLOCK.getSharedPreferencesName(), WhitelistType.SPONSORBLOCK.getPreferenceEnabledName(), false)); initialize(); } private void initialize() { - this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_sb_logo", "drawable")); - this.button_text.setText("SB"); - changeEnabled(Whitelist.shouldShowSegments()); + this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_yt_sb_button", "drawable")); + this.button_text.setText(str("action_segments")); + changeEnabled(Whitelist.isChannelSBWhitelisted()); } public void changeEnabled(boolean enabled) { if (debug) { Log.d(TAG, "changeEnabled " + enabled); } - this.button_icon.setEnabled(enabled); + this.button_icon.setEnabled(!enabled); // enabled == true -> strikethrough (no segments), enabled == false -> clear (segments) } @Override public void onClick(View view) { this.view.setEnabled(false); - if (this.button_icon.isEnabled()) { + if (Whitelist.isChannelSBWhitelisted()) { removeFromWhitelist(); return; } @@ -55,7 +55,7 @@ public class SBWhitelistButton extends SlimButton { private void removeFromWhitelist() { try { Whitelist.removeFromWhitelist(WhitelistType.SPONSORBLOCK, this.context, VideoInformation.channelName); - this.button_icon.setEnabled(false); + changeEnabled(false); } catch (Exception ex) { Log.e(TAG, "Failed to remove from whitelist", ex); 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 bfceb36f..12ae3e83 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 @@ -78,7 +78,7 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa WhitelistType whitelistAds = WhitelistType.ADS; String adsEnabledPreferenceName = whitelistAds.getPreferenceEnabledName(); if (adsEnabledPreferenceName.equals(key) && adBlockButton != null) { - boolean enabled = SharedPrefUtils.getBoolean(context, "youtube", adsEnabledPreferenceName, false); + boolean enabled = SharedPrefUtils.getBoolean(context, whitelistAds.getSharedPreferencesName(), adsEnabledPreferenceName, false); Whitelist.setEnabled(whitelistAds, enabled); adBlockButton.setVisible(enabled); return; @@ -86,7 +86,7 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa WhitelistType whitelistSB = WhitelistType.SPONSORBLOCK; String sbEnabledPreferenceName = whitelistSB.getPreferenceEnabledName(); if (sbEnabledPreferenceName.equals(key) && sbWhitelistButton != null) { - boolean enabled = SharedPrefUtils.getBoolean(context, "youtube", sbEnabledPreferenceName, false); + boolean enabled = SharedPrefUtils.getBoolean(context, whitelistSB.getSharedPreferencesName(), sbEnabledPreferenceName, false); Whitelist.setEnabled(whitelistSB, enabled); sbWhitelistButton.setVisible(enabled); return; @@ -97,7 +97,9 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa } }; - context.getSharedPreferences("youtube", Context.MODE_PRIVATE) + context.getSharedPreferences(WhitelistType.ADS.getSharedPreferencesName(), Context.MODE_PRIVATE) + .registerOnSharedPreferenceChangeListener(listener); + context.getSharedPreferences(WhitelistType.SPONSORBLOCK.getSharedPreferencesName(), Context.MODE_PRIVATE) .registerOnSharedPreferenceChangeListener(listener); } } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java index 553b8040..21a9ab02 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/Whitelist.java @@ -51,14 +51,14 @@ public class Whitelist { adBlockButton.changeEnabled(shouldShowAds()); } if (enabledMap.get(WhitelistType.SPONSORBLOCK) && sbWhitelistButton != null) { - sbWhitelistButton.changeEnabled(shouldShowSegments()); + sbWhitelistButton.changeEnabled(isChannelSBWhitelisted()); } } // the rest - public static boolean shouldShowSegments() { - return !isWhitelisted(WhitelistType.SPONSORBLOCK); + public static boolean isChannelSBWhitelisted() { + return isWhitelisted(WhitelistType.SPONSORBLOCK); } private static Map> parseWhitelist(Context context) { @@ -98,7 +98,7 @@ public class Whitelist { private static Map parseEnabledMap(Context context) { Map enabledMap = new EnumMap<>(WhitelistType.class); for (WhitelistType whitelistType : WhitelistType.values()) { - enabledMap.put(whitelistType, SharedPrefUtils.getBoolean(context, "youtube", whitelistType.getPreferenceEnabledName())); + enabledMap.put(whitelistType, SharedPrefUtils.getBoolean(context, whitelistType.getSharedPreferencesName(), whitelistType.getPreferenceEnabledName())); } return enabledMap; } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java index 0e9a7e56..261470a9 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java @@ -1,15 +1,19 @@ package fi.vanced.libraries.youtube.whitelisting; +import pl.jakubweg.SponsorBlockSettings; + public enum WhitelistType { - ADS("Ads", "vanced_whitelist_ads_enabled"), - SPONSORBLOCK("SponsorBlock", "vanced_whitelist_sb_enabled"); + ADS("Ads", "youtube", "vanced_whitelist_ads_enabled"), + SPONSORBLOCK("SponsorBlock", SponsorBlockSettings.PREFERENCES_NAME, "vanced_whitelist_sb_enabled"); private final String friendlyName; private final String preferencesName; + private final String sharedPreferencesName; private final String preferenceEnabledName; - WhitelistType(String friendlyName, String preferenceEnabledName) { + WhitelistType(String friendlyName, String sharedPreferencesName, String preferenceEnabledName) { this.friendlyName = friendlyName; + this.sharedPreferencesName = sharedPreferencesName; this.preferencesName = "whitelist_" + name(); this.preferenceEnabledName = preferenceEnabledName; } @@ -18,6 +22,10 @@ public enum WhitelistType { return friendlyName; } + public String getSharedPreferencesName() { + return sharedPreferencesName; + } + public String getPreferencesName() { return preferencesName; } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java index 534d7402..45fd8d17 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java @@ -47,7 +47,8 @@ public class WhitelistRequester { byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } - if (connection.getResponseCode() == 200) { + int responseCode = connection.getResponseCode(); + if (responseCode == 200) { JSONObject json = getJSONObject(connection); JSONObject videoInfo = json.getJSONObject("videoDetails"); ChannelModel channelModel = new ChannelModel(videoInfo.getString("author"), videoInfo.getString("channelId")); @@ -58,36 +59,41 @@ public class WhitelistRequester { boolean success = Whitelist.addToWhitelist(whitelistType, context, channelModel); String whitelistTypeName = whitelistType.getFriendlyName(); - new Handler(Looper.getMainLooper()).post(() -> { + runOnMainThread(() -> { if (success) { - buttonIcon.setEnabled(true); + buttonIcon.setEnabled(whitelistType != WhitelistType.SPONSORBLOCK); Toast.makeText(context, str("vanced_whitelisting_added", author, whitelistTypeName), Toast.LENGTH_SHORT).show(); } else { - buttonIcon.setEnabled(false); + buttonIcon.setEnabled(whitelistType == WhitelistType.SPONSORBLOCK); Toast.makeText(context, str("vanced_whitelisting_add_failed", author, whitelistTypeName), Toast.LENGTH_SHORT).show(); } - view.setEnabled(true); }); } else { if (debug) { - Log.d(TAG, "player fetch response was " + connection.getResponseCode()); + Log.d(TAG, "player fetch response was " + responseCode); } - - buttonIcon.setEnabled(false); - view.setEnabled(true); + runOnMainThread(() -> { + Toast.makeText(context, str("vanced_whitelisting_fetch_failed", responseCode), Toast.LENGTH_SHORT).show(); + buttonIcon.setEnabled(true); + view.setEnabled(true); + }); } } catch (Exception ex) { Log.e(TAG, "Failed to fetch channelId", ex); - view.setEnabled(true); + runOnMainThread(() -> view.setEnabled(true)); } } // helpers + private static void runOnMainThread(Runnable runnable) { + new Handler(Looper.getMainLooper()).post(runnable); + } + private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { return Requester.getConnectionFromRoute(YT_API_URL, route, params); } diff --git a/app/src/main/java/pl/jakubweg/PlayerController.java b/app/src/main/java/pl/jakubweg/PlayerController.java index edbd27c8..256278ac 100644 --- a/app/src/main/java/pl/jakubweg/PlayerController.java +++ b/app/src/main/java/pl/jakubweg/PlayerController.java @@ -128,7 +128,7 @@ public class PlayerController { public static void executeDownloadSegments(String videoId) { videoHasSegments = false; timeWithoutSegments = ""; - if (!Whitelist.shouldShowSegments()) + if (Whitelist.isChannelSBWhitelisted()) return; SponsorSegment[] segments = SBRequester.getSegments(videoId); Arrays.sort(segments); diff --git a/app/src/main/res/drawable/vanced_yt_sb.xml b/app/src/main/res/drawable/vanced_yt_sb.xml new file mode 100644 index 00000000..ba07674e --- /dev/null +++ b/app/src/main/res/drawable/vanced_yt_sb.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/vanced_yt_sb_blocked.xml b/app/src/main/res/drawable/vanced_yt_sb_blocked.xml new file mode 100644 index 00000000..2a98161f --- /dev/null +++ b/app/src/main/res/drawable/vanced_yt_sb_blocked.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/vanced_yt_sb_button.xml b/app/src/main/res/drawable/vanced_yt_sb_button.xml new file mode 100644 index 00000000..6bba8187 --- /dev/null +++ b/app/src/main/res/drawable/vanced_yt_sb_button.xml @@ -0,0 +1,6 @@ + + + + + \ 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 7c91628e..aa8b082f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -339,6 +339,7 @@ Copy TCopy Ads + Segments Video ad settings Video ad whitelisting @@ -348,6 +349,7 @@ Channel %s was removed from the %s whitelist Failed to add channel %s to the %s whitelist Failed to remove channel %s from the %s whitelist + Failed to retrieve channel details, received code %d Hidden In player Under player From e8a31fb6f490d1f8bb7fa159eb3b69d71c140613 Mon Sep 17 00:00:00 2001 From: caneleex Date: Sun, 23 Jan 2022 22:43:45 +0100 Subject: [PATCH 11/46] fix field name --- app/src/main/java/pl/jakubweg/requests/SBRequester.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index e1cb1922..8831cb92 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -29,7 +29,7 @@ import pl.jakubweg.objects.SponsorSegment; import pl.jakubweg.objects.UserStats; public class SBRequester { - private static final String SPONSORLOCK_API_URL = "https://sponsor.ajay.app/api/"; + private static final String SPONSORBLOCK_API_URL = "https://sponsor.ajay.app/api/"; private static final String TIME_TEMPLATE = "%.3f"; private SBRequester() {} @@ -180,7 +180,7 @@ public class SBRequester { } private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { - return Requester.getConnectionFromRoute(SPONSORLOCK_API_URL, route, params); + return Requester.getConnectionFromRoute(SPONSORBLOCK_API_URL, route, params); } private static JSONObject getJSONObject(Route route, String... params) throws Exception { From 3469121bbff3d5b4c8ef07c2090bab54a978fee1 Mon Sep 17 00:00:00 2001 From: caneleex Date: Sun, 23 Jan 2022 22:46:12 +0100 Subject: [PATCH 12/46] disconnect even if not 200 --- app/src/main/java/pl/jakubweg/requests/SBRequester.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index 8831cb92..b639fc11 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -60,6 +60,7 @@ public class SBRequester { videoHasSegments = true; timeWithoutSegments = SponsorBlockUtils.getTimeWithoutSegments(segments.toArray(new SponsorSegment[0])); } + connection.disconnect(); } catch (Exception ex) { ex.printStackTrace(); From a3c40d10cefa347db2757fdfd070ca7ffb225cb3 Mon Sep 17 00:00:00 2001 From: caneleex Date: Sun, 23 Jan 2022 22:56:16 +0100 Subject: [PATCH 13/46] add more disconnects --- .../youtube/ryd/requests/RYDRequester.java | 5 +++++ .../whitelisting/requests/WhitelistRequester.java | 1 + app/src/main/res/drawable/vanced_yt_sb.xml | 14 -------------- app/src/main/res/drawable/vanced_yt_sb_blocked.xml | 14 -------------- app/src/main/res/drawable/vanced_yt_sb_button.xml | 6 ------ 5 files changed, 6 insertions(+), 34 deletions(-) delete mode 100644 app/src/main/res/drawable/vanced_yt_sb.xml delete mode 100644 app/src/main/res/drawable/vanced_yt_sb_blocked.xml delete mode 100644 app/src/main/res/drawable/vanced_yt_sb_button.xml diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java index da6832a4..9777f335 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java @@ -48,6 +48,7 @@ public class RYDRequester { else if (debug) { Log.d(TAG, "dislikes fetch response was " + connection.getResponseCode()); } + connection.disconnect(); } catch (Exception ex) { dislikeCount = null; @@ -79,6 +80,7 @@ public class RYDRequester { else if (debug) { Log.d(TAG, "Registration response was " + connection.getResponseCode()); } + connection.disconnect(); } catch (Exception ex){ Log.e(TAG, "Failed to register userId", ex); @@ -118,6 +120,7 @@ public class RYDRequester { else if (debug) { Log.d(TAG, "Registration confirmation response was " + connection.getResponseCode()); } + connection.disconnect(); } catch (Exception ex) { Log.e(TAG, "Failed to confirm registration", ex); @@ -157,6 +160,7 @@ public class RYDRequester { else if (debug) { Log.d(TAG, "Vote response was " + connection.getResponseCode()); } + connection.disconnect(); } catch (Exception ex) { Log.e(TAG, "Failed to send vote", ex); @@ -191,6 +195,7 @@ public class RYDRequester { else if (debug) { Log.d(TAG, "Vote confirmation response was " + connection.getResponseCode()); } + connection.disconnect(); } catch (Exception ex) { Log.e(TAG, "Failed to confirm vote", ex); diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java index 45fd8d17..2d8b0058 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java @@ -81,6 +81,7 @@ public class WhitelistRequester { view.setEnabled(true); }); } + connection.disconnect(); } catch (Exception ex) { Log.e(TAG, "Failed to fetch channelId", ex); diff --git a/app/src/main/res/drawable/vanced_yt_sb.xml b/app/src/main/res/drawable/vanced_yt_sb.xml deleted file mode 100644 index ba07674e..00000000 --- a/app/src/main/res/drawable/vanced_yt_sb.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/vanced_yt_sb_blocked.xml b/app/src/main/res/drawable/vanced_yt_sb_blocked.xml deleted file mode 100644 index 2a98161f..00000000 --- a/app/src/main/res/drawable/vanced_yt_sb_blocked.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/vanced_yt_sb_button.xml b/app/src/main/res/drawable/vanced_yt_sb_button.xml deleted file mode 100644 index 6bba8187..00000000 --- a/app/src/main/res/drawable/vanced_yt_sb_button.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file From 2ebad96acff83d556c57e7d9aa0fe030af21421b Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 12:47:32 +0100 Subject: [PATCH 14/46] fix 400 --- .../youtube/whitelisting/requests/WhitelistRequester.java | 4 +--- .../youtube/whitelisting/requests/WhitelistRoutes.java | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java index 2d8b0058..65f682d1 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java @@ -28,14 +28,12 @@ import fi.vanced.utils.requests.Route; public class WhitelistRequester { private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1/"; - private static final String YT_API_KEY = "replaceMeWithTheYouTubeAPIKey"; private WhitelistRequester() {} public static void addChannelToWhitelist(WhitelistType whitelistType, View view, ImageView buttonIcon, Context context) { try { - HttpURLConnection connection = getConnectionFromRoute(WhitelistRoutes.GET_CHANNEL_DETAILS, YT_API_KEY); - connection.setRequestMethod("POST"); + HttpURLConnection connection = getConnectionFromRoute(WhitelistRoutes.GET_CHANNEL_DETAILS, "replaceMeWithTheYouTubeAPIKey"); connection.setRequestProperty("Content-Type", "application/json; utf-8"); connection.setRequestProperty("Accept", "application/json"); connection.setDoOutput(true); diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java index 41d2f11c..609105bc 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRoutes.java @@ -1,11 +1,11 @@ package fi.vanced.libraries.youtube.whitelisting.requests; -import static fi.vanced.utils.requests.Route.Method.GET; +import static fi.vanced.utils.requests.Route.Method.POST; import fi.vanced.utils.requests.Route; public class WhitelistRoutes { - public static final Route GET_CHANNEL_DETAILS = new Route(GET, "player?key={api_key}"); + public static final Route GET_CHANNEL_DETAILS = new Route(POST, "player?key={api_key}"); private WhitelistRoutes() {} } \ No newline at end of file From b3768cb2c484c9015acd8483fd0cf9a5b77c66ef Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 13:45:07 +0100 Subject: [PATCH 15/46] SB request fixes --- .../requests/WhitelistRequester.java | 7 +- .../java/fi/vanced/utils/VancedUtils.java | 6 ++ .../java/pl/jakubweg/SponsorBlockUtils.java | 8 +- .../pl/jakubweg/requests/SBRequester.java | 95 ++++++++++--------- 4 files changed, 63 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java index 65f682d1..ab8fe046 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java @@ -3,11 +3,10 @@ package fi.vanced.libraries.youtube.whitelisting.requests; import static fi.razerman.youtube.XGlobals.debug; import static fi.vanced.libraries.youtube.player.VideoInformation.currentVideoId; import static fi.vanced.libraries.youtube.ui.AdButton.TAG; +import static fi.vanced.utils.VancedUtils.runOnMainThread; import static pl.jakubweg.StringRef.str; import android.content.Context; -import android.os.Handler; -import android.os.Looper; import android.util.Log; import android.view.View; import android.widget.ImageView; @@ -89,10 +88,6 @@ public class WhitelistRequester { // helpers - private static void runOnMainThread(Runnable runnable) { - new Handler(Looper.getMainLooper()).post(runnable); - } - private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { return Requester.getConnectionFromRoute(YT_API_URL, route, params); } diff --git a/app/src/main/java/fi/vanced/utils/VancedUtils.java b/app/src/main/java/fi/vanced/utils/VancedUtils.java index aced35ab..0c7820fa 100644 --- a/app/src/main/java/fi/vanced/utils/VancedUtils.java +++ b/app/src/main/java/fi/vanced/utils/VancedUtils.java @@ -2,6 +2,8 @@ package fi.vanced.utils; import android.content.Context; import android.content.SharedPreferences; +import android.os.Handler; +import android.os.Looper; import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; @@ -40,4 +42,8 @@ public class VancedUtils { } return count; } + + public static void runOnMainThread(Runnable runnable) { + new Handler(Looper.getMainLooper()).post(runnable); + } } \ No newline at end of file diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java index 52488782..f27d44ea 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java @@ -223,10 +223,10 @@ public abstract class SponsorBlockUtils { appContext = new WeakReference<>(context.getApplicationContext()); switch (voteOptions[which1]) { case UPVOTE: - voteForSegment(segment, VoteOption.UPVOTE, appContext.get(), toastRunnable); + voteForSegment(segment, VoteOption.UPVOTE, appContext.get()); break; case DOWNVOTE: - voteForSegment(segment, VoteOption.DOWNVOTE, appContext.get(), toastRunnable); + voteForSegment(segment, VoteOption.DOWNVOTE, appContext.get()); break; case CATEGORY_CHANGE: onNewCategorySelect(segment, context); @@ -401,7 +401,7 @@ public abstract class SponsorBlockUtils { new AlertDialog.Builder(context) .setTitle(str("new_segment_choose_category")) - .setItems(titles, (dialog, which) -> voteForSegment(segment, VoteOption.CATEGORY_CHANGE, appContext.get(), toastRunnable, values[which].key)) + .setItems(titles, (dialog, which) -> voteForSegment(segment, VoteOption.CATEGORY_CHANGE, appContext.get(), values[which].key)) .show(); } @@ -505,7 +505,7 @@ public abstract class SponsorBlockUtils { preference.setText(userName); preference.setOnPreferenceChangeListener((preference1, newUsername) -> { appContext = new WeakReference<>(context.getApplicationContext()); - SBRequester.setUsername((String) newUsername, toastRunnable); + SBRequester.setUsername((String) newUsername, preference, toastRunnable); return false; }); } diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index b639fc11..822295ac 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -1,12 +1,13 @@ package pl.jakubweg.requests; +import static android.text.Html.fromHtml; +import static fi.vanced.utils.VancedUtils.runOnMainThread; import static pl.jakubweg.SponsorBlockUtils.timeWithoutSegments; import static pl.jakubweg.SponsorBlockUtils.videoHasSegments; import static pl.jakubweg.StringRef.str; import android.content.Context; -import android.os.Handler; -import android.os.Looper; +import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.widget.Toast; @@ -92,7 +93,7 @@ public class SBRequester { SponsorBlockUtils.messageToToast = str("submit_failed_unknown_error", responseCode, connection.getResponseMessage()); break; } - new Handler(Looper.getMainLooper()).post(toastRunnable); + runOnMainThread(toastRunnable); connection.disconnect(); } catch (Exception ex) { @@ -110,36 +111,38 @@ public class SBRequester { } } - public static void voteForSegment(SponsorSegment segment, VoteOption voteOption, Context context, Runnable toastRunnable, String... args) { - try { - String segmentUuid = segment.UUID; - String uuid = SponsorBlockSettings.uuid; - String vote = Integer.toString(voteOption == VoteOption.UPVOTE ? 1 : 0); + public static void voteForSegment(SponsorSegment segment, VoteOption voteOption, Context context, String... args) { + new Thread(() -> { + try { + String segmentUuid = segment.UUID; + String uuid = SponsorBlockSettings.uuid; + String vote = Integer.toString(voteOption == VoteOption.UPVOTE ? 1 : 0); - Toast.makeText(context, str("vote_started"), Toast.LENGTH_SHORT).show(); + runOnMainThread(() -> Toast.makeText(context, str("vote_started"), Toast.LENGTH_SHORT).show()); - HttpURLConnection connection = voteOption == VoteOption.CATEGORY_CHANGE - ? getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_CATEGORY, segmentUuid, uuid, args[0]) - : getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_QUALITY, segmentUuid, uuid, vote); - int responseCode = connection.getResponseCode(); + HttpURLConnection connection = voteOption == VoteOption.CATEGORY_CHANGE + ? getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_CATEGORY, segmentUuid, uuid, args[0]) + : getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_QUALITY, segmentUuid, uuid, vote); + int responseCode = connection.getResponseCode(); - switch (responseCode) { - case 200: - SponsorBlockUtils.messageToToast = str("vote_succeeded"); - break; - case 403: - SponsorBlockUtils.messageToToast = str("vote_failed_forbidden"); - break; - default: - SponsorBlockUtils.messageToToast = str("vote_failed_unknown_error", responseCode, connection.getResponseMessage()); - break; + switch (responseCode) { + case 200: + SponsorBlockUtils.messageToToast = str("vote_succeeded"); + break; + case 403: + SponsorBlockUtils.messageToToast = str("vote_failed_forbidden"); + break; + default: + SponsorBlockUtils.messageToToast = str("vote_failed_unknown_error", responseCode, connection.getResponseMessage()); + break; + } + runOnMainThread(() -> Toast.makeText(context, SponsorBlockUtils.messageToToast, Toast.LENGTH_LONG).show()); + connection.disconnect(); } - new Handler(Looper.getMainLooper()).post(toastRunnable); - connection.disconnect(); - } - catch (Exception ex) { - ex.printStackTrace(); - } + catch (Exception ex) { + ex.printStackTrace(); + } + }).start(); } public static void retrieveUserStats(PreferenceCategory category, Preference loadingPreference) { @@ -161,23 +164,29 @@ public class SBRequester { }).start(); } - public static void setUsername(String username, Runnable toastRunnable) { - try { - HttpURLConnection connection = getConnectionFromRoute(SBRoutes.CHANGE_USERNAME, SponsorBlockSettings.uuid, username); - int responseCode = connection.getResponseCode(); + public static void setUsername(String username, EditTextPreference preference, Runnable toastRunnable) { + new Thread(() -> { + try { + HttpURLConnection connection = getConnectionFromRoute(SBRoutes.CHANGE_USERNAME, SponsorBlockSettings.uuid, username); + int responseCode = connection.getResponseCode(); - if (responseCode == 200) { - SponsorBlockUtils.messageToToast = str("stats_username_changed"); + if (responseCode == 200) { + SponsorBlockUtils.messageToToast = str("stats_username_changed"); + runOnMainThread(() -> { + preference.setTitle(fromHtml(str("stats_username", username))); + preference.setText(username); + }); + } + else { + SponsorBlockUtils.messageToToast = str("stats_username_change_unknown_error", responseCode, connection.getResponseMessage()); + } + runOnMainThread(toastRunnable); + connection.disconnect(); } - else { - SponsorBlockUtils.messageToToast = str("stats_username_change_unknown_error", responseCode, connection.getResponseMessage()); + catch (Exception ex) { + ex.printStackTrace(); } - new Handler(Looper.getMainLooper()).post(toastRunnable); - connection.disconnect(); - } - catch (Exception ex) { - ex.printStackTrace(); - } + }).start(); } private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { From f1feec008e4e7ed6155c0b1d13fb0399b05eb3b6 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 15:14:56 +0100 Subject: [PATCH 16/46] display full responses on vote/submit 403 --- app/src/main/java/fi/vanced/utils/requests/Requester.java | 7 +++++-- app/src/main/java/pl/jakubweg/requests/SBRequester.java | 6 ++++-- app/src/main/res/values/strings.xml | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/fi/vanced/utils/requests/Requester.java b/app/src/main/java/fi/vanced/utils/requests/Requester.java index a39b28a3..99ddee6c 100644 --- a/app/src/main/java/fi/vanced/utils/requests/Requester.java +++ b/app/src/main/java/fi/vanced/utils/requests/Requester.java @@ -21,12 +21,15 @@ public class Requester { } public static String parseJson(HttpURLConnection connection) throws IOException { + return parseJson(connection.getInputStream()); + } + + public static String parseJson(InputStream inputStream) throws IOException { StringBuilder jsonBuilder = new StringBuilder(); - InputStream inputStream = connection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { - jsonBuilder.append(line); + jsonBuilder.append(line).append("\n"); } inputStream.close(); return jsonBuilder.toString(); diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index 822295ac..d18954f8 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -84,7 +84,7 @@ public class SBRequester { SponsorBlockUtils.messageToToast = str("submit_failed_duplicate"); break; case 403: - SponsorBlockUtils.messageToToast = str("submit_failed_forbidden"); + SponsorBlockUtils.messageToToast = str("submit_failed_forbidden", Requester.parseJson(connection.getErrorStream())); break; case 429: SponsorBlockUtils.messageToToast = str("submit_failed_rate_limit"); @@ -130,7 +130,7 @@ public class SBRequester { SponsorBlockUtils.messageToToast = str("vote_succeeded"); break; case 403: - SponsorBlockUtils.messageToToast = str("vote_failed_forbidden"); + SponsorBlockUtils.messageToToast = str("vote_failed_forbidden", Requester.parseJson(connection.getErrorStream())); break; default: SponsorBlockUtils.messageToToast = str("vote_failed_unknown_error", responseCode, connection.getResponseMessage()); @@ -189,6 +189,8 @@ public class SBRequester { }).start(); } + // helpers + private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { return Requester.getConnectionFromRoute(SPONSORBLOCK_API_URL, route, params); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aa8b082f..2f2e0fae 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -193,14 +193,14 @@ Unable to submit segments: Status: %d %s Can\'t submit the segment.\nRate Limited (Too many from the same user or IP) - Can\'t submit the segment.\nRejected by auto moderator + Can\'t submit the segment.\n\n%s Can\'t submit the segment.\nAlready exists Segment submitted successfully Submitting segment… Unable to vote for segment: Status: %d %s Can\'t vote for segment.\nRate Limited (Too many from the same user or IP) - Can\'t vote for segment.\nA moderator has decided that this segment is correct + Can\'t vote for segment.\n\n%s Voted successfully Voting for segment… Upvote From 2febb924d84442536c8041c040d30b49d5b05580 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 15:36:55 +0100 Subject: [PATCH 17/46] add strings for whitelist types --- .../libraries/youtube/whitelisting/WhitelistType.java | 10 ++++++---- app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java index 261470a9..5b6ddf87 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/WhitelistType.java @@ -1,18 +1,20 @@ package fi.vanced.libraries.youtube.whitelisting; +import static pl.jakubweg.StringRef.str; + import pl.jakubweg.SponsorBlockSettings; public enum WhitelistType { - ADS("Ads", "youtube", "vanced_whitelist_ads_enabled"), - SPONSORBLOCK("SponsorBlock", SponsorBlockSettings.PREFERENCES_NAME, "vanced_whitelist_sb_enabled"); + ADS("youtube", "vanced_whitelist_ads_enabled"), + SPONSORBLOCK(SponsorBlockSettings.PREFERENCES_NAME, "vanced_whitelist_sb_enabled"); private final String friendlyName; private final String preferencesName; private final String sharedPreferencesName; private final String preferenceEnabledName; - WhitelistType(String friendlyName, String sharedPreferencesName, String preferenceEnabledName) { - this.friendlyName = friendlyName; + WhitelistType(String sharedPreferencesName, String preferenceEnabledName) { + this.friendlyName = str("vanced_whitelisting_" + name().toLowerCase()); this.sharedPreferencesName = sharedPreferencesName; this.preferencesName = "whitelist_" + name(); this.preferenceEnabledName = preferenceEnabledName; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2f2e0fae..fdc57713 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -345,6 +345,8 @@ 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 + Ads + SponsorBlock Channel %s was added to the %s whitelist Channel %s was removed from the %s whitelist Failed to add channel %s to the %s whitelist From 8020f1b704f1dcbd6cc5d123f2756f72d62d5efe Mon Sep 17 00:00:00 2001 From: VancedOfficial Date: Mon, 24 Jan 2022 18:10:33 +0200 Subject: [PATCH 18/46] Video info testing --- .../youtube/player/VideoInformation.java | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java index 968055b8..e11d5414 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java @@ -10,22 +10,29 @@ public class VideoInformation { private static final String TAG = "VI - VideoInfo"; public static String currentVideoId; - public static Integer dislikeCount = null; - public static String channelName = null; + public static Integer dislikeCount; + public static String channelName; public static long lastKnownVideoTime = -1L; + private static boolean tempInfoSaved = false; + private static String tempVideoId; + private static Integer tempDislikeCount; + // Call hook in the YT code when the video changes public static void setCurrentVideoId(final String videoId) { if (videoId == null) { if (debug) { Log.d(TAG, "setCurrentVideoId - new id was null - currentVideoId was" + currentVideoId); } - currentVideoId = null; - dislikeCount = null; - channelName = null; + clearInformation(); return; } + // Restore temporary information that was stored from the last watched video + if (tempInfoSaved) { + restoreTempInformation(); + } + if (videoId.equals(currentVideoId)) { if (debug) { Log.d(TAG, "setCurrentVideoId - new and current video were equal - " + videoId); @@ -42,4 +49,36 @@ public class VideoInformation { // New video ReturnYouTubeDislikes.newVideoLoaded(videoId); } + + // Call hook in the YT code when the video ends + public static void videoEnded() { + saveTempInformation(); + clearInformation(); + } + + // Information is cleared once a video ends + // It's cleared because the setCurrentVideoId isn't called for Shorts + // so Shorts would otherwise use the information from the last watched video + private static void clearInformation() { + currentVideoId = null; + dislikeCount = null; + channelName = null; + } + + // Temporary information is saved once a video ends + // so that if the user watches the same video again, + // the information can be restored without having to fetch again + private static void saveTempInformation() { + tempVideoId = currentVideoId; + tempDislikeCount = dislikeCount; + tempInfoSaved = true; + } + + private static void restoreTempInformation() { + currentVideoId = tempVideoId; + dislikeCount = tempDislikeCount; + tempVideoId = null; + tempDislikeCount = null; + tempInfoSaved = false; + } } From 32a3675211457c374dd841e7cdd45997a5e2fe53 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 17:22:21 +0100 Subject: [PATCH 19/46] add min duration option --- .../jakubweg/SponsorBlockPreferenceFragment.java | 15 ++++++++++++++- .../java/pl/jakubweg/SponsorBlockSettings.java | 6 ++++++ .../main/java/pl/jakubweg/SponsorBlockUtils.java | 4 ++++ .../java/pl/jakubweg/requests/SBRequester.java | 11 +++++++++-- app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index 4af3e7a3..9fd3dfee 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -3,6 +3,7 @@ package pl.jakubweg; import static pl.jakubweg.SponsorBlockSettings.DefaultBehaviour; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_COUNT_SKIPS; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_MIN_DURATION; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_NEW_SEGMENT_ENABLED; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP; @@ -12,6 +13,7 @@ import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_VOTING_ENABLED; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_NAME; import static pl.jakubweg.SponsorBlockSettings.adjustNewSegmentMillis; import static pl.jakubweg.SponsorBlockSettings.countSkips; +import static pl.jakubweg.SponsorBlockSettings.minDuration; import static pl.jakubweg.SponsorBlockSettings.setSeenGuidelines; import static pl.jakubweg.SponsorBlockSettings.showTimeWithoutSegments; import static pl.jakubweg.SponsorBlockSettings.showToastWhenSkippedAutomatically; @@ -290,7 +292,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement { EditTextPreference preference = new EditTextPreference(context); - preference.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED); + preference.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER); preference.setTitle(str("general_adjusting")); preference.setSummary(str("general_adjusting_sum")); preference.setKey(PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP); @@ -299,6 +301,17 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement preferencesToDisableWhenSBDisabled.add(preference); } + { + EditTextPreference preference = new EditTextPreference(context); + preference.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); + preference.setTitle(str("general_min_duration")); + preference.setSummary(str("general_min_duration_sum")); + preference.setKey(PREFERENCES_KEY_MIN_DURATION); + preference.setDefaultValue(String.valueOf(minDuration)); + screen.addPreference(preference); + preferencesToDisableWhenSBDisabled.add(preference); + } + { Preference preference = new EditTextPreference(context); preference.setTitle(str("general_uuid")); diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java index 5a617d00..0ded1b68 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java @@ -22,6 +22,7 @@ public class SponsorBlockSettings { public static final String PREFERENCES_KEY_COUNT_SKIPS = "count-skips"; public static final String PREFERENCES_KEY_UUID = "uuid"; public static final String PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP = "new-segment-step-accuracy"; + public static final String PREFERENCES_KEY_MIN_DURATION = "sb-min-duration"; public static final String PREFERENCES_KEY_SPONSOR_BLOCK_ENABLED = "sb-enabled"; public static final String PREFERENCES_KEY_SEEN_GUIDELINES = "sb-seen-gl"; public static final String PREFERENCES_KEY_NEW_SEGMENT_ENABLED = "sb-new-segment-enabled"; @@ -41,6 +42,7 @@ public class SponsorBlockSettings { public static boolean countSkips = true; public static boolean showTimeWithoutSegments = true; public static int adjustNewSegmentMillis = 150; + public static float minDuration = 0f; public static String uuid = ""; public static String sponsorBlockUrlCategories = "[]"; public static int skippedSegments; @@ -132,6 +134,10 @@ public class SponsorBlockSettings { if (tmp1 != null) adjustNewSegmentMillis = Integer.parseInt(tmp1); + String minTmp = preferences.getString(PREFERENCES_KEY_MIN_DURATION, null); + if (minTmp != null) + minDuration = Float.parseFloat(minTmp); + countSkips = preferences.getBoolean(PREFERENCES_KEY_COUNT_SKIPS, countSkips); showTimeWithoutSegments = preferences.getBoolean(PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS, showTimeWithoutSegments); diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java index f27d44ea..db78ce3c 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java @@ -12,12 +12,14 @@ import static pl.jakubweg.SponsorBlockPreferenceFragment.FORMATTER; import static pl.jakubweg.SponsorBlockPreferenceFragment.SAVED_TEMPLATE; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_COUNT_SKIPS; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_MIN_DURATION; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_UUID; import static pl.jakubweg.SponsorBlockSettings.countSkips; import static pl.jakubweg.SponsorBlockSettings.getPreferences; import static pl.jakubweg.SponsorBlockSettings.isSponsorBlockEnabled; +import static pl.jakubweg.SponsorBlockSettings.minDuration; import static pl.jakubweg.SponsorBlockSettings.showTimeWithoutSegments; import static pl.jakubweg.SponsorBlockSettings.showToastWhenSkippedAutomatically; import static pl.jakubweg.SponsorBlockSettings.skippedSegments; @@ -588,6 +590,7 @@ public abstract class SponsorBlockUtils { editor.putBoolean(PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP, !settingsJson.getBoolean("dontShowNotice")); editor.putBoolean(PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS, settingsJson.getBoolean("showTimeWithSkips")); editor.putBoolean(PREFERENCES_KEY_COUNT_SKIPS, settingsJson.getBoolean("trackViewCount")); + editor.putString(PREFERENCES_KEY_MIN_DURATION, settingsJson.getString("minDuration")); editor.putString(PREFERENCES_KEY_UUID, settingsJson.getString("userID")); editor.apply(); @@ -624,6 +627,7 @@ public abstract class SponsorBlockUtils { json.put("dontShowNotice", !showToastWhenSkippedAutomatically); json.put("barTypes", barTypesObject); json.put("showTimeWithSkips", showTimeWithoutSegments); + json.put("minDuration", minDuration); json.put("trackViewCount", countSkips); json.put("categorySelections", categorySelectionsArray); json.put("userID", uuid); diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index d18954f8..0356c515 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -49,6 +49,11 @@ public class SBRequester { JSONArray segment = obj.getJSONArray("segment"); long start = (long) (segment.getDouble(0) * 1000); long end = (long) (segment.getDouble(1) * 1000); + + long minDuration = (long) (SponsorBlockSettings.minDuration * 1000); + if ((end - start) < minDuration) + continue; + String category = obj.getString("category"); String uuid = obj.getString("UUID"); @@ -58,8 +63,10 @@ public class SBRequester { segments.add(sponsorSegment); } } - videoHasSegments = true; - timeWithoutSegments = SponsorBlockUtils.getTimeWithoutSegments(segments.toArray(new SponsorSegment[0])); + if (!segments.isEmpty()) { + videoHasSegments = true; + timeWithoutSegments = SponsorBlockUtils.getTimeWithoutSegments(segments.toArray(new SponsorSegment[0])); + } } connection.disconnect(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fdc57713..443bc4b6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -151,6 +151,8 @@ This lets SponsorBlock leaderboard system know how much time people have saved. The extension sends a message to the server each time you skip a segment Adjusting new segment step This is the number of milliseconds you can move when you use the time adjustment buttons while adding new segment + Minimum segment duration + Segments shorter than the set value (in seconds) will not be skipped or show in the player Your unique user id This should be kept private. This is like a password and should not be shared with anyone. If someone has this, they can impersonate you Import/Export settings From c36ab429206160b082c21d531beb6d5a6aede28e Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 17:23:18 +0100 Subject: [PATCH 20/46] update default whitelist button strings --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 443bc4b6..2779c181 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -314,7 +314,7 @@ Show time without segments This time appears in brackets next to the current time. This shows the total video duration minus any segments. Channel whitelisting - Use the whitelist button under the player to whitelist a channel + Use the Segments button under the player to whitelist a channel Preview/Recap Quick recap of previous episodes, or a preview of what\'s coming up later in the current video. Meant for edited together clips, not for spoken summaries. Stats @@ -346,7 +346,7 @@ 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 + Video ad whitelisting is turned on. Use the Ads button under the player to whitelist a channel Ads SponsorBlock Channel %s was added to the %s whitelist From 92f5c742f095bfc38b44b435c92030be57bad6cf Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 18:26:41 +0100 Subject: [PATCH 21/46] replace color changing dialog with a custom preference thanks @X1nto --- .../SponsorBlockPreferenceFragment.java | 20 +--- .../java/pl/jakubweg/SponsorBlockUtils.java | 36 ------- .../objects/EditTextListPreference.java | 97 +++++++++++++++++++ app/src/main/res/values/strings.xml | 3 +- 4 files changed, 103 insertions(+), 53 deletions(-) create mode 100644 app/src/main/java/pl/jakubweg/objects/EditTextListPreference.java diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index 9fd3dfee..4b2d0474 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -28,7 +28,6 @@ import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.preference.EditTextPreference; -import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; @@ -41,6 +40,7 @@ import java.text.DecimalFormat; import java.util.ArrayList; import fi.vanced.libraries.youtube.whitelisting.WhitelistType; +import pl.jakubweg.objects.EditTextListPreference; import pl.jakubweg.requests.SBRequester; @SuppressWarnings({"unused", "deprecation"}) // injected @@ -159,7 +159,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement SponsorBlockSettings.SegmentInfo[] categories = SponsorBlockSettings.SegmentInfo.valuesWithoutUnsubmitted(); for (SponsorBlockSettings.SegmentInfo segmentInfo : categories) { - ListPreference preference = new ListPreference(context); + EditTextListPreference preference = new EditTextListPreference(context); preference.setTitle(segmentInfo.getTitleWithDot()); preference.setSummary(segmentInfo.description.toString()); preference.setKey(segmentInfo.key); @@ -170,22 +170,10 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement category.addPreference(preference); } - Preference colorPreference = new Preference(context); + Preference colorPreference = new Preference(context); // TODO remove this after the next major update screen.addPreference(colorPreference); colorPreference.setTitle(str("color_change")); - - colorPreference.setOnPreferenceClickListener(preference1 -> { - CharSequence[] items = new CharSequence[categories.length]; - for (int i = 0; i < items.length; i++) { - items[i] = categories[i].getTitleWithDot(); - } - - new AlertDialog.Builder(context) - .setTitle(str("color_choose_category")) - .setItems(items, SponsorBlockUtils.categoryColorChangeClickListener) - .show(); - return true; - }); + colorPreference.setSummary(str("color_change_sum")); preferencesToDisableWhenSBDisabled.add(colorPreference); } diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java index db78ce3c..dff52955 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java @@ -34,13 +34,11 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.Color; import android.net.Uri; import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.text.Html; -import android.text.InputType; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -237,40 +235,6 @@ public abstract class SponsorBlockUtils { }) .show(); }; - public static final DialogInterface.OnClickListener categoryColorChangeClickListener = (dialog, which) -> { - SponsorBlockSettings.SegmentInfo segmentInfo = SponsorBlockSettings.SegmentInfo.valuesWithoutUnsubmitted()[which]; - String key = segmentInfo.key + PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX; - - Context context = ((AlertDialog) dialog).getContext(); - EditText editText = new EditText(context); - editText.setInputType(InputType.TYPE_CLASS_TEXT); - editText.setText(formatColorString(segmentInfo.color)); - - Context applicationContext = context.getApplicationContext(); - SharedPreferences preferences = SponsorBlockSettings.getPreferences(context); - - new AlertDialog.Builder(context) - .setView(editText) - .setPositiveButton(str("change"), (dialog1, which1) -> { - try { - int color = Color.parseColor(editText.getText().toString()); - segmentInfo.setColor(color); - Toast.makeText(applicationContext, str("color_changed"), Toast.LENGTH_SHORT).show(); - preferences.edit().putString(key, formatColorString(color)).apply(); - } - catch (Exception ex) { - Toast.makeText(applicationContext, str("color_invalid"), Toast.LENGTH_SHORT).show(); - } - }) - .setNeutralButton(str("reset"), (dialog1, which1) -> { - int defaultColor = segmentInfo.defaultColor; - segmentInfo.setColor(defaultColor); - Toast.makeText(applicationContext, str("color_reset"), Toast.LENGTH_SHORT).show(); - preferences.edit().putString(key, formatColorString(defaultColor)).apply(); - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); - }; private static final Runnable submitRunnable = () -> { messageToToast = null; final String uuid = SponsorBlockSettings.uuid; diff --git a/app/src/main/java/pl/jakubweg/objects/EditTextListPreference.java b/app/src/main/java/pl/jakubweg/objects/EditTextListPreference.java new file mode 100644 index 00000000..80b8565b --- /dev/null +++ b/app/src/main/java/pl/jakubweg/objects/EditTextListPreference.java @@ -0,0 +1,97 @@ +package pl.jakubweg.objects; + +import static pl.jakubweg.SponsorBlockUtils.formatColorString; +import static pl.jakubweg.StringRef.str; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Color; +import android.preference.ListPreference; +import android.text.InputType; +import android.util.AttributeSet; +import android.widget.EditText; +import android.widget.Toast; + +import pl.jakubweg.SponsorBlockSettings; + +@SuppressWarnings("deprecation") +public class EditTextListPreference extends ListPreference { + + private EditText mEditText; + private int mClickedDialogEntryIndex; + + public EditTextListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public EditTextListPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public EditTextListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public EditTextListPreference(Context context) { + super(context); + } + + @Override + protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { + SponsorBlockSettings.SegmentInfo category = getCategoryBySelf(); + + mEditText = new EditText(builder.getContext()); + mEditText.setInputType(InputType.TYPE_CLASS_TEXT); + mEditText.setText(formatColorString(category.color)); + builder.setView(mEditText); + + builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { + EditTextListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE); + }); + builder.setNeutralButton(str("reset"), (dialog, which) -> { + //EditTextListPreference.this.onClick(dialog, DialogInterface.BUTTON_NEUTRAL); + int defaultColor = category.defaultColor; + category.setColor(defaultColor); + Toast.makeText(getContext().getApplicationContext(), str("color_reset"), Toast.LENGTH_SHORT).show(); + getSharedPreferences().edit().putString(getColorPreferenceKey(), formatColorString(defaultColor)).apply(); + }); + builder.setNegativeButton(android.R.string.cancel, null); + + mClickedDialogEntryIndex = findIndexOfValue(getValue()); + builder.setSingleChoiceItems(getEntries(), mClickedDialogEntryIndex, (dialog, which) -> mClickedDialogEntryIndex = which); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + if (positiveResult && mClickedDialogEntryIndex >= 0 && getEntryValues() != null) { + String value = getEntryValues()[mClickedDialogEntryIndex].toString(); + if (callChangeListener(value)) { + setValue(value); + } + String colorString = mEditText.getText().toString(); + SponsorBlockSettings.SegmentInfo category = getCategoryBySelf(); + if (colorString.equals(formatColorString(category.color))) { + return; + } + Context applicationContext = getContext().getApplicationContext(); + try { + int color = Color.parseColor(colorString); + category.setColor(color); + Toast.makeText(applicationContext, str("color_changed"), Toast.LENGTH_SHORT).show(); + getSharedPreferences().edit().putString(getColorPreferenceKey(), formatColorString(color)).apply(); + } + catch (Exception ex) { + Toast.makeText(applicationContext, str("color_invalid"), Toast.LENGTH_SHORT).show(); + } + } + } + + private SponsorBlockSettings.SegmentInfo getCategoryBySelf() { + return SponsorBlockSettings.SegmentInfo.byCategoryKey(getKey()); + } + + private String getColorPreferenceKey() { + return getKey() + SponsorBlockSettings.PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX; + } +} \ 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 2779c181..a2ea7edb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -330,7 +330,8 @@ You\'ve skipped <b>%s</b> segments. That\'s <b>%s</b>. minutes - Change colors + Are you looking for changing colors? + You can now change a category\'s color by clicking on it above. Choose the category Color changed Color reset From 8d2a84b066f0a71d76bf5c3fff0d8393e2a30eda Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 20:51:53 +0100 Subject: [PATCH 22/46] bump AGP --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b36aed27..4a22cbf3 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.0' + classpath 'com.android.tools.build:gradle:7.0.4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From 643dd8585d2b921d3b38e29c8e10d6fd7726d8d0 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 23:01:21 +0100 Subject: [PATCH 23/46] add support for the filler category (#63) --- .../SponsorBlockPreferenceFragment.java | 2 +- .../pl/jakubweg/SponsorBlockSettings.java | 31 ++++++++++--------- app/src/main/res/values/strings.xml | 3 ++ 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index 4b2d0474..b4023592 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -163,7 +163,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement preference.setTitle(segmentInfo.getTitleWithDot()); preference.setSummary(segmentInfo.description.toString()); preference.setKey(segmentInfo.key); - preference.setDefaultValue(defaultValue); + preference.setDefaultValue(segmentInfo.behaviour.key); preference.setEntries(entries); preference.setEntryValues(entryValues); diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java index 0ded1b68..9bdd5f4b 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java @@ -102,9 +102,7 @@ public class SponsorBlockSettings { SegmentBehaviour behaviour = null; String value = preferences.getString(segment.key, null); - if (value == null) - behaviour = DefaultBehaviour; - else { + if (value != null) { for (SegmentBehaviour possibleBehaviour : possibleBehaviours) { if (possibleBehaviour.key.equals(value)) { behaviour = possibleBehaviour; @@ -112,10 +110,13 @@ public class SponsorBlockSettings { } } } - if (behaviour == null) - behaviour = DefaultBehaviour; + if (behaviour != null) { + segment.behaviour = behaviour; + } + else { + behaviour = segment.behaviour; + } - segment.behaviour = behaviour; if (behaviour.showOnTimeBar && segment != SegmentInfo.UNSUBMITTED) enabledCategories.add(segment.key); } @@ -185,13 +186,14 @@ public class SponsorBlockSettings { } public enum SegmentInfo { - SPONSOR("sponsor", sf("segments_sponsor"), sf("skipped_sponsor"), sf("segments_sponsor_sum"), null, 0xFF00d400), - INTRO("intro", sf("segments_intermission"), sf("skipped_intermission"), sf("segments_intermission_sum"), null, 0xFF00ffff), - OUTRO("outro", sf("segments_endcards"), sf("skipped_endcard"), sf("segments_endcards_sum"), null, 0xFF0202ed), - INTERACTION("interaction", sf("segments_subscribe"), sf("skipped_subscribe"), sf("segments_subscribe_sum"), null, 0xFFcc00ff), - SELF_PROMO("selfpromo", sf("segments_selfpromo"), sf("skipped_selfpromo"), sf("segments_selfpromo_sum"), null, 0xFFffff00), - MUSIC_OFFTOPIC("music_offtopic", sf("segments_nomusic"), sf("skipped_nomusic"), sf("segments_nomusic_sum"), null, 0xFFff9900), - PREVIEW("preview", sf("segments_preview"), sf("skipped_preview"), sf("segments_preview_sum"), null, 0xFF008fd6), + SPONSOR("sponsor", sf("segments_sponsor"), sf("skipped_sponsor"), sf("segments_sponsor_sum"), DefaultBehaviour, 0xFF00d400), + INTRO("intro", sf("segments_intermission"), sf("skipped_intermission"), sf("segments_intermission_sum"), DefaultBehaviour, 0xFF00ffff), + OUTRO("outro", sf("segments_endcards"), sf("skipped_endcard"), sf("segments_endcards_sum"), DefaultBehaviour, 0xFF0202ed), + INTERACTION("interaction", sf("segments_subscribe"), sf("skipped_subscribe"), sf("segments_subscribe_sum"), DefaultBehaviour, 0xFFcc00ff), + SELF_PROMO("selfpromo", sf("segments_selfpromo"), sf("skipped_selfpromo"), sf("segments_selfpromo_sum"), DefaultBehaviour, 0xFFffff00), + MUSIC_OFFTOPIC("music_offtopic", sf("segments_nomusic"), sf("skipped_nomusic"), sf("segments_nomusic_sum"), DefaultBehaviour, 0xFFff9900), + PREVIEW("preview", sf("segments_preview"), sf("skipped_preview"), sf("segments_preview_sum"), DefaultBehaviour, 0xFF008fd6), + FILLER("filler", sf("segments_filler"), sf("skipped_filler"), sf("segments_filler_sum"), SegmentBehaviour.IGNORE, 0xFF7300FF), UNSUBMITTED("unsubmitted", StringRef.empty, sf("skipped_unsubmitted"), StringRef.empty, SegmentBehaviour.SKIP_AUTOMATICALLY, 0xFFFFFFFF); private static final SegmentInfo[] mValuesWithoutUnsubmitted = new SegmentInfo[]{ @@ -201,7 +203,8 @@ public class SponsorBlockSettings { INTERACTION, SELF_PROMO, MUSIC_OFFTOPIC, - PREVIEW + PREVIEW, + FILLER }; private static final Map mValuesMap = new HashMap<>(values().length); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a2ea7edb..a4da718c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -175,6 +175,8 @@ Similar to "sponsor" except for unpaid or self promotion. This includes sections about merchandise, donations, or information about who they collaborated with Music: Non-Music Section Only for use in music videos. This includes introductions or outros in music videos + Filler/Tangent + Tangential scenes added only for filler or humor that are not required to understand the main content of the video. This should not include segments providing context or background details Skipped a sponsor segment Skipped sponsor Skipped intro @@ -183,6 +185,7 @@ Skipped self promotion Skipped silence Skipped preview + Skipped filler Skipped unsubmitted segment Skip automatically Show a skip button From 3303643161bfcd4b2ce293be12e858bd78d1af00 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 24 Jan 2022 23:02:21 +0100 Subject: [PATCH 24/46] remove unused variable --- .../main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index b4023592..8d504422 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -1,6 +1,5 @@ package pl.jakubweg; -import static pl.jakubweg.SponsorBlockSettings.DefaultBehaviour; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_COUNT_SKIPS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_MIN_DURATION; @@ -146,7 +145,6 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement preferencesToDisableWhenSBDisabled.add(category); category.setTitle(str("diff_segments")); - String defaultValue = DefaultBehaviour.key; SponsorBlockSettings.SegmentBehaviour[] segmentBehaviours = SponsorBlockSettings.SegmentBehaviour.values(); String[] entries = new String[segmentBehaviours.length]; String[] entryValues = new String[segmentBehaviours.length]; From 7c71435e5dce36572045c87212a44b22b9332cf3 Mon Sep 17 00:00:00 2001 From: VancedOfficial Date: Tue, 25 Jan 2022 20:25:47 +0200 Subject: [PATCH 25/46] Small fixes and changes - Added versionName fetching - Reduced RYD API spam for Shorts - Fixes and additions to strings - YT API Key from local.properties --- app/build.gradle | 9 +++- app/src/main/AndroidManifest.xml | 2 +- .../youtube/player/VideoInformation.java | 49 +++++++++++++++++-- .../requests/WhitelistRequester.java | 8 +-- .../java/fi/vanced/utils/VancedUtils.java | 14 ++++++ app/src/main/res/values/strings.xml | 14 ++++-- 6 files changed, 81 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a08ece6e..f6f39a83 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,12 +5,19 @@ android { buildToolsVersion "30.0.2" defaultConfig { - applicationId "pl.jakubweg" + applicationId "vanced.integrations" minSdkVersion 21 targetSdkVersion 30 versionCode 1 versionName "1.0" multiDexEnabled false + + Properties properties = new Properties() + if (rootProject.file("local.properties").exists()) { + properties.load(rootProject.file("local.properties").newDataInputStream()) + } + + buildConfigField "String", "YT_API_KEY", "\"${properties.getProperty("youtubeAPIKey", "")}\"" } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 430ba65c..7609a74c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ + package="vanced.integrations"> \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java index 968055b8..e11d5414 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java @@ -10,22 +10,29 @@ public class VideoInformation { private static final String TAG = "VI - VideoInfo"; public static String currentVideoId; - public static Integer dislikeCount = null; - public static String channelName = null; + public static Integer dislikeCount; + public static String channelName; public static long lastKnownVideoTime = -1L; + private static boolean tempInfoSaved = false; + private static String tempVideoId; + private static Integer tempDislikeCount; + // Call hook in the YT code when the video changes public static void setCurrentVideoId(final String videoId) { if (videoId == null) { if (debug) { Log.d(TAG, "setCurrentVideoId - new id was null - currentVideoId was" + currentVideoId); } - currentVideoId = null; - dislikeCount = null; - channelName = null; + clearInformation(); return; } + // Restore temporary information that was stored from the last watched video + if (tempInfoSaved) { + restoreTempInformation(); + } + if (videoId.equals(currentVideoId)) { if (debug) { Log.d(TAG, "setCurrentVideoId - new and current video were equal - " + videoId); @@ -42,4 +49,36 @@ public class VideoInformation { // New video ReturnYouTubeDislikes.newVideoLoaded(videoId); } + + // Call hook in the YT code when the video ends + public static void videoEnded() { + saveTempInformation(); + clearInformation(); + } + + // Information is cleared once a video ends + // It's cleared because the setCurrentVideoId isn't called for Shorts + // so Shorts would otherwise use the information from the last watched video + private static void clearInformation() { + currentVideoId = null; + dislikeCount = null; + channelName = null; + } + + // Temporary information is saved once a video ends + // so that if the user watches the same video again, + // the information can be restored without having to fetch again + private static void saveTempInformation() { + tempVideoId = currentVideoId; + tempDislikeCount = dislikeCount; + tempInfoSaved = true; + } + + private static void restoreTempInformation() { + currentVideoId = tempVideoId; + dislikeCount = tempDislikeCount; + tempVideoId = null; + tempDislikeCount = null; + tempInfoSaved = false; + } } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java index ab8fe046..8f1bbe4a 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/whitelisting/requests/WhitelistRequester.java @@ -22,8 +22,10 @@ import java.nio.charset.StandardCharsets; import fi.vanced.libraries.youtube.player.ChannelModel; import fi.vanced.libraries.youtube.whitelisting.Whitelist; import fi.vanced.libraries.youtube.whitelisting.WhitelistType; +import fi.vanced.utils.VancedUtils; import fi.vanced.utils.requests.Requester; import fi.vanced.utils.requests.Route; +import vanced.integrations.BuildConfig; public class WhitelistRequester { private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1/"; @@ -32,14 +34,14 @@ public class WhitelistRequester { public static void addChannelToWhitelist(WhitelistType whitelistType, View view, ImageView buttonIcon, Context context) { try { - HttpURLConnection connection = getConnectionFromRoute(WhitelistRoutes.GET_CHANNEL_DETAILS, "replaceMeWithTheYouTubeAPIKey"); + HttpURLConnection connection = getConnectionFromRoute(WhitelistRoutes.GET_CHANNEL_DETAILS, BuildConfig.YT_API_KEY); connection.setRequestProperty("Content-Type", "application/json; utf-8"); connection.setRequestProperty("Accept", "application/json"); connection.setDoOutput(true); connection.setConnectTimeout(2 * 1000); - // TODO: Actually fetch the version - String jsonInputString = "{\"context\": {\"client\": { \"clientName\": \"Android\", \"clientVersion\": \"16.49.37\" } }, \"videoId\": \"" + currentVideoId + "\"}"; + String versionName = VancedUtils.getVersionName(context); + String jsonInputString = "{\"context\": {\"client\": { \"clientName\": \"Android\", \"clientVersion\": \"" + versionName + "\" } }, \"videoId\": \"" + currentVideoId + "\"}"; try(OutputStream os = connection.getOutputStream()) { byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); diff --git a/app/src/main/java/fi/vanced/utils/VancedUtils.java b/app/src/main/java/fi/vanced/utils/VancedUtils.java index 0c7820fa..0333f642 100644 --- a/app/src/main/java/fi/vanced/utils/VancedUtils.java +++ b/app/src/main/java/fi/vanced/utils/VancedUtils.java @@ -2,6 +2,8 @@ package fi.vanced.utils; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; @@ -43,6 +45,18 @@ public class VancedUtils { return count; } + public static String getVersionName(Context context) { + try { + PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + String version = pInfo.versionName; + return (version); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + return ("17.03.35"); + } + public static void runOnMainThread(Runnable runnable) { new Handler(Looper.getMainLooper()).post(runnable); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a2ea7edb..24853508 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -320,9 +320,9 @@ Stats Loading.. SponsorBlock is disabled - Your username: <b>%s</b> + Your username: <b>%s</b> Click to change your username - Unable to change username: Status: %d %s + Unable to change username: Status: %d %s Username successfully changed Submissions: <b>%s</b> You\'ve saved people from <b>%s</b> segments. @@ -339,8 +339,8 @@ Change Reset - Copy - TCopy + Copy link + Timestamp Ads Segments @@ -359,10 +359,14 @@ In player Under player Both - RYD settings + Return YouTube Dislike 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 + + Tablet style + Tablet style is turned on. For example suggested videos are only partially working + Tablet style is turned off From 770bacfd8f73dc4e78d794a67538ae60e9819474 Mon Sep 17 00:00:00 2001 From: VancedOfficial Date: Tue, 25 Jan 2022 23:28:04 +0200 Subject: [PATCH 26/46] RYD and SB hints at startup --- .../libraries/youtube/dialog/Dialogs.java | 139 ++++++++++++++++++ .../libraries/youtube/ryd/RYDFragment.java | 14 ++ .../libraries/youtube/ryd/RYDSettings.java | 1 + .../SponsorBlockPreferenceFragment.java | 15 ++ .../pl/jakubweg/SponsorBlockSettings.java | 1 + app/src/main/res/values/strings.xml | 8 + 6 files changed, 178 insertions(+) create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/dialog/Dialogs.java diff --git a/app/src/main/java/fi/vanced/libraries/youtube/dialog/Dialogs.java b/app/src/main/java/fi/vanced/libraries/youtube/dialog/Dialogs.java new file mode 100644 index 00000000..c22fc157 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/dialog/Dialogs.java @@ -0,0 +1,139 @@ +package fi.vanced.libraries.youtube.dialog; + +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_KEY_RYD_ENABLED; +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_KEY_RYD_HINT_SHOWN; +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_NAME; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SPONSOR_BLOCK_ENABLED; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SPONSOR_BLOCK_HINT_SHOWN; +import static pl.jakubweg.StringRef.str; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.LightingColorFilter; +import android.net.Uri; +import android.os.Build; + +import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; + +import fi.vanced.utils.SharedPrefUtils; +import fi.vanced.utils.VancedUtils; +import pl.jakubweg.SponsorBlockSettings; + +public class Dialogs { + // Inject call from YT to this + public static void showDialogsAtStartup(Activity activity) { + rydFirstRun(activity); + sbFirstRun(activity); + } + + private static void rydFirstRun(Activity activity) { + Context context = YouTubeTikTokRoot_Application.getAppContext(); + boolean enabled = SharedPrefUtils.getBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_ENABLED, false); + boolean hintShown = SharedPrefUtils.getBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_HINT_SHOWN, false); + + // If RYD is enabled or hint has been shown, exit + if (enabled || hintShown) { + // If RYD is enabled but hint hasn't been shown, mark it as shown + if (enabled && !hintShown) { + SharedPrefUtils.saveBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_HINT_SHOWN, true); + } + return; + } + + AlertDialog.Builder builder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + builder = new AlertDialog.Builder(activity, android.R.style.Theme_Material_Dialog_Alert); + } else { + builder = new AlertDialog.Builder(activity); + } + builder.setTitle(str("vanced_ryd")); + builder.setIcon(VancedUtils.getIdentifier("reel_dislike_icon", "drawable")); + builder.setCancelable(false); + builder.setMessage(str("vanced_ryd_firstrun")); + builder.setPositiveButton(str("vanced_enable"), + (dialog, id) -> { + SharedPrefUtils.saveBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_HINT_SHOWN, true); + SharedPrefUtils.saveBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_ENABLED, true); + dialog.dismiss(); + }); + + builder.setNegativeButton(str("vanced_disable"), + (dialog, id) -> { + SharedPrefUtils.saveBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_HINT_SHOWN, true); + SharedPrefUtils.saveBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_ENABLED, false); + dialog.dismiss(); + }); + + builder.setNeutralButton(str("vanced_learnmore"), null); + + AlertDialog dialog = builder.create(); + dialog.show(); + + // Set black background + dialog.getWindow().getDecorView().getBackground().setColorFilter(new LightingColorFilter(0xFF000000, VancedUtils.getIdentifier("ytBrandBackgroundSolid", "color"))); + + // Set learn more action (set here so clicking it doesn't dismiss the dialog) + dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener(v -> { + Uri uri = Uri.parse("https://www.returnyoutubedislike.com/"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + activity.startActivity(intent); + }); + } + + private static void sbFirstRun(Activity activity) { + Context context = YouTubeTikTokRoot_Application.getAppContext(); + boolean enabled = SharedPrefUtils.getBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, PREFERENCES_KEY_SPONSOR_BLOCK_ENABLED, false); + boolean hintShown = SharedPrefUtils.getBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, PREFERENCES_KEY_SPONSOR_BLOCK_HINT_SHOWN, false); + + // If SB is enabled or hint has been shown, exit + if (enabled || hintShown) { + // If SB is enabled but hint hasn't been shown, mark it as shown + if (enabled && !hintShown) { + SharedPrefUtils.saveBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, PREFERENCES_KEY_SPONSOR_BLOCK_HINT_SHOWN, true); + } + return; + } + + AlertDialog.Builder builder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + builder = new AlertDialog.Builder(activity, android.R.style.Theme_Material_Dialog_Alert); + } else { + builder = new AlertDialog.Builder(activity); + } + builder.setTitle(str("vanced_sb")); + builder.setIcon(VancedUtils.getIdentifier("ic_sb_logo", "drawable")); + builder.setCancelable(false); + builder.setMessage(str("vanced_sb_firstrun")); + builder.setPositiveButton(str("vanced_enable"), + (dialog, id) -> { + SharedPrefUtils.saveBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, PREFERENCES_KEY_SPONSOR_BLOCK_HINT_SHOWN, true); + SharedPrefUtils.saveBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, PREFERENCES_KEY_SPONSOR_BLOCK_ENABLED, true); + dialog.dismiss(); + }); + + builder.setNegativeButton(str("vanced_disable"), + (dialog, id) -> { + SharedPrefUtils.saveBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, PREFERENCES_KEY_SPONSOR_BLOCK_HINT_SHOWN, true); + SharedPrefUtils.saveBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, PREFERENCES_KEY_SPONSOR_BLOCK_ENABLED, false); + dialog.dismiss(); + }); + + builder.setNeutralButton(str("vanced_learnmore"), null); + + AlertDialog dialog = builder.create(); + dialog.show(); + + // Set black background + dialog.getWindow().getDecorView().getBackground().setColorFilter(new LightingColorFilter(0xFF000000, VancedUtils.getIdentifier("ytBrandBackgroundSolid", "color"))); + + // Set learn more action (set here so clicking it doesn't dismiss the dialog) + dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener(v -> { + Uri uri = Uri.parse("https://sponsor.ajay.app/"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + activity.startActivity(intent); + }); + } +} 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 index 93b1cc90..617e1c1a 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDFragment.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDFragment.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_RYD_ENABLED; +import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_KEY_RYD_HINT_SHOWN; import static fi.vanced.libraries.youtube.ryd.RYDSettings.PREFERENCES_NAME; import static pl.jakubweg.StringRef.str; @@ -44,6 +46,18 @@ public class RYDFragment extends PreferenceFragment { }); } + // Clear hint + if (debug) { + SwitchPreference preference = new SwitchPreference(context); + preferenceScreen.addPreference(preference); + preference.setKey(PREFERENCES_KEY_RYD_HINT_SHOWN); + preference.setDefaultValue(false); + preference.setChecked(SharedPrefUtils.getBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_HINT_SHOWN)); + preference.setTitle("Hint debug"); + preference.setSummary("Debug toggle for clearing the hint shown preference"); + preference.setOnPreferenceChangeListener((pref, newValue) -> true); + } + // About category addAboutCategory(context, preferenceScreen); } 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 index fda4808e..aa436a09 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDSettings.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/RYDSettings.java @@ -4,4 +4,5 @@ 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"; + public static final String PREFERENCES_KEY_RYD_HINT_SHOWN = "ryd_hint_shown"; } diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index 8d504422..b88bfb7e 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -1,5 +1,6 @@ package pl.jakubweg; +import static fi.razerman.youtube.XGlobals.debug; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_COUNT_SKIPS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_MIN_DURATION; @@ -7,6 +8,7 @@ import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_NEW_SEGMENT_ENABL import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SPONSOR_BLOCK_ENABLED; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SPONSOR_BLOCK_HINT_SHOWN; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_UUID; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_VOTING_ENABLED; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_NAME; @@ -39,6 +41,7 @@ import java.text.DecimalFormat; import java.util.ArrayList; import fi.vanced.libraries.youtube.whitelisting.WhitelistType; +import fi.vanced.utils.SharedPrefUtils; import pl.jakubweg.objects.EditTextListPreference; import pl.jakubweg.requests.SBRequester; @@ -77,6 +80,18 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement }); } + // Clear hint + if (debug) { + SwitchPreference preference = new SwitchPreference(context); + preferenceScreen.addPreference(preference); + preference.setKey(PREFERENCES_KEY_SPONSOR_BLOCK_HINT_SHOWN); + preference.setDefaultValue(false); + preference.setChecked(SharedPrefUtils.getBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_SPONSOR_BLOCK_HINT_SHOWN)); + preference.setTitle("Hint debug"); + preference.setSummary("Debug toggle for clearing the hint shown preference"); + preference.setOnPreferenceChangeListener((pref, newValue) -> true); + } + { SwitchPreference preference = new SwitchPreference(context); preferenceScreen.addPreference(preference); diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java index 9bdd5f4b..9fb85016 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java @@ -24,6 +24,7 @@ public class SponsorBlockSettings { public static final String PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP = "new-segment-step-accuracy"; public static final String PREFERENCES_KEY_MIN_DURATION = "sb-min-duration"; public static final String PREFERENCES_KEY_SPONSOR_BLOCK_ENABLED = "sb-enabled"; + public static final String PREFERENCES_KEY_SPONSOR_BLOCK_HINT_SHOWN = "sb_hint_shown"; public static final String PREFERENCES_KEY_SEEN_GUIDELINES = "sb-seen-gl"; public static final String PREFERENCES_KEY_NEW_SEGMENT_ENABLED = "sb-new-segment-enabled"; public static final String PREFERENCES_KEY_VOTING_ENABLED = "sb-voting-enabled"; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fc1677e8..5d1f3523 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -372,4 +372,12 @@ Tablet style Tablet style is turned on. For example suggested videos are only partially working Tablet style is turned off + + Return YouTube Dislike + Want to enable Return YouTube Dislikes to see dislikes again? Your likes/dislikes will be sent to RYD API (anonymously) after enabling RYD integration. You can enable/disable this in the settings at any time. + SponsorBlock + Are you aware of the SponsorBlock integration in Vanced? With it you can skip sponsored segments in the videos. You can enable/disable this in the settings at any time. + Learn more + Disable + Enable From 48c268b0bcb133463d06328db58c670c4a26b48f Mon Sep 17 00:00:00 2001 From: caneleex Date: Tue, 25 Jan 2022 22:52:11 +0100 Subject: [PATCH 27/46] simplify vote switch --- app/src/main/java/pl/jakubweg/SponsorBlockUtils.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java index dff52955..15b9d65d 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java @@ -221,12 +221,11 @@ public abstract class SponsorBlockUtils { new AlertDialog.Builder(context) .setItems(items, (dialog1, which1) -> { appContext = new WeakReference<>(context.getApplicationContext()); - switch (voteOptions[which1]) { + VoteOption voteOption = voteOptions[which1]; + switch (voteOption) { case UPVOTE: - voteForSegment(segment, VoteOption.UPVOTE, appContext.get()); - break; case DOWNVOTE: - voteForSegment(segment, VoteOption.DOWNVOTE, appContext.get()); + voteForSegment(segment, voteOption, appContext.get()); break; case CATEGORY_CHANGE: onNewCategorySelect(segment, context); From 0ed0e7d0e7323e8e1747b1b97628fea8e21b01b2 Mon Sep 17 00:00:00 2001 From: VancedOfficial Date: Wed, 26 Jan 2022 00:32:26 +0200 Subject: [PATCH 28/46] Button container, button gone fix --- .../libraries/youtube/ui/SlimButton.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButton.java index 41164ff3..40e881a1 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButton.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SlimButton.java @@ -20,6 +20,7 @@ public abstract class SlimButton implements View.OnClickListener { private final ViewGroup container; protected final ImageView button_icon; protected final TextView button_text; + private boolean viewAdded = false; static { SLIM_METADATA_BUTTON_ID = VancedUtils.getIdentifier("slim_metadata_button", "layout"); @@ -36,14 +37,25 @@ public abstract class SlimButton implements View.OnClickListener { button_text = (TextView)view.findViewById(VancedUtils.getIdentifier("button_text", "id")); view.setOnClickListener(this); - setVisible(visible); - container.addView(view); + setVisible(visible); } public void setVisible(boolean visible) { - view.setVisibility(visible ? View.VISIBLE : View.GONE); - setContainerVisibility(); + try { + if (!viewAdded && visible) { + container.addView(view); + viewAdded = true; + } + else if (viewAdded && !visible) { + container.removeView(view); + viewAdded = false; + } + setContainerVisibility(); + } + catch (Exception ex) { + Log.e(TAG, "Error while changing button visibility", ex); + } } private void setContainerVisibility() { From 6bac0407bb579bb06333b1b4a26e90ce29b7294f Mon Sep 17 00:00:00 2001 From: caneleex Date: Wed, 26 Jan 2022 14:43:52 +0100 Subject: [PATCH 29/46] fix RYD voting --- .../java/fi/vanced/utils/requests/Requester.java | 12 +++++++++--- .../main/java/pl/jakubweg/requests/SBRequester.java | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fi/vanced/utils/requests/Requester.java b/app/src/main/java/fi/vanced/utils/requests/Requester.java index 99ddee6c..b881d5cc 100644 --- a/app/src/main/java/fi/vanced/utils/requests/Requester.java +++ b/app/src/main/java/fi/vanced/utils/requests/Requester.java @@ -21,20 +21,26 @@ public class Requester { } public static String parseJson(HttpURLConnection connection) throws IOException { - return parseJson(connection.getInputStream()); + return parseJson(connection.getInputStream(), false); } - public static String parseJson(InputStream inputStream) throws IOException { + public static String parseJson(InputStream inputStream, boolean isError) throws IOException { StringBuilder jsonBuilder = new StringBuilder(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { - jsonBuilder.append(line).append("\n"); + jsonBuilder.append(line); + if (isError) + jsonBuilder.append("\n"); } inputStream.close(); return jsonBuilder.toString(); } + public static String parseErrorJson(HttpURLConnection connection) throws IOException { + return parseJson(connection.getErrorStream(), true); + } + public static JSONObject getJSONObject(HttpURLConnection connection) throws Exception { return new JSONObject(parseJsonAndDisconnect(connection)); } diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index 0356c515..9de71b75 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -91,7 +91,7 @@ public class SBRequester { SponsorBlockUtils.messageToToast = str("submit_failed_duplicate"); break; case 403: - SponsorBlockUtils.messageToToast = str("submit_failed_forbidden", Requester.parseJson(connection.getErrorStream())); + SponsorBlockUtils.messageToToast = str("submit_failed_forbidden", Requester.parseErrorJson(connection)); break; case 429: SponsorBlockUtils.messageToToast = str("submit_failed_rate_limit"); @@ -137,7 +137,7 @@ public class SBRequester { SponsorBlockUtils.messageToToast = str("vote_succeeded"); break; case 403: - SponsorBlockUtils.messageToToast = str("vote_failed_forbidden", Requester.parseJson(connection.getErrorStream())); + SponsorBlockUtils.messageToToast = str("vote_failed_forbidden", Requester.parseErrorJson(connection)); break; default: SponsorBlockUtils.messageToToast = str("vote_failed_unknown_error", responseCode, connection.getResponseMessage()); From 5fafc46cb7a7acb30ca3a1d97c66b0aaf03e40d4 Mon Sep 17 00:00:00 2001 From: caneleex Date: Thu, 27 Jan 2022 23:05:46 +0100 Subject: [PATCH 30/46] add open in sb.ltn.fi button --- .../libraries/youtube/ui/SBBrowserButton.java | 37 ++++++++++++++ .../youtube/ui/SBWhitelistButton.java | 4 +- .../youtube/ui/SlimButtonContainer.java | 48 ++++++++++++++++--- .../SponsorBlockPreferenceFragment.java | 10 ++++ .../pl/jakubweg/SponsorBlockSettings.java | 1 + .../java/pl/jakubweg/SponsorBlockUtils.java | 6 +++ app/src/main/res/values/strings.xml | 3 ++ 7 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/fi/vanced/libraries/youtube/ui/SBBrowserButton.java diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBBrowserButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBBrowserButton.java new file mode 100644 index 00000000..fc0de2c0 --- /dev/null +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBBrowserButton.java @@ -0,0 +1,37 @@ +package fi.vanced.libraries.youtube.ui; + +import static pl.jakubweg.StringRef.str; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.view.View; +import android.view.ViewGroup; + +import fi.vanced.libraries.youtube.player.VideoInformation; +import fi.vanced.utils.VancedUtils; +import pl.jakubweg.SponsorBlockSettings; +import pl.jakubweg.SponsorBlockUtils; + +public class SBBrowserButton extends SlimButton { + private static final String BROWSER_URL = "https://sb.ltn.fi/video/"; + + public SBBrowserButton(Context context, ViewGroup container) { + super(context, container, SLIM_METADATA_BUTTON_ID, + SponsorBlockUtils.isSBButtonEnabled(context, SponsorBlockSettings.PREFERENCES_KEY_BROWSER_BUTTON)); + + initialize(); + } + + private void initialize() { + this.button_icon.setImageResource(VancedUtils.getIdentifier("vanced_sb_browser", "drawable")); + this.button_text.setText(str("action_browser")); + } + + @Override + public void onClick(View v) { + Uri uri = Uri.parse(BROWSER_URL + VideoInformation.currentVideoId); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + context.startActivity(intent); + } +} \ No newline at end of file diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java index c759ee63..5ea5a6f4 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ui/SBWhitelistButton.java @@ -14,15 +14,15 @@ import fi.vanced.libraries.youtube.player.VideoInformation; import fi.vanced.libraries.youtube.whitelisting.Whitelist; import fi.vanced.libraries.youtube.whitelisting.WhitelistType; import fi.vanced.libraries.youtube.whitelisting.requests.WhitelistRequester; -import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; +import pl.jakubweg.SponsorBlockUtils; public class SBWhitelistButton extends SlimButton { public static final String TAG = "VI - SBWhitelistButton"; public SBWhitelistButton(Context context, ViewGroup container) { super(context, container, SlimButton.SLIM_METADATA_BUTTON_ID, - SharedPrefUtils.getBoolean(context, WhitelistType.SPONSORBLOCK.getSharedPreferencesName(), WhitelistType.SPONSORBLOCK.getPreferenceEnabledName(), false)); + SponsorBlockUtils.isSBButtonEnabled(context, WhitelistType.SPONSORBLOCK.getPreferenceEnabledName())); 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 12ae3e83..996578f9 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,6 +1,8 @@ package fi.vanced.libraries.youtube.ui; import static fi.razerman.youtube.XGlobals.debug; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_BROWSER_BUTTON; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SPONSOR_BLOCK_ENABLED; import android.content.Context; import android.content.SharedPreferences; @@ -14,6 +16,7 @@ import fi.vanced.libraries.youtube.whitelisting.Whitelist; import fi.vanced.libraries.youtube.whitelisting.WhitelistType; import fi.vanced.utils.SharedPrefUtils; import fi.vanced.utils.VancedUtils; +import pl.jakubweg.SponsorBlockSettings; public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLayout { private static final String TAG = "VI - Slim - Container"; @@ -22,6 +25,7 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa private CopyWithTimestamp copyWithTimestampButton; public static AdButton adBlockButton; public static SBWhitelistButton sbWhitelistButton; + private SBBrowserButton sbBrowserButton; private final Context context; SharedPreferences.OnSharedPreferenceChangeListener listener; @@ -52,6 +56,7 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa copyWithTimestampButton = new CopyWithTimestamp(context, this); adBlockButton = new AdButton(context, this); sbWhitelistButton = new SBWhitelistButton(context, this); + sbBrowserButton = new SBBrowserButton(context, this); new SponsorBlockVoting(context, this); addSharedPrefsChangeListener(); @@ -75,6 +80,29 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa copyWithTimestampButton.setVisible(ButtonVisibility.isVisibleInContainer(context, "pref_copy_video_url_timestamp_button_list")); return; } + if (PREFERENCES_KEY_SPONSOR_BLOCK_ENABLED.equals(key)) { + if (sbWhitelistButton != null) { + if (SponsorBlockSettings.isSponsorBlockEnabled) { + toggleWhitelistButton(); + } + else { + Whitelist.setEnabled(WhitelistType.SPONSORBLOCK, false); + sbWhitelistButton.setVisible(false); + } + } + if (sbBrowserButton != null) { + if (SponsorBlockSettings.isSponsorBlockEnabled) { + toggleBrowserButton(); + } + else { + sbBrowserButton.setVisible(false); + } + } + } + if (PREFERENCES_KEY_BROWSER_BUTTON.equals(key) && sbBrowserButton != null) { + toggleBrowserButton(); + return; + } WhitelistType whitelistAds = WhitelistType.ADS; String adsEnabledPreferenceName = whitelistAds.getPreferenceEnabledName(); if (adsEnabledPreferenceName.equals(key) && adBlockButton != null) { @@ -83,12 +111,8 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa adBlockButton.setVisible(enabled); return; } - WhitelistType whitelistSB = WhitelistType.SPONSORBLOCK; - String sbEnabledPreferenceName = whitelistSB.getPreferenceEnabledName(); - if (sbEnabledPreferenceName.equals(key) && sbWhitelistButton != null) { - boolean enabled = SharedPrefUtils.getBoolean(context, whitelistSB.getSharedPreferencesName(), sbEnabledPreferenceName, false); - Whitelist.setEnabled(whitelistSB, enabled); - sbWhitelistButton.setVisible(enabled); + if (WhitelistType.SPONSORBLOCK.getPreferenceEnabledName().equals(key) && sbWhitelistButton != null) { + toggleWhitelistButton(); return; } } @@ -102,4 +126,16 @@ public class SlimButtonContainer extends SlimMetadataScrollableButtonContainerLa context.getSharedPreferences(WhitelistType.SPONSORBLOCK.getSharedPreferencesName(), Context.MODE_PRIVATE) .registerOnSharedPreferenceChangeListener(listener); } + + private void toggleWhitelistButton() { + WhitelistType whitelistSB = WhitelistType.SPONSORBLOCK; + String sbEnabledPreferenceName = whitelistSB.getPreferenceEnabledName(); + boolean enabled = SharedPrefUtils.getBoolean(context, whitelistSB.getSharedPreferencesName(), sbEnabledPreferenceName, false); + Whitelist.setEnabled(whitelistSB, enabled); + sbWhitelistButton.setVisible(enabled); + } + + private void toggleBrowserButton() { + sbBrowserButton.setVisible(SharedPrefUtils.getBoolean(context, SponsorBlockSettings.PREFERENCES_NAME, PREFERENCES_KEY_BROWSER_BUTTON, false)); + } } diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index b88bfb7e..b8bc660e 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -2,6 +2,7 @@ package pl.jakubweg; import static fi.razerman.youtube.XGlobals.debug; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_BROWSER_BUTTON; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_COUNT_SKIPS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_MIN_DURATION; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_NEW_SEGMENT_ENABLED; @@ -291,6 +292,15 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement screen.addPreference(preference); } + { + Preference preference = new SwitchPreference(context); + preference.setTitle(str("general_browser_button")); + preference.setSummary(str("general_browser_button_sum")); + preference.setKey(PREFERENCES_KEY_BROWSER_BUTTON); + preferencesToDisableWhenSBDisabled.add(preference); + screen.addPreference(preference); + } + { EditTextPreference preference = new EditTextPreference(context); preference.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER); diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java index 9fb85016..429524d9 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java @@ -32,6 +32,7 @@ public class SponsorBlockSettings { public static final String PREFERENCES_KEY_SKIPPED_SEGMENTS_TIME = "sb-skipped-segments-time"; public static final String PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS = "sb-length-without-segments"; public static final String PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX = "_color"; + public static final String PREFERENCES_KEY_BROWSER_BUTTON = "sb-browser-button"; public static final SegmentBehaviour DefaultBehaviour = SegmentBehaviour.SKIP_AUTOMATICALLY; diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java index 15b9d65d..31c678a1 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java @@ -16,6 +16,7 @@ import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_MIN_DURATION; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_UUID; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_NAME; import static pl.jakubweg.SponsorBlockSettings.countSkips; import static pl.jakubweg.SponsorBlockSettings.getPreferences; import static pl.jakubweg.SponsorBlockSettings.isSponsorBlockEnabled; @@ -59,6 +60,7 @@ import java.util.List; import java.util.Objects; import java.util.TimeZone; +import fi.vanced.utils.SharedPrefUtils; import pl.jakubweg.objects.SponsorSegment; import pl.jakubweg.objects.UserStats; import pl.jakubweg.requests.SBRequester; @@ -608,6 +610,10 @@ public abstract class SponsorBlockUtils { return isSponsorBlockEnabled && setting; } + public static boolean isSBButtonEnabled(Context context, String key) { + return isSettingEnabled(SharedPrefUtils.getBoolean(context, PREFERENCES_NAME, key, false)); + } + public enum VoteOption { UPVOTE(str("vote_upvote")), DOWNVOTE(str("vote_downvote")), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5d1f3523..c4f268e3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -318,6 +318,8 @@ This time appears in brackets next to the current time. This shows the total video duration minus any segments. Channel whitelisting Use the Segments button under the player to whitelist a channel + Enable SB Browser button + Clicking this button under the player will open a sb.ltn.fi page where you can take a look at the segments Preview/Recap Quick recap of previous episodes, or a preview of what\'s coming up later in the current video. Meant for edited together clips, not for spoken summaries. Stats @@ -346,6 +348,7 @@ Timestamp Ads Segments + SB Browser Video ad settings Video ad whitelisting From 5018bed0005be1a4600e99887ebb1ea0e3dd8d4d Mon Sep 17 00:00:00 2001 From: caneleex Date: Thu, 27 Jan 2022 23:07:51 +0100 Subject: [PATCH 31/46] change the browser button string sum a bit --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c4f268e3..ac59e4bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -319,7 +319,7 @@ Channel whitelisting Use the Segments button under the player to whitelist a channel Enable SB Browser button - Clicking this button under the player will open a sb.ltn.fi page where you can take a look at the segments + Clicking this button under the player will open sb.ltn.fi where you can take a look at the segments Preview/Recap Quick recap of previous episodes, or a preview of what\'s coming up later in the current video. Meant for edited together clips, not for spoken summaries. Stats From 073ba21dec933933fe4552f0f0db3970e19a2067 Mon Sep 17 00:00:00 2001 From: caneleex Date: Thu, 27 Jan 2022 23:22:08 +0100 Subject: [PATCH 32/46] commit drawables --- app/src/main/res/drawable/vanced_sb_browser.xml | 9 +++++++++ app/src/main/res/drawable/vanced_yt_sb.xml | 14 ++++++++++++++ app/src/main/res/drawable/vanced_yt_sb_blocked.xml | 14 ++++++++++++++ app/src/main/res/drawable/vanced_yt_sb_button.xml | 6 ++++++ 4 files changed, 43 insertions(+) create mode 100644 app/src/main/res/drawable/vanced_sb_browser.xml create mode 100644 app/src/main/res/drawable/vanced_yt_sb.xml create mode 100644 app/src/main/res/drawable/vanced_yt_sb_blocked.xml create mode 100644 app/src/main/res/drawable/vanced_yt_sb_button.xml diff --git a/app/src/main/res/drawable/vanced_sb_browser.xml b/app/src/main/res/drawable/vanced_sb_browser.xml new file mode 100644 index 00000000..178f21b4 --- /dev/null +++ b/app/src/main/res/drawable/vanced_sb_browser.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/vanced_yt_sb.xml b/app/src/main/res/drawable/vanced_yt_sb.xml new file mode 100644 index 00000000..ba07674e --- /dev/null +++ b/app/src/main/res/drawable/vanced_yt_sb.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/vanced_yt_sb_blocked.xml b/app/src/main/res/drawable/vanced_yt_sb_blocked.xml new file mode 100644 index 00000000..2a98161f --- /dev/null +++ b/app/src/main/res/drawable/vanced_yt_sb_blocked.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/vanced_yt_sb_button.xml b/app/src/main/res/drawable/vanced_yt_sb_button.xml new file mode 100644 index 00000000..6bba8187 --- /dev/null +++ b/app/src/main/res/drawable/vanced_yt_sb_button.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file From dd961378cf3012136a5b705767e6f4a4ecddedfc Mon Sep 17 00:00:00 2001 From: caneleex Date: Thu, 27 Jan 2022 23:28:05 +0100 Subject: [PATCH 33/46] round down --- app/src/main/res/drawable/vanced_sb_browser.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/drawable/vanced_sb_browser.xml b/app/src/main/res/drawable/vanced_sb_browser.xml index 178f21b4..525ae7d9 100644 --- a/app/src/main/res/drawable/vanced_sb_browser.xml +++ b/app/src/main/res/drawable/vanced_sb_browser.xml @@ -1,7 +1,7 @@ Date: Thu, 27 Jan 2022 23:37:15 +0100 Subject: [PATCH 34/46] change sbb sum sum more :^) --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ac59e4bd..ce3a83c3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -319,7 +319,7 @@ Channel whitelisting Use the Segments button under the player to whitelist a channel Enable SB Browser button - Clicking this button under the player will open sb.ltn.fi where you can take a look at the segments + Clicking this button under the player will open sb.ltn.fi where you can see more details about segments. Preview/Recap Quick recap of previous episodes, or a preview of what\'s coming up later in the current video. Meant for edited together clips, not for spoken summaries. Stats From 3773422f4c74d43f367bf0a4592bca76a7b1b095 Mon Sep 17 00:00:00 2001 From: caneleex Date: Fri, 28 Jan 2022 14:47:17 +0100 Subject: [PATCH 35/46] fix guidelines url --- .../main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index b8bc660e..c1e19c55 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -140,7 +140,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement setSeenGuidelines(context); Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse("https://github.com/ajayyy/SponsorBlock/wiki/Guidelines")); + intent.setData(Uri.parse("https://wiki.sponsor.ajay.app/w/Guidelines")); context.startActivity(intent); } From fde1f3d95b82e8cf059d4a928a50ce46f35818f2 Mon Sep 17 00:00:00 2001 From: caneleex Date: Fri, 28 Jan 2022 15:47:58 +0100 Subject: [PATCH 36/46] submit video duration with segments --- app/src/main/java/pl/jakubweg/requests/SBRequester.java | 4 +++- app/src/main/java/pl/jakubweg/requests/SBRoutes.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index 9de71b75..5e939aa6 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -23,6 +23,7 @@ import java.util.Locale; import fi.vanced.utils.requests.Requester; import fi.vanced.utils.requests.Route; +import pl.jakubweg.PlayerController; import pl.jakubweg.SponsorBlockSettings; import pl.jakubweg.SponsorBlockUtils; import pl.jakubweg.SponsorBlockUtils.VoteOption; @@ -80,7 +81,8 @@ public class SBRequester { try { String start = String.format(Locale.US, TIME_TEMPLATE, startTime); String end = String.format(Locale.US, TIME_TEMPLATE, endTime); - HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, videoId, uuid, start, end, category); + String duration = String.valueOf(PlayerController.getCurrentVideoLength() / 1000); + HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, videoId, uuid, start, end, category, duration); int responseCode = connection.getResponseCode(); switch (responseCode) { diff --git a/app/src/main/java/pl/jakubweg/requests/SBRoutes.java b/app/src/main/java/pl/jakubweg/requests/SBRoutes.java index 840e6b10..f904363c 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRoutes.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRoutes.java @@ -10,7 +10,7 @@ public class SBRoutes { public static final Route VIEWED_SEGMENT = new Route(POST, "viewedVideoSponsorTime?UUID={segment_id}"); public static final Route GET_USER_STATS = new Route(GET, "userInfo?userID={user_id}&values=[\"userName\", \"minutesSaved\", \"segmentCount\", \"viewCount\"]"); public static final Route CHANGE_USERNAME = new Route(POST, "setUsername?userID={user_id}&username={username}"); - public static final Route SUBMIT_SEGMENTS = new Route(POST, "skipSegments?videoID={video_id}&userID={user_id}&startTime={start_time}&endTime={end_time}&category={category}"); + public static final Route SUBMIT_SEGMENTS = new Route(POST, "skipSegments?videoID={video_id}&userID={user_id}&startTime={start_time}&endTime={end_time}&category={category}&videoDuration={duration}"); public static final Route VOTE_ON_SEGMENT_QUALITY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&type={type}"); public static final Route VOTE_ON_SEGMENT_CATEGORY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&category={category}"); From 4c7f21f5a96444c5e7a6e5e904c8d104d38d5094 Mon Sep 17 00:00:00 2001 From: caneleex Date: Fri, 28 Jan 2022 15:48:14 +0100 Subject: [PATCH 37/46] uncommit xml because AS is being a little bitch --- app/src/main/res/drawable/vanced_sb_browser.xml | 9 --------- app/src/main/res/drawable/vanced_yt_sb.xml | 14 -------------- app/src/main/res/drawable/vanced_yt_sb_blocked.xml | 14 -------------- app/src/main/res/drawable/vanced_yt_sb_button.xml | 6 ------ 4 files changed, 43 deletions(-) delete mode 100644 app/src/main/res/drawable/vanced_sb_browser.xml delete mode 100644 app/src/main/res/drawable/vanced_yt_sb.xml delete mode 100644 app/src/main/res/drawable/vanced_yt_sb_blocked.xml delete mode 100644 app/src/main/res/drawable/vanced_yt_sb_button.xml diff --git a/app/src/main/res/drawable/vanced_sb_browser.xml b/app/src/main/res/drawable/vanced_sb_browser.xml deleted file mode 100644 index 525ae7d9..00000000 --- a/app/src/main/res/drawable/vanced_sb_browser.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/vanced_yt_sb.xml b/app/src/main/res/drawable/vanced_yt_sb.xml deleted file mode 100644 index ba07674e..00000000 --- a/app/src/main/res/drawable/vanced_yt_sb.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/vanced_yt_sb_blocked.xml b/app/src/main/res/drawable/vanced_yt_sb_blocked.xml deleted file mode 100644 index 2a98161f..00000000 --- a/app/src/main/res/drawable/vanced_yt_sb_blocked.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/vanced_yt_sb_button.xml b/app/src/main/res/drawable/vanced_yt_sb_button.xml deleted file mode 100644 index 6bba8187..00000000 --- a/app/src/main/res/drawable/vanced_yt_sb_button.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file From b809ac6111c07ff94cb310f91b8a2c31ddf8a3dc Mon Sep 17 00:00:00 2001 From: caneleex Date: Fri, 28 Jan 2022 22:38:43 +0100 Subject: [PATCH 38/46] add support for changing the API URL --- .../SponsorBlockPreferenceFragment.java | 76 +++++++++++++++++++ .../pl/jakubweg/SponsorBlockSettings.java | 5 ++ .../pl/jakubweg/requests/SBRequester.java | 3 +- app/src/main/res/values/strings.xml | 6 ++ 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index c1e19c55..79eb4ef9 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -1,7 +1,9 @@ package pl.jakubweg; import static fi.razerman.youtube.XGlobals.debug; +import static pl.jakubweg.SponsorBlockSettings.DEFAULT_API_URL; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_API_URL; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_BROWSER_BUTTON; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_COUNT_SKIPS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_MIN_DURATION; @@ -25,6 +27,7 @@ import static pl.jakubweg.StringRef.str; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; @@ -35,9 +38,14 @@ import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import android.preference.SwitchPreference; +import android.text.Editable; +import android.text.Html; import android.text.InputType; +import android.util.Patterns; +import android.widget.EditText; import android.widget.Toast; +import java.lang.ref.WeakReference; import java.text.DecimalFormat; import java.util.ArrayList; @@ -50,6 +58,7 @@ import pl.jakubweg.requests.SBRequester; public class SponsorBlockPreferenceFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { public static final DecimalFormat FORMATTER = new DecimalFormat("#,###,###"); public static final String SAVED_TEMPLATE = "%dh %.1f %s"; + private static final APIURLChangeListener API_URL_CHANGE_LISTENER = new APIURLChangeListener(); private final ArrayList preferencesToDisableWhenSBDisabled = new ArrayList<>(); @Override @@ -347,6 +356,73 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement screen.addPreference(preference); preferencesToDisableWhenSBDisabled.add(preference); } + + { + Preference preference = new Preference(context); + String title = str("general_api_url"); + preference.setTitle(title); + preference.setSummary(Html.fromHtml(str("general_api_url_sum"))); + preference.setOnPreferenceClickListener(preference1 -> { + EditText editText = new EditText(context); + editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI); + editText.setText(SponsorBlockSettings.apiUrl); + + API_URL_CHANGE_LISTENER.setEditTextRef(editText); + new AlertDialog.Builder(context) + .setTitle(title) + .setView(editText) + .setNegativeButton(android.R.string.cancel, null) + .setNeutralButton(str("reset"), API_URL_CHANGE_LISTENER) + .setPositiveButton(android.R.string.ok, API_URL_CHANGE_LISTENER) + .show(); + return true; + }); + + screen.addPreference(preference); + preferencesToDisableWhenSBDisabled.add(preference); + } + } + + private static class APIURLChangeListener implements DialogInterface.OnClickListener { + private WeakReference editTextRef; + + @Override + public void onClick(DialogInterface dialog, int which) { + EditText editText = editTextRef.get(); + if (editText == null) + return; + Context context = ((AlertDialog) dialog).getContext(); + Context applicationContext = context.getApplicationContext(); + SharedPreferences preferences = SponsorBlockSettings.getPreferences(context); + + switch (which) { + case DialogInterface.BUTTON_NEUTRAL: + preferences.edit().putString(PREFERENCES_KEY_API_URL, DEFAULT_API_URL).apply(); + Toast.makeText(applicationContext, str("api_url_reset"), Toast.LENGTH_SHORT).show(); + break; + case DialogInterface.BUTTON_POSITIVE: + Editable text = editText.getText(); + Toast invalidToast = Toast.makeText(applicationContext, str("api_url_invalid"), Toast.LENGTH_SHORT); + if (text == null) { + invalidToast.show(); + } + else { + String textAsString = text.toString(); + if (textAsString.isEmpty() || !Patterns.WEB_URL.matcher(textAsString).matches()) { + invalidToast.show(); + } + else { + preferences.edit().putString(PREFERENCES_KEY_API_URL, textAsString).apply(); + Toast.makeText(applicationContext, str("api_url_changed"), Toast.LENGTH_SHORT).show(); + } + } + break; + } + } + + public void setEditTextRef(EditText editText) { + editTextRef = new WeakReference<>(editText); + } } @Override diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java index 429524d9..86522c25 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java @@ -33,8 +33,10 @@ public class SponsorBlockSettings { public static final String PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS = "sb-length-without-segments"; public static final String PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX = "_color"; public static final String PREFERENCES_KEY_BROWSER_BUTTON = "sb-browser-button"; + public static final String PREFERENCES_KEY_API_URL = "sb-api-url"; public static final SegmentBehaviour DefaultBehaviour = SegmentBehaviour.SKIP_AUTOMATICALLY; + public static final String DEFAULT_API_URL = "https://sponsor.ajay.app/api/"; public static boolean isSponsorBlockEnabled = false; public static boolean seenGuidelinesPopup = false; @@ -46,6 +48,7 @@ public class SponsorBlockSettings { public static int adjustNewSegmentMillis = 150; public static float minDuration = 0f; public static String uuid = ""; + public static String apiUrl = DEFAULT_API_URL; public static String sponsorBlockUrlCategories = "[]"; public static int skippedSegments; public static long skippedTime; @@ -144,6 +147,8 @@ public class SponsorBlockSettings { countSkips = preferences.getBoolean(PREFERENCES_KEY_COUNT_SKIPS, countSkips); showTimeWithoutSegments = preferences.getBoolean(PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS, showTimeWithoutSegments); + apiUrl = preferences.getString(PREFERENCES_KEY_API_URL, DEFAULT_API_URL); + uuid = preferences.getString(PREFERENCES_KEY_UUID, null); if (uuid == null) { uuid = (UUID.randomUUID().toString() + diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index 5e939aa6..77be1459 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -31,7 +31,6 @@ import pl.jakubweg.objects.SponsorSegment; import pl.jakubweg.objects.UserStats; public class SBRequester { - private static final String SPONSORBLOCK_API_URL = "https://sponsor.ajay.app/api/"; private static final String TIME_TEMPLATE = "%.3f"; private SBRequester() {} @@ -201,7 +200,7 @@ public class SBRequester { // helpers private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { - return Requester.getConnectionFromRoute(SPONSORBLOCK_API_URL, route, params); + return Requester.getConnectionFromRoute(SponsorBlockSettings.apiUrl, route, params); } private static JSONObject getJSONObject(Route route, String... params) throws Exception { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ce3a83c3..879bbea9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -157,6 +157,8 @@ This should be kept private. This is like a password and should not be shared with anyone. If someone has this, they can impersonate you Import/Export settings This is your entire configuration that is applicable in the desktop extension in JSON. This includes your userID, so be sure to share this wisely. + Change API URL + The address SponsorBlock uses to make calls to the server. <b>Don\'t change this unless you know what you\'re doing.</b> Settings were successfully imported Failed to import settings Failed to export settings @@ -350,6 +352,10 @@ Segments SB Browser + API URL changed + API URL reset + Provided API URL is invalid + Video ad settings Video ad whitelisting Video ad whitelisting is turned off From 15267516c114aab7be99cf6dff12c34bbd87450a Mon Sep 17 00:00:00 2001 From: caneleex Date: Tue, 1 Feb 2022 14:05:11 +0100 Subject: [PATCH 39/46] fix filler category name --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 879bbea9..f5b5e63c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -177,7 +177,7 @@ Similar to "sponsor" except for unpaid or self promotion. This includes sections about merchandise, donations, or information about who they collaborated with Music: Non-Music Section Only for use in music videos. This includes introductions or outros in music videos - Filler/Tangent + Filler Tangent Tangential scenes added only for filler or humor that are not required to understand the main content of the video. This should not include segments providing context or background details Skipped a sponsor segment Skipped sponsor From e3ec5430c947014c10b0ab298e80720339f7bde4 Mon Sep 17 00:00:00 2001 From: caneleex Date: Tue, 1 Feb 2022 15:17:44 +0100 Subject: [PATCH 40/46] preview new category color while editing --- .../objects/EditTextListPreference.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/src/main/java/pl/jakubweg/objects/EditTextListPreference.java b/app/src/main/java/pl/jakubweg/objects/EditTextListPreference.java index 80b8565b..c4412f8b 100644 --- a/app/src/main/java/pl/jakubweg/objects/EditTextListPreference.java +++ b/app/src/main/java/pl/jakubweg/objects/EditTextListPreference.java @@ -8,7 +8,10 @@ import android.content.Context; import android.content.DialogInterface; import android.graphics.Color; import android.preference.ListPreference; +import android.text.Editable; +import android.text.Html; import android.text.InputType; +import android.text.TextWatcher; import android.util.AttributeSet; import android.widget.EditText; import android.widget.Toast; @@ -44,7 +47,24 @@ public class EditTextListPreference extends ListPreference { mEditText = new EditText(builder.getContext()); mEditText.setInputType(InputType.TYPE_CLASS_TEXT); mEditText.setText(formatColorString(category.color)); + mEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + try { + Color.parseColor(s.toString()); // validation + getDialog().setTitle(Html.fromHtml(String.format(" %s", s, category.title))); + } + catch (Exception ex) {} + } + }); builder.setView(mEditText); + builder.setTitle(category.getTitleWithDot()); builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { EditTextListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE); @@ -55,6 +75,7 @@ public class EditTextListPreference extends ListPreference { category.setColor(defaultColor); Toast.makeText(getContext().getApplicationContext(), str("color_reset"), Toast.LENGTH_SHORT).show(); getSharedPreferences().edit().putString(getColorPreferenceKey(), formatColorString(defaultColor)).apply(); + reformatTitle(); }); builder.setNegativeButton(android.R.string.cancel, null); @@ -80,6 +101,7 @@ public class EditTextListPreference extends ListPreference { category.setColor(color); Toast.makeText(applicationContext, str("color_changed"), Toast.LENGTH_SHORT).show(); getSharedPreferences().edit().putString(getColorPreferenceKey(), formatColorString(color)).apply(); + reformatTitle(); } catch (Exception ex) { Toast.makeText(applicationContext, str("color_invalid"), Toast.LENGTH_SHORT).show(); @@ -94,4 +116,8 @@ public class EditTextListPreference extends ListPreference { private String getColorPreferenceKey() { return getKey() + SponsorBlockSettings.PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX; } + + private void reformatTitle() { + this.setTitle(getCategoryBySelf().getTitleWithDot()); + } } \ No newline at end of file From e5c6cff7dc086d505fbbe3da8ca8d3320e7cb808 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 7 Feb 2022 16:15:22 +0100 Subject: [PATCH 41/46] mark locked segments as orange if vip --- .../pl/jakubweg/SponsorBlockSettings.java | 9 +++++ .../java/pl/jakubweg/SponsorBlockUtils.java | 33 +++++++++++++++---- .../pl/jakubweg/objects/SponsorSegment.java | 5 ++- .../pl/jakubweg/requests/SBRequester.java | 29 +++++++++++++++- .../java/pl/jakubweg/requests/SBRoutes.java | 15 +++++---- 5 files changed, 75 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java index 86522c25..0b0b4384 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java @@ -33,6 +33,8 @@ public class SponsorBlockSettings { public static final String PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS = "sb-length-without-segments"; public static final String PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX = "_color"; public static final String PREFERENCES_KEY_BROWSER_BUTTON = "sb-browser-button"; + public static final String PREFERENCES_KEY_IS_VIP = "sb-is-vip"; + public static final String PREFERENCES_KEY_LAST_VIP_CHECK = "sb-last-vip-check"; public static final String PREFERENCES_KEY_API_URL = "sb-api-url"; public static final SegmentBehaviour DefaultBehaviour = SegmentBehaviour.SKIP_AUTOMATICALLY; @@ -45,6 +47,8 @@ public class SponsorBlockSettings { public static boolean showToastWhenSkippedAutomatically = true; public static boolean countSkips = true; public static boolean showTimeWithoutSegments = true; + public static boolean vip = false; + public static long lastVipCheck = 0; public static int adjustNewSegmentMillis = 150; public static float minDuration = 0f; public static String uuid = ""; @@ -146,6 +150,11 @@ public class SponsorBlockSettings { countSkips = preferences.getBoolean(PREFERENCES_KEY_COUNT_SKIPS, countSkips); showTimeWithoutSegments = preferences.getBoolean(PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS, showTimeWithoutSegments); + vip = preferences.getBoolean(PREFERENCES_KEY_IS_VIP, false); + + String vipCheckTmp = preferences.getString(PREFERENCES_KEY_LAST_VIP_CHECK, null); + if (vipCheckTmp != null) + lastVipCheck = Long.parseLong(vipCheckTmp); apiUrl = preferences.getString(PREFERENCES_KEY_API_URL, DEFAULT_API_URL); diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java index 31c678a1..3fa0d9c2 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java @@ -12,6 +12,8 @@ import static pl.jakubweg.SponsorBlockPreferenceFragment.FORMATTER; import static pl.jakubweg.SponsorBlockPreferenceFragment.SAVED_TEMPLATE; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_COUNT_SKIPS; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_IS_VIP; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_LAST_VIP_CHECK; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_MIN_DURATION; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP; @@ -20,12 +22,14 @@ import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_NAME; import static pl.jakubweg.SponsorBlockSettings.countSkips; import static pl.jakubweg.SponsorBlockSettings.getPreferences; import static pl.jakubweg.SponsorBlockSettings.isSponsorBlockEnabled; +import static pl.jakubweg.SponsorBlockSettings.lastVipCheck; import static pl.jakubweg.SponsorBlockSettings.minDuration; import static pl.jakubweg.SponsorBlockSettings.showTimeWithoutSegments; import static pl.jakubweg.SponsorBlockSettings.showToastWhenSkippedAutomatically; import static pl.jakubweg.SponsorBlockSettings.skippedSegments; import static pl.jakubweg.SponsorBlockSettings.skippedTime; import static pl.jakubweg.SponsorBlockSettings.uuid; +import static pl.jakubweg.SponsorBlockSettings.vip; import static pl.jakubweg.StringRef.str; import static pl.jakubweg.requests.SBRequester.voteForSegment; @@ -74,6 +78,7 @@ public abstract class SponsorBlockUtils { public static boolean videoHasSegments = false; public static String timeWithoutSegments = ""; private static final int sponsorBtnId = 1234; + private static final String LOCKED_COLOR = "#FFC83D"; public static final View.OnClickListener sponsorBlockBtnListener = v -> { if (debug) { Log.d(TAG, "Shield button clicked"); @@ -214,10 +219,17 @@ public abstract class SponsorBlockUtils { final SponsorSegment segment = sponsorSegmentsOfCurrentVideo[which]; final VoteOption[] voteOptions = VoteOption.values(); - String[] items = new String[voteOptions.length]; + CharSequence[] items = new CharSequence[voteOptions.length]; for (int i = 0; i < voteOptions.length; i++) { - items[i] = voteOptions[i].title; + VoteOption voteOption = voteOptions[i]; + String title = voteOption.title; + if (vip && segment.isLocked && voteOption.shouldHighlight) { + items[i] = Html.fromHtml(String.format("%s", LOCKED_COLOR, title)); + } + else { + items[i] = title; + } } new AlertDialog.Builder(context) @@ -383,7 +395,7 @@ public abstract class SponsorBlockUtils { final SponsorSegment[] segments = original == null ? new SponsorSegment[1] : Arrays.copyOf(original, original.length + 1); segments[segments.length - 1] = new SponsorSegment(newSponsorSegmentStartMillis, newSponsorSegmentEndMillis, - SponsorBlockSettings.SegmentInfo.UNSUBMITTED, null); + SponsorBlockSettings.SegmentInfo.UNSUBMITTED, null, false); Arrays.sort(segments); sponsorSegmentsOfCurrentVideo = segments; @@ -555,8 +567,10 @@ public abstract class SponsorBlockUtils { editor.putBoolean(PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP, !settingsJson.getBoolean("dontShowNotice")); editor.putBoolean(PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS, settingsJson.getBoolean("showTimeWithSkips")); editor.putBoolean(PREFERENCES_KEY_COUNT_SKIPS, settingsJson.getBoolean("trackViewCount")); + editor.putBoolean(PREFERENCES_KEY_IS_VIP, settingsJson.getBoolean("isVip")); editor.putString(PREFERENCES_KEY_MIN_DURATION, settingsJson.getString("minDuration")); editor.putString(PREFERENCES_KEY_UUID, settingsJson.getString("userID")); + editor.putString(PREFERENCES_KEY_LAST_VIP_CHECK, settingsJson.getString("lastIsVipUpdate")); editor.apply(); Toast.makeText(context, str("settings_import_successful"), Toast.LENGTH_SHORT).show(); @@ -596,6 +610,8 @@ public abstract class SponsorBlockUtils { json.put("trackViewCount", countSkips); json.put("categorySelections", categorySelectionsArray); json.put("userID", uuid); + json.put("isVip", vip); + json.put("lastIsVipUpdate", lastVipCheck); return json.toString(); } @@ -615,14 +631,17 @@ public abstract class SponsorBlockUtils { } public enum VoteOption { - UPVOTE(str("vote_upvote")), - DOWNVOTE(str("vote_downvote")), - CATEGORY_CHANGE(str("vote_category")); + UPVOTE(str("vote_upvote"), false), + DOWNVOTE(str("vote_downvote"), true), + CATEGORY_CHANGE(str("vote_category"), true); public final String title; + public final boolean shouldHighlight; - VoteOption(String title) { + + VoteOption(String title, boolean shouldHighlight) { this.title = title; + this.shouldHighlight = shouldHighlight; } } diff --git a/app/src/main/java/pl/jakubweg/objects/SponsorSegment.java b/app/src/main/java/pl/jakubweg/objects/SponsorSegment.java index 1819bba7..2fed40a3 100644 --- a/app/src/main/java/pl/jakubweg/objects/SponsorSegment.java +++ b/app/src/main/java/pl/jakubweg/objects/SponsorSegment.java @@ -7,12 +7,14 @@ public class SponsorSegment implements Comparable { public final long end; public final SponsorBlockSettings.SegmentInfo category; public final String UUID; + public final boolean isLocked; - public SponsorSegment(long start, long end, SponsorBlockSettings.SegmentInfo category, String UUID) { + public SponsorSegment(long start, long end, SponsorBlockSettings.SegmentInfo category, String UUID, boolean isLocked) { this.start = start; this.end = end; this.category = category; this.UUID = UUID; + this.isLocked = isLocked; } @Override @@ -21,6 +23,7 @@ public class SponsorSegment implements Comparable { "start=" + start + ", end=" + end + ", category='" + category + '\'' + + ", locked=" + isLocked + '}'; } diff --git a/app/src/main/java/pl/jakubweg/requests/SBRequester.java b/app/src/main/java/pl/jakubweg/requests/SBRequester.java index 77be1459..8fbe8f66 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRequester.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRequester.java @@ -7,11 +7,14 @@ import static pl.jakubweg.SponsorBlockUtils.videoHasSegments; import static pl.jakubweg.StringRef.str; import android.content.Context; +import android.content.SharedPreferences; import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.widget.Toast; +import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; + import org.json.JSONArray; import org.json.JSONObject; @@ -20,6 +23,7 @@ import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.concurrent.TimeUnit; import fi.vanced.utils.requests.Requester; import fi.vanced.utils.requests.Route; @@ -40,6 +44,7 @@ public class SBRequester { try { HttpURLConnection connection = getConnectionFromRoute(SBRoutes.GET_SEGMENTS, videoId, SponsorBlockSettings.sponsorBlockUrlCategories); int responseCode = connection.getResponseCode(); + runVipCheck(); if (responseCode == 200) { JSONArray responseArray = Requester.getJSONArray(connection); @@ -56,10 +61,11 @@ public class SBRequester { String category = obj.getString("category"); String uuid = obj.getString("UUID"); + boolean locked = obj.getInt("locked") == 1; SponsorBlockSettings.SegmentInfo segmentCategory = SponsorBlockSettings.SegmentInfo.byCategoryKey(category); if (segmentCategory != null && segmentCategory.behaviour.showOnTimeBar) { - SponsorSegment sponsorSegment = new SponsorSegment(start, end, segmentCategory, uuid); + SponsorSegment sponsorSegment = new SponsorSegment(start, end, segmentCategory, uuid, locked); segments.add(sponsorSegment); } } @@ -197,6 +203,27 @@ public class SBRequester { }).start(); } + public static void runVipCheck() { + long now = System.currentTimeMillis(); + if (now < (SponsorBlockSettings.lastVipCheck + TimeUnit.DAYS.toMillis(3))) { + return; + } + try { + JSONObject json = getJSONObject(SBRoutes.IS_USER_VIP, SponsorBlockSettings.uuid); + boolean vip = json.getBoolean("vip"); + SponsorBlockSettings.vip = vip; + SponsorBlockSettings.lastVipCheck = now; + + SharedPreferences.Editor edit = SponsorBlockSettings.getPreferences(YouTubeTikTokRoot_Application.getAppContext()).edit(); + edit.putString(SponsorBlockSettings.PREFERENCES_KEY_LAST_VIP_CHECK, String.valueOf(now)); + edit.putBoolean(SponsorBlockSettings.PREFERENCES_KEY_IS_VIP, vip); + edit.apply(); + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + // helpers private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException { diff --git a/app/src/main/java/pl/jakubweg/requests/SBRoutes.java b/app/src/main/java/pl/jakubweg/requests/SBRoutes.java index f904363c..ab0acdad 100644 --- a/app/src/main/java/pl/jakubweg/requests/SBRoutes.java +++ b/app/src/main/java/pl/jakubweg/requests/SBRoutes.java @@ -6,13 +6,14 @@ import static fi.vanced.utils.requests.Route.Method.POST; import fi.vanced.utils.requests.Route; public class SBRoutes { - public static final Route GET_SEGMENTS = new Route(GET, "skipSegments?videoID={video_id}&categories={categories}"); - public static final Route VIEWED_SEGMENT = new Route(POST, "viewedVideoSponsorTime?UUID={segment_id}"); - public static final Route GET_USER_STATS = new Route(GET, "userInfo?userID={user_id}&values=[\"userName\", \"minutesSaved\", \"segmentCount\", \"viewCount\"]"); - public static final Route CHANGE_USERNAME = new Route(POST, "setUsername?userID={user_id}&username={username}"); - public static final Route SUBMIT_SEGMENTS = new Route(POST, "skipSegments?videoID={video_id}&userID={user_id}&startTime={start_time}&endTime={end_time}&category={category}&videoDuration={duration}"); - public static final Route VOTE_ON_SEGMENT_QUALITY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&type={type}"); - public static final Route VOTE_ON_SEGMENT_CATEGORY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&category={category}"); + public static final Route IS_USER_VIP = new Route(GET, "isUserVIP?userID={user_id}"); + public static final Route GET_SEGMENTS = new Route(GET, "skipSegments?videoID={video_id}&categories={categories}"); + public static final Route VIEWED_SEGMENT = new Route(POST, "viewedVideoSponsorTime?UUID={segment_id}"); + public static final Route GET_USER_STATS = new Route(GET, "userInfo?userID={user_id}&values=[\"userName\", \"minutesSaved\", \"segmentCount\", \"viewCount\"]"); + public static final Route CHANGE_USERNAME = new Route(POST, "setUsername?userID={user_id}&username={username}"); + public static final Route SUBMIT_SEGMENTS = new Route(POST, "skipSegments?videoID={video_id}&userID={user_id}&startTime={start_time}&endTime={end_time}&category={category}&videoDuration={duration}"); + public static final Route VOTE_ON_SEGMENT_QUALITY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&type={type}"); + public static final Route VOTE_ON_SEGMENT_CATEGORY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&category={category}"); private SBRoutes() {} } \ No newline at end of file From 9a45757824c57bd9d3cd8f903e19dfcd84648307 Mon Sep 17 00:00:00 2001 From: caneleex Date: Mon, 7 Feb 2022 16:29:42 +0100 Subject: [PATCH 42/46] import/export api url --- .../SponsorBlockPreferenceFragment.java | 30 +++++++++---------- .../pl/jakubweg/SponsorBlockSettings.java | 3 +- .../java/pl/jakubweg/SponsorBlockUtils.java | 17 +++++++++++ 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java index 79eb4ef9..7fd0be55 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockPreferenceFragment.java @@ -342,21 +342,6 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement preferencesToDisableWhenSBDisabled.add(preference); } - { - EditTextPreference preference = new EditTextPreference(context); - Context applicationContext = context.getApplicationContext(); - - preference.setTitle(str("settings_ie")); - preference.setSummary(str("settings_ie_sum")); - preference.setText(SponsorBlockUtils.exportSettings(applicationContext)); - preference.setOnPreferenceChangeListener((preference1, newValue) -> { - SponsorBlockUtils.importSettings((String) newValue, applicationContext); - return false; - }); - screen.addPreference(preference); - preferencesToDisableWhenSBDisabled.add(preference); - } - { Preference preference = new Preference(context); String title = str("general_api_url"); @@ -381,6 +366,21 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement screen.addPreference(preference); preferencesToDisableWhenSBDisabled.add(preference); } + + { + EditTextPreference preference = new EditTextPreference(context); + Context applicationContext = context.getApplicationContext(); + + preference.setTitle(str("settings_ie")); + preference.setSummary(str("settings_ie_sum")); + preference.setText(SponsorBlockUtils.exportSettings(applicationContext)); + preference.setOnPreferenceChangeListener((preference1, newValue) -> { + SponsorBlockUtils.importSettings((String) newValue, applicationContext); + return false; + }); + screen.addPreference(preference); + preferencesToDisableWhenSBDisabled.add(preference); + } } private static class APIURLChangeListener implements DialogInterface.OnClickListener { diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java index 0b0b4384..3a8cd93d 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockSettings.java @@ -38,7 +38,8 @@ public class SponsorBlockSettings { public static final String PREFERENCES_KEY_API_URL = "sb-api-url"; public static final SegmentBehaviour DefaultBehaviour = SegmentBehaviour.SKIP_AUTOMATICALLY; - public static final String DEFAULT_API_URL = "https://sponsor.ajay.app/api/"; + public static final String DEFAULT_SERVER_URL = "https://sponsor.ajay.app"; + public static final String DEFAULT_API_URL = DEFAULT_SERVER_URL + "/api/"; public static boolean isSponsorBlockEnabled = false; public static boolean seenGuidelinesPopup = false; diff --git a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java index 3fa0d9c2..4d2e7171 100644 --- a/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java +++ b/app/src/main/java/pl/jakubweg/SponsorBlockUtils.java @@ -10,6 +10,9 @@ import static pl.jakubweg.PlayerController.getLastKnownVideoTime; import static pl.jakubweg.PlayerController.sponsorSegmentsOfCurrentVideo; import static pl.jakubweg.SponsorBlockPreferenceFragment.FORMATTER; import static pl.jakubweg.SponsorBlockPreferenceFragment.SAVED_TEMPLATE; +import static pl.jakubweg.SponsorBlockSettings.DEFAULT_API_URL; +import static pl.jakubweg.SponsorBlockSettings.DEFAULT_SERVER_URL; +import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_API_URL; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_COUNT_SKIPS; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_IS_VIP; @@ -19,6 +22,7 @@ import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TIME_WITHOUT import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_UUID; import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_NAME; +import static pl.jakubweg.SponsorBlockSettings.apiUrl; import static pl.jakubweg.SponsorBlockSettings.countSkips; import static pl.jakubweg.SponsorBlockSettings.getPreferences; import static pl.jakubweg.SponsorBlockSettings.isSponsorBlockEnabled; @@ -571,6 +575,13 @@ public abstract class SponsorBlockUtils { editor.putString(PREFERENCES_KEY_MIN_DURATION, settingsJson.getString("minDuration")); editor.putString(PREFERENCES_KEY_UUID, settingsJson.getString("userID")); editor.putString(PREFERENCES_KEY_LAST_VIP_CHECK, settingsJson.getString("lastIsVipUpdate")); + + String serverAddress = settingsJson.getString("serverAddress"); + if (serverAddress.equalsIgnoreCase(DEFAULT_SERVER_URL)) { + serverAddress = DEFAULT_API_URL; + } + editor.putString(PREFERENCES_KEY_API_URL, serverAddress); + editor.apply(); Toast.makeText(context, str("settings_import_successful"), Toast.LENGTH_SHORT).show(); @@ -613,6 +624,12 @@ public abstract class SponsorBlockUtils { json.put("isVip", vip); json.put("lastIsVipUpdate", lastVipCheck); + String apiAddress = apiUrl; + if (apiAddress.equalsIgnoreCase(DEFAULT_API_URL)) { + apiAddress = DEFAULT_SERVER_URL; + } + json.put("serverAddress", apiAddress); + return json.toString(); } catch (Exception ex) { From 2318490793ca249c6988434b28b1f32d494b1b6a Mon Sep 17 00:00:00 2001 From: caneleex Date: Tue, 8 Feb 2022 17:55:22 +0100 Subject: [PATCH 43/46] rename filler --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f5b5e63c..c6e5edac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -177,7 +177,7 @@ Similar to "sponsor" except for unpaid or self promotion. This includes sections about merchandise, donations, or information about who they collaborated with Music: Non-Music Section Only for use in music videos. This includes introductions or outros in music videos - Filler Tangent + Filler Tangent/Jokes Tangential scenes added only for filler or humor that are not required to understand the main content of the video. This should not include segments providing context or background details Skipped a sponsor segment Skipped sponsor From 1e115e1d6936810ace7c73d6231eb7caa009cb98 Mon Sep 17 00:00:00 2001 From: caneleex Date: Fri, 11 Feb 2022 20:02:16 +0100 Subject: [PATCH 44/46] format dislikes from the json --- .../vanced/libraries/youtube/ryd/requests/RYDRequester.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java b/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java index 9777f335..ed588b3d 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/ryd/requests/RYDRequester.java @@ -37,13 +37,14 @@ public class RYDRequester { connection.setConnectTimeout(5 * 1000); if (connection.getResponseCode() == 200) { JSONObject json = getJSONObject(connection); - dislikeCount = json.getInt("dislikes"); + int dislikes = json.getInt("dislikes"); + dislikeCount = dislikes; if (debug) { Log.d(TAG, "dislikes fetched - " + dislikeCount); } // Set the dislikes - new Handler(Looper.getMainLooper()).post(() -> ReturnYouTubeDislikes.trySetDislikes(ReturnYouTubeDislikes.formatDislikes(dislikeCount))); + new Handler(Looper.getMainLooper()).post(() -> ReturnYouTubeDislikes.trySetDislikes(ReturnYouTubeDislikes.formatDislikes(dislikes))); } else if (debug) { Log.d(TAG, "dislikes fetch response was " + connection.getResponseCode()); From dfb25653b209dd156976081a7fac82afd582ad5c Mon Sep 17 00:00:00 2001 From: KevinX8 Date: Wed, 16 Feb 2022 01:05:23 +0000 Subject: [PATCH 45/46] Updated deps + fixed player buttons and dislike formatting --- app/build.gradle | 7 +++---- .../youtube/player/VideoInformation.java | 12 +++++++----- .../youtube/ryd/ReturnYouTubeDislikes.java | 16 +++++++--------- app/src/main/res/values/strings.xml | 4 ++-- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f6f39a83..f13687c7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 30 - buildToolsVersion "30.0.2" + compileSdkVersion 32 defaultConfig { applicationId "vanced.integrations" minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 31 versionCode 1 versionName "1.0" multiDexEnabled false @@ -33,6 +32,6 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.2.0' + implementation 'androidx.annotation:annotation:1.3.0' } diff --git a/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java index e11d5414..b4ee0138 100644 --- a/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java +++ b/app/src/main/java/fi/vanced/libraries/youtube/player/VideoInformation.java @@ -24,7 +24,7 @@ public class VideoInformation { if (debug) { Log.d(TAG, "setCurrentVideoId - new id was null - currentVideoId was" + currentVideoId); } - clearInformation(); + clearInformation(true); return; } @@ -53,15 +53,17 @@ public class VideoInformation { // Call hook in the YT code when the video ends public static void videoEnded() { saveTempInformation(); - clearInformation(); + clearInformation(false); } // Information is cleared once a video ends // It's cleared because the setCurrentVideoId isn't called for Shorts // so Shorts would otherwise use the information from the last watched video - private static void clearInformation() { - currentVideoId = null; - dislikeCount = null; + private static void clearInformation(boolean full) { + if (full) { + currentVideoId = null; + dislikeCount = null; + } channelName = null; } 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 d8f2a1ee..31724b08 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 @@ -17,6 +17,7 @@ import android.widget.TextView; import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application; import java.util.Locale; +import java.util.Objects; import fi.vanced.libraries.youtube.ryd.requests.RYDRequester; import fi.vanced.utils.SharedPrefUtils; @@ -36,7 +37,7 @@ public class ReturnYouTubeDislikes { static { Context context = YouTubeTikTokRoot_Application.getAppContext(); - isEnabled = SharedPrefUtils.getBoolean(context, PREFERENCES_NAME, PREFERENCES_KEY_RYD_ENABLED, false); + isEnabled = SharedPrefUtils.getBoolean(Objects.requireNonNull(context), PREFERENCES_NAME, PREFERENCES_KEY_RYD_ENABLED, false); if (isEnabled) { registration = new Registration(context); voting = new Voting(context, registration); @@ -51,9 +52,6 @@ public class ReturnYouTubeDislikes { locale, CompactDecimalFormat.CompactStyle.SHORT ); - compactNumberFormatter.setMaximumIntegerDigits(3); - compactNumberFormatter.setMaximumFractionDigits(1); - compactNumberFormatter.setSignificantDigitsUsed(false); } } @@ -199,7 +197,8 @@ public class ReturnYouTubeDislikes { } private static void handleOnClick(View view, boolean previousState) { - if (!isEnabled) return; + Context context = YouTubeTikTokRoot_Application.getAppContext(); + if (!isEnabled || SharedPrefUtils.getBoolean(Objects.requireNonNull(context),"youtube","user_signed_out",true)) return; try { String tag = (String) view.getTag(); @@ -210,7 +209,7 @@ public class ReturnYouTubeDislikes { // If active status was removed, vote should be none if (previousState) { votingValue = 0; } - if (tag == "like") { + if (tag.equals("like")) { dislikeActive = false; // Like was activated @@ -218,9 +217,9 @@ public class ReturnYouTubeDislikes { else { likeActive = false; } // Like was activated and dislike was previously activated - if (!previousState && dislikeActive) { dislikeCount--; trySetDislikes(formatDislikes(dislikeCount)); } + if (!previousState) { dislikeCount--; trySetDislikes(formatDislikes(dislikeCount)); } } - else if (tag == "dislike") { + else if (tag.equals("dislike")) { likeActive = false; // Dislike was activated @@ -274,7 +273,6 @@ public class ReturnYouTubeDislikes { } catch (Exception ex) { Log.e(TAG, "Failed to send vote", ex); - return; } }); _votingThread.start(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c6e5edac..4c8a11c2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -369,11 +369,11 @@ Failed to retrieve channel details, received code %d Hidden In player - Under player + Under player (ALPHA) Both Return YouTube Dislike settings Uses the RYD API - Enable RYD + Enable RYD (ALPHA) 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 diff --git a/build.gradle b/build.gradle index 4a22cbf3..69833568 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.4' + classpath 'com.android.tools.build:gradle:7.1.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e7c8788f..25999d56 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Jun 07 19:51:48 CEST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 5e78704276538b636de3ff9c9917193a84573551 Mon Sep 17 00:00:00 2001 From: KevinX8 Date: Wed, 16 Feb 2022 01:17:55 +0000 Subject: [PATCH 46/46] fix previous dislike logic and change MinSDK --- app/build.gradle | 2 +- .../vanced/libraries/youtube/ryd/ReturnYouTubeDislikes.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f13687c7..ac1034e4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ android { defaultConfig { applicationId "vanced.integrations" - minSdkVersion 21 + minSdkVersion 23 targetSdkVersion 31 versionCode 1 versionName "1.0" 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 31724b08..fde142ad 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 @@ -210,14 +210,14 @@ public class ReturnYouTubeDislikes { // If active status was removed, vote should be none if (previousState) { votingValue = 0; } if (tag.equals("like")) { - dislikeActive = false; // Like was activated if (!previousState) { votingValue = 1; likeActive = true; } else { likeActive = false; } // Like was activated and dislike was previously activated - if (!previousState) { dislikeCount--; trySetDislikes(formatDislikes(dislikeCount)); } + if (!previousState && dislikeActive) { dislikeCount--; trySetDislikes(formatDislikes(dislikeCount)); } + dislikeActive = false; } else if (tag.equals("dislike")) { likeActive = false;