Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
c43d65c
Add Either type with left, right and match functions
Stypox Jan 28, 2025
659b4c7
Start implementing LongPressMenu
Stypox Jan 28, 2025
cb626a1
Calculate button placing in long press menu
Stypox Jan 28, 2025
f0206bd
Treat decorations better
Stypox Jan 28, 2025
a8140a9
Add buttons to long press menu
Stypox Jan 28, 2025
4887852
Remove button background and make text 2 lines
Stypox Jan 28, 2025
bed5cdd
Add small Edit button in LongPressMenu
Stypox Jan 31, 2025
7f4cfcd
Click on long press menu subtitle opens channel details
Stypox Feb 11, 2025
be1aa0a
Initial work on handling many long-press actions
Stypox Feb 11, 2025
b3d7bf2
Make LongPressable a data class
Stypox Feb 11, 2025
522972b
Move long press menu drag handle to its own composable
Stypox Feb 11, 2025
34dac78
Improve how long press menu buttons are laid out
Stypox Feb 11, 2025
88f7a31
Dismiss long press menu after click on a button
Stypox Feb 11, 2025
0db7021
Add download long press menu action
Stypox Feb 11, 2025
16a9ec5
Move LongPressable builders to LongPressable class
Stypox Feb 11, 2025
2ec6eaa
Replace InfoItemDialog with LongPressMenu
Stypox Feb 12, 2025
3aac0e1
Remove InfoItemDialog
Stypox Feb 12, 2025
5d1d09a
Slight adjustments to long press menu
Stypox Feb 12, 2025
337be32
Add more previews to LongPressMenu
Stypox Feb 13, 2025
9ce22db
Use LongPressMenu in BookmarkFragment
Stypox Feb 14, 2025
549890e
Tune transparencies of decorations in long press menu
Stypox Feb 14, 2025
986da07
Use faded marquee text in long press menu header
Stypox Feb 14, 2025
df69602
Add long press actions to channels and playlists info items
Stypox Mar 14, 2025
7e27f76
Add OpenInNew icon next to channel name
Stypox Aug 17, 2025
7e46117
Fix some lints
Stypox Aug 27, 2025
e40856c
Add icons for play/background/popup from here
Stypox Aug 28, 2025
768fa8b
Uniform localizing view counts
Stypox Aug 28, 2025
cec06d2
Implement background/popup/play from here
Stypox Aug 28, 2025
f9aa6ea
Implement "play from here" for feed fragment
Stypox Aug 28, 2025
64ab0f4
Implement "play from here" for channels
Stypox Aug 28, 2025
fb3e286
Improve icons for background/popup/play from here
Stypox Aug 29, 2025
c6df92a
Implement long pressing on subscriptions
Stypox Aug 29, 2025
c6412e8
Consider duration 0 as duration not known
Stypox Oct 19, 2025
4ca5569
Address Isira review comment
Stypox Oct 19, 2025
00d8d11
Extract FixedHeightCenteredText from LongPressMenu
Stypox Oct 21, 2025
390413e
Implement LongPressMenuEditor UI (still not persisted)
Stypox Oct 21, 2025
75898fc
Add Back content description to toolbar back buttons
Stypox Oct 21, 2025
3bcb6de
Access editor from long press menu + fix scrolling
Stypox Oct 21, 2025
3e37912
Rewrite LongPressMenuEditor logic
Stypox Oct 22, 2025
5391a5b
Make LongPressMenuEditor work with DPAD / Android TV
Stypox Oct 23, 2025
d2b5f9f
Fix an edge case on the DragMarker position logic
Stypox Oct 23, 2025
3b49138
Handle scrolling on Android TV
Stypox Dec 24, 2025
b5d3a74
Autoscroll when dragging close to border
Stypox Dec 24, 2025
ef1bbcc
Fix long press menu on DPAD clicks onEditActions right after opened
Stypox Dec 24, 2025
9118b7a
DetectDragModifier now detects long-presses
Stypox Dec 30, 2025
7602089
Distinguish between isDraggable and isCaption
Stypox Dec 30, 2025
734a351
Make channel link less attractive
Stypox Dec 30, 2025
e0c8719
Tune long press menu UI
Stypox Dec 30, 2025
4d4fe70
Make it clearer when items are being dragged under the finger
Stypox Dec 30, 2025
793bd6f
Fix strange animations when quickly reording items
Stypox Jan 6, 2026
0f25b38
Improve some strings and add some comments
Stypox Jan 6, 2026
44cb2dc
Separate @Composables from state logic for actions editor
Stypox Jan 6, 2026
f053aec
Persist long press actions to settings
Stypox Jan 6, 2026
c88e943
Load settings in LongPressMenu too
Stypox Jan 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1379,14 +1379,9 @@ class VideoDetailFragment :
}

