Replace InfoItemDialog with LongPressMenu
This commit is contained in:
parent
eca3486e09
commit
985872f2c1
@ -2,6 +2,7 @@ package org.schabi.newpipe.fragments.list;
|
||||
|
||||
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;
|
||||
@ -27,7 +28,8 @@ 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;
|
||||
@ -387,11 +389,11 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
openLongPressMenuInActivity(
|
||||
requireActivity(),
|
||||
LongPressable.fromStreamInfoItem(item),
|
||||
LongPressAction.fromStreamInfoItem(item)
|
||||
);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -3,10 +3,9 @@ package org.schabi.newpipe.fragments.list.playlist;
|
||||
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.getLongPressMenuView;
|
||||
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;
|
||||
@ -43,8 +42,6 @@ import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
|
||||
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;
|
||||
@ -153,35 +150,12 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
|
||||
|
||||
@Override
|
||||
protected void showInfoItemDialog(final StreamInfoItem item) {
|
||||
activity.addContentView(
|
||||
getLongPressMenuView(
|
||||
requireContext(),
|
||||
LongPressable.from(item),
|
||||
LongPressAction.buildActionList(item, false)
|
||||
),
|
||||
new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
openLongPressMenuInActivity(
|
||||
activity,
|
||||
LongPressable.fromStreamInfoItem(item),
|
||||
// TODO handle play queue starting at
|
||||
LongPressAction.fromStreamInfoItem(item)
|
||||
);
|
||||
if (Context.class.getSimpleName().startsWith("C")) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
package org.schabi.newpipe.local.feed
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
@ -65,17 +64,18 @@ import org.schabi.newpipe.error.ErrorUtil
|
||||
import org.schabi.newpipe.error.UserAction
|
||||
import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty
|
||||
import org.schabi.newpipe.fragments.BaseStateFragment
|
||||
import org.schabi.newpipe.info_list.ItemViewMode
|
||||
import org.schabi.newpipe.info_list.dialog.InfoItemDialog
|
||||
import org.schabi.newpipe.ktx.animate
|
||||
import org.schabi.newpipe.ktx.animateHideRecyclerViewAllowingScrolling
|
||||
import org.schabi.newpipe.ktx.slideUp
|
||||
import org.schabi.newpipe.local.feed.item.StreamItem
|
||||
import org.schabi.newpipe.local.feed.service.FeedLoadService
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
||||
import org.schabi.newpipe.ui.components.menu.LongPressAction
|
||||
import org.schabi.newpipe.ui.components.menu.LongPressable
|
||||
import org.schabi.newpipe.ui.components.menu.openLongPressMenuInActivity
|
||||
import org.schabi.newpipe.ui.emptystate.setEmptyStateComposable
|
||||
import org.schabi.newpipe.util.DeviceUtils
|
||||
import org.schabi.newpipe.util.Localization
|
||||
@ -381,14 +381,6 @@ class FeedFragment : BaseStateFragment<FeedState>() {
|
||||
feedBinding.loadingProgressBar.max = progressState.maxProgress
|
||||
}
|
||||
|
||||
private fun showInfoItemDialog(item: StreamInfoItem) {
|
||||
val context = context
|
||||
val activity: Activity? = getActivity()
|
||||
if (context == null || context.resources == null || activity == null) return
|
||||
|
||||
InfoItemDialog.Builder(activity, context, this, item).create().show()
|
||||
}
|
||||
|
||||
private val listenerStreamItem = object : OnItemClickListener, OnItemLongClickListener {
|
||||
override fun onItemClick(item: Item<*>, view: View) {
|
||||
if (item is StreamItem && !isRefreshing) {
|
||||
@ -407,7 +399,11 @@ class FeedFragment : BaseStateFragment<FeedState>() {
|
||||
|
||||
override fun onItemLongClick(item: Item<*>, view: View): Boolean {
|
||||
if (item is StreamItem && !isRefreshing) {
|
||||
showInfoItemDialog(item.streamWithState.stream.toStreamInfoItem())
|
||||
openLongPressMenuInActivity(
|
||||
requireActivity(),
|
||||
LongPressable.fromStreamEntity(item.streamWithState.stream),
|
||||
LongPressAction.fromStreamEntity(item.streamWithState.stream),
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe.local.history;
|
||||
|
||||
import android.content.Context;
|
||||
import static org.schabi.newpipe.ui.components.menu.LongPressMenuKt.openLongPressMenuInActivity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.view.LayoutInflater;
|
||||
@ -9,13 +10,11 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.evernote.android.state.State;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
@ -29,12 +28,12 @@ import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.list.playlist.PlaylistControlViewHolder;
|
||||
import org.schabi.newpipe.info_list.dialog.InfoItemDialog;
|
||||
import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry;
|
||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.settings.HistorySettingsFragment;
|
||||
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.PlayButtonHelper;
|
||||
@ -48,7 +47,6 @@ import java.util.function.Supplier;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public class StatisticsPlaylistFragment
|
||||
extends BaseLocalListFragment<List<StreamStatisticsEntry>, Void>
|
||||
@ -318,50 +316,11 @@ public class StatisticsPlaylistFragment
|
||||
}
|
||||
|
||||
private void showInfoItemDialog(final StreamStatisticsEntry item) {
|
||||
final Context context = getContext();
|
||||
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
||||
|
||||
try {
|
||||
final InfoItemDialog.Builder dialogBuilder =
|
||||
new InfoItemDialog.Builder(getActivity(), context, this, infoItem);
|
||||
|
||||
// set entries in the middle; the others are added automatically
|
||||
dialogBuilder
|
||||
.addEntry(StreamDialogDefaultEntry.DELETE)
|
||||
.setAction(
|
||||
StreamDialogDefaultEntry.DELETE,
|
||||
(f, i) -> deleteEntry(
|
||||
Math.max(itemListAdapter.getItemsList().indexOf(item), 0)))
|
||||
.create()
|
||||
.show();
|
||||
} catch (final IllegalArgumentException e) {
|
||||
InfoItemDialog.Builder.reportErrorDuringInitialization(e, infoItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteEntry(final int index) {
|
||||
final LocalItem infoItem = itemListAdapter.getItemsList().get(index);
|
||||
if (infoItem instanceof StreamStatisticsEntry) {
|
||||
final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem;
|
||||
final Disposable onDelete = recordManager
|
||||
.deleteStreamHistoryAndState(entry.getStreamId())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
() -> {
|
||||
if (getView() != null) {
|
||||
Snackbar.make(getView(), R.string.one_item_deleted,
|
||||
Snackbar.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(getContext(),
|
||||
R.string.one_item_deleted,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
},
|
||||
throwable -> showSnackBarError(new ErrorInfo(throwable,
|
||||
UserAction.DELETE_FROM_HISTORY, "Deleting item")));
|
||||
|
||||
disposables.add(onDelete);
|
||||
}
|
||||
openLongPressMenuInActivity(
|
||||
requireActivity(),
|
||||
LongPressable.fromStreamEntity(item.getStreamEntity()),
|
||||
LongPressAction.fromStreamStatisticsEntry(item)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -8,6 +8,7 @@ import static org.schabi.newpipe.local.playlist.ExportPlaylistKt.export;
|
||||
import static org.schabi.newpipe.local.playlist.PlayListShareMode.JUST_URLS;
|
||||
import static org.schabi.newpipe.local.playlist.PlayListShareMode.WITH_TITLES;
|
||||
import static org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST;
|
||||
import static org.schabi.newpipe.ui.components.menu.LongPressMenuKt.openLongPressMenuInActivity;
|
||||
import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout;
|
||||
|
||||
|
||||
@ -52,13 +53,13 @@ import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.MainFragment;
|
||||
import org.schabi.newpipe.fragments.list.playlist.PlaylistControlViewHolder;
|
||||
import org.schabi.newpipe.info_list.dialog.InfoItemDialog;
|
||||
import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry;
|
||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.util.DeviceUtils;
|
||||
import org.schabi.newpipe.ui.components.menu.LongPressAction;
|
||||
import org.schabi.newpipe.ui.components.menu.LongPressable;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.OnClickGesture;
|
||||
@ -789,39 +790,16 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||
}
|
||||
|
||||
protected void showInfoItemDialog(final PlaylistStreamEntry item) {
|
||||
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
||||
|
||||
try {
|
||||
final Context context = getContext();
|
||||
final InfoItemDialog.Builder dialogBuilder =
|
||||
new InfoItemDialog.Builder(getActivity(), context, this, infoItem);
|
||||
|
||||
// add entries in the middle
|
||||
dialogBuilder.addAllEntries(
|
||||
StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL,
|
||||
StreamDialogDefaultEntry.DELETE
|
||||
);
|
||||
|
||||
// set custom actions
|
||||
// all entries modified below have already been added within the builder
|
||||
dialogBuilder
|
||||
.setAction(
|
||||
StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND,
|
||||
(f, i) -> NavigationHelper.playOnBackgroundPlayer(
|
||||
context, getPlayQueueStartingAt(item), true))
|
||||
.setAction(
|
||||
StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL,
|
||||
(f, i) ->
|
||||
changeThumbnailStreamId(item.getStreamEntity().getUid(),
|
||||
true))
|
||||
.setAction(
|
||||
StreamDialogDefaultEntry.DELETE,
|
||||
(f, i) -> deleteItem(item))
|
||||
.create()
|
||||
.show();
|
||||
} catch (final IllegalArgumentException e) {
|
||||
InfoItemDialog.Builder.reportErrorDuringInitialization(e, infoItem);
|
||||
}
|
||||
openLongPressMenuInActivity(
|
||||
requireActivity(),
|
||||
LongPressable.fromStreamEntity(item.getStreamEntity()),
|
||||
// TODO getPlayQueueStartingAt(), resumePlayback=true
|
||||
LongPressAction.fromPlaylistStreamEntry(
|
||||
item,
|
||||
() -> deleteItem(item),
|
||||
() -> changeThumbnailStreamId(item.getStreamEntity().getUid(), true)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void setInitialData(final long pid, final String title) {
|
||||
|
||||
@ -5,10 +5,7 @@ import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@ -59,20 +56,6 @@ fun ItemList(
|
||||
}
|
||||
}
|
||||
|
||||
// Handle long clicks for stream items
|
||||
// TODO: Adjust the menu display depending on where it was triggered
|
||||
var selectedStream by remember { mutableStateOf<StreamInfoItem?>(null) }
|
||||
val onLongClick = remember {
|
||||
{ stream: StreamInfoItem ->
|
||||
selectedStream = stream
|
||||
}
|
||||
}
|
||||
val onDismissPopup = remember {
|
||||
{
|
||||
selectedStream = null
|
||||
}
|
||||
}
|
||||
|
||||
val showProgress = DependentPreferenceHelper.getPositionsInListsEnabled(context)
|
||||
val nestedScrollModifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())
|
||||
|
||||
@ -89,15 +72,7 @@ fun ItemList(
|
||||
val item = items[it]
|
||||
|
||||
if (item is StreamInfoItem) {
|
||||
val isSelected = selectedStream == item
|
||||
StreamListItem(
|
||||
item,
|
||||
showProgress,
|
||||
isSelected,
|
||||
onClick,
|
||||
onLongClick,
|
||||
onDismissPopup
|
||||
)
|
||||
StreamListItem(item, showProgress, onClick)
|
||||
} else if (item is PlaylistInfoItem) {
|
||||
PlaylistListItem(item, onClick)
|
||||
}
|
||||
|
||||
@ -10,10 +10,15 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@ -21,22 +26,26 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.ui.components.menu.LongPressAction
|
||||
import org.schabi.newpipe.ui.components.menu.LongPressMenu
|
||||
import org.schabi.newpipe.ui.components.menu.LongPressable
|
||||
import org.schabi.newpipe.ui.theme.AppTheme
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun StreamListItem(
|
||||
stream: StreamInfoItem,
|
||||
showProgress: Boolean,
|
||||
isSelected: Boolean,
|
||||
onClick: (StreamInfoItem) -> Unit = {},
|
||||
onLongClick: (StreamInfoItem) -> Unit = {},
|
||||
onDismissPopup: () -> Unit = {}
|
||||
) {
|
||||
// Box serves as an anchor for the dropdown menu
|
||||
var showLongPressMenu by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.combinedClickable(onLongClick = { onLongClick(stream) }, onClick = { onClick(stream) })
|
||||
.combinedClickable(
|
||||
onLongClick = { showLongPressMenu = true },
|
||||
onClick = { onClick(stream) }
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp)
|
||||
) {
|
||||
@ -67,7 +76,13 @@ fun StreamListItem(
|
||||
}
|
||||
}
|
||||
|
||||
StreamMenu(stream, isSelected, onDismissPopup)
|
||||
if (showLongPressMenu) {
|
||||
LongPressMenu(
|
||||
longPressable = LongPressable.fromStreamInfoItem(stream),
|
||||
longPressActions = LongPressAction.fromStreamInfoItem(stream),
|
||||
onDismissRequest = { showLongPressMenu = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +94,7 @@ private fun StreamListItemPreview(
|
||||
) {
|
||||
AppTheme {
|
||||
Surface {
|
||||
StreamListItem(stream, showProgress = false, isSelected = false)
|
||||
StreamListItem(stream, showProgress = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,142 +0,0 @@
|
||||
package org.schabi.newpipe.ui.components.items.stream
|
||||
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import org.schabi.newpipe.download.DownloadDialog
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.ktx.findFragmentActivity
|
||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog
|
||||
import org.schabi.newpipe.local.dialog.PlaylistDialog
|
||||
import org.schabi.newpipe.player.helper.PlayerHolder
|
||||
import org.schabi.newpipe.util.NavigationHelper
|
||||
import org.schabi.newpipe.util.SparseItemUtil
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils
|
||||
import org.schabi.newpipe.viewmodels.StreamViewModel
|
||||
|
||||
@Composable
|
||||
fun StreamMenu(
|
||||
stream: StreamInfoItem,
|
||||
expanded: Boolean,
|
||||
onDismissRequest: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val streamViewModel = viewModel<StreamViewModel>()
|
||||
|
||||
DropdownMenu(expanded = expanded, onDismissRequest = onDismissRequest) {
|
||||
if (PlayerHolder.isPlayQueueReady) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.enqueue_stream)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
SparseItemUtil.fetchItemInfoIfSparse(context, stream) {
|
||||
NavigationHelper.enqueueOnPlayer(context, it)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (PlayerHolder.queuePosition < PlayerHolder.queueSize - 1) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.enqueue_next_stream)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
SparseItemUtil.fetchItemInfoIfSparse(context, stream) {
|
||||
NavigationHelper.enqueueNextOnPlayer(context, it)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.start_here_on_background)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
SparseItemUtil.fetchItemInfoIfSparse(context, stream) {
|
||||
NavigationHelper.playOnBackgroundPlayer(context, it, true)
|
||||
}
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.start_here_on_popup)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
SparseItemUtil.fetchItemInfoIfSparse(context, stream) {
|
||||
NavigationHelper.playOnPopupPlayer(context, it, true)
|
||||
}
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.download)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
SparseItemUtil.fetchStreamInfoAndSaveToDatabase(
|
||||
context,
|
||||
stream.serviceId,
|
||||
stream.url
|
||||
) { info ->
|
||||
// TODO: Use an AlertDialog composable instead.
|
||||
val downloadDialog = DownloadDialog(context, info)
|
||||
val fragmentManager = context.findFragmentActivity().supportFragmentManager
|
||||
downloadDialog.show(fragmentManager, "downloadDialog")
|
||||
}
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.add_to_playlist)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
val list = listOf(StreamEntity(stream))
|
||||
PlaylistDialog.createCorrespondingDialog(context, list) { dialog ->
|
||||
val tag = if (dialog is PlaylistAppendDialog) "append" else "create"
|
||||
dialog.show(
|
||||
context.findFragmentActivity().supportFragmentManager,
|
||||
"StreamDialogEntry@${tag}_playlist"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.share)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
ShareUtils.shareText(context, stream.name, stream.url, stream.thumbnails)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.open_in_browser)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
ShareUtils.openUrlInBrowser(context, stream.url)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.mark_as_watched)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
streamViewModel.markAsWatched(stream)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.show_channel_details)) },
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
SparseItemUtil.fetchUploaderUrlIfSparse(
|
||||
context,
|
||||
stream.serviceId,
|
||||
stream.url,
|
||||
stream.uploaderUrl
|
||||
) { url ->
|
||||
val activity = context.findFragmentActivity()
|
||||
NavigationHelper.openChannelFragment(activity, stream, url)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
package org.schabi.newpipe.ui.components.menu
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.PlaylistAdd
|
||||
@ -12,6 +12,7 @@ import androidx.compose.material.icons.filled.Done
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.Headset
|
||||
import androidx.compose.material.icons.filled.OpenInBrowser
|
||||
import androidx.compose.material.icons.filled.Panorama
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.PictureInPicture
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
@ -20,6 +21,8 @@ import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry
|
||||
import org.schabi.newpipe.database.stream.StreamStatisticsEntry
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import org.schabi.newpipe.download.DownloadDialog
|
||||
import org.schabi.newpipe.error.ErrorInfo
|
||||
@ -35,7 +38,6 @@ import org.schabi.newpipe.player.playqueue.PlayQueue
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue
|
||||
import org.schabi.newpipe.util.NavigationHelper
|
||||
import org.schabi.newpipe.util.SparseItemUtil
|
||||
import org.schabi.newpipe.util.external_communication.KoreUtils
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils
|
||||
|
||||
data class LongPressAction(
|
||||
@ -60,6 +62,7 @@ data class LongPressAction(
|
||||
ShowChannelDetails(R.string.show_channel_details, Icons.Default.Person),
|
||||
MarkAsWatched(R.string.mark_as_watched, Icons.Default.Done),
|
||||
Delete(R.string.delete, Icons.Default.Delete),
|
||||
SetAsPlaylistThumbnail(R.string.set_as_playlist_thumbnail, Icons.Default.Panorama),
|
||||
;
|
||||
|
||||
// TODO allow actions to return disposables
|
||||
@ -104,9 +107,9 @@ data class LongPressAction(
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun buildActionList(
|
||||
fun fromStreamInfoItem(
|
||||
item: StreamInfoItem,
|
||||
isKodiEnabled: Boolean,
|
||||
/* TODO isKodiEnabled: Boolean, */
|
||||
/* TODO wholeListQueue: (() -> PlayQueue)? */
|
||||
): List<LongPressAction> {
|
||||
return buildPlayerActionList { SinglePlayQueue(item) } +
|
||||
@ -163,11 +166,57 @@ data class LongPressAction(
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe()
|
||||
},
|
||||
) + if (isKodiEnabled) listOf(
|
||||
)
|
||||
/* TODO handle kodi
|
||||
+ if (isKodiEnabled) listOf(
|
||||
Type.PlayWithKodi.buildAction { context ->
|
||||
KoreUtils.playWithKore(context, Uri.parse(item.url))
|
||||
},
|
||||
) else listOf()
|
||||
) else listOf()*/
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromStreamEntity(
|
||||
item: StreamEntity,
|
||||
): List<LongPressAction> {
|
||||
// TODO decide if it's fine to just convert to StreamInfoItem here (it poses an
|
||||
// unnecessary dependency on the extractor, when we want to just look at data; maybe
|
||||
// using something like LongPressable would work)
|
||||
return fromStreamInfoItem(item.toStreamInfoItem())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromStreamStatisticsEntry(
|
||||
item: StreamStatisticsEntry,
|
||||
): List<LongPressAction> {
|
||||
return fromStreamEntity(item.streamEntity) +
|
||||
listOf(
|
||||
Type.Delete.buildAction { context ->
|
||||
HistoryRecordManager(context)
|
||||
.deleteStreamHistoryAndState(item.streamId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
Toast.makeText(
|
||||
context,
|
||||
R.string.one_item_deleted,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromPlaylistStreamEntry(
|
||||
item: PlaylistStreamEntry,
|
||||
onDelete: Runnable,
|
||||
onSetAsPlaylistThumbnail: Runnable,
|
||||
): List<LongPressAction> {
|
||||
return fromStreamEntity(item.streamEntity) +
|
||||
listOf(
|
||||
Type.Delete.buildAction { onDelete.run() },
|
||||
Type.SetAsPlaylistThumbnail.buildAction { onSetAsPlaylistThumbnail.run() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,9 +2,11 @@
|
||||
|
||||
package org.schabi.newpipe.ui.components.menu
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
@ -73,6 +75,17 @@ import org.schabi.newpipe.util.Either
|
||||
import org.schabi.newpipe.util.Localization
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
fun openLongPressMenuInActivity(
|
||||
activity: Activity,
|
||||
longPressable: LongPressable,
|
||||
longPressActions: List<LongPressAction>,
|
||||
) {
|
||||
activity.addContentView(
|
||||
getLongPressMenuView(activity, longPressable, longPressActions),
|
||||
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||
)
|
||||
}
|
||||
|
||||
fun getLongPressMenuView(
|
||||
context: Context,
|
||||
longPressable: LongPressable,
|
||||
@ -83,9 +96,8 @@ fun getLongPressMenuView(
|
||||
AppTheme {
|
||||
LongPressMenu(
|
||||
longPressable = longPressable,
|
||||
onDismissRequest = { (this.parent as ViewGroup).removeView(this) },
|
||||
longPressActions = longPressActions,
|
||||
onEditActions = {},
|
||||
onDismissRequest = { (this.parent as ViewGroup).removeView(this) },
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -95,9 +107,9 @@ fun getLongPressMenuView(
|
||||
@Composable
|
||||
fun LongPressMenu(
|
||||
longPressable: LongPressable,
|
||||
onDismissRequest: () -> Unit,
|
||||
longPressActions: List<LongPressAction>,
|
||||
onEditActions: () -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
onEditActions: () -> Unit = {}, // TODO handle this menu
|
||||
sheetState: SheetState = rememberModalBottomSheetState(),
|
||||
) {
|
||||
ModalBottomSheet(
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
package org.schabi.newpipe.ui.components.menu
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.extractor.stream.StreamType
|
||||
import org.schabi.newpipe.extractor.stream.StreamType.AUDIO_LIVE_STREAM
|
||||
import org.schabi.newpipe.extractor.stream.StreamType.LIVE_STREAM
|
||||
import org.schabi.newpipe.util.Either
|
||||
import org.schabi.newpipe.util.image.ImageStrategy
|
||||
import java.time.OffsetDateTime
|
||||
@ -22,11 +25,20 @@ data class LongPressable(
|
||||
data class Duration(val duration: Long) : Decoration
|
||||
data object Live : Decoration
|
||||
data class Playlist(val itemCount: Long) : Decoration
|
||||
|
||||
companion object {
|
||||
internal fun from(streamType: StreamType, duration: Long) =
|
||||
if (streamType == LIVE_STREAM || streamType == AUDIO_LIVE_STREAM) {
|
||||
Live
|
||||
} else {
|
||||
duration.takeIf { it >= 0 }?.let { Duration(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun from(item: StreamInfoItem) = LongPressable(
|
||||
fun fromStreamInfoItem(item: StreamInfoItem) = LongPressable(
|
||||
title = item.name,
|
||||
url = item.url?.takeIf { it.isNotBlank() },
|
||||
thumbnailUrl = ImageStrategy.choosePreferredImage(item.thumbnails),
|
||||
@ -35,15 +47,20 @@ data class LongPressable(
|
||||
viewCount = item.viewCount.takeIf { it >= 0 },
|
||||
uploadDate = item.uploadDate?.let { Either.right(it.offsetDateTime()) }
|
||||
?: item.textualUploadDate?.let { Either.left(it) },
|
||||
decoration = if (item.streamType == StreamType.LIVE_STREAM ||
|
||||
item.streamType == StreamType.AUDIO_LIVE_STREAM
|
||||
) {
|
||||
LongPressable.Decoration.Live
|
||||
} else {
|
||||
item.duration.takeIf { it >= 0 }?.let {
|
||||
LongPressable.Decoration.Duration(it)
|
||||
}
|
||||
},
|
||||
decoration = Decoration.from(item.streamType, item.duration),
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun fromStreamEntity(item: StreamEntity) = LongPressable(
|
||||
title = item.title,
|
||||
url = item.url.takeIf { it.isNotBlank() },
|
||||
thumbnailUrl = item.thumbnailUrl,
|
||||
uploader = item.uploader.takeIf { it.isNotBlank() },
|
||||
uploaderUrl = item.uploaderUrl?.takeIf { it.isNotBlank() },
|
||||
viewCount = item.viewCount?.takeIf { it >= 0 },
|
||||
uploadDate = item.uploadDate?.let { Either.right(it) }
|
||||
?: item.textualUploadDate?.let { Either.left(it) },
|
||||
decoration = Decoration.from(item.streamType, item.duration),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user