From 0bdd0ebbcb1966db6dbf3636151936878fe20d28 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 24 Apr 2024 13:55:44 +0400 Subject: [PATCH 1/3] fix(YouTube): Remove obsolete code --- .../patches/HDRAutoBrightnessPatch.java | 42 --- .../patches/HideEmailAddressPatch.java | 17 -- .../announcements/AnnouncementsPatch.java | 17 -- .../patches/spoof/SpoofSignaturePatch.java | 241 ------------------ .../patches/spoof/StoryboardRenderer.java | 47 ---- .../patches/spoof/requests/PlayerRoutes.java | 101 -------- .../requests/StoryboardRendererRequester.java | 160 ------------ .../youtube/settings/Settings.java | 16 -- 8 files changed, 641 deletions(-) delete mode 100644 app/src/main/java/app/revanced/integrations/youtube/patches/HDRAutoBrightnessPatch.java delete mode 100644 app/src/main/java/app/revanced/integrations/youtube/patches/HideEmailAddressPatch.java delete mode 100644 app/src/main/java/app/revanced/integrations/youtube/patches/spoof/SpoofSignaturePatch.java delete mode 100644 app/src/main/java/app/revanced/integrations/youtube/patches/spoof/StoryboardRenderer.java delete mode 100644 app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/PlayerRoutes.java delete mode 100644 app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StoryboardRendererRequester.java diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/HDRAutoBrightnessPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/HDRAutoBrightnessPatch.java deleted file mode 100644 index 3c644308..00000000 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/HDRAutoBrightnessPatch.java +++ /dev/null @@ -1,42 +0,0 @@ -package app.revanced.integrations.youtube.patches; - -import android.view.WindowManager; - -import app.revanced.integrations.youtube.settings.Settings; -import app.revanced.integrations.youtube.swipecontrols.SwipeControlsHostActivity; - -/** - * Patch class for 'hdr-auto-brightness' patch. - * - * Edit: This patch no longer does anything, as YT already uses BRIGHTNESS_OVERRIDE_NONE - * as the default brightness level. The hooked code was also removed from YT 19.09+ as well. - */ -@Deprecated -@SuppressWarnings("unused") -public class HDRAutoBrightnessPatch { - /** - * get brightness override for HDR brightness - * - * @param original brightness youtube would normally set - * @return brightness to set on HRD video - */ - public static float getHDRBrightness(float original) { - // do nothing if disabled - if (!Settings.HDR_AUTO_BRIGHTNESS.get()) { - return original; - } - - // override with brightness set by swipe-controls - // only when swipe-controls is active and has overridden the brightness - final SwipeControlsHostActivity swipeControlsHost = SwipeControlsHostActivity.getCurrentHost().get(); - if (swipeControlsHost != null - && swipeControlsHost.getScreen() != null - && swipeControlsHost.getConfig().getEnableBrightnessControl() - && !swipeControlsHost.getScreen().isDefaultBrightness()) { - return swipeControlsHost.getScreen().getRawScreenBrightness(); - } - - // otherwise, set the brightness to auto - return WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE; - } -} diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/HideEmailAddressPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/HideEmailAddressPatch.java deleted file mode 100644 index 3cbd5b19..00000000 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/HideEmailAddressPatch.java +++ /dev/null @@ -1,17 +0,0 @@ -package app.revanced.integrations.youtube.patches; - -import app.revanced.integrations.youtube.settings.Settings; - -/** - * Patch is obsolete and will be deleted in a future release - */ -@SuppressWarnings("unused") -@Deprecated() -public class HideEmailAddressPatch { - //Used by app.revanced.patches.youtube.layout.personalinformation.patch.HideEmailAddressPatch - public static int hideEmailAddress(int originalValue) { - if (Settings.HIDE_EMAIL_ADDRESS.get()) - return 8; - return originalValue; - } -} diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/announcements/AnnouncementsPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/announcements/AnnouncementsPatch.java index eec599ec..5afba471 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/announcements/AnnouncementsPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/announcements/AnnouncementsPatch.java @@ -84,27 +84,10 @@ public final class AnnouncementsPatch { message = jsonString; } - // TODO: Remove this migration code after a few months. - if (!Settings.DEPRECATED_ANNOUNCEMENT_LAST_HASH.isSetToDefault()){ - final byte[] hashBytes = MessageDigest - .getInstance("SHA-256") - .digest(jsonString.getBytes(StandardCharsets.UTF_8)); - - final var hash = java.util.Base64.getEncoder().encodeToString(hashBytes); - - // Migrate to saving the id instead of the hash. - if (hash.equals(Settings.DEPRECATED_ANNOUNCEMENT_LAST_HASH.get())) { - Settings.ANNOUNCEMENT_LAST_ID.save(id); - } - - Settings.DEPRECATED_ANNOUNCEMENT_LAST_HASH.resetToDefault(); - } - // Do not show the announcement, if the last announcement id is the same as the current one. if (Settings.ANNOUNCEMENT_LAST_ID.get() == id) return; - int finalId = id; final var finalTitle = title; final var finalMessage = Html.fromHtml(message, FROM_HTML_MODE_COMPACT); diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/SpoofSignaturePatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/SpoofSignaturePatch.java deleted file mode 100644 index 6573467a..00000000 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/SpoofSignaturePatch.java +++ /dev/null @@ -1,241 +0,0 @@ -package app.revanced.integrations.youtube.patches.spoof; - -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import androidx.annotation.Nullable; -import app.revanced.integrations.shared.Logger; -import app.revanced.integrations.shared.Utils; -import app.revanced.integrations.youtube.patches.VideoInformation; -import app.revanced.integrations.youtube.settings.Settings; -import app.revanced.integrations.youtube.shared.PlayerType; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static app.revanced.integrations.shared.Utils.containsAny; -import static app.revanced.integrations.youtube.patches.spoof.requests.StoryboardRendererRequester.getStoryboardRenderer; - -/** @noinspection unused*/ -@Deprecated -public class SpoofSignaturePatch { - /** - * Parameter (also used by - * yt-dlp) - * to fix playback issues. - */ - private static final String INCOGNITO_PARAMETERS = "CgIQBg=="; - - /** - * Parameters used when playing clips. - */ - private static final String CLIPS_PARAMETERS = "kAIB"; - - /** - * Parameters causing playback issues. - */ - private static final String[] AUTOPLAY_PARAMETERS = { - "YAHI", // Autoplay in feed. - "SAFg" // Autoplay in scrim. - }; - - /** - * Parameter used for autoplay in scrim. - * Prepend this parameter to mute video playback (for autoplay in feed). - */ - private static final String SCRIM_PARAMETER = "SAFgAXgB"; - - /** - * Last video id loaded. Used to prevent reloading the same spec multiple times. - */ - @Nullable - private static volatile String lastPlayerResponseVideoId; - - @Nullable - private static volatile Future rendererFuture; - - private static volatile boolean useOriginalStoryboardRenderer; - - private static volatile boolean isPlayingShorts; - - @Nullable - private static StoryboardRenderer getRenderer(boolean waitForCompletion) { - Future future = rendererFuture; - if (future != null) { - try { - if (waitForCompletion || future.isDone()) { - return future.get(20000, TimeUnit.MILLISECONDS); // Any arbitrarily large timeout. - } // else, return null. - } catch (TimeoutException ex) { - Logger.printDebug(() -> "Could not get renderer (get timed out)"); - } catch (ExecutionException | InterruptedException ex) { - // Should never happen. - Logger.printException(() -> "Could not get renderer", ex); - } - } - return null; - } - - /** - * Injection point. - * - * Called off the main thread, and called multiple times for each video. - * - * @param parameters Original protobuf parameter value. - */ - public static String spoofParameter(String parameters, boolean isShortAndOpeningOrPlaying) { - try { - Logger.printDebug(() -> "Original protobuf parameter value: " + parameters); - - if (parameters == null || !Settings.SPOOF_SIGNATURE.get()) { - return parameters; - } - - // Clip's player parameters contain a lot of information (e.g. video start and end time or whether it loops) - // For this reason, the player parameters of a clip are usually very long (150~300 characters). - // Clips are 60 seconds or less in length, so no spoofing. - //noinspection AssignmentUsedAsCondition - if (useOriginalStoryboardRenderer = parameters.length() > 150 || parameters.startsWith(CLIPS_PARAMETERS)) { - return parameters; - } - - // Shorts do not need to be spoofed. - //noinspection AssignmentUsedAsCondition - if (useOriginalStoryboardRenderer = VideoInformation.playerParametersAreShort(parameters)) { - isPlayingShorts = true; - return parameters; - } - isPlayingShorts = false; - - boolean isPlayingFeed = PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL - && containsAny(parameters, AUTOPLAY_PARAMETERS); - if (isPlayingFeed) { - //noinspection AssignmentUsedAsCondition - if (useOriginalStoryboardRenderer = !Settings.SPOOF_SIGNATURE_IN_FEED.get()) { - // Don't spoof the feed video playback. This will cause video playback issues, - // but only if user continues watching for more than 1 minute. - return parameters; - } - // Spoof the feed video. Video will show up in watch history and video subtitles are missing. - fetchStoryboardRenderer(); - return SCRIM_PARAMETER + INCOGNITO_PARAMETERS; - } - - fetchStoryboardRenderer(); - } catch (Exception ex) { - Logger.printException(() -> "spoofParameter failure", ex); - } - return INCOGNITO_PARAMETERS; - } - - private static void fetchStoryboardRenderer() { - if (!Settings.SPOOF_STORYBOARD_RENDERER.get()) { - lastPlayerResponseVideoId = null; - rendererFuture = null; - return; - } - String videoId = VideoInformation.getPlayerResponseVideoId(); - if (!videoId.equals(lastPlayerResponseVideoId)) { - rendererFuture = Utils.submitOnBackgroundThread(() -> getStoryboardRenderer(videoId)); - lastPlayerResponseVideoId = videoId; - } - // Block until the renderer fetch completes. - // This is desired because if this returns without finishing the fetch - // then video will start playback but the storyboard is not ready yet. - getRenderer(true); - } - - private static String getStoryboardRendererSpec(String originalStoryboardRendererSpec, - boolean returnNullIfLiveStream) { - if (Settings.SPOOF_SIGNATURE.get() && !useOriginalStoryboardRenderer) { - StoryboardRenderer renderer = getRenderer(false); - if (renderer != null) { - if (returnNullIfLiveStream && renderer.isLiveStream()) { - return null; - } - String spec = renderer.getSpec(); - if (spec != null) { - return spec; - } - } - } - - return originalStoryboardRendererSpec; - } - - /** - * Injection point. - * Called from background threads and from the main thread. - */ - @Nullable - public static String getStoryboardRendererSpec(String originalStoryboardRendererSpec) { - return getStoryboardRendererSpec(originalStoryboardRendererSpec, false); - } - - /** - * Injection point. - * Uses additional check to handle live streams. - * Called from background threads and from the main thread. - */ - @Nullable - public static String getStoryboardDecoderRendererSpec(String originalStoryboardRendererSpec) { - return getStoryboardRendererSpec(originalStoryboardRendererSpec, true); - } - - /** - * Injection point. - */ - public static int getRecommendedLevel(int originalLevel) { - if (Settings.SPOOF_SIGNATURE.get() && !useOriginalStoryboardRenderer) { - StoryboardRenderer renderer = getRenderer(false); - if (renderer != null) { - Integer recommendedLevel = renderer.getRecommendedLevel(); - if (recommendedLevel != null) return recommendedLevel; - } - } - - return originalLevel; - } - - /** - * Injection point. Forces seekbar to be shown for paid videos or - * if {@link Settings#SPOOF_STORYBOARD_RENDERER} is not enabled. - */ - public static boolean getSeekbarThumbnailOverrideValue() { - if (!Settings.SPOOF_SIGNATURE.get()) { - return false; - } - StoryboardRenderer renderer = getRenderer(false); - if (renderer == null) { - // Spoof storyboard renderer is turned off, - // video is paid, or the storyboard fetch timed out. - // Show empty thumbnails so the seek time and chapters still show up. - return true; - } - return renderer.getSpec() != null; - } - - /** - * Injection point. - * - * @param view seekbar thumbnail view. Includes both shorts and regular videos. - */ - public static void seekbarImageViewCreated(ImageView view) { - try { - if (!Settings.SPOOF_SIGNATURE.get() - || Settings.SPOOF_STORYBOARD_RENDERER.get()) { - return; - } - if (isPlayingShorts) return; - - view.setVisibility(View.GONE); - // Also hide the border around the thumbnail (otherwise a 1 pixel wide bordered frame is visible). - ViewGroup parentLayout = (ViewGroup) view.getParent(); - parentLayout.setPadding(0, 0, 0, 0); - } catch (Exception ex) { - Logger.printException(() -> "seekbarImageViewCreated failure", ex); - } - } -} diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/StoryboardRenderer.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/StoryboardRenderer.java deleted file mode 100644 index 8e337297..00000000 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/StoryboardRenderer.java +++ /dev/null @@ -1,47 +0,0 @@ -package app.revanced.integrations.youtube.patches.spoof; - -import androidx.annotation.Nullable; - -import org.jetbrains.annotations.NotNull; - -@Deprecated -public final class StoryboardRenderer { - @Nullable - private final String spec; - private final boolean isLiveStream; - @Nullable - private final Integer recommendedLevel; - - public StoryboardRenderer(@Nullable String spec, boolean isLiveStream, @Nullable Integer recommendedLevel) { - this.spec = spec; - this.isLiveStream = isLiveStream; - this.recommendedLevel = recommendedLevel; - } - - @Nullable - public String getSpec() { - return spec; - } - - public boolean isLiveStream() { - return isLiveStream; - } - - /** - * @return Recommended image quality level, or NULL if no recommendation exists. - */ - @Nullable - public Integer getRecommendedLevel() { - return recommendedLevel; - } - - @NotNull - @Override - public String toString() { - return "StoryboardRenderer{" + - "isLiveStream=" + isLiveStream + - ", spec='" + spec + '\'' + - ", recommendedLevel=" + recommendedLevel + - '}'; - } -} diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/PlayerRoutes.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/PlayerRoutes.java deleted file mode 100644 index 7909387f..00000000 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/PlayerRoutes.java +++ /dev/null @@ -1,101 +0,0 @@ -package app.revanced.integrations.youtube.patches.spoof.requests; - -import app.revanced.integrations.youtube.requests.Requester; -import app.revanced.integrations.youtube.requests.Route; -import app.revanced.integrations.shared.Logger; -import app.revanced.integrations.shared.Utils; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.net.HttpURLConnection; - -@Deprecated -final class PlayerRoutes { - private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1/"; - static final Route.CompiledRoute GET_STORYBOARD_SPEC_RENDERER = new Route( - Route.Method.POST, - "player" + - "?fields=storyboards.playerStoryboardSpecRenderer," + - "storyboards.playerLiveStoryboardSpecRenderer," + - "playabilityStatus.status" - ).compile(); - - static final String ANDROID_INNER_TUBE_BODY; - static final String TV_EMBED_INNER_TUBE_BODY; - - /** - * TCP connection and HTTP read timeout - */ - private static final int CONNECTION_TIMEOUT_MILLISECONDS = 4 * 1000; // 4 Seconds. - - static { - JSONObject innerTubeBody = new JSONObject(); - - try { - JSONObject context = new JSONObject(); - - JSONObject client = new JSONObject(); - client.put("clientName", "ANDROID"); - client.put("clientVersion", Utils.getAppVersionName()); - client.put("androidSdkVersion", 34); - - context.put("client", client); - - innerTubeBody.put("context", context); - innerTubeBody.put("videoId", "%s"); - } catch (JSONException e) { - Logger.printException(() -> "Failed to create innerTubeBody", e); - } - - ANDROID_INNER_TUBE_BODY = innerTubeBody.toString(); - - JSONObject tvEmbedInnerTubeBody = new JSONObject(); - - try { - JSONObject context = new JSONObject(); - - JSONObject client = new JSONObject(); - client.put("clientName", "TVHTML5_SIMPLY_EMBEDDED_PLAYER"); - client.put("clientVersion", "2.0"); - client.put("platform", "TV"); - client.put("clientScreen", "EMBED"); - - JSONObject thirdParty = new JSONObject(); - thirdParty.put("embedUrl", "https://www.youtube.com/watch?v=%s"); - - context.put("thirdParty", thirdParty); - context.put("client", client); - - tvEmbedInnerTubeBody.put("context", context); - tvEmbedInnerTubeBody.put("videoId", "%s"); - } catch (JSONException e) { - Logger.printException(() -> "Failed to create tvEmbedInnerTubeBody", e); - } - - TV_EMBED_INNER_TUBE_BODY = tvEmbedInnerTubeBody.toString(); - } - - private PlayerRoutes() { - } - - /** @noinspection SameParameterValue*/ - static HttpURLConnection getPlayerResponseConnectionFromRoute(Route.CompiledRoute route) throws IOException { - var connection = Requester.getConnectionFromCompiledRoute(YT_API_URL, route); - - connection.setRequestProperty( - "User-Agent", "com.google.android.youtube/" + - Utils.getAppVersionName() + - " (Linux; U; Android 12; GB) gzip" - ); - connection.setRequestProperty("X-Goog-Api-Format-Version", "2"); - connection.setRequestProperty("Content-Type", "application/json"); - - connection.setUseCaches(false); - connection.setDoOutput(true); - - connection.setConnectTimeout(CONNECTION_TIMEOUT_MILLISECONDS); - connection.setReadTimeout(CONNECTION_TIMEOUT_MILLISECONDS); - return connection; - } -} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StoryboardRendererRequester.java b/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StoryboardRendererRequester.java deleted file mode 100644 index 31d8c3ef..00000000 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/spoof/requests/StoryboardRendererRequester.java +++ /dev/null @@ -1,160 +0,0 @@ -package app.revanced.integrations.youtube.patches.spoof.requests; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import app.revanced.integrations.shared.settings.BaseSettings; -import app.revanced.integrations.youtube.patches.spoof.StoryboardRenderer; -import app.revanced.integrations.youtube.requests.Requester; -import app.revanced.integrations.shared.Logger; -import app.revanced.integrations.shared.Utils; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.SocketTimeoutException; -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -import static app.revanced.integrations.shared.StringRef.str; -import static app.revanced.integrations.youtube.patches.spoof.requests.PlayerRoutes.*; - -@Deprecated -public class StoryboardRendererRequester { - - /** - * For videos that have no storyboard. - * Usually for low resolution videos as old as YouTube itself. - * Does not include paid videos where the renderer fetch fails. - */ - private static final StoryboardRenderer emptyStoryboard - = new StoryboardRenderer(null, false, null); - - private StoryboardRendererRequester() { - } - - private static void randomlyWaitIfLocallyDebugging() { - final boolean randomlyWait = false; // Enable to simulate slow connection responses. - if (randomlyWait) { - final long maximumTimeToRandomlyWait = 10000; - Utils.doNothingForDuration(maximumTimeToRandomlyWait); - } - } - - private static void handleConnectionError(@NonNull String toastMessage, @Nullable Exception ex, - boolean showToastOnIOException) { - if (showToastOnIOException) Utils.showToastShort(toastMessage); - Logger.printInfo(() -> toastMessage, ex); - } - - @Nullable - private static JSONObject fetchPlayerResponse(@NonNull String requestBody, boolean showToastOnIOException) { - final long startTime = System.currentTimeMillis(); - try { - Utils.verifyOffMainThread(); - Objects.requireNonNull(requestBody); - - final byte[] innerTubeBody = requestBody.getBytes(StandardCharsets.UTF_8); - - HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STORYBOARD_SPEC_RENDERER); - connection.getOutputStream().write(innerTubeBody, 0, innerTubeBody.length); - - final int responseCode = connection.getResponseCode(); - randomlyWaitIfLocallyDebugging(); - if (responseCode == 200) return Requester.parseJSONObject(connection); - - // Always show a toast for this, as a non 200 response means something is broken. - // Not a normal code path and should not be reached, so no translations are needed. - handleConnectionError("Spoof storyboard not available: " + responseCode, - null, showToastOnIOException || BaseSettings.DEBUG_TOAST_ON_ERROR.get()); - connection.disconnect(); - } catch (SocketTimeoutException ex) { - handleConnectionError(str("revanced_spoof_storyboard_timeout"), ex, showToastOnIOException); - } catch (IOException ex) { - handleConnectionError(str("revanced_spoof_storyboard_io_exception", ex.getMessage()), - ex, showToastOnIOException); - } catch (Exception ex) { - Logger.printException(() -> "Spoof storyboard fetch failed", ex); // Should never happen. - } finally { - Logger.printDebug(() -> "Request took: " + (System.currentTimeMillis() - startTime) + "ms"); - } - - return null; - } - - private static boolean isPlayabilityStatusOk(@NonNull JSONObject playerResponse) { - try { - return playerResponse.getJSONObject("playabilityStatus").getString("status").equals("OK"); - } catch (JSONException e) { - Logger.printDebug(() -> "Failed to get playabilityStatus for response: " + playerResponse); - } - - return false; - } - - /** - * Fetches the storyboardRenderer from the innerTubeBody. - * @param innerTubeBody The innerTubeBody to use to fetch the storyboardRenderer. - * @return StoryboardRenderer or null if playabilityStatus is not OK. - */ - @Nullable - private static StoryboardRenderer getStoryboardRendererUsingBody(@NonNull String innerTubeBody, - boolean showToastOnIOException) { - final JSONObject playerResponse = fetchPlayerResponse(innerTubeBody, showToastOnIOException); - if (playerResponse != null && isPlayabilityStatusOk(playerResponse)) - return getStoryboardRendererUsingResponse(playerResponse); - - return null; - } - - @Nullable - private static StoryboardRenderer getStoryboardRendererUsingResponse(@NonNull JSONObject playerResponse) { - try { - Logger.printDebug(() -> "Parsing response: " + playerResponse); - if (!playerResponse.has("storyboards")) { - Logger.printDebug(() -> "Using empty storyboard"); - return emptyStoryboard; - } - final JSONObject storyboards = playerResponse.getJSONObject("storyboards"); - final boolean isLiveStream = storyboards.has("playerLiveStoryboardSpecRenderer"); - final String storyboardsRendererTag = isLiveStream - ? "playerLiveStoryboardSpecRenderer" - : "playerStoryboardSpecRenderer"; - - final var rendererElement = storyboards.getJSONObject(storyboardsRendererTag); - StoryboardRenderer renderer = new StoryboardRenderer( - rendererElement.getString("spec"), - isLiveStream, - rendererElement.has("recommendedLevel") - ? rendererElement.getInt("recommendedLevel") - : null - ); - - Logger.printDebug(() -> "Fetched: " + renderer); - - return renderer; - } catch (JSONException e) { - Logger.printException(() -> "Failed to get storyboardRenderer", e); - } - - return null; - } - - @Nullable - public static StoryboardRenderer getStoryboardRenderer(@NonNull String videoId) { - Objects.requireNonNull(videoId); - - var renderer = getStoryboardRendererUsingBody( - String.format(ANDROID_INNER_TUBE_BODY, videoId), false); - if (renderer == null) { - Logger.printDebug(() -> videoId + " not available using Android client"); - renderer = getStoryboardRendererUsingBody( - String.format(TV_EMBED_INNER_TUBE_BODY, videoId, videoId), true); - if (renderer == null) { - Logger.printDebug(() -> videoId + " not available using TV embedded client"); - } - } - - return renderer; - } -} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java b/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java index c2556984..adcebc64 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java +++ b/app/src/main/java/app/revanced/integrations/youtube/settings/Settings.java @@ -30,8 +30,6 @@ public class Settings extends BaseSettings { public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", 1.0f); public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds", "0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true); - @Deprecated // Patch is obsolete and no longer works with 19.09+ - public static final BooleanSetting HDR_AUTO_BRIGHTNESS = new BooleanSetting("revanced_hdr_auto_brightness", TRUE); // Ads public static final BooleanSetting HIDE_FULLSCREEN_ADS = new BooleanSetting("revanced_hide_fullscreen_ads", TRUE); @@ -83,7 +81,6 @@ public class Settings extends BaseSettings { public static final BooleanSetting HIDE_COMMUNITY_POSTS = new BooleanSetting("revanced_hide_community_posts", FALSE); public static final BooleanSetting HIDE_COMPACT_BANNER = new BooleanSetting("revanced_hide_compact_banner", TRUE); public static final BooleanSetting HIDE_CROWDFUNDING_BOX = new BooleanSetting("revanced_hide_crowdfunding_box", FALSE, true); - @Deprecated public static final BooleanSetting HIDE_EMAIL_ADDRESS = new BooleanSetting("revanced_hide_email_address", FALSE); public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE); public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", TRUE); public static final BooleanSetting HIDE_EXPANDABLE_CHIP = new BooleanSetting("revanced_hide_expandable_chip", TRUE); @@ -99,7 +96,6 @@ public class Settings extends BaseSettings { public static final BooleanSetting HIDE_IMAGE_SHELF = new BooleanSetting("revanced_hide_image_shelf", TRUE); public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", TRUE); public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE); - @Deprecated public static final BooleanSetting HIDE_LOAD_MORE_BUTTON = new BooleanSetting("revanced_hide_load_more_button", TRUE); public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true); public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE); public static final BooleanSetting HIDE_MIX_PLAYLISTS = new BooleanSetting("revanced_hide_mix_playlists", TRUE); @@ -235,8 +231,6 @@ public class Settings extends BaseSettings { "revanced_spoof_device_dimensions_user_dialog_message"); public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE); public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE); - @Deprecated - public static final StringSetting DEPRECATED_ANNOUNCEMENT_LAST_HASH = new StringSetting("revanced_announcement_last_hash", ""); public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1); public static final BooleanSetting REMOVE_TRACKING_QUERY_PARAMETER = new BooleanSetting("revanced_remove_tracking_query_parameter", TRUE); @@ -247,14 +241,6 @@ public class Settings extends BaseSettings { */ public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, parent(BaseSettings.DEBUG)); - // Old deprecated signature spoofing - @Deprecated public static final BooleanSetting SPOOF_SIGNATURE = new BooleanSetting("revanced_spoof_signature_verification_enabled", TRUE, true, false, - "revanced_spoof_signature_verification_enabled_user_dialog_message", null); - @Deprecated public static final BooleanSetting SPOOF_SIGNATURE_IN_FEED = new BooleanSetting("revanced_spoof_signature_in_feed_enabled", FALSE, false, false, null, - parent(SPOOF_SIGNATURE)); - @Deprecated public static final BooleanSetting SPOOF_STORYBOARD_RENDERER = new BooleanSetting("revanced_spoof_storyboard", TRUE, true, false, null, - parent(SPOOF_SIGNATURE)); - // Swipe controls public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", TRUE); public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", TRUE); @@ -396,8 +382,6 @@ public class Settings extends BaseSettings { // Remove any previously saved announcement consumer (a random generated string). Setting.preferences.removeKey("revanced_announcement_consumer"); - migrateOldSettingToNew(HIDE_LOAD_MORE_BUTTON, HIDE_SHOW_MORE_BUTTON); - // endregion } } From 24e1c952d547d5b5b17354c6042a4366e45b8d35 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:07:26 +0400 Subject: [PATCH 2/3] fix(YouTube - Return YouTube Dislike): Remove obsolete 16.x code --- .../patches/ReturnYouTubeDislikePatch.java | 100 +----------------- .../integrations/youtube/shared/PlayerType.kt | 12 +-- 2 files changed, 5 insertions(+), 107 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/ReturnYouTubeDislikePatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/ReturnYouTubeDislikePatch.java index 71f40393..7499aa29 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/ReturnYouTubeDislikePatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/ReturnYouTubeDislikePatch.java @@ -91,99 +91,6 @@ public class ReturnYouTubeDislikePatch { // while a regular video is on screen. } - // - // 17.x non litho regular video player. - // - - /** - * Resource identifier of old UI dislike button. - */ - private static final int OLD_UI_DISLIKE_BUTTON_RESOURCE_ID - = Utils.getResourceIdentifier("dislike_button", "id"); - - /** - * Dislikes text label used by old UI. - */ - @NonNull - private static WeakReference oldUITextViewRef = new WeakReference<>(null); - - /** - * Original old UI 'Dislikes' text before patch modifications. - * Required to reset the dislikes when changing videos and RYD is not available. - * Set only once during the first load. - */ - private static Spanned oldUIOriginalSpan; - - /** - * Replacement span that contains dislike value. Used by {@link #oldUiTextWatcher}. - */ - @Nullable - private static Spanned oldUIReplacementSpan; - - /** - * Old UI dislikes can be set multiple times by YouTube. - * To prevent reverting changes made here, this listener overrides any future changes YouTube makes. - */ - private static final TextWatcher oldUiTextWatcher = new TextWatcher() { - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - public void afterTextChanged(Editable s) { - if (oldUIReplacementSpan == null || oldUIReplacementSpan.toString().equals(s.toString())) { - return; - } - s.replace(0, s.length(), oldUIReplacementSpan); // Causes a recursive call back into this listener - } - }; - - private static void updateOldUIDislikesTextView() { - ReturnYouTubeDislike videoData = currentVideoData; - if (videoData == null) { - return; - } - TextView oldUITextView = oldUITextViewRef.get(); - if (oldUITextView == null) { - return; - } - oldUIReplacementSpan = videoData.getDislikesSpanForRegularVideo(oldUIOriginalSpan, false, false); - if (!oldUIReplacementSpan.equals(oldUITextView.getText())) { - oldUITextView.setText(oldUIReplacementSpan); - } - } - - /** - * Injection point. Called on main thread. - * - * Used when spoofing to 16.x and 17.x versions. - */ - public static void setOldUILayoutDislikes(int buttonViewResourceId, @Nullable TextView textView) { - try { - if (!Settings.RYD_ENABLED.get() - || buttonViewResourceId != OLD_UI_DISLIKE_BUTTON_RESOURCE_ID - || textView == null) { - return; - } - Logger.printDebug(() -> "setOldUILayoutDislikes"); - - if (oldUIOriginalSpan == null) { - // Use value of the first instance, as it appears TextViews can be recycled - // and might contain dislikes previously added by the patch. - oldUIOriginalSpan = (Spanned) textView.getText(); - } - oldUITextViewRef = new WeakReference<>(textView); - // No way to check if a listener is already attached, so remove and add again. - textView.removeTextChangedListener(oldUiTextWatcher); - textView.addTextChangedListener(oldUiTextWatcher); - - updateOldUIDislikesTextView(); - - } catch (Exception ex) { - Logger.printException(() -> "setOldUILayoutDislikes failure", ex); - } - } - - // // Litho player for both regular videos and Shorts. // @@ -690,11 +597,8 @@ public class ReturnYouTubeDislikePatch { if (v.value == vote) { videoData.sendVote(v); - if (isNoneHiddenOrMinimized) { - if (lastLithoShortsVideoData != null) { - lithoShortsShouldUseCurrentData = true; - } - updateOldUIDislikesTextView(); + if (isNoneHiddenOrMinimized && lastLithoShortsVideoData != null) { + lithoShortsShouldUseCurrentData = true; } return; diff --git a/app/src/main/java/app/revanced/integrations/youtube/shared/PlayerType.kt b/app/src/main/java/app/revanced/integrations/youtube/shared/PlayerType.kt index 7db4a3fd..9a5275b6 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/shared/PlayerType.kt +++ b/app/src/main/java/app/revanced/integrations/youtube/shared/PlayerType.kt @@ -21,9 +21,6 @@ enum class PlayerType { /** * A regular video is minimized. - * - * When spoofing to 16.x YouTube and watching a short with a regular video in the background, - * the type can be this (and not [HIDDEN]). */ WATCH_WHILE_MINIMIZED, WATCH_WHILE_MAXIMIZED, @@ -87,9 +84,8 @@ enum class PlayerType { * Check if the current player type is [NONE] or [HIDDEN]. * Useful to check if a short is currently playing. * - * Does not include the first moment after a short is opened when a regular video is minimized on screen, - * or while watching a short with a regular video present on a spoofed 16.x version of YouTube. - * To include those situations instead use [isNoneHiddenOrMinimized]. + * Does not include the first moment after a short is opened when a regular video is minimized on screen. + * To include that, instead use [isNoneHiddenOrMinimized]. * * @see VideoInformation */ @@ -103,9 +99,7 @@ enum class PlayerType { * * Useful to check if a Short is being played or opened. * - * Usually covers all use cases with no false positives, except if called from some hooks - * when spoofing to an old version this will return false even - * though a Short is being opened or is on screen (see [isNoneHiddenOrMinimized]). + * Usually covers all use cases with no false positives. * * @return If nothing, a Short, or a regular video is sliding off screen to a dismissed or hidden state. * @see VideoInformation From 003a84305d552896f81141375116c71b2fc6af65 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:36:00 +0400 Subject: [PATCH 3/3] refactor: Fix and suppress warnings --- .../RemoveScreencaptureRestrictionPatch.java | 1 + .../RemoveScreenshotRestrictionPatch.java | 1 + .../reddit/patches/FilterPromotedLinksPatch.java | 4 ++++ .../app/revanced/integrations/shared/GmsCoreSupport.java | 6 +++++- .../main/java/app/revanced/integrations/shared/Utils.java | 1 + .../integrations/syncforreddit/FixSLinksPatch.java | 5 +++++ .../integrations/tiktok/feedfilter/AdsFilter.java | 1 + .../integrations/tiktok/feedfilter/FeedItemsFilter.java | 4 ++++ .../integrations/tiktok/feedfilter/ImageVideoFilter.java | 1 + .../integrations/tiktok/feedfilter/LiveFilter.java | 1 + .../integrations/tiktok/settings/SettingsStatus.java | 2 ++ .../settings/preference/ReVancedPreferenceFragment.java | 3 ++- .../categories/ConditionalPreferenceCategory.java | 2 +- .../categories/DownloadsPreferenceCategory.java | 2 +- .../categories/FeedFilterPreferenceCategory.java | 2 +- .../categories/IntegrationsPreferenceCategory.java | 2 +- .../integrations/tiktok/speed/PlaybackSpeedPatch.java | 2 ++ .../settings/preference/CustomPreferenceCategory.java | 1 + .../settings/preference/ReVancedPreferenceFragment.java | 1 + .../patches/links/OpenLinksWithAppChooserPatch.java | 2 ++ .../app/revanced/integrations/youtube/ThemeHelper.java | 2 ++ .../app/revanced/integrations/youtube/TrieSearch.java | 4 ++-- .../youtube/patches/BypassURLRedirectsPatch.java | 2 +- .../patches/components/PlaybackSpeedMenuFilterPatch.java | 1 + .../components/ReturnYouTubeDislikeFilterPatch.java | 1 + .../returnyoutubedislike/ReturnYouTubeDislike.java | 2 +- .../requests/ReturnYouTubeDislikeApi.java | 8 +++++++- .../settings/preference/ReVancedPreferenceFragment.java | 3 +-- .../revanced/integrations/youtube/shared/PlayerType.kt | 2 +- .../revanced/integrations/youtube/shared/VideoState.kt | 2 +- .../youtube/sponsorblock/SegmentPlaybackController.java | 1 + .../youtube/sponsorblock/requests/SBRequester.java | 1 + .../sponsorblock/ui/CreateSegmentButtonController.java | 1 + .../youtube/sponsorblock/ui/SkipSponsorButton.java | 1 + .../youtube/sponsorblock/ui/VotingButtonController.java | 4 +--- .../youtube/videoplayer/CopyVideoUrlButton.java | 1 + .../youtube/videoplayer/CopyVideoUrlTimestampButton.java | 1 + 37 files changed, 63 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch.java b/app/src/main/java/app/revanced/integrations/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch.java index c76d395d..f4e4739b 100644 --- a/app/src/main/java/app/revanced/integrations/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch.java +++ b/app/src/main/java/app/revanced/integrations/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch.java @@ -5,6 +5,7 @@ import android.os.Build; import androidx.annotation.RequiresApi; +@SuppressWarnings("unused") public final class RemoveScreencaptureRestrictionPatch { // Member of AudioAttributes.Builder @RequiresApi(api = Build.VERSION_CODES.Q) diff --git a/app/src/main/java/app/revanced/integrations/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch.java b/app/src/main/java/app/revanced/integrations/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch.java index d2ebeb87..710bb195 100644 --- a/app/src/main/java/app/revanced/integrations/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch.java +++ b/app/src/main/java/app/revanced/integrations/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch.java @@ -3,6 +3,7 @@ package app.revanced.integrations.all.screenshot.removerestriction; import android.view.Window; import android.view.WindowManager; +@SuppressWarnings("unused") public class RemoveScreenshotRestrictionPatch { public static void addFlags(Window window, int flags) { diff --git a/app/src/main/java/app/revanced/integrations/reddit/patches/FilterPromotedLinksPatch.java b/app/src/main/java/app/revanced/integrations/reddit/patches/FilterPromotedLinksPatch.java index de8469e2..6a97712e 100644 --- a/app/src/main/java/app/revanced/integrations/reddit/patches/FilterPromotedLinksPatch.java +++ b/app/src/main/java/app/revanced/integrations/reddit/patches/FilterPromotedLinksPatch.java @@ -5,8 +5,12 @@ import com.reddit.domain.model.ILink; import java.util.ArrayList; import java.util.List; +@SuppressWarnings("unused") public final class FilterPromotedLinksPatch { + /** + * Injection point. + * * Filters list from promoted links. **/ public static List filterChildren(final Iterable links) { diff --git a/app/src/main/java/app/revanced/integrations/shared/GmsCoreSupport.java b/app/src/main/java/app/revanced/integrations/shared/GmsCoreSupport.java index 16f0ed0c..e9dee1a5 100644 --- a/app/src/main/java/app/revanced/integrations/shared/GmsCoreSupport.java +++ b/app/src/main/java/app/revanced/integrations/shared/GmsCoreSupport.java @@ -138,7 +138,11 @@ public class GmsCoreSupport { } } - // Modified by a patch. Do not touch. + /** + * Modified by a patch. Do not touch. + * + * @noinspection SameReturnValue + */ private static String getGmsCoreVendorGroupId() { return "app.revanced"; } diff --git a/app/src/main/java/app/revanced/integrations/shared/Utils.java b/app/src/main/java/app/revanced/integrations/shared/Utils.java index bdad852f..bacef1b5 100644 --- a/app/src/main/java/app/revanced/integrations/shared/Utils.java +++ b/app/src/main/java/app/revanced/integrations/shared/Utils.java @@ -52,6 +52,7 @@ public class Utils { * Injection point. * * @return The manifest 'Version' entry of the patches.jar used during patching. + * @noinspection SameReturnValue */ public static String getPatchesReleaseVersion() { return ""; // Value is replaced during patching. diff --git a/app/src/main/java/app/revanced/integrations/syncforreddit/FixSLinksPatch.java b/app/src/main/java/app/revanced/integrations/syncforreddit/FixSLinksPatch.java index a3c04ad6..8acd4b99 100644 --- a/app/src/main/java/app/revanced/integrations/syncforreddit/FixSLinksPatch.java +++ b/app/src/main/java/app/revanced/integrations/syncforreddit/FixSLinksPatch.java @@ -6,7 +6,12 @@ import app.revanced.integrations.shared.Logger; import java.net.HttpURLConnection; import java.net.URL; +@SuppressWarnings("unused") public final class FixSLinksPatch { + + /** + * Injection point. + */ public static String resolveSLink(String link) { if (link.matches(".*reddit\\.com/r/[^/]+/s/[^/]+")) { Logger.printInfo(() -> "Resolving " + link); diff --git a/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/AdsFilter.java b/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/AdsFilter.java index c246e622..20a99aae 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/AdsFilter.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/AdsFilter.java @@ -3,6 +3,7 @@ package app.revanced.integrations.tiktok.feedfilter; import app.revanced.integrations.tiktok.settings.Settings; import com.ss.android.ugc.aweme.feed.model.Aweme; +/** @noinspection unused*/ public class AdsFilter implements IFilter { @Override public boolean getEnabled() { diff --git a/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/FeedItemsFilter.java b/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/FeedItemsFilter.java index d74ea59e..d84bb2ca 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/FeedItemsFilter.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/FeedItemsFilter.java @@ -6,6 +6,7 @@ import com.ss.android.ugc.aweme.feed.model.FeedItemList; import java.util.Iterator; import java.util.List; +@SuppressWarnings("unused") public final class FeedItemsFilter { private static final List FILTERS = List.of( new AdsFilter(), @@ -16,6 +17,9 @@ public final class FeedItemsFilter { new LikeCountFilter() ); + /** + * Injection point. + */ public static void filter(FeedItemList feedItemList) { Iterator feedItemListIterator = feedItemList.items.iterator(); while (feedItemListIterator.hasNext()) { diff --git a/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/ImageVideoFilter.java b/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/ImageVideoFilter.java index 50472197..a0616cce 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/ImageVideoFilter.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/ImageVideoFilter.java @@ -3,6 +3,7 @@ package app.revanced.integrations.tiktok.feedfilter; import app.revanced.integrations.tiktok.settings.Settings; import com.ss.android.ugc.aweme.feed.model.Aweme; +@SuppressWarnings("unused") public class ImageVideoFilter implements IFilter { @Override public boolean getEnabled() { diff --git a/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/LiveFilter.java b/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/LiveFilter.java index bec7f11c..27fa8b1b 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/LiveFilter.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/feedfilter/LiveFilter.java @@ -3,6 +3,7 @@ package app.revanced.integrations.tiktok.feedfilter; import app.revanced.integrations.tiktok.settings.Settings; import com.ss.android.ugc.aweme.feed.model.Aweme; +@SuppressWarnings("unused") public class LiveFilter implements IFilter { @Override public boolean getEnabled() { diff --git a/app/src/main/java/app/revanced/integrations/tiktok/settings/SettingsStatus.java b/app/src/main/java/app/revanced/integrations/tiktok/settings/SettingsStatus.java index 81c74be4..519a776a 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/settings/SettingsStatus.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/settings/SettingsStatus.java @@ -1,5 +1,6 @@ package app.revanced.integrations.tiktok.settings; +@SuppressWarnings("unused") public class SettingsStatus { public static boolean feedFilterEnabled = false; public static boolean downloadEnabled = false; @@ -17,6 +18,7 @@ public class SettingsStatus { simSpoofEnabled = true; } + /** @noinspection EmptyMethod*/ public static void load() { } diff --git a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/ReVancedPreferenceFragment.java b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/ReVancedPreferenceFragment.java index 66359350..4b981eca 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/ReVancedPreferenceFragment.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/ReVancedPreferenceFragment.java @@ -14,7 +14,7 @@ import org.jetbrains.annotations.NotNull; /** * Preference fragment for ReVanced settings */ -@SuppressWarnings("deprecation") +@SuppressWarnings({"deprecation", "unused"}) public class ReVancedPreferenceFragment extends AbstractPreferenceFragment { @Override @@ -33,6 +33,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment { } @Override + @SuppressWarnings("unused") protected void initialize() { final var context = getContext(); diff --git a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/ConditionalPreferenceCategory.java b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/ConditionalPreferenceCategory.java index 24eaa1e9..9f85c3f7 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/ConditionalPreferenceCategory.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/ConditionalPreferenceCategory.java @@ -4,7 +4,7 @@ import android.content.Context; import android.preference.PreferenceCategory; import android.preference.PreferenceScreen; -@SuppressWarnings("deprecation") +@SuppressWarnings({"deprecation", "unused"}) public abstract class ConditionalPreferenceCategory extends PreferenceCategory { public ConditionalPreferenceCategory(Context context, PreferenceScreen screen) { super(context); diff --git a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/DownloadsPreferenceCategory.java b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/DownloadsPreferenceCategory.java index 8ecbfb36..1bf0f6d6 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/DownloadsPreferenceCategory.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/DownloadsPreferenceCategory.java @@ -7,7 +7,7 @@ import app.revanced.integrations.tiktok.settings.SettingsStatus; import app.revanced.integrations.tiktok.settings.preference.DownloadPathPreference; import app.revanced.integrations.tiktok.settings.preference.TogglePreference; -@SuppressWarnings("deprecation") +@SuppressWarnings({"deprecation", "unused"}) public class DownloadsPreferenceCategory extends ConditionalPreferenceCategory { public DownloadsPreferenceCategory(Context context, PreferenceScreen screen) { super(context, screen); diff --git a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/FeedFilterPreferenceCategory.java b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/FeedFilterPreferenceCategory.java index 5bb40a2e..98d71855 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/FeedFilterPreferenceCategory.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/FeedFilterPreferenceCategory.java @@ -7,7 +7,7 @@ import app.revanced.integrations.tiktok.settings.Settings; import app.revanced.integrations.tiktok.settings.SettingsStatus; import app.revanced.integrations.tiktok.settings.preference.TogglePreference; -@SuppressWarnings("deprecation") +@SuppressWarnings({"deprecation", "unused"}) public class FeedFilterPreferenceCategory extends ConditionalPreferenceCategory { public FeedFilterPreferenceCategory(Context context, PreferenceScreen screen) { super(context, screen); diff --git a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/IntegrationsPreferenceCategory.java b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/IntegrationsPreferenceCategory.java index 96e6e892..ec53d5f1 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/IntegrationsPreferenceCategory.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/settings/preference/categories/IntegrationsPreferenceCategory.java @@ -6,7 +6,7 @@ import android.preference.PreferenceScreen; import app.revanced.integrations.shared.settings.BaseSettings; import app.revanced.integrations.tiktok.settings.preference.TogglePreference; -@SuppressWarnings("deprecation") +@SuppressWarnings({"deprecation", "unused"}) public class IntegrationsPreferenceCategory extends ConditionalPreferenceCategory { public IntegrationsPreferenceCategory(Context context, PreferenceScreen screen) { super(context, screen); diff --git a/app/src/main/java/app/revanced/integrations/tiktok/speed/PlaybackSpeedPatch.java b/app/src/main/java/app/revanced/integrations/tiktok/speed/PlaybackSpeedPatch.java index 1681ad0c..c5d15d78 100644 --- a/app/src/main/java/app/revanced/integrations/tiktok/speed/PlaybackSpeedPatch.java +++ b/app/src/main/java/app/revanced/integrations/tiktok/speed/PlaybackSpeedPatch.java @@ -2,7 +2,9 @@ package app.revanced.integrations.tiktok.speed; import app.revanced.integrations.tiktok.settings.Settings; +@SuppressWarnings("unused") public class PlaybackSpeedPatch { + public static void rememberPlaybackSpeed(float newSpeed) { Settings.REMEMBERED_SPEED.save(newSpeed); } diff --git a/app/src/main/java/app/revanced/integrations/twitch/settings/preference/CustomPreferenceCategory.java b/app/src/main/java/app/revanced/integrations/twitch/settings/preference/CustomPreferenceCategory.java index 64f0a3d8..a3238ded 100644 --- a/app/src/main/java/app/revanced/integrations/twitch/settings/preference/CustomPreferenceCategory.java +++ b/app/src/main/java/app/revanced/integrations/twitch/settings/preference/CustomPreferenceCategory.java @@ -7,6 +7,7 @@ import android.util.AttributeSet; import android.view.View; import android.widget.TextView; +/** @noinspection unused*/ public class CustomPreferenceCategory extends PreferenceCategory { public CustomPreferenceCategory(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/app/src/main/java/app/revanced/integrations/twitch/settings/preference/ReVancedPreferenceFragment.java b/app/src/main/java/app/revanced/integrations/twitch/settings/preference/ReVancedPreferenceFragment.java index 1d3cca4b..39fb5f07 100644 --- a/app/src/main/java/app/revanced/integrations/twitch/settings/preference/ReVancedPreferenceFragment.java +++ b/app/src/main/java/app/revanced/integrations/twitch/settings/preference/ReVancedPreferenceFragment.java @@ -10,6 +10,7 @@ import app.revanced.integrations.twitch.settings.Settings; public class ReVancedPreferenceFragment extends AbstractPreferenceFragment { @Override + @SuppressWarnings("unused") protected void initialize() { super.initialize(); diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/links/OpenLinksWithAppChooserPatch.java b/app/src/main/java/app/revanced/integrations/twitter/patches/links/OpenLinksWithAppChooserPatch.java index 18ccaa78..c2907414 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/links/OpenLinksWithAppChooserPatch.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/links/OpenLinksWithAppChooserPatch.java @@ -4,7 +4,9 @@ import android.content.Context; import android.content.Intent; import android.util.Log; +@SuppressWarnings("unused") public final class OpenLinksWithAppChooserPatch { + public static void openWithChooser(final Context context, final Intent intent) { Log.d("ReVanced", "Opening intent with chooser: " + intent); diff --git a/app/src/main/java/app/revanced/integrations/youtube/ThemeHelper.java b/app/src/main/java/app/revanced/integrations/youtube/ThemeHelper.java index c9f7536b..4a8fcea4 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/ThemeHelper.java +++ b/app/src/main/java/app/revanced/integrations/youtube/ThemeHelper.java @@ -38,6 +38,7 @@ public class ThemeHelper { /** * Injection point. + * @noinspection SameReturnValue */ private static String darkThemeResourceName() { // Value is changed by Theme patch, if included. @@ -57,6 +58,7 @@ public class ThemeHelper { /** * Injection point. + * @noinspection SameReturnValue */ private static String lightThemeResourceName() { // Value is changed by Theme patch, if included. diff --git a/app/src/main/java/app/revanced/integrations/youtube/TrieSearch.java b/app/src/main/java/app/revanced/integrations/youtube/TrieSearch.java index 1c927cd2..12b385a7 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/TrieSearch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/TrieSearch.java @@ -380,7 +380,7 @@ public abstract class TrieSearch { throw new IllegalArgumentException("endIndex: " + endIndex + " is greater than texToSearchLength: " + textToSearchLength); } - if (patterns.size() == 0) { + if (patterns.isEmpty()) { return false; // No patterns were added. } for (int i = startIndex; i < endIndex; i++) { @@ -393,7 +393,7 @@ public abstract class TrieSearch { * @return Estimated memory size (in kilobytes) of this instance. */ public int getEstimatedMemorySize() { - if (patterns.size() == 0) { + if (patterns.isEmpty()) { return 0; } // Assume the device has less than 32GB of ram (and can use pointer compression), diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/BypassURLRedirectsPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/BypassURLRedirectsPatch.java index c49f8bbd..bc5ce486 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/BypassURLRedirectsPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/BypassURLRedirectsPatch.java @@ -18,7 +18,7 @@ public class BypassURLRedirectsPatch { public static Uri parseRedirectUri(String uri) { final var parsed = Uri.parse(uri); - if (Settings.BYPASS_URL_REDIRECTS.get() && parsed.getPath().equals(YOUTUBE_REDIRECT_PATH)) { + if (Settings.BYPASS_URL_REDIRECTS.get() && YOUTUBE_REDIRECT_PATH.equals(parsed.getPath())) { var query = Uri.parse(Uri.decode(parsed.getQueryParameter("q"))); Logger.printDebug(() -> "Bypassing YouTube redirect URI: " + query); diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java index 03319588..73c38d29 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java @@ -7,6 +7,7 @@ import app.revanced.integrations.youtube.patches.playback.speed.CustomPlaybackSp /** * Abuse LithoFilter for {@link CustomPlaybackSpeedPatch}. */ +@SuppressWarnings("unused") public final class PlaybackSpeedMenuFilterPatch extends Filter { // Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread. public static volatile boolean isPlaybackSpeedMenuVisible; diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/components/ReturnYouTubeDislikeFilterPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/components/ReturnYouTubeDislikeFilterPatch.java index 927e4493..bb45e3c2 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/components/ReturnYouTubeDislikeFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/components/ReturnYouTubeDislikeFilterPatch.java @@ -26,6 +26,7 @@ import app.revanced.integrations.youtube.TrieSearch; * * Once a way to asynchronously update litho text is found, this strategy will no longer be needed. */ +@SuppressWarnings("unused") public final class ReturnYouTubeDislikeFilterPatch extends Filter { /** diff --git a/app/src/main/java/app/revanced/integrations/youtube/returnyoutubedislike/ReturnYouTubeDislike.java b/app/src/main/java/app/revanced/integrations/youtube/returnyoutubedislike/ReturnYouTubeDislike.java index bfff1b15..6632eb41 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/returnyoutubedislike/ReturnYouTubeDislike.java +++ b/app/src/main/java/app/revanced/integrations/youtube/returnyoutubedislike/ReturnYouTubeDislike.java @@ -69,7 +69,7 @@ public class ReturnYouTubeDislike { * Maximum amount of time to block the UI from updates while waiting for network call to complete. * * Must be less than 5 seconds, as per: - * https://developer.android.com/topic/performance/vitals/anr + * ... */ private static final long MAX_MILLISECONDS_TO_BLOCK_UI_WAITING_FOR_FETCH = 4000; diff --git a/app/src/main/java/app/revanced/integrations/youtube/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java b/app/src/main/java/app/revanced/integrations/youtube/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java index bc729e47..2d301e0b 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java +++ b/app/src/main/java/app/revanced/integrations/youtube/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java @@ -147,6 +147,7 @@ public class ReturnYouTubeDislikeApi { */ private static void randomlyWaitIfLocallyDebugging() { final boolean DEBUG_RANDOMLY_DELAY_NETWORK_CALLS = false; // set true to debug UI + //noinspection ConstantValue if (DEBUG_RANDOMLY_DELAY_NETWORK_CALLS) { final long amountOfTimeToWaste = (long) (Math.random() * (API_GET_VOTES_TCP_TIMEOUT_MILLISECONDS + API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS)); @@ -187,6 +188,7 @@ public class ReturnYouTubeDislikeApi { */ private static boolean checkIfRateLimitWasHit(int httpResponseCode) { final boolean DEBUG_RATE_LIMIT = false; // set to true, to verify rate limit works + //noinspection ConstantValue if (DEBUG_RATE_LIMIT) { final double RANDOM_RATE_LIMIT_PERCENTAGE = 0.2; // 20% chance of a triggering a rate limit if (Math.random() < RANDOM_RATE_LIMIT_PERCENTAGE) { @@ -569,7 +571,11 @@ public class ReturnYouTubeDislikeApi { throw new IllegalStateException("Failed to solve puzzle challenge: " + challenge + " difficulty: " + difficulty); } - // https://stackoverflow.com/a/157202 + /** + * ... + * + * @noinspection SameParameterValue + */ private static String randomString(int len) { String AB = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; SecureRandom rnd = new SecureRandom(); diff --git a/app/src/main/java/app/revanced/integrations/youtube/settings/preference/ReVancedPreferenceFragment.java b/app/src/main/java/app/revanced/integrations/youtube/settings/preference/ReVancedPreferenceFragment.java index 1f3bd6c0..2e4bc4c1 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/settings/preference/ReVancedPreferenceFragment.java +++ b/app/src/main/java/app/revanced/integrations/youtube/settings/preference/ReVancedPreferenceFragment.java @@ -3,13 +3,11 @@ package app.revanced.integrations.youtube.settings.preference; import android.os.Build; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceGroup; import androidx.annotation.RequiresApi; import app.revanced.integrations.shared.Logger; import app.revanced.integrations.shared.settings.preference.AbstractPreferenceFragment; -import app.revanced.integrations.youtube.patches.DownloadsPatch; import app.revanced.integrations.youtube.patches.playback.speed.CustomPlaybackSpeedPatch; import app.revanced.integrations.youtube.settings.Settings; @@ -21,6 +19,7 @@ import app.revanced.integrations.youtube.settings.Settings; public class ReVancedPreferenceFragment extends AbstractPreferenceFragment { @RequiresApi(api = Build.VERSION_CODES.O) + @SuppressWarnings("unused") @Override protected void initialize() { super.initialize(); diff --git a/app/src/main/java/app/revanced/integrations/youtube/shared/PlayerType.kt b/app/src/main/java/app/revanced/integrations/youtube/shared/PlayerType.kt index 9a5275b6..6461c18a 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/shared/PlayerType.kt +++ b/app/src/main/java/app/revanced/integrations/youtube/shared/PlayerType.kt @@ -46,7 +46,7 @@ enum class PlayerType { companion object { - private val nameToPlayerType = values().associateBy { it.name } + private val nameToPlayerType = entries.associateBy { it.name } @JvmStatic fun setFromString(enumName: String) { diff --git a/app/src/main/java/app/revanced/integrations/youtube/shared/VideoState.kt b/app/src/main/java/app/revanced/integrations/youtube/shared/VideoState.kt index 75db347b..893360b1 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/shared/VideoState.kt +++ b/app/src/main/java/app/revanced/integrations/youtube/shared/VideoState.kt @@ -22,7 +22,7 @@ enum class VideoState { companion object { - private val nameToVideoState = values().associateBy { it.name } + private val nameToVideoState = entries.associateBy { it.name } @JvmStatic fun setFromString(enumName: String) { diff --git a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/SegmentPlaybackController.java b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/SegmentPlaybackController.java index 9a35adce..d8576f71 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/SegmentPlaybackController.java +++ b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/SegmentPlaybackController.java @@ -182,6 +182,7 @@ public class SegmentPlaybackController { * Injection point. * Initializes SponsorBlock when the video player starts playing a new video. */ + @SuppressWarnings("unused") public static void initialize(Object ignoredPlayerController) { try { Utils.verifyOnMainThread(); diff --git a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/requests/SBRequester.java b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/requests/SBRequester.java index 9e25e9f6..c2487dbf 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/requests/SBRequester.java +++ b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/requests/SBRequester.java @@ -113,6 +113,7 @@ public class SBRequester { // Could benefit from: // 1) collection of YouTube videos with test segment times (verify client skip timing matches the video, verify seekbar draws correctly) // 2) unit tests (verify everything else) + //noinspection ConstantValue if (false) { segments.clear(); // Test auto-hide skip button: diff --git a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/CreateSegmentButtonController.java b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/CreateSegmentButtonController.java index dffdf9ff..ff0dec8f 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/CreateSegmentButtonController.java +++ b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/CreateSegmentButtonController.java @@ -14,6 +14,7 @@ import app.revanced.integrations.shared.Logger; import app.revanced.integrations.shared.Utils; import app.revanced.integrations.youtube.videoplayer.BottomControlButton; +/** @noinspection unused*/ public class CreateSegmentButtonController { private static WeakReference buttonReference = new WeakReference<>(null); private static boolean isShowing; diff --git a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/SkipSponsorButton.java b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/SkipSponsorButton.java index f3b954d4..d98282a4 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/SkipSponsorButton.java +++ b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/SkipSponsorButton.java @@ -92,6 +92,7 @@ public class SkipSponsorButton extends FrameLayout { public boolean updateSkipButtonText(@NonNull SponsorSegment segment) { this.segment = segment; CharSequence newText = segment.getSkipButtonText(); + //noinspection StringEqualsCharSequence if (newText.equals(skipSponsorTextView.getText())) { return false; } diff --git a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/VotingButtonController.java b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/VotingButtonController.java index 581ba1ad..2f753475 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/VotingButtonController.java +++ b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/ui/VotingButtonController.java @@ -29,9 +29,7 @@ public class VotingButtonController { ImageView imageView = Objects.requireNonNull(youtubeControlsLayout.findViewById( getResourceIdentifier("revanced_sb_voting_button", "id"))); imageView.setVisibility(View.GONE); - imageView.setOnClickListener(v -> { - SponsorBlockUtils.onVotingClicked(v.getContext()); - }); + imageView.setOnClickListener(v -> SponsorBlockUtils.onVotingClicked(v.getContext())); buttonReference = new WeakReference<>(imageView); } catch (Exception ex) { diff --git a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlButton.java b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlButton.java index 5995da96..fa240dcf 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlButton.java +++ b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlButton.java @@ -9,6 +9,7 @@ import app.revanced.integrations.youtube.patches.CopyVideoUrlPatch; import app.revanced.integrations.youtube.settings.Settings; import app.revanced.integrations.shared.Logger; +/** @noinspection unused*/ public class CopyVideoUrlButton extends BottomControlButton { @Nullable private static CopyVideoUrlButton instance; diff --git a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlTimestampButton.java b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlTimestampButton.java index 41af50ac..abf927b5 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlTimestampButton.java +++ b/app/src/main/java/app/revanced/integrations/youtube/videoplayer/CopyVideoUrlTimestampButton.java @@ -9,6 +9,7 @@ import app.revanced.integrations.youtube.patches.CopyVideoUrlPatch; import app.revanced.integrations.youtube.settings.Settings; import app.revanced.integrations.shared.Logger; +/** @noinspection unused*/ public class CopyVideoUrlTimestampButton extends BottomControlButton { @Nullable private static CopyVideoUrlTimestampButton instance;