mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-05-29 04:50:19 +02:00
Show total downloaded content duration, Indicator how many subscriptions, save queue as playlist
This commit is contained in:
parent
8839d9f1c6
commit
83843f192d
@ -79,6 +79,36 @@ class UISlideOverlays {
|
|||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showQueueOptionsOverlay(context: Context, container: ViewGroup) {
|
||||||
|
UISlideOverlays.showOverlay(container, "Queue options", null, {
|
||||||
|
|
||||||
|
}, SlideUpMenuItem(context, R.drawable.ic_playlist, "Save as playlist", "", "Creates a new playlist with queue as videos", null, {
|
||||||
|
val nameInput = SlideUpMenuTextInput(container.context, container.context.getString(R.string.name));
|
||||||
|
val addPlaylistOverlay = SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.create_new_playlist), container.context.getString(R.string.ok), false, nameInput);
|
||||||
|
|
||||||
|
addPlaylistOverlay.onOK.subscribe {
|
||||||
|
val text = nameInput.text.trim()
|
||||||
|
if (text.isBlank()) {
|
||||||
|
return@subscribe;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPlaylistOverlay.hide();
|
||||||
|
nameInput.deactivate();
|
||||||
|
nameInput.clear();
|
||||||
|
StatePlayer.instance.saveQueueAsPlaylist(text);
|
||||||
|
UIDialogs.appToast("Playlist [${text}] created");
|
||||||
|
};
|
||||||
|
|
||||||
|
addPlaylistOverlay.onCancel.subscribe {
|
||||||
|
nameInput.deactivate();
|
||||||
|
nameInput.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
addPlaylistOverlay.show();
|
||||||
|
nameInput.activate();
|
||||||
|
}, false));
|
||||||
|
}
|
||||||
|
|
||||||
fun showSubscriptionOptionsOverlay(subscription: Subscription, container: ViewGroup): SlideUpMenuOverlay {
|
fun showSubscriptionOptionsOverlay(subscription: Subscription, container: ViewGroup): SlideUpMenuOverlay {
|
||||||
val items = arrayListOf<View>();
|
val items = arrayListOf<View>();
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import android.widget.EditText
|
|||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
@ -26,6 +27,7 @@ class CreatorsFragment : MainFragment() {
|
|||||||
private var _overlayContainer: FrameLayout? = null;
|
private var _overlayContainer: FrameLayout? = null;
|
||||||
private var _containerSearch: FrameLayout? = null;
|
private var _containerSearch: FrameLayout? = null;
|
||||||
private var _editSearch: EditText? = null;
|
private var _editSearch: EditText? = null;
|
||||||
|
private var _textMeta: TextView? = null;
|
||||||
private var _buttonClearSearch: ImageButton? = null
|
private var _buttonClearSearch: ImageButton? = null
|
||||||
|
|
||||||
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
@ -34,6 +36,7 @@ class CreatorsFragment : MainFragment() {
|
|||||||
val editSearch: EditText = view.findViewById(R.id.edit_search);
|
val editSearch: EditText = view.findViewById(R.id.edit_search);
|
||||||
val buttonClearSearch: ImageButton = view.findViewById(R.id.button_clear_search)
|
val buttonClearSearch: ImageButton = view.findViewById(R.id.button_clear_search)
|
||||||
_editSearch = editSearch
|
_editSearch = editSearch
|
||||||
|
_textMeta = view.findViewById(R.id.text_meta);
|
||||||
_buttonClearSearch = buttonClearSearch
|
_buttonClearSearch = buttonClearSearch
|
||||||
buttonClearSearch.setOnClickListener {
|
buttonClearSearch.setOnClickListener {
|
||||||
editSearch.text.clear()
|
editSearch.text.clear()
|
||||||
@ -41,7 +44,11 @@ class CreatorsFragment : MainFragment() {
|
|||||||
_buttonClearSearch?.visibility = View.INVISIBLE;
|
_buttonClearSearch?.visibility = View.INVISIBLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
val adapter = SubscriptionAdapter(inflater, getString(R.string.confirm_delete_subscription));
|
val adapter = SubscriptionAdapter(inflater, getString(R.string.confirm_delete_subscription)) { subs ->
|
||||||
|
_textMeta?.let {
|
||||||
|
it.text = "${subs.size} creator${if(subs.size > 1) "s" else ""}";
|
||||||
|
}
|
||||||
|
};
|
||||||
adapter.onClick.subscribe { platformUser -> navigate<ChannelFragment>(platformUser) };
|
adapter.onClick.subscribe { platformUser -> navigate<ChannelFragment>(platformUser) };
|
||||||
adapter.onSettings.subscribe { sub -> _overlayContainer?.let { UISlideOverlays.showSubscriptionOptionsOverlay(sub, it) } }
|
adapter.onSettings.subscribe { sub -> _overlayContainer?.let { UISlideOverlays.showSubscriptionOptionsOverlay(sub, it) } }
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import com.futo.platformplayer.states.StateDownloads
|
|||||||
import com.futo.platformplayer.states.StatePlayer
|
import com.futo.platformplayer.states.StatePlayer
|
||||||
import com.futo.platformplayer.states.StatePlaylists
|
import com.futo.platformplayer.states.StatePlaylists
|
||||||
import com.futo.platformplayer.toHumanBytesSize
|
import com.futo.platformplayer.toHumanBytesSize
|
||||||
|
import com.futo.platformplayer.toHumanDuration
|
||||||
import com.futo.platformplayer.views.AnyInsertedAdapterView
|
import com.futo.platformplayer.views.AnyInsertedAdapterView
|
||||||
import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithTop
|
import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithTop
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.VideoDownloadViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.VideoDownloadViewHolder
|
||||||
@ -215,7 +216,7 @@ class DownloadsFragment : MainFragment() {
|
|||||||
_listDownloadedHeader.visibility = GONE;
|
_listDownloadedHeader.visibility = GONE;
|
||||||
} else {
|
} else {
|
||||||
_listDownloadedHeader.visibility = VISIBLE;
|
_listDownloadedHeader.visibility = VISIBLE;
|
||||||
_listDownloadedMeta.text = "(${downloaded.size} ${context.getString(R.string.videos).lowercase()})";
|
_listDownloadedMeta.text = "(${downloaded.size} ${context.getString(R.string.videos).lowercase()}${if(downloaded.size > 0) ", ${downloaded.sumOf { it.duration }.toHumanDuration(false)}" else ""})";
|
||||||
}
|
}
|
||||||
|
|
||||||
lastDownloads = downloaded;
|
lastDownloads = downloaded;
|
||||||
|
@ -13,6 +13,7 @@ import com.futo.platformplayer.UIDialogs
|
|||||||
import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails
|
import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails
|
||||||
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
||||||
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
||||||
|
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
|
||||||
import com.futo.platformplayer.constructs.Event0
|
import com.futo.platformplayer.constructs.Event0
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
@ -130,6 +131,12 @@ class StatePlayer {
|
|||||||
closeMediaSession();
|
closeMediaSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveQueueAsPlaylist(name: String){
|
||||||
|
val videos = _queue.toList();
|
||||||
|
val playlist = Playlist(name, videos.map { SerializedPlatformVideo.fromVideo(it) });
|
||||||
|
StatePlaylists.instance.createOrUpdatePlaylist(playlist);
|
||||||
|
}
|
||||||
|
|
||||||
//Notifications
|
//Notifications
|
||||||
fun hasMediaSession() : Boolean {
|
fun hasMediaSession() : Boolean {
|
||||||
return MediaPlaybackService.getService() != null;
|
return MediaPlaybackService.getService() != null;
|
||||||
|
@ -16,6 +16,7 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> {
|
|||||||
private lateinit var _sortedDataset: List<Subscription>;
|
private lateinit var _sortedDataset: List<Subscription>;
|
||||||
private val _inflater: LayoutInflater;
|
private val _inflater: LayoutInflater;
|
||||||
private val _confirmationMessage: String;
|
private val _confirmationMessage: String;
|
||||||
|
private val _onDatasetChanged: ((List<Subscription>)->Unit)?;
|
||||||
|
|
||||||
var onClick = Event1<Subscription>();
|
var onClick = Event1<Subscription>();
|
||||||
var onSettings = Event1<Subscription>();
|
var onSettings = Event1<Subscription>();
|
||||||
@ -30,9 +31,10 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> {
|
|||||||
updateDataset();
|
updateDataset();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(inflater: LayoutInflater, confirmationMessage: String) : super() {
|
constructor(inflater: LayoutInflater, confirmationMessage: String, onDatasetChanged: ((List<Subscription>)->Unit)? = null) : super() {
|
||||||
_inflater = inflater;
|
_inflater = inflater;
|
||||||
_confirmationMessage = confirmationMessage;
|
_confirmationMessage = confirmationMessage;
|
||||||
|
_onDatasetChanged = onDatasetChanged;
|
||||||
|
|
||||||
StateSubscriptions.instance.onSubscriptionsChanged.subscribe { _, _ -> if(Looper.myLooper() != Looper.getMainLooper())
|
StateSubscriptions.instance.onSubscriptionsChanged.subscribe { _, _ -> if(Looper.myLooper() != Looper.getMainLooper())
|
||||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) { updateDataset() }
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) { updateDataset() }
|
||||||
@ -78,6 +80,8 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> {
|
|||||||
.filter { (queryLower.isNullOrBlank() || it.channel.name.lowercase().contains(queryLower)) }
|
.filter { (queryLower.isNullOrBlank() || it.channel.name.lowercase().contains(queryLower)) }
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
_onDatasetChanged?.invoke(_sortedDataset);
|
||||||
|
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,16 +2,26 @@ package com.futo.platformplayer.views.overlays
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import com.futo.platformplayer.states.StatePlayer
|
import com.futo.platformplayer.states.StatePlayer
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.UISlideOverlays
|
||||||
import com.futo.platformplayer.constructs.Event0
|
import com.futo.platformplayer.constructs.Event0
|
||||||
import com.futo.platformplayer.views.lists.VideoListEditorView
|
import com.futo.platformplayer.views.lists.VideoListEditorView
|
||||||
|
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
|
||||||
|
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
|
||||||
|
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuTextInput
|
||||||
|
|
||||||
class QueueEditorOverlay : LinearLayout {
|
class QueueEditorOverlay : LinearLayout {
|
||||||
|
|
||||||
private val _topbar : OverlayTopbar;
|
private val _topbar : OverlayTopbar;
|
||||||
private val _editor : VideoListEditorView;
|
private val _editor : VideoListEditorView;
|
||||||
|
private val _btnSettings: ImageView;
|
||||||
|
|
||||||
|
private val _overlayContainer: FrameLayout;
|
||||||
|
|
||||||
|
|
||||||
val onClose = Event0();
|
val onClose = Event0();
|
||||||
|
|
||||||
@ -19,6 +29,9 @@ class QueueEditorOverlay : LinearLayout {
|
|||||||
inflate(context, R.layout.overlay_queue, this)
|
inflate(context, R.layout.overlay_queue, this)
|
||||||
_topbar = findViewById(R.id.topbar);
|
_topbar = findViewById(R.id.topbar);
|
||||||
_editor = findViewById(R.id.editor);
|
_editor = findViewById(R.id.editor);
|
||||||
|
_btnSettings = findViewById(R.id.button_settings);
|
||||||
|
_overlayContainer = findViewById(R.id.overlay_container);
|
||||||
|
|
||||||
|
|
||||||
_topbar.onClose.subscribe(this, onClose::emit);
|
_topbar.onClose.subscribe(this, onClose::emit);
|
||||||
_editor.onVideoOrderChanged.subscribe { StatePlayer.instance.setQueueWithExisting(it) }
|
_editor.onVideoOrderChanged.subscribe { StatePlayer.instance.setQueueWithExisting(it) }
|
||||||
@ -28,6 +41,10 @@ class QueueEditorOverlay : LinearLayout {
|
|||||||
}
|
}
|
||||||
_editor.onVideoClicked.subscribe { v -> StatePlayer.instance.setQueuePosition(v) }
|
_editor.onVideoClicked.subscribe { v -> StatePlayer.instance.setQueuePosition(v) }
|
||||||
|
|
||||||
|
_btnSettings.setOnClickListener {
|
||||||
|
handleSettings();
|
||||||
|
}
|
||||||
|
|
||||||
_topbar.setInfo(context.getString(R.string.queue), "");
|
_topbar.setInfo(context.getString(R.string.queue), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,4 +57,8 @@ class QueueEditorOverlay : LinearLayout {
|
|||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
_topbar.onClose.remove(this);
|
_topbar.onClose.remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun handleSettings() {
|
||||||
|
UISlideOverlays.showQueueOptionsOverlay(context, _overlayContainer);
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@
|
|||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="100dp"
|
android:layout_height="110dp"
|
||||||
android:minHeight="0dp"
|
android:minHeight="0dp"
|
||||||
app:layout_scrollFlags="scroll"
|
app:layout_scrollFlags="scroll"
|
||||||
app:contentInsetStart="0dp"
|
app:contentInsetStart="0dp"
|
||||||
@ -77,7 +77,16 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="20dp"
|
android:paddingStart="20dp"
|
||||||
android:paddingEnd="20dp" />
|
android:paddingEnd="20dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_meta"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="9dp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="#333333"
|
||||||
|
android:text="0 creators" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
@ -21,5 +21,20 @@
|
|||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/topbar"
|
app:layout_constraintTop_toBottomOf="@id/topbar"
|
||||||
app:layout_constraintBottom_toBottomOf="parent" />
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/button_settings"
|
||||||
|
android:background="@drawable/background_pill"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_settings" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/overlay_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
x
Reference in New Issue
Block a user