mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-02 22:30:40 +00:00
Added support for long-press gesture to open options menu.
This commit is contained in:
parent
116dc90d21
commit
d34cb0f9c1
9 changed files with 111 additions and 25 deletions
|
@ -27,6 +27,7 @@ import com.futo.platformplayer.views.adapters.InsertedViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.PreviewNestedVideoViewHolder
|
import com.futo.platformplayer.views.adapters.PreviewNestedVideoViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.PreviewVideoViewHolder
|
import com.futo.platformplayer.views.adapters.PreviewVideoViewHolder
|
||||||
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
|
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
|
||||||
|
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent, IPlatformContent, IPager<IPlatformContent>, ContentPreviewViewHolder> where TFragment : MainFragment {
|
abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent, IPlatformContent, IPager<IPlatformContent>, ContentPreviewViewHolder> where TFragment : MainFragment {
|
||||||
|
@ -37,6 +38,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||||
private var _previewsEnabled: Boolean = true;
|
private var _previewsEnabled: Boolean = true;
|
||||||
override val visibleThreshold: Int get() = if (feedStyle == FeedStyle.PREVIEW) { 5 } else { 10 };
|
override val visibleThreshold: Int get() = if (feedStyle == FeedStyle.PREVIEW) { 5 } else { 10 };
|
||||||
protected lateinit var headerView: LinearLayout;
|
protected lateinit var headerView: LinearLayout;
|
||||||
|
private var _videoOptionsOverlay: SlideUpMenuOverlay? = null;
|
||||||
|
|
||||||
constructor(fragment: TFragment, inflater: LayoutInflater, cachedRecyclerData: RecyclerData<InsertedViewAdapterWithLoader<ContentPreviewViewHolder>, LinearLayoutManager, IPager<IPlatformContent>, IPlatformContent, IPlatformContent, InsertedViewHolder<ContentPreviewViewHolder>>? = null) : super(fragment, inflater, cachedRecyclerData) {
|
constructor(fragment: TFragment, inflater: LayoutInflater, cachedRecyclerData: RecyclerData<InsertedViewAdapterWithLoader<ContentPreviewViewHolder>, LinearLayoutManager, IPager<IPlatformContent>, IPlatformContent, IPlatformContent, InsertedViewHolder<ContentPreviewViewHolder>>? = null) : super(fragment, inflater, cachedRecyclerData) {
|
||||||
|
|
||||||
|
@ -70,26 +72,8 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||||
adapter.onChannelClicked.subscribe(this) { fragment.navigate<ChannelFragment>(it) };
|
adapter.onChannelClicked.subscribe(this) { fragment.navigate<ChannelFragment>(it) };
|
||||||
adapter.onAddToClicked.subscribe(this) { content ->
|
adapter.onAddToClicked.subscribe(this) { content ->
|
||||||
//TODO: Reconstruct search video from detail if search is null
|
//TODO: Reconstruct search video from detail if search is null
|
||||||
_overlayContainer.let {
|
if(content is IPlatformVideo) {
|
||||||
if(content is IPlatformVideo)
|
showVideoOptionsOverlay(content)
|
||||||
UISlideOverlays.showVideoOptionsOverlay(content, it, SlideUpMenuItem(context, R.drawable.ic_visibility_off, context.getString(R.string.hide), context.getString(R.string.hide_from_home), "hide",
|
|
||||||
{ StateMeta.instance.addHiddenVideo(content.url);
|
|
||||||
if (fragment is HomeFragment) {
|
|
||||||
val removeIndex = recyclerData.results.indexOf(content);
|
|
||||||
if (removeIndex >= 0) {
|
|
||||||
recyclerData.results.removeAt(removeIndex);
|
|
||||||
recyclerData.adapter.notifyItemRemoved(recyclerData.adapter.childToParentPosition(removeIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
SlideUpMenuItem(context, R.drawable.ic_playlist, context.getString(R.string.play_feed_as_queue), context.getString(R.string.play_entire_feed), "playFeed",
|
|
||||||
{
|
|
||||||
val newQueue = listOf(content) + recyclerData.results
|
|
||||||
.filterIsInstance<IPlatformVideo>()
|
|
||||||
.filter { it != content };
|
|
||||||
StatePlayer.instance.setQueue(newQueue, StatePlayer.TYPE_QUEUE, "Feed Queue", true, false);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
adapter.onAddToQueueClicked.subscribe(this) {
|
adapter.onAddToQueueClicked.subscribe(this) {
|
||||||
|
@ -99,6 +83,50 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||||
UIDialogs.toast(context, context.getString(R.string.queued) + " [$name]", false);
|
UIDialogs.toast(context, context.getString(R.string.queued) + " [$name]", false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
adapter.onLongPress.subscribe(this) {
|
||||||
|
if (it is IPlatformVideo) {
|
||||||
|
showVideoOptionsOverlay(it)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBackPressed(): Boolean {
|
||||||
|
val videoOptionsOverlay = _videoOptionsOverlay
|
||||||
|
if (videoOptionsOverlay != null) {
|
||||||
|
if (videoOptionsOverlay.isVisible) {
|
||||||
|
videoOptionsOverlay.hide();
|
||||||
|
_videoOptionsOverlay = null
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_videoOptionsOverlay = null
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showVideoOptionsOverlay(content: IPlatformVideo) {
|
||||||
|
_overlayContainer.let {
|
||||||
|
_videoOptionsOverlay = UISlideOverlays.showVideoOptionsOverlay(content, it, SlideUpMenuItem(context, R.drawable.ic_visibility_off, context.getString(R.string.hide), context.getString(R.string.hide_from_home), "hide",
|
||||||
|
{ StateMeta.instance.addHiddenVideo(content.url);
|
||||||
|
if (fragment is HomeFragment) {
|
||||||
|
val removeIndex = recyclerData.results.indexOf(content);
|
||||||
|
if (removeIndex >= 0) {
|
||||||
|
recyclerData.results.removeAt(removeIndex);
|
||||||
|
recyclerData.adapter.notifyItemRemoved(recyclerData.adapter.childToParentPosition(removeIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
SlideUpMenuItem(context, R.drawable.ic_playlist, context.getString(R.string.play_feed_as_queue), context.getString(R.string.play_entire_feed), "playFeed",
|
||||||
|
{
|
||||||
|
val newQueue = listOf(content) + recyclerData.results
|
||||||
|
.filterIsInstance<IPlatformVideo>()
|
||||||
|
.filter { it != content };
|
||||||
|
StatePlayer.instance.setQueue(newQueue, StatePlayer.TYPE_QUEUE, "Feed Queue", true, false);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun detachAdapterEvents() {
|
private fun detachAdapterEvents() {
|
||||||
|
@ -108,6 +136,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||||
adapter.onChannelClicked.remove(this);
|
adapter.onChannelClicked.remove(this);
|
||||||
adapter.onAddToClicked.remove(this);
|
adapter.onAddToClicked.remove(this);
|
||||||
adapter.onAddToQueueClicked.remove(this);
|
adapter.onAddToQueueClicked.remove(this);
|
||||||
|
adapter.onLongPress.remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRestoreCachedData(cachedData: RecyclerData<InsertedViewAdapterWithLoader<ContentPreviewViewHolder>, LinearLayoutManager, IPager<IPlatformContent>, IPlatformContent, IPlatformContent, InsertedViewHolder<ContentPreviewViewHolder>>) {
|
override fun onRestoreCachedData(cachedData: RecyclerData<InsertedViewAdapterWithLoader<ContentPreviewViewHolder>, LinearLayoutManager, IPager<IPlatformContent>, IPlatformContent, IPlatformContent, InsertedViewHolder<ContentPreviewViewHolder>>) {
|
||||||
|
|
|
@ -62,6 +62,13 @@ class ContentSearchResultsFragment : MainFragment() {
|
||||||
_view = null;
|
_view = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean {
|
||||||
|
if (_view?.onBackPressed() == true)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
fun setPreviewsEnabled(previewsEnabled: Boolean) {
|
fun setPreviewsEnabled(previewsEnabled: Boolean) {
|
||||||
_view?.setPreviewsEnabled(previewsEnabled && Settings.instance.search.previewFeedItems);
|
_view?.setPreviewsEnabled(previewsEnabled && Settings.instance.search.previewFeedItems);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,13 @@ class HomeFragment : MainFragment() {
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean {
|
||||||
|
if (_view?.onBackPressed() == true)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyMainView() {
|
override fun onDestroyMainView() {
|
||||||
super.onDestroyMainView();
|
super.onDestroyMainView();
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,13 @@ class PlaylistSearchResultsFragment : MainFragment() {
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean {
|
||||||
|
if (_view?.onBackPressed() == true)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyMainView() {
|
override fun onDestroyMainView() {
|
||||||
super.onDestroyMainView();
|
super.onDestroyMainView();
|
||||||
_view?.cleanup();
|
_view?.cleanup();
|
||||||
|
|
|
@ -80,6 +80,13 @@ class SubscriptionsFeedFragment : MainFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean {
|
||||||
|
if (_view?.onBackPressed() == true)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
fun setPreviewsEnabled(previewsEnabled: Boolean) {
|
fun setPreviewsEnabled(previewsEnabled: Boolean) {
|
||||||
_view?.setPreviewsEnabled(previewsEnabled && Settings.instance.subscriptions.previewFeedItems);
|
_view?.setPreviewsEnabled(previewsEnabled && Settings.instance.subscriptions.previewFeedItems);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ class PreviewContentListAdapter : InsertedViewAdapterWithLoader<ContentPreviewVi
|
||||||
val onChannelClicked = Event1<PlatformAuthorLink>();
|
val onChannelClicked = Event1<PlatformAuthorLink>();
|
||||||
val onAddToClicked = Event1<IPlatformContent>();
|
val onAddToClicked = Event1<IPlatformContent>();
|
||||||
val onAddToQueueClicked = Event1<IPlatformContent>();
|
val onAddToQueueClicked = Event1<IPlatformContent>();
|
||||||
|
val onLongPress = Event1<IPlatformContent>();
|
||||||
|
|
||||||
private var _taskLoadContent = TaskHandler<Pair<ContentPreviewViewHolder, IPlatformContent>, Pair<ContentPreviewViewHolder, IPlatformContentDetails>>(
|
private var _taskLoadContent = TaskHandler<Pair<ContentPreviewViewHolder, IPlatformContent>, Pair<ContentPreviewViewHolder, IPlatformContentDetails>>(
|
||||||
StateApp.instance.scopeGetter, { (viewHolder, video) ->
|
StateApp.instance.scopeGetter, { (viewHolder, video) ->
|
||||||
|
@ -93,6 +94,7 @@ class PreviewContentListAdapter : InsertedViewAdapterWithLoader<ContentPreviewVi
|
||||||
this.onChannelClicked.subscribe(this@PreviewContentListAdapter.onChannelClicked::emit);
|
this.onChannelClicked.subscribe(this@PreviewContentListAdapter.onChannelClicked::emit);
|
||||||
this.onAddToClicked.subscribe(this@PreviewContentListAdapter.onAddToClicked::emit);
|
this.onAddToClicked.subscribe(this@PreviewContentListAdapter.onAddToClicked::emit);
|
||||||
this.onAddToQueueClicked.subscribe(this@PreviewContentListAdapter.onAddToQueueClicked::emit);
|
this.onAddToQueueClicked.subscribe(this@PreviewContentListAdapter.onAddToQueueClicked::emit);
|
||||||
|
this.onLongPress.subscribe(this@PreviewContentListAdapter.onLongPress::emit);
|
||||||
};
|
};
|
||||||
private fun createPlaylistViewHolder(viewGroup: ViewGroup): PreviewPlaylistViewHolder = PreviewPlaylistViewHolder(viewGroup, _feedStyle).apply {
|
private fun createPlaylistViewHolder(viewGroup: ViewGroup): PreviewPlaylistViewHolder = PreviewPlaylistViewHolder(viewGroup, _feedStyle).apply {
|
||||||
this.onPlaylistClicked.subscribe { this@PreviewContentListAdapter.onContentClicked.emit(it, 0L) };
|
this.onPlaylistClicked.subscribe { this@PreviewContentListAdapter.onContentClicked.emit(it, 0L) };
|
||||||
|
|
|
@ -68,6 +68,7 @@ open class PreviewVideoView : LinearLayout {
|
||||||
};
|
};
|
||||||
|
|
||||||
val onVideoClicked = Event2<IPlatformVideo, Long>();
|
val onVideoClicked = Event2<IPlatformVideo, Long>();
|
||||||
|
val onLongPress = Event1<IPlatformVideo>();
|
||||||
val onChannelClicked = Event1<PlatformAuthorLink>();
|
val onChannelClicked = Event1<PlatformAuthorLink>();
|
||||||
val onAddToClicked = Event1<IPlatformVideo>();
|
val onAddToClicked = Event1<IPlatformVideo>();
|
||||||
val onAddToQueueClicked = Event1<IPlatformVideo>();
|
val onAddToQueueClicked = Event1<IPlatformVideo>();
|
||||||
|
@ -119,7 +120,13 @@ open class PreviewVideoView : LinearLayout {
|
||||||
|
|
||||||
this._exoPlayer = exoPlayer
|
this._exoPlayer = exoPlayer
|
||||||
|
|
||||||
setOnClickListener { onOpenClicked() };
|
setOnLongClickListener {
|
||||||
|
onLongPress()
|
||||||
|
true
|
||||||
|
};
|
||||||
|
setOnClickListener {
|
||||||
|
onOpenClicked()
|
||||||
|
};
|
||||||
_imageChannel.setOnClickListener { currentVideo?.let { onChannelClicked.emit(it.author) } };
|
_imageChannel.setOnClickListener { currentVideo?.let { onChannelClicked.emit(it.author) } };
|
||||||
_textChannelName.setOnClickListener { currentVideo?.let { onChannelClicked.emit(it.author) } };
|
_textChannelName.setOnClickListener { currentVideo?.let { onChannelClicked.emit(it.author) } };
|
||||||
_textVideoMetadata.setOnClickListener { currentVideo?.let { onChannelClicked.emit(it.author) } };
|
_textVideoMetadata.setOnClickListener { currentVideo?.let { onChannelClicked.emit(it.author) } };
|
||||||
|
@ -145,6 +152,12 @@ open class PreviewVideoView : LinearLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun onLongPress() {
|
||||||
|
currentVideo?.let {
|
||||||
|
onLongPress.emit(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun bind(content: IPlatformContent) {
|
open fun bind(content: IPlatformContent) {
|
||||||
_taskLoadProfile.cancel();
|
_taskLoadProfile.cancel();
|
||||||
|
|
|
@ -17,6 +17,7 @@ class PreviewVideoViewHolder : ContentPreviewViewHolder {
|
||||||
val onChannelClicked = Event1<PlatformAuthorLink>();
|
val onChannelClicked = Event1<PlatformAuthorLink>();
|
||||||
val onAddToClicked = Event1<IPlatformVideo>();
|
val onAddToClicked = Event1<IPlatformVideo>();
|
||||||
val onAddToQueueClicked = Event1<IPlatformVideo>();
|
val onAddToQueueClicked = Event1<IPlatformVideo>();
|
||||||
|
val onLongPress = Event1<IPlatformVideo>();
|
||||||
|
|
||||||
//val context: Context;
|
//val context: Context;
|
||||||
val currentVideo: IPlatformVideo? get() = view.currentVideo;
|
val currentVideo: IPlatformVideo? get() = view.currentVideo;
|
||||||
|
@ -30,6 +31,7 @@ class PreviewVideoViewHolder : ContentPreviewViewHolder {
|
||||||
view.onChannelClicked.subscribe(onChannelClicked::emit);
|
view.onChannelClicked.subscribe(onChannelClicked::emit);
|
||||||
view.onAddToClicked.subscribe(onAddToClicked::emit);
|
view.onAddToClicked.subscribe(onAddToClicked::emit);
|
||||||
view.onAddToQueueClicked.subscribe(onAddToQueueClicked::emit);
|
view.onAddToQueueClicked.subscribe(onAddToQueueClicked::emit);
|
||||||
|
view.onLongPress.subscribe(onLongPress::emit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,10 @@ class SlideUpMenuOverlay : RelativeLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun show(){
|
fun show(){
|
||||||
|
if (isVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
isVisible = true;
|
isVisible = true;
|
||||||
_container?.post {
|
_container?.post {
|
||||||
_container?.visibility = View.VISIBLE;
|
_container?.visibility = View.VISIBLE;
|
||||||
|
@ -146,8 +150,8 @@ class SlideUpMenuOverlay : RelativeLayout {
|
||||||
_viewBackground.alpha = 0f;
|
_viewBackground.alpha = 0f;
|
||||||
|
|
||||||
val animations = arrayListOf<Animator>();
|
val animations = arrayListOf<Animator>();
|
||||||
animations.add(ObjectAnimator.ofFloat(_viewBackground, "alpha", 0.0f, 1.0f).setDuration(500));
|
animations.add(ObjectAnimator.ofFloat(_viewBackground, "alpha", 0.0f, 1.0f).setDuration(ANIMATION_DURATION_MS));
|
||||||
animations.add(ObjectAnimator.ofFloat(_viewOverlayContainer, "translationY", _viewOverlayContainer.measuredHeight.toFloat(), 0.0f).setDuration(500));
|
animations.add(ObjectAnimator.ofFloat(_viewOverlayContainer, "translationY", _viewOverlayContainer.measuredHeight.toFloat(), 0.0f).setDuration(ANIMATION_DURATION_MS));
|
||||||
|
|
||||||
val animatorSet = AnimatorSet();
|
val animatorSet = AnimatorSet();
|
||||||
animatorSet.playTogether(animations);
|
animatorSet.playTogether(animations);
|
||||||
|
@ -159,11 +163,15 @@ class SlideUpMenuOverlay : RelativeLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hide(animate: Boolean = true){
|
fun hide(animate: Boolean = true){
|
||||||
|
if (!isVisible) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
isVisible = false;
|
isVisible = false;
|
||||||
if (_animated && animate) {
|
if (_animated && animate) {
|
||||||
val animations = arrayListOf<Animator>();
|
val animations = arrayListOf<Animator>();
|
||||||
animations.add(ObjectAnimator.ofFloat(_viewBackground, "alpha", 1.0f, 0.0f).setDuration(500));
|
animations.add(ObjectAnimator.ofFloat(_viewBackground, "alpha", 1.0f, 0.0f).setDuration(ANIMATION_DURATION_MS));
|
||||||
animations.add(ObjectAnimator.ofFloat(_viewOverlayContainer, "translationY", 0.0f, _viewOverlayContainer.measuredHeight.toFloat()).setDuration(500));
|
animations.add(ObjectAnimator.ofFloat(_viewOverlayContainer, "translationY", 0.0f, _viewOverlayContainer.measuredHeight.toFloat()).setDuration(ANIMATION_DURATION_MS));
|
||||||
|
|
||||||
val animatorSet = AnimatorSet();
|
val animatorSet = AnimatorSet();
|
||||||
animatorSet.doOnEnd {
|
animatorSet.doOnEnd {
|
||||||
|
@ -180,4 +188,8 @@ class SlideUpMenuOverlay : RelativeLayout {
|
||||||
_container?.visibility = View.GONE;
|
_container?.visibility = View.GONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val ANIMATION_DURATION_MS = 350L
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue