From fff07160b6597b6fcee870719d33e8017e2a1dde Mon Sep 17 00:00:00 2001 From: aliernfrog <45766489+aliernfrog@users.noreply.github.com> Date: Sat, 31 Dec 2022 20:47:57 +0300 Subject: [PATCH 1/2] feat(youtube): `copy-video-url` patch (#263) Signed-off-by: oSumAtrIX Co-authored-by: oSumAtrIX --- .../patches/CopyVideoUrlPatch.java | 27 +++ .../patches/VideoInformation.java | 21 ++ .../patches/downloads/DownloadsPatch.java | 27 --- .../integrations/settings/SettingsEnum.java | 4 + .../ReVancedSettingsFragment.java | 5 - .../integrations/utils/ReVancedUtils.java | 6 + .../videoplayer/BottomControlButton.java | 86 ++++++++ .../videoplayer/CopyVideoUrlButton.java | 26 +++ .../CopyVideoUrlTimestampButton.java | 26 +++ .../videoplayer/DownloadButton.java | 184 +++++------------- 10 files changed, 242 insertions(+), 170 deletions(-) create mode 100644 integrations/java/app/revanced/integrations/patches/CopyVideoUrlPatch.java delete mode 100644 integrations/java/app/revanced/integrations/patches/downloads/DownloadsPatch.java create mode 100644 integrations/java/app/revanced/integrations/videoplayer/BottomControlButton.java create mode 100644 integrations/java/app/revanced/integrations/videoplayer/CopyVideoUrlButton.java create mode 100644 integrations/java/app/revanced/integrations/videoplayer/CopyVideoUrlTimestampButton.java diff --git a/integrations/java/app/revanced/integrations/patches/CopyVideoUrlPatch.java b/integrations/java/app/revanced/integrations/patches/CopyVideoUrlPatch.java new file mode 100644 index 000000000..f59134ed3 --- /dev/null +++ b/integrations/java/app/revanced/integrations/patches/CopyVideoUrlPatch.java @@ -0,0 +1,27 @@ +package app.revanced.integrations.patches; + +import android.content.Context; +import android.widget.Toast; + +import app.revanced.integrations.sponsorblock.StringRef; +import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.utils.ReVancedUtils; + +public class CopyVideoUrlPatch { + public static void copyUrl(Boolean withTimestamp) { + try { + String url = String.format("https://youtu.be/%s", VideoInformation.getCurrentVideoId()); + if (withTimestamp) { + long seconds = VideoInformation.getVideoTime() / 1000; + url += String.format("?t=%s", seconds); + } + + Context context = ReVancedUtils.getContext(); + + ReVancedUtils.setClipboard(url); + if (context != null) Toast.makeText(context, StringRef.str("share_copy_url_success"), Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + LogHelper.printException(() -> "Failed to generate video url", e); + } + } +} diff --git a/integrations/java/app/revanced/integrations/patches/VideoInformation.java b/integrations/java/app/revanced/integrations/patches/VideoInformation.java index 6daf14e77..e25744c7a 100644 --- a/integrations/java/app/revanced/integrations/patches/VideoInformation.java +++ b/integrations/java/app/revanced/integrations/patches/VideoInformation.java @@ -17,6 +17,7 @@ public final class VideoInformation { private static WeakReference playerController; private static Method seekMethod; + private static String videoId = ""; private static long videoLength = 1; private static long videoTime = -1; @@ -39,6 +40,17 @@ public final class VideoInformation { } } + /** + * Set the video id. + * + * @param videoId The id of the video. + */ + public static void setVideoId(String videoId) { + LogHelper.printDebug(() -> "Setting current video id to: " + videoId); + + VideoInformation.videoId = videoId; + } + /** * Set the video length. * @@ -80,6 +92,15 @@ public final class VideoInformation { }); } + /** + * Get the id of the current video playing. + * + * @return The id of the video. Empty string if not set yet. + */ + public static String getCurrentVideoId() { + return videoId; + } + /** * Get the length of the current video playing. * diff --git a/integrations/java/app/revanced/integrations/patches/downloads/DownloadsPatch.java b/integrations/java/app/revanced/integrations/patches/downloads/DownloadsPatch.java deleted file mode 100644 index 192088859..000000000 --- a/integrations/java/app/revanced/integrations/patches/downloads/DownloadsPatch.java +++ /dev/null @@ -1,27 +0,0 @@ -package app.revanced.integrations.patches.downloads; - -import app.revanced.integrations.utils.LogHelper; - -/** - * Used by app.revanced.patches.youtube.interaction.downloads.bytecode.patch.DownloadsBytecodePatch - */ -public class DownloadsPatch { - private static String videoId; - - /** - * Called when the video changes - * @param videoId The current video id - */ - public static void setVideoId(String videoId) { - LogHelper.printDebug(() -> "newVideoLoaded - " + videoId); - - DownloadsPatch.videoId = videoId; - } - - /** - * @return The current video id - */ - public static String getCurrentVideoId() { - return videoId; - } -} diff --git a/integrations/java/app/revanced/integrations/settings/SettingsEnum.java b/integrations/java/app/revanced/integrations/settings/SettingsEnum.java index 707a3db71..47fdb3435 100644 --- a/integrations/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/integrations/java/app/revanced/integrations/settings/SettingsEnum.java @@ -16,6 +16,10 @@ public enum SettingsEnum { DOWNLOADS_BUTTON_SHOWN("revanced_downloads", true, ReturnType.BOOLEAN, true), DOWNLOADS_PACKAGE_NAME("revanced_downloads_package_name", "org.schabi.newpipe" /* NewPipe */, ReturnType.STRING), + // Copy video URL settings + COPY_VIDEO_URL_BUTTON_SHOWN("revanced_copy_video_url", true, ReturnType.BOOLEAN, true), + COPY_VIDEO_URL_TIMESTAMP_BUTTON_SHOWN("revanced_copy_video_url_timestamp", true, ReturnType.BOOLEAN, true), + // Video settings OLD_STYLE_VIDEO_QUALITY_PLAYER_SETTINGS("revanced_use_old_style_quality_settings", true, ReturnType.BOOLEAN), PREFERRED_VIDEO_SPEED("revanced_pref_video_speed", -2.0f, ReturnType.FLOAT), diff --git a/integrations/java/app/revanced/integrations/settingsmenu/ReVancedSettingsFragment.java b/integrations/java/app/revanced/integrations/settingsmenu/ReVancedSettingsFragment.java index b0a17cdeb..1744f1b50 100644 --- a/integrations/java/app/revanced/integrations/settingsmenu/ReVancedSettingsFragment.java +++ b/integrations/java/app/revanced/integrations/settingsmenu/ReVancedSettingsFragment.java @@ -26,7 +26,6 @@ import app.revanced.integrations.settings.SettingsEnum; import app.revanced.integrations.utils.LogHelper; import app.revanced.integrations.utils.ReVancedUtils; import app.revanced.integrations.utils.SharedPrefHelper; -import app.revanced.integrations.videoplayer.DownloadButton; public class ReVancedSettingsFragment extends PreferenceFragment { @@ -85,10 +84,6 @@ public class ReVancedSettingsFragment extends PreferenceFragment { } else { LogHelper.printException(() -> ("No valid setting found: " + setting.toString())); } - - if ("pref_download_button_list".equals(str)) { - DownloadButton.refreshShouldBeShown(); - } } else { LogHelper.printException(() -> ("Setting cannot be handled! " + pref.toString())); } diff --git a/integrations/java/app/revanced/integrations/utils/ReVancedUtils.java b/integrations/java/app/revanced/integrations/utils/ReVancedUtils.java index 6799c3f33..f95a2c81b 100644 --- a/integrations/java/app/revanced/integrations/utils/ReVancedUtils.java +++ b/integrations/java/app/revanced/integrations/utils/ReVancedUtils.java @@ -123,6 +123,12 @@ public class ReVancedUtils { } } + public static void setClipboard(String text) { + android.content.ClipboardManager clipboard = (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + android.content.ClipData clip = android.content.ClipData.newPlainText("ReVanced", text); + clipboard.setPrimaryClip(clip); + } + public static boolean isTablet(Context context) { return context.getResources().getConfiguration().smallestScreenWidthDp >= 600; } diff --git a/integrations/java/app/revanced/integrations/videoplayer/BottomControlButton.java b/integrations/java/app/revanced/integrations/videoplayer/BottomControlButton.java new file mode 100644 index 000000000..b2401345b --- /dev/null +++ b/integrations/java/app/revanced/integrations/videoplayer/BottomControlButton.java @@ -0,0 +1,86 @@ +package app.revanced.integrations.videoplayer; + +import android.content.Context; +import android.support.constraint.ConstraintLayout; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.ImageView; + +import java.lang.ref.WeakReference; + +import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.utils.ReVancedUtils; + +public abstract class BottomControlButton { + WeakReference button = new WeakReference<>(null); + ConstraintLayout constraintLayout; + Boolean isButtonEnabled; + Boolean isShowing; + + private Animation fadeIn; + private Animation fadeOut; + + public BottomControlButton(Object obj, String viewId, Boolean isEnabled, View.OnClickListener onClickListener) { + try { + LogHelper.printDebug(() -> "Initializing button with id: " + viewId); + constraintLayout = (ConstraintLayout) obj; + isButtonEnabled = isEnabled; + + ImageView imageView = constraintLayout.findViewById(getIdentifier(viewId, "id")); + if (imageView == null) { + LogHelper.printDebug(() -> "Couldn't find ImageView with id: " + viewId); + return; + } + + imageView.setOnClickListener(onClickListener); + + button = new WeakReference<>(imageView); + fadeIn = getAnimation("fade_in"); + fadeOut = getAnimation("fade_out"); + + int fadeDurationFast = getInteger("fade_duration_fast"); + int fadeDurationScheduled = getInteger("fade_duration_scheduled"); + fadeIn.setDuration(fadeDurationFast); + fadeOut.setDuration(fadeDurationScheduled); + isShowing = true; + setVisibility(false); + } catch (Exception e) { + LogHelper.printException(() -> "Failed to initialize button with id: " + viewId, e); + } + } + + public void setVisibility(boolean showing) { + if (isShowing == showing) return; + + isShowing = showing; + ImageView imageView = button.get(); + + if (constraintLayout == null || imageView == null) + return; + + if (showing && isButtonEnabled) { + LogHelper.printDebug(() -> "Fading in"); + imageView.setVisibility(View.VISIBLE); + imageView.startAnimation(fadeIn); + } + else if (imageView.getVisibility() == View.VISIBLE) { + LogHelper.printDebug(() -> "Fading out"); + imageView.startAnimation(fadeOut); + imageView.setVisibility(View.GONE); + } + } + + private static int getIdentifier(String str, String str2) { + Context appContext = ReVancedUtils.getContext(); + return appContext.getResources().getIdentifier(str, str2, appContext.getPackageName()); + } + + private static int getInteger(String str) { + return ReVancedUtils.getContext().getResources().getInteger(getIdentifier(str, "integer")); + } + + private static Animation getAnimation(String str) { + return AnimationUtils.loadAnimation(ReVancedUtils.getContext(), getIdentifier(str, "anim")); + } +} diff --git a/integrations/java/app/revanced/integrations/videoplayer/CopyVideoUrlButton.java b/integrations/java/app/revanced/integrations/videoplayer/CopyVideoUrlButton.java new file mode 100644 index 000000000..38a621140 --- /dev/null +++ b/integrations/java/app/revanced/integrations/videoplayer/CopyVideoUrlButton.java @@ -0,0 +1,26 @@ +package app.revanced.integrations.videoplayer; + + +import app.revanced.integrations.patches.CopyVideoUrlPatch; +import app.revanced.integrations.settings.SettingsEnum; + +public class CopyVideoUrlButton extends BottomControlButton { + public static CopyVideoUrlButton instance; + + public CopyVideoUrlButton(Object obj) { + super( + obj, + "copy_video_url_button", + SettingsEnum.COPY_VIDEO_URL_BUTTON_SHOWN.getBoolean(), + view -> CopyVideoUrlPatch.copyUrl(false) + ); + } + + public static void initializeButton(Object obj) { + instance = new CopyVideoUrlButton(obj); + } + + public static void changeVisibility(boolean showing) { + if (instance != null) instance.setVisibility(showing); + } +} \ No newline at end of file diff --git a/integrations/java/app/revanced/integrations/videoplayer/CopyVideoUrlTimestampButton.java b/integrations/java/app/revanced/integrations/videoplayer/CopyVideoUrlTimestampButton.java new file mode 100644 index 000000000..482ace149 --- /dev/null +++ b/integrations/java/app/revanced/integrations/videoplayer/CopyVideoUrlTimestampButton.java @@ -0,0 +1,26 @@ +package app.revanced.integrations.videoplayer; + +import app.revanced.integrations.patches.CopyVideoUrlPatch; +import app.revanced.integrations.settings.SettingsEnum; + +public class CopyVideoUrlTimestampButton extends BottomControlButton { + public static CopyVideoUrlTimestampButton instance; + + public CopyVideoUrlTimestampButton(Object obj) { + super( + obj, + "copy_video_url_timestamp_button", + SettingsEnum.COPY_VIDEO_URL_TIMESTAMP_BUTTON_SHOWN.getBoolean(), + view -> CopyVideoUrlPatch.copyUrl(true) + ); + } + + public static void initializeButton(Object obj) { + instance = new CopyVideoUrlTimestampButton(obj); + } + + public static void changeVisibility(boolean showing) { + if (instance != null) instance.setVisibility(showing); + } + +} \ No newline at end of file diff --git a/integrations/java/app/revanced/integrations/videoplayer/DownloadButton.java b/integrations/java/app/revanced/integrations/videoplayer/DownloadButton.java index 86f70ec1d..5f4b56eca 100644 --- a/integrations/java/app/revanced/integrations/videoplayer/DownloadButton.java +++ b/integrations/java/app/revanced/integrations/videoplayer/DownloadButton.java @@ -1,161 +1,69 @@ package app.revanced.integrations.videoplayer; -import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.support.constraint.ConstraintLayout; import android.view.View; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.ImageView; import android.widget.Toast; - -import java.lang.ref.WeakReference; - -import app.revanced.integrations.patches.downloads.DownloadsPatch; +import app.revanced.integrations.patches.VideoInformation; import app.revanced.integrations.settings.SettingsEnum; import app.revanced.integrations.sponsorblock.StringRef; import app.revanced.integrations.utils.LogHelper; -import app.revanced.integrations.utils.ReVancedUtils; -import app.revanced.integrations.utils.SharedPrefHelper; -/* loaded from: classes6.dex */ -//ToDo: Refactor -public class DownloadButton { - static WeakReference _button = new WeakReference<>(null); - static ConstraintLayout _constraintLayout; - static int fadeDurationFast; - static int fadeDurationScheduled; - static Animation fadeIn; - static Animation fadeOut; - public static boolean isDownloadButtonEnabled; - static boolean isShowing; +public class DownloadButton extends BottomControlButton { + public static DownloadButton instance; - public static void initializeDownloadButton(Object obj) { + public DownloadButton(Object obj) { + super( + obj, + "download_button", + SettingsEnum.DOWNLOADS_BUTTON_SHOWN.getBoolean(), + DownloadButton::onDownloadClick + ); + } + + public static void initializeButton(Object obj) { + instance = new DownloadButton(obj); + } + + public static void changeVisibility(boolean showing) { + if (instance != null) instance.setVisibility(showing); + } + + private static void onDownloadClick(View view) { + LogHelper.printDebug(() -> "Download button clicked"); + + final var context = view.getContext(); + var downloaderPackageName = SettingsEnum.DOWNLOADS_PACKAGE_NAME.getString(); + + boolean packageEnabled = false; try { - LogHelper.printDebug(() -> "initializing"); - _constraintLayout = (ConstraintLayout) obj; - isDownloadButtonEnabled = shouldBeShown(); - ImageView imageView = _constraintLayout.findViewById(getIdentifier("download_button", "id")); - if (imageView == null) { - LogHelper.printDebug(() -> "Couldn't find imageView with id \"download_button\""); - return; - } - - imageView.setOnClickListener(view -> { - LogHelper.printDebug(() -> "Download button clicked"); - - final var context = view.getContext(); - var downloaderPackageName = SettingsEnum.DOWNLOADS_PACKAGE_NAME.getString(); - - boolean packageEnabled = false; - try { - assert context != null; - packageEnabled = context.getPackageManager().getApplicationInfo(downloaderPackageName, 0).enabled; - } catch (PackageManager.NameNotFoundException error) { - LogHelper.printDebug(() -> "Downloader could not be found: " + error); - } - - // If the package is not installed, show the toast - if (!packageEnabled) { - Toast.makeText(context, downloaderPackageName + " " + StringRef.str("downloader_not_installed_warning"), Toast.LENGTH_LONG).show(); - return; - } - - // Launch PowerTube intent - try { - String content = String.format("https://youtu.be/%s", DownloadsPatch.getCurrentVideoId()); - - Intent intent = new Intent("android.intent.action.SEND"); - intent.setType("text/plain"); - intent.setPackage(downloaderPackageName); - intent.putExtra("android.intent.extra.TEXT", content); - context.startActivity(intent); - - LogHelper.printDebug(() -> "Launched the intent with the content: " + content); - } catch (Exception error) { - LogHelper.printDebug(() -> "Failed to launch the intent: " + error); - } - - //var options = Arrays.asList("Video", "Audio").toArray(new CharSequence[0]); - // - //new AlertDialog.Builder(view.getContext()) - // .setItems(options, (dialog, which) -> { - // LogHelper.debug(DownloadButton.class, String.valueOf(options[which])); - // }) - // .show(); - // TODO: show popup and download via newpipe - }); - _button = new WeakReference<>(imageView); - fadeDurationFast = getInteger("fade_duration_fast"); - fadeDurationScheduled = getInteger("fade_duration_scheduled"); - Animation animation = getAnimation("fade_in"); - fadeIn = animation; - animation.setDuration(fadeDurationFast); - Animation animation2 = getAnimation("fade_out"); - fadeOut = animation2; - animation2.setDuration(fadeDurationScheduled); - isShowing = true; - changeVisibility(false); - - } catch (Exception e) { - LogHelper.printException(() -> ("Unable to set FrameLayout"), e); + assert context != null; + packageEnabled = context.getPackageManager().getApplicationInfo(downloaderPackageName, 0).enabled; + } catch (PackageManager.NameNotFoundException error) { + LogHelper.printDebug(() -> "Downloader could not be found: " + error); } - } - public static void changeVisibility(boolean z) { - if (isShowing == z) return; - - isShowing = z; - ImageView imageView = _button.get(); - - if (_constraintLayout == null || imageView == null) + // If the package is not installed, show the toast + if (!packageEnabled) { + Toast.makeText(context, downloaderPackageName + " " + StringRef.str("downloader_not_installed_warning"), Toast.LENGTH_LONG).show(); return; - - if (z && isDownloadButtonEnabled) { - LogHelper.printDebug(() -> "Fading in"); - imageView.setVisibility(View.VISIBLE); - imageView.startAnimation(fadeIn); - } - else if (imageView.getVisibility() == View.VISIBLE) { - LogHelper.printDebug(() -> "Fading out"); - imageView.startAnimation(fadeOut); - imageView.setVisibility(View.GONE); - } - } - - public static void refreshShouldBeShown() { - isDownloadButtonEnabled = shouldBeShown(); - } - - private static boolean shouldBeShown() { - if (!SettingsEnum.DOWNLOADS_BUTTON_SHOWN.getBoolean()) { - return false; } - Context appContext = ReVancedUtils.getContext(); - if (appContext == null) { - LogHelper.printException(() -> ("shouldBeShown - context is null!")); - return false; - } - String string = SharedPrefHelper.getString(appContext, SharedPrefHelper.SharedPrefNames.YOUTUBE, "pref_download_button_list", "PLAYER" /* TODO: set the default to null, as this will be set by the settings page later */); - if (string == null || string.isEmpty()) { - return false; - } - return string.equalsIgnoreCase("PLAYER"); - } + // Launch PowerTube intent + try { + String content = String.format("https://youtu.be/%s", VideoInformation.getCurrentVideoId()); - private static int getIdentifier(String str, String str2) { - Context appContext = ReVancedUtils.getContext(); - return appContext.getResources().getIdentifier(str, str2, appContext.getPackageName()); - } + Intent intent = new Intent("android.intent.action.SEND"); + intent.setType("text/plain"); + intent.setPackage(downloaderPackageName); + intent.putExtra("android.intent.extra.TEXT", content); + context.startActivity(intent); - private static int getInteger(String str) { - return ReVancedUtils.getContext().getResources().getInteger(getIdentifier(str, "integer")); - } - private static Animation getAnimation(String str) { - return AnimationUtils.loadAnimation(ReVancedUtils.getContext(), getIdentifier(str, "anim")); + LogHelper.printDebug(() -> "Launched the intent with the content: " + content); + } catch (Exception error) { + LogHelper.printDebug(() -> "Failed to launch the intent: " + error); + } } } From 537dd40c9a845fcadb632c580f5cca7c074128a5 Mon Sep 17 00:00:00 2001 From: 0xrxL <120989892+0xrxL@users.noreply.github.com> Date: Sat, 31 Dec 2022 19:16:52 +0100 Subject: [PATCH 2/2] fix(youtube/general-ads): restore swipe back to exit gesture (#264) Co-authored-by: oSumAtrIX --- .../patches/FixBackToExitGesturePatch.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 integrations/java/app/revanced/integrations/patches/FixBackToExitGesturePatch.java diff --git a/integrations/java/app/revanced/integrations/patches/FixBackToExitGesturePatch.java b/integrations/java/app/revanced/integrations/patches/FixBackToExitGesturePatch.java new file mode 100644 index 000000000..ab210d276 --- /dev/null +++ b/integrations/java/app/revanced/integrations/patches/FixBackToExitGesturePatch.java @@ -0,0 +1,42 @@ +package app.revanced.integrations.patches; + +import com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity; +import app.revanced.integrations.utils.LogHelper; + +public class FixBackToExitGesturePatch { + /** + * State whether the scroll position reaches the top. + */ + public static boolean isTopView = false; + + /** + * Handle the event after clicking the back button. + * + * @param activity The activity, the app is launched with to finish. + */ + public static void onBackPressed(WatchWhileActivity activity) { + if (!isTopView) return; + + LogHelper.printDebug(() -> "Activity is closed"); + + activity.finish(); + } + + /** + * Handle the event when the homepage list of views is being scrolled. + */ + public static void onScrollingViews() { + LogHelper.printDebug(() -> "Views are scrolling"); + + isTopView = false; + } + + /** + * Handle the event when the homepage list of views reached the top. + */ + public static void onTopView() { + LogHelper.printDebug(() -> "Scrolling reached the top"); + + isTopView = true; + } +}