if (info.viewCount >= 0) {
binding.detailViewCountView.text =
if (info.streamType == StreamType.AUDIO_LIVE_STREAM) {
Localization.listeningCount(activity, info.viewCount)
} else if (info.streamType == StreamType.LIVE_STREAM) {
Localization.localizeWatchingCount(activity, info.viewCount)
} else {
Localization.localizeViewCount(activity, info.viewCount)
}
binding.detailViewCountView.text = Localization.localizeViewCount(
activity, false, info.streamType, info.viewCount
)
binding.detailViewCountView.visibility = View.VISIBLE
} else {
binding.detailViewCountView.visibility = View.GONE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
import static org.schabi.newpipe.ui.components.menu.LongPressMenuKt.openLongPressMenuInActivity;

import android.content.Context;
import android.content.SharedPreferences;
Expand All @@ -22,12 +23,15 @@
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.info_list.ItemViewMode;
import org.schabi.newpipe.info_list.dialog.InfoItemDialog;
import org.schabi.newpipe.ui.components.menu.LongPressAction;
import org.schabi.newpipe.ui.components.menu.LongPressable;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.util.StateSaver;
Expand Down Expand Up @@ -256,7 +260,10 @@ protected void initListeners() {
infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<>() {
@Override
public void selected(final StreamInfoItem selectedItem) {
onStreamSelected(selectedItem);
onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName(),
null, false);
}

@Override
Expand All @@ -265,23 +272,50 @@ public void held(final StreamInfoItem selectedItem) {
}
});

infoListAdapter.setOnChannelSelectedListener(selectedItem -> {
try {
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(getFM(), selectedItem.getServiceId(),
selectedItem.getUrl(), selectedItem.getName());
} catch (final Exception e) {
ErrorUtil.showUiErrorSnackbar(this, "Opening channel fragment", e);
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<>() {
@Override
public void selected(final ChannelInfoItem selectedItem) {
try {
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(getFM(), selectedItem.getServiceId(),
selectedItem.getUrl(), selectedItem.getName());
} catch (final Exception e) {
ErrorUtil.showUiErrorSnackbar(BaseListFragment.this, "Opening channel fragment",
e);
}
}

@Override
public void held(final ChannelInfoItem selectedItem) {
openLongPressMenuInActivity(
requireActivity(),
LongPressable.fromChannelInfoItem(selectedItem),
LongPressAction.fromChannelInfoItem(selectedItem, null)
);
}
});

infoListAdapter.setOnPlaylistSelectedListener(selectedItem -> {
try {
onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(getFM(), selectedItem.getServiceId(),
selectedItem.getUrl(), selectedItem.getName());
} catch (final Exception e) {
ErrorUtil.showUiErrorSnackbar(this, "Opening playlist fragment", e);
infoListAdapter.setOnPlaylistSelectedListener(new OnClickGesture<>() {
@Override
public void selected(final PlaylistInfoItem selectedItem) {
try {
BaseListFragment.this.onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(BaseListFragment.this.getFM(),
selectedItem.getServiceId(),
selectedItem.getUrl(), selectedItem.getName());
} catch (final Exception e) {
ErrorUtil.showUiErrorSnackbar(BaseListFragment.this,
"Opening playlist fragment", e);
}
}

@Override
public void held(final PlaylistInfoItem selectedItem) {
openLongPressMenuInActivity(
requireActivity(),
LongPressable.fromPlaylistInfoItem(selectedItem),
LongPressAction.fromPlaylistInfoItem(selectedItem)
);
}
});

Expand All @@ -291,6 +325,15 @@ public void held(final StreamInfoItem selectedItem) {
useNormalItemListScrollListener();
}

protected void showInfoItemDialog(final StreamInfoItem item) {
openLongPressMenuInActivity(
requireActivity(),
LongPressable.fromStreamInfoItem(item),
// TODO generalize obtaining queue from here when fully migrating to Compose
LongPressAction.fromStreamInfoItem(item, null)
);
}

/**
* Removes all listeners and adds the normal scroll listener to the {@link #itemsList}.
*/
Expand Down Expand Up @@ -373,27 +416,12 @@ public void onScrolledDown(final RecyclerView recyclerView) {
}
}

private void onStreamSelected(final StreamInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName(),
null, false);
}

protected void onScrollToBottom() {
if (hasMoreItems() && !isLoading.get()) {
loadMoreItems();
}
}

protected void showInfoItemDialog(final StreamInfoItem item) {
try {
new InfoItemDialog.Builder(getActivity(), getContext(), this, item).create().show();
} catch (final IllegalArgumentException e) {
InfoItemDialog.Builder.reportErrorDuringInitialization(e, item);
}
}

/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.schabi.newpipe.fragments.list.channel;

import static org.schabi.newpipe.ui.components.menu.LongPressMenuKt.openLongPressMenuInActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
Expand All @@ -26,12 +28,15 @@
import org.schabi.newpipe.fragments.list.playlist.PlaylistControlViewHolder;
import org.schabi.newpipe.player.playqueue.ChannelTabPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.ui.components.menu.LongPressAction;
import org.schabi.newpipe.ui.components.menu.LongPressable;
import org.schabi.newpipe.ui.emptystate.EmptyStateUtil;
import org.schabi.newpipe.util.ChannelTabHelper;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.PlayButtonHelper;

import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -165,13 +170,30 @@ public void handleResult(@NonNull final ChannelTabInfo result) {
}

@Override
public PlayQueue getPlayQueue() {
protected void showInfoItemDialog(final StreamInfoItem item) {
openLongPressMenuInActivity(
requireActivity(),
LongPressable.fromStreamInfoItem(item),
LongPressAction.fromStreamInfoItem(item, () -> getPlayQueueStartingAt(item))
);
}

private PlayQueue getPlayQueueStartingAt(final StreamInfoItem infoItem) {
return getPlayQueue(streamItems -> Math.max(streamItems.indexOf(infoItem), 0));
}

public PlayQueue getPlayQueue(final Function<List<StreamInfoItem>, Integer> index) {
final List<StreamInfoItem> streamItems = infoListAdapter.getItemsList().stream()
.filter(StreamInfoItem.class::isInstance)
.map(StreamInfoItem.class::cast)
.collect(Collectors.toList());

return new ChannelTabPlayQueue(currentInfo.getServiceId(), tabHandler,
currentInfo.getNextPage(), streamItems, 0);
currentInfo.getNextPage(), streamItems, index.apply(streamItems));
}

@Override
public PlayQueue getPlayQueue() {
return getPlayQueue(streamItems -> 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
import static org.schabi.newpipe.ui.components.menu.LongPressMenuKt.openLongPressMenuInActivity;
import static org.schabi.newpipe.util.ServiceHelper.getServiceById;

import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
Expand Down Expand Up @@ -42,12 +42,12 @@
import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.info_list.dialog.InfoItemDialog;
import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry;
import org.schabi.newpipe.local.dialog.PlaylistDialog;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
import org.schabi.newpipe.ui.components.menu.LongPressAction;
import org.schabi.newpipe.ui.components.menu.LongPressable;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
Expand Down Expand Up @@ -150,21 +150,11 @@ private PlayQueue getPlayQueueStartingAt(final StreamInfoItem infoItem) {

@Override
protected void showInfoItemDialog(final StreamInfoItem item) {
final Context context = getContext();
try {
final InfoItemDialog.Builder dialogBuilder =
new InfoItemDialog.Builder(getActivity(), context, this, item);

dialogBuilder
.setAction(
StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND,
(f, infoItem) -> NavigationHelper.playOnBackgroundPlayer(
context, getPlayQueueStartingAt(infoItem), true))
.create()
.show();
} catch (final IllegalArgumentException e) {
InfoItemDialog.Builder.reportErrorDuringInitialization(e, item);
}
openLongPressMenuInActivity(
activity,
LongPressable.fromStreamInfoItem(item),
LongPressAction.fromStreamInfoItem(item, () -> getPlayQueueStartingAt(item))
);
}

@Override
Expand Down
Loading