From f4fb960c629f895044b08fbc69f54110ccce6ae6 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 28 Mar 2024 23:54:35 +0100 Subject: [PATCH 01/11] Migrate to non-transitive R classes --- .../org/schabi/newpipe/error/ErrorUtil.kt | 4 +- .../newpipe/fragments/MainFragment.java | 4 +- .../schabi/newpipe/local/feed/FeedFragment.kt | 4 +- .../newpipe/player/PlayQueueActivity.java | 12 +-- .../helper/PlaybackParameterDialog.java | 8 +- .../notification/NotificationActionData.java | 74 ++++++++++++------- .../notification/NotificationConstants.java | 52 ++++++++----- .../newpipe/player/ui/VideoPlayerUi.java | 9 ++- gradle.properties | 2 +- 9 files changed, 101 insertions(+), 68 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt index daa598509..dcbc11413 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt @@ -54,7 +54,7 @@ class ErrorUtil { */ @JvmStatic fun showSnackbar(context: Context, errorInfo: ErrorInfo) { - val rootView = if (context is Activity) context.findViewById(R.id.content) else null + val rootView = (context as? Activity)?.findViewById(android.R.id.content) showSnackbar(context, rootView, errorInfo) } @@ -71,7 +71,7 @@ class ErrorUtil { fun showSnackbar(fragment: Fragment, errorInfo: ErrorInfo) { var rootView = fragment.view if (rootView == null && fragment.activity != null) { - rootView = fragment.requireActivity().findViewById(R.id.content) + rootView = fragment.requireActivity().findViewById(android.R.id.content) } showSnackbar(fragment.requireContext(), rootView, errorInfo) } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index d50f0b0d8..7adfa1e39 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -245,10 +245,10 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte // change the background and icon color of the tab layout: // service-colored at the top, app-background-colored at the bottom tabLayout.setBackgroundColor(ThemeHelper.resolveColorFromAttr(requireContext(), - bottom ? R.attr.colorSecondary : R.attr.colorPrimary)); + bottom ? android.R.attr.windowBackground : R.attr.colorPrimary)); @ColorInt final int iconColor = bottom - ? ThemeHelper.resolveColorFromAttr(requireContext(), R.attr.colorAccent) + ? ThemeHelper.resolveColorFromAttr(requireContext(), android.R.attr.colorAccent) : Color.WHITE; tabLayout.setTabRippleColor(ColorStateList.valueOf(iconColor).withAlpha(32)); tabLayout.setTabIconTint(ColorStateList.valueOf(iconColor)); diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index 0b61f45fb..e8c5b1e34 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -549,7 +549,7 @@ class FeedFragment : BaseStateFragment() { var typeface = Typeface.DEFAULT var backgroundSupplier = { ctx: Context -> - resolveDrawable(ctx, R.attr.selectableItemBackground) + resolveDrawable(ctx, android.R.attr.selectableItemBackground) } if (doCheck) { // If the uploadDate is null or true we should highlight the item @@ -562,7 +562,7 @@ class FeedFragment : BaseStateFragment() { LayerDrawable( arrayOf( resolveDrawable(ctx, R.attr.dashed_border), - resolveDrawable(ctx, R.attr.selectableItemBackground) + resolveDrawable(ctx, android.R.attr.selectableItemBackground) ) ) } diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java index c012f6008..195baecbd 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java @@ -569,16 +569,16 @@ public final class PlayQueueActivity extends AppCompatActivity private void onPlayModeChanged(final int repeatMode, final boolean shuffled) { switch (repeatMode) { case com.google.android.exoplayer2.Player.REPEAT_MODE_OFF: - queueControlBinding.controlRepeat - .setImageResource(R.drawable.exo_controls_repeat_off); + queueControlBinding.controlRepeat.setImageResource( + com.google.android.exoplayer2.ui.R.drawable.exo_controls_repeat_off); break; case com.google.android.exoplayer2.Player.REPEAT_MODE_ONE: - queueControlBinding.controlRepeat - .setImageResource(R.drawable.exo_controls_repeat_one); + queueControlBinding.controlRepeat.setImageResource( + com.google.android.exoplayer2.ui.R.drawable.exo_controls_repeat_one); break; case com.google.android.exoplayer2.Player.REPEAT_MODE_ALL: - queueControlBinding.controlRepeat - .setImageResource(R.drawable.exo_controls_repeat_all); + queueControlBinding.controlRepeat.setImageResource( + com.google.android.exoplayer2.ui.R.drawable.exo_controls_repeat_all); break; } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java index 796208a04..dfb49a25b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java @@ -342,14 +342,14 @@ public class PlaybackParameterDialog extends DialogFragment { final Map pitchCtrlModeComponentMapping = getPitchControlModeComponentMappings(); pitchCtrlModeComponentMapping.forEach((v, textView) -> textView.setBackground( - resolveDrawable(requireContext(), R.attr.selectableItemBackground))); + resolveDrawable(requireContext(), android.R.attr.selectableItemBackground))); // Mark the selected textview final TextView textView = pitchCtrlModeComponentMapping.get(semitones); if (textView != null) { textView.setBackground(new LayerDrawable(new Drawable[]{ resolveDrawable(requireContext(), R.attr.dashed_border), - resolveDrawable(requireContext(), R.attr.selectableItemBackground) + resolveDrawable(requireContext(), android.R.attr.selectableItemBackground) })); } @@ -415,14 +415,14 @@ public class PlaybackParameterDialog extends DialogFragment { // Bring all textviews into a normal state final Map stepSiteComponentMapping = getStepSizeComponentMappings(); stepSiteComponentMapping.forEach((v, textView) -> textView.setBackground( - resolveDrawable(requireContext(), R.attr.selectableItemBackground))); + resolveDrawable(requireContext(), android.R.attr.selectableItemBackground))); // Mark the selected textview final TextView textView = stepSiteComponentMapping.get(newStepSize); if (textView != null) { textView.setBackground(new LayerDrawable(new Drawable[]{ resolveDrawable(requireContext(), R.attr.dashed_border), - resolveDrawable(requireContext(), R.attr.selectableItemBackground) + resolveDrawable(requireContext(), android.R.attr.selectableItemBackground) })); } diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java index b3abcd0b5..17ae732c6 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationActionData.java @@ -69,41 +69,48 @@ public final class NotificationActionData { switch (selectedAction) { case NotificationConstants.PREVIOUS: return new NotificationActionData(ACTION_PLAY_PREVIOUS, - ctx.getString(R.string.exo_controls_previous_description), baseActionIcon); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_previous_description), baseActionIcon); case NotificationConstants.NEXT: return new NotificationActionData(ACTION_PLAY_NEXT, - ctx.getString(R.string.exo_controls_next_description), baseActionIcon); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_next_description), baseActionIcon); case NotificationConstants.REWIND: return new NotificationActionData(ACTION_FAST_REWIND, - ctx.getString(R.string.exo_controls_rewind_description), baseActionIcon); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_rewind_description), baseActionIcon); case NotificationConstants.FORWARD: return new NotificationActionData(ACTION_FAST_FORWARD, - ctx.getString(R.string.exo_controls_fastforward_description), - baseActionIcon); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_fastforward_description), baseActionIcon); case NotificationConstants.SMART_REWIND_PREVIOUS: if (player.getPlayQueue() != null && player.getPlayQueue().size() > 1) { return new NotificationActionData(ACTION_PLAY_PREVIOUS, - ctx.getString(R.string.exo_controls_previous_description), - R.drawable.exo_notification_previous); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_previous_description), + com.google.android.exoplayer2.ui.R.drawable.exo_notification_previous); } else { return new NotificationActionData(ACTION_FAST_REWIND, - ctx.getString(R.string.exo_controls_rewind_description), - R.drawable.exo_controls_rewind); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_rewind_description), + com.google.android.exoplayer2.ui.R.drawable.exo_controls_rewind); } case NotificationConstants.SMART_FORWARD_NEXT: if (player.getPlayQueue() != null && player.getPlayQueue().size() > 1) { return new NotificationActionData(ACTION_PLAY_NEXT, - ctx.getString(R.string.exo_controls_next_description), - R.drawable.exo_notification_next); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_next_description), + com.google.android.exoplayer2.ui.R.drawable.exo_notification_next); } else { return new NotificationActionData(ACTION_FAST_FORWARD, - ctx.getString(R.string.exo_controls_fastforward_description), - R.drawable.exo_controls_fastforward); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_fastforward_description), + com.google.android.exoplayer2.ui.R.drawable.exo_controls_fastforward); } case NotificationConstants.PLAY_PAUSE_BUFFERING: @@ -119,45 +126,56 @@ public final class NotificationActionData { case NotificationConstants.PLAY_PAUSE: if (player.getCurrentState() == Player.STATE_COMPLETED) { return new NotificationActionData(ACTION_PLAY_PAUSE, - ctx.getString(R.string.exo_controls_pause_description), + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_pause_description), R.drawable.ic_replay); } else if (player.isPlaying() || player.getCurrentState() == Player.STATE_PREFLIGHT || player.getCurrentState() == Player.STATE_BLOCKED || player.getCurrentState() == Player.STATE_BUFFERING) { return new NotificationActionData(ACTION_PLAY_PAUSE, - ctx.getString(R.string.exo_controls_pause_description), - R.drawable.exo_notification_pause); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_pause_description), + com.google.android.exoplayer2.ui.R.drawable.exo_notification_pause); } else { return new NotificationActionData(ACTION_PLAY_PAUSE, - ctx.getString(R.string.exo_controls_play_description), - R.drawable.exo_notification_play); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_play_description), + com.google.android.exoplayer2.ui.R.drawable.exo_notification_play); } case NotificationConstants.REPEAT: if (player.getRepeatMode() == REPEAT_MODE_ALL) { return new NotificationActionData(ACTION_REPEAT, - ctx.getString(R.string.exo_controls_repeat_all_description), - R.drawable.exo_media_action_repeat_all); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_repeat_all_description), + com.google.android.exoplayer2.ext.mediasession.R.drawable + .exo_media_action_repeat_all); } else if (player.getRepeatMode() == REPEAT_MODE_ONE) { return new NotificationActionData(ACTION_REPEAT, - ctx.getString(R.string.exo_controls_repeat_one_description), - R.drawable.exo_media_action_repeat_one); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_repeat_one_description), + com.google.android.exoplayer2.ext.mediasession.R.drawable + .exo_media_action_repeat_one); } else /* player.getRepeatMode() == REPEAT_MODE_OFF */ { return new NotificationActionData(ACTION_REPEAT, - ctx.getString(R.string.exo_controls_repeat_off_description), - R.drawable.exo_media_action_repeat_off); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_repeat_off_description), + com.google.android.exoplayer2.ext.mediasession.R.drawable + .exo_media_action_repeat_off); } case NotificationConstants.SHUFFLE: if (player.getPlayQueue() != null && player.getPlayQueue().isShuffled()) { return new NotificationActionData(ACTION_SHUFFLE, - ctx.getString(R.string.exo_controls_shuffle_on_description), - R.drawable.exo_controls_shuffle_on); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_shuffle_on_description), + com.google.android.exoplayer2.ui.R.drawable.exo_controls_shuffle_on); } else { return new NotificationActionData(ACTION_SHUFFLE, - ctx.getString(R.string.exo_controls_shuffle_off_description), - R.drawable.exo_controls_shuffle_off); + ctx.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_shuffle_off_description), + com.google.android.exoplayer2.ui.R.drawable.exo_controls_shuffle_off); } case NotificationConstants.CLOSE: diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java index b9607f7ea..4f304b405 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java @@ -78,16 +78,16 @@ public final class NotificationConstants { @DrawableRes public static final int[] ACTION_ICONS = { 0, - R.drawable.exo_icon_previous, - R.drawable.exo_icon_next, - R.drawable.exo_icon_rewind, - R.drawable.exo_icon_fastforward, - R.drawable.exo_icon_previous, - R.drawable.exo_icon_next, + com.google.android.exoplayer2.ui.R.drawable.exo_icon_previous, + com.google.android.exoplayer2.ui.R.drawable.exo_icon_next, + com.google.android.exoplayer2.ui.R.drawable.exo_icon_rewind, + com.google.android.exoplayer2.ui.R.drawable.exo_icon_fastforward, + com.google.android.exoplayer2.ui.R.drawable.exo_icon_previous, + com.google.android.exoplayer2.ui.R.drawable.exo_icon_next, R.drawable.ic_pause, R.drawable.ic_hourglass_top, - R.drawable.exo_icon_repeat_all, - R.drawable.exo_icon_shuffle_on, + com.google.android.exoplayer2.ui.R.drawable.exo_icon_repeat_all, + com.google.android.exoplayer2.ui.R.drawable.exo_icon_shuffle_on, R.drawable.ic_close, }; @@ -122,29 +122,41 @@ public final class NotificationConstants { public static String getActionName(@NonNull final Context context, @Action final int action) { switch (action) { case PREVIOUS: - return context.getString(R.string.exo_controls_previous_description); + return context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_previous_description); case NEXT: - return context.getString(R.string.exo_controls_next_description); + return context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_next_description); case REWIND: - return context.getString(R.string.exo_controls_rewind_description); + return context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_rewind_description); case FORWARD: - return context.getString(R.string.exo_controls_fastforward_description); + return context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_fastforward_description); case SMART_REWIND_PREVIOUS: return Localization.concatenateStrings( - context.getString(R.string.exo_controls_rewind_description), - context.getString(R.string.exo_controls_previous_description)); + context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_rewind_description), + context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_previous_description)); case SMART_FORWARD_NEXT: return Localization.concatenateStrings( - context.getString(R.string.exo_controls_fastforward_description), - context.getString(R.string.exo_controls_next_description)); + context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_fastforward_description), + context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_next_description)); case PLAY_PAUSE: return Localization.concatenateStrings( - context.getString(R.string.exo_controls_play_description), - context.getString(R.string.exo_controls_pause_description)); + context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_play_description), + context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_pause_description)); case PLAY_PAUSE_BUFFERING: return Localization.concatenateStrings( - context.getString(R.string.exo_controls_play_description), - context.getString(R.string.exo_controls_pause_description), + context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_play_description), + context.getString(com.google.android.exoplayer2.ui.R.string + .exo_controls_pause_description), context.getString(R.string.notification_action_buffering)); case REPEAT: return context.getString(R.string.notification_action_repeat); diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java index b51aaa638..ec9d6783b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java @@ -952,11 +952,14 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa super.onRepeatModeChanged(repeatMode); if (repeatMode == REPEAT_MODE_ALL) { - binding.repeatButton.setImageResource(R.drawable.exo_controls_repeat_all); + binding.repeatButton.setImageResource( + com.google.android.exoplayer2.ui.R.drawable.exo_controls_repeat_all); } else if (repeatMode == REPEAT_MODE_ONE) { - binding.repeatButton.setImageResource(R.drawable.exo_controls_repeat_one); + binding.repeatButton.setImageResource( + com.google.android.exoplayer2.ui.R.drawable.exo_controls_repeat_one); } else /* repeatMode == REPEAT_MODE_OFF */ { - binding.repeatButton.setImageResource(R.drawable.exo_controls_repeat_off); + binding.repeatButton.setImageResource( + com.google.android.exoplayer2.ui.R.drawable.exo_controls_repeat_off); } } diff --git a/gradle.properties b/gradle.properties index 7b138b24e..0ca913222 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ android.defaults.buildfeatures.buildconfig=true android.enableJetifier=false android.nonFinalResIds=false -android.nonTransitiveRClass=false +android.nonTransitiveRClass=true android.useAndroidX=true org.gradle.jvmargs=-Xmx2048M --add-opens jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED systemProp.file.encoding=utf-8 From d9d6fff48f6ff9ce69f9922a65a753fa12cf3f45 Mon Sep 17 00:00:00 2001 From: ashutosh001 <43110324+ashutosh001@users.noreply.github.com> Date: Fri, 26 Apr 2024 06:23:46 +0530 Subject: [PATCH 02/11] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e22f9260..001f0eeab 100644 --- a/README.md +++ b/README.md @@ -104,10 +104,10 @@ You can install NewPipe using one of the following methods: We recommend method 1 for most users. APKs installed using method 1 or 2 are compatible with each other (meaning that if you installed NewPipe using either method 1 or 2, you can also update NewPipe using the other), but not with those installed using method 3. This is due to the same signing key (ours) being used for 1 and 2, but a different signing key (F-Droid's) being used for 3. Building a debug APK using method 4 excludes a key entirely. Signing keys help ensure that a user isn't tricked into installing a malicious update to an app. When using method 5, each APK is signed with a different random key supplied by GitHub Actions, so you cannot even update it. You will have to backup and restore the app data each time you wish to use a new APK. In the meanwhile, if you want to switch sources for some reason (e.g. NewPipe's core functionality breaks and F-Droid doesn't have the latest update yet), we recommend following this procedure: -1. Back up your data via Settings > Content > Export Database so you keep your history, subscriptions, and playlists +1. Back up your data via Settings > Backup and Restore > Export Database so you keep your history, subscriptions, and playlists 2. Uninstall NewPipe 3. Download the APK from the new source and install it -4. Import the data from step 1 via Settings > Content > Import Database +4. Import the data from step 1 via Settings > Backup and Restore > Import Database Note: when you're importing a database into the official app, always make sure that it is the one you exported _from_ the official app. If you import a database exported from an APK other than the official app, it may break things. Such an action is unsupported, and you should only do so when you're absolutely certain you know what you're doing. From 879d7a24f05ab06510be249f2f3b5109f7c89dcc Mon Sep 17 00:00:00 2001 From: Siddhesh Naik <87667048+snaik20@users.noreply.github.com> Date: Mon, 29 Apr 2024 02:45:18 +0530 Subject: [PATCH 03/11] Fix github worklow for Android tests (#11014) - The github workflow fails when running android tests. - The workflow is trying to launch an x86 emulator on aarch-64 (macos-latest) host. - The macos-latest system seem to be used originally as it supports hardware acceleration. - This is no longer recomended, and ubuntu-latest host can handle the same and be faster than macos-latest. Doc: https://github.com/marketplace/actions/android-emulator-runner#running-hardware-accelerated-emulators-on-linux-runners --- .github/workflows/ci.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d76e1645..0c750157c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,8 +63,7 @@ jobs: path: app/build/outputs/apk/debug/*.apk test-android: - # macos has hardware acceleration. See android-emulator-runner action - runs-on: macos-latest + runs-on: ubuntu-latest timeout-minutes: 20 strategy: matrix: @@ -82,6 +81,12 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: set up JDK 17 uses: actions/setup-java@v4 with: From 7204407690929666e066e8b6f509c43a205e6e17 Mon Sep 17 00:00:00 2001 From: Siddhesh Naik Date: Mon, 13 May 2024 02:22:13 +0530 Subject: [PATCH 04/11] Fix RSS button visibility - The `onPrepareMenu` callback is invoked after setting the visibility of the menu items. - Due to this, the menu item resets to it's default visibility. - Now updating the menu item within the callback. - Also migrated to the MenuHost framework to reduce dependency on deprecated APIs. --- .../list/channel/ChannelFragment.java | 131 +++++++++--------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 7e83d9958..fd382adbf 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -22,6 +22,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; +import androidx.core.view.MenuProvider; import androidx.preference.PreferenceManager; import com.google.android.material.snackbar.Snackbar; @@ -99,6 +100,7 @@ public class ChannelFragment extends BaseStateFragment private MenuItem menuRssButton; private MenuItem menuNotifyButton; private SubscriptionEntity channelSubscription; + private MenuProvider menuProvider; public static ChannelFragment getInstance(final int serviceId, final String url, final String name) { @@ -121,7 +123,62 @@ public class ChannelFragment extends BaseStateFragment @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); + menuProvider = new MenuProvider() { + @Override + public void onCreateMenu(@NonNull final Menu menu, + @NonNull final MenuInflater inflater) { + inflater.inflate(R.menu.menu_channel, menu); + + if (DEBUG) { + Log.d(TAG, "onCreateOptionsMenu() called with: " + + "menu = [" + menu + "], inflater = [" + inflater + "]"); + } + + } + + @Override + public void onPrepareMenu(@NonNull final Menu menu) { + menuRssButton = menu.findItem(R.id.menu_item_rss); + menuNotifyButton = menu.findItem(R.id.menu_item_notify); + updateRssButton(); + updateNotifyButton(channelSubscription); + } + + @Override + public boolean onMenuItemSelected(@NonNull final MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_item_notify: + final boolean value = !item.isChecked(); + item.setEnabled(false); + setNotify(value); + break; + case R.id.action_settings: + NavigationHelper.openSettings(requireContext()); + break; + case R.id.menu_item_rss: + if (currentInfo != null) { + ShareUtils.openUrlInApp(requireContext(), currentInfo.getFeedUrl()); + } + break; + case R.id.menu_item_openInBrowser: + if (currentInfo != null) { + ShareUtils.openUrlInBrowser(requireContext(), + currentInfo.getOriginalUrl()); + } + break; + case R.id.menu_item_share: + if (currentInfo != null) { + ShareUtils.shareText(requireContext(), name, + currentInfo.getOriginalUrl(), currentInfo.getAvatars()); + } + break; + default: + return false; + } + return true; + } + }; + activity.addMenuProvider(menuProvider); } @Override @@ -183,67 +240,10 @@ public class ChannelFragment extends BaseStateFragment } disposables.clear(); binding = null; + activity.removeMenuProvider(menuProvider); + menuProvider = null; } - - /*////////////////////////////////////////////////////////////////////////// - // Menu - //////////////////////////////////////////////////////////////////////////*/ - - @Override - public void onCreateOptionsMenu(@NonNull final Menu menu, - @NonNull final MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_channel, menu); - - if (DEBUG) { - Log.d(TAG, "onCreateOptionsMenu() called with: " - + "menu = [" + menu + "], inflater = [" + inflater + "]"); - } - } - - @Override - public void onPrepareOptionsMenu(@NonNull final Menu menu) { - super.onPrepareOptionsMenu(menu); - menuRssButton = menu.findItem(R.id.menu_item_rss); - menuNotifyButton = menu.findItem(R.id.menu_item_notify); - updateNotifyButton(channelSubscription); - } - - @Override - public boolean onOptionsItemSelected(@NonNull final MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_item_notify: - final boolean value = !item.isChecked(); - item.setEnabled(false); - setNotify(value); - break; - case R.id.action_settings: - NavigationHelper.openSettings(requireContext()); - break; - case R.id.menu_item_rss: - if (currentInfo != null) { - ShareUtils.openUrlInApp(requireContext(), currentInfo.getFeedUrl()); - } - break; - case R.id.menu_item_openInBrowser: - if (currentInfo != null) { - ShareUtils.openUrlInBrowser(requireContext(), currentInfo.getOriginalUrl()); - } - break; - case R.id.menu_item_share: - if (currentInfo != null) { - ShareUtils.shareText(requireContext(), name, currentInfo.getOriginalUrl(), - currentInfo.getAvatars()); - } - break; - default: - return super.onOptionsItemSelected(item); - } - return true; - } - - /*////////////////////////////////////////////////////////////////////////// // Channel Subscription //////////////////////////////////////////////////////////////////////////*/ @@ -408,6 +408,13 @@ public class ChannelFragment extends BaseStateFragment animate(binding.channelSubscribeButton, true, 100, AnimationType.LIGHT_SCALE_AND_ALPHA); } + private void updateRssButton() { + if (menuRssButton == null || currentInfo == null) { + return; + } + menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.getFeedUrl())); + } + private void updateNotifyButton(@Nullable final SubscriptionEntity subscription) { if (menuNotifyButton == null) { return; @@ -610,9 +617,7 @@ public class ChannelFragment extends BaseStateFragment binding.subChannelAvatarView.setVisibility(View.VISIBLE); } - if (menuRssButton != null) { - menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl())); - } + updateRssButton(); channelContentNotSupported = false; for (final Throwable throwable : result.getErrors()) { From 46139340fed514498eb9fc97c88f61a836dc1d99 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Wed, 15 May 2024 06:51:52 +0530 Subject: [PATCH 05/11] Convert comment touch listener to a lambda --- .../holder/CommentInfoItemHolder.java | 26 +++++++++++- .../util/text/CommentTextOnTouchListener.java | 42 ------------------- 2 files changed, 24 insertions(+), 44 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/util/text/CommentTextOnTouchListener.java diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java index a3f0384ad..839aa1813 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java @@ -1,9 +1,13 @@ package org.schabi.newpipe.info_list.holder; import static org.schabi.newpipe.util.ServiceHelper.getServiceById; +import static org.schabi.newpipe.util.text.TouchUtils.getOffsetForHorizontalLine; +import android.text.Spanned; import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; import android.text.style.URLSpan; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -25,7 +29,6 @@ import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.util.image.ImageStrategy; import org.schabi.newpipe.util.image.PicassoHelper; -import org.schabi.newpipe.util.text.CommentTextOnTouchListener; import org.schabi.newpipe.util.text.TextEllipsizer; public class CommentInfoItemHolder extends InfoItemHolder { @@ -128,7 +131,26 @@ public class CommentInfoItemHolder extends InfoItemHolder { textEllipsizer.ellipsize(); //noinspection ClickableViewAccessibility - itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE); + itemContentView.setOnTouchListener((v, event) -> { + final CharSequence text = itemContentView.getText(); + if (text instanceof Spanned buffer) { + final int action = event.getAction(); + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { + final int offset = getOffsetForHorizontalLine(itemContentView, event); + final var links = buffer.getSpans(offset, offset, ClickableSpan.class); + + if (links.length != 0) { + if (action == MotionEvent.ACTION_UP) { + links[0].onClick(itemContentView); + } + // we handle events that intersect links, so return true + return true; + } + } + } + return false; + }); itemView.setOnClickListener(view -> { textEllipsizer.toggle(); diff --git a/app/src/main/java/org/schabi/newpipe/util/text/CommentTextOnTouchListener.java b/app/src/main/java/org/schabi/newpipe/util/text/CommentTextOnTouchListener.java deleted file mode 100644 index 5018a6120..000000000 --- a/app/src/main/java/org/schabi/newpipe/util/text/CommentTextOnTouchListener.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.schabi.newpipe.util.text; - -import static org.schabi.newpipe.util.text.TouchUtils.getOffsetForHorizontalLine; - -import android.annotation.SuppressLint; -import android.text.Spanned; -import android.text.style.ClickableSpan; -import android.view.MotionEvent; -import android.view.View; -import android.widget.TextView; - -public class CommentTextOnTouchListener implements View.OnTouchListener { - public static final CommentTextOnTouchListener INSTANCE = new CommentTextOnTouchListener(); - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouch(final View v, final MotionEvent event) { - if (!(v instanceof TextView)) { - return false; - } - final TextView widget = (TextView) v; - final CharSequence text = widget.getText(); - if (text instanceof Spanned) { - final Spanned buffer = (Spanned) text; - final int action = event.getAction(); - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { - final int offset = getOffsetForHorizontalLine(widget, event); - final ClickableSpan[] links = buffer.getSpans(offset, offset, ClickableSpan.class); - - if (links.length != 0) { - if (action == MotionEvent.ACTION_UP) { - links[0].onClick(widget); - } - // we handle events that intersect links, so return true - return true; - } - } - } - return false; - } -} From e48ce5a103112e97a3478d4656f41c8d03063281 Mon Sep 17 00:00:00 2001 From: moontoaster <74986506+moontoaster@users.noreply.github.com> Date: Thu, 23 May 2024 20:57:05 +0300 Subject: [PATCH 06/11] Update PrettyTime to 5.0.8 This version contains a fix for Ukrainian locale which fixes #11092. --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 28a208195..9aec02267 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -282,7 +282,7 @@ dependencies { implementation "com.jakewharton.rxbinding4:rxbinding:4.0.0" // Date and time formatting - implementation "org.ocpsoft.prettytime:prettytime:5.0.7.Final" + implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final" /** Debugging **/ // Memory leak detection From 2a3d133bcf74aeb6aff5a3e72c9d9955b08dc2e0 Mon Sep 17 00:00:00 2001 From: Neznak Date: Mon, 27 May 2024 14:08:18 +0300 Subject: [PATCH 07/11] Add missing Peertube instance subscribeto.me to the links Newpipe handles --- app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1127c55a4..d11de9f47 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -367,6 +367,7 @@ + From 8583c482640ccc7496b78ea97b8e50178af4142f Mon Sep 17 00:00:00 2001 From: Aryan Yadav <155174267+aryn-ydv@users.noreply.github.com> Date: Tue, 28 May 2024 10:14:46 +0530 Subject: [PATCH 08/11] fixes #11093 --- .../schabi/newpipe/fragments/list/playlist/PlaylistFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 9afb06344..6410fb9ee 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -352,6 +352,7 @@ public class PlaylistFragment extends BaseListInfoFragment ellipsizer.toggle()); + headerBinding.playlistDescription.setOnClickListener(v -> ellipsizer.toggle()); } else { headerBinding.playlistDescription.setVisibility(View.GONE); headerBinding.playlistDescriptionReadMore.setVisibility(View.GONE); From e64c01d2daf35c33001dfd12f46f30df765dfc01 Mon Sep 17 00:00:00 2001 From: Eric Driussi Date: Mon, 24 Jun 2024 09:47:29 +0100 Subject: [PATCH 09/11] Remove kotlin restriction --- .github/CONTRIBUTING.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 088707b6e..70c81c7b1 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -42,10 +42,6 @@ You'll see *exactly* what is sent, be able to add **your comments**, and then se * Create PRs that cover only **one specific issue/solution/bug**. Do not create PRs that are huge monoliths and could have been split into multiple independent contributions. * NewPipe uses [NewPipeExtractor](https://github.com/TeamNewPipe/NewPipeExtractor) to fetch data from services. If you need to change something there, you must test your changes in NewPipe. Telling NewPipe to use your extractor version can be accomplished by editing the `app/build.gradle` file: the comments under the "NewPipe libraries" section of `dependencies` will help you out. -### Kotlin in NewPipe -* NewPipe will remain mostly Java for time being -* Contributions containing a simple conversion from Java to Kotlin should be avoided. Conversions to Kotlin should only be done if Kotlin actually brings improvements like bug fixes or better performance which are not, or only with much more effort, implementable in Java. The core team sees Java as an easier to learn and generally well adopted programming language. - ### Creating a Pull Request (PR) * Make changes on a **separate branch** with a meaningful name, not on the _master_ branch or the _dev_ branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request (PR) on GitHub. From 2ac0d1f13a808a377fd9b8ebaa4e416d0059809c Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 2 Jul 2024 09:31:34 +0200 Subject: [PATCH 10/11] add NP icon for Android Studio's NewUI --- .idea/icon.svg | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .idea/icon.svg diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 000000000..51fdf95de --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + From 1f309854bcf36db8cb60d7da08c1c117aea58bf3 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 2 Jul 2024 17:37:09 +0200 Subject: [PATCH 11/11] Run CI on pull requests to refactor branch, too --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c750157c..9ae3a77c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: branches: - dev - master + - refactor - release** paths-ignore: - 'README.md'