Compare commits

...

4 Commits

Author SHA1 Message Date
Kelvin 4655d8369d Reduce subscription calls, Improve subs sorting, Improve view sorting 2023-11-03 13:34:23 +01:00
Kelvin aeaaace3a4 Subscription settings from creators tab 2023-11-02 23:42:51 +01:00
Kelvin e6997004ff Fix new user crash, show/hide subscription settings button on change, raise import limit to 90 2023-11-02 23:22:42 +01:00
Kelvin 5e1896b7f2 Stable ref 2023-11-02 22:52:29 +01:00
15 changed files with 78 additions and 22 deletions
@@ -170,6 +170,10 @@ class ChannelFragment : MainFragment() {
_buttonSubscribe.onSubscribed.subscribe {
UISlideOverlays.showSubscriptionOptionsOverlay(it, _overlayContainer);
_buttonSubscriptionSettings.visibility = if(_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE;
}
_buttonSubscribe.onUnSubscribed.subscribe {
_buttonSubscriptionSettings.visibility = if(_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE;
}
_buttonSubscriptionSettings.setOnClickListener {
@@ -6,10 +6,12 @@ import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.FrameLayout
import android.widget.Spinner
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.futo.platformplayer.R
import com.futo.platformplayer.UISlideOverlays
import com.futo.platformplayer.views.adapters.SubscriptionAdapter
class CreatorsFragment : MainFragment() {
@@ -18,13 +20,16 @@ class CreatorsFragment : MainFragment() {
override val hasBottomBar: Boolean get() = true;
private var _spinnerSortBy: Spinner? = null;
private var _overlayContainer: FrameLayout? = null;
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val view = inflater.inflate(R.layout.fragment_creators, container, false);
val adapter = SubscriptionAdapter(inflater, getString(R.string.confirm_delete_subscription));
adapter.onClick.subscribe { platformUser -> navigate<ChannelFragment>(platformUser) };
adapter.onSettings.subscribe { sub -> _overlayContainer?.let { UISlideOverlays.showSubscriptionOptionsOverlay(sub, it) } }
_overlayContainer = view.findViewById(R.id.overlay_container);
val spinnerSortBy: Spinner = view.findViewById(R.id.spinner_sortby);
spinnerSortBy.adapter = ArrayAdapter(view.context, R.layout.spinner_item_simple, resources.getStringArray(R.array.subscriptions_sortby_array)).also {
it.setDropDownViewResource(R.layout.spinner_dropdownitem_simple);
@@ -48,6 +53,7 @@ class CreatorsFragment : MainFragment() {
override fun onDestroyMainView() {
super.onDestroyMainView();
_spinnerSortBy = null;
_overlayContainer = null;
}
companion object {
@@ -210,7 +210,7 @@ class ImportSubscriptionsFragment : MainFragment() {
companion object {
val TAG = "ImportSubscriptionsFragment";
private const val MAXIMUM_BATCH_SIZE = 75;
private const val MAXIMUM_BATCH_SIZE = 90;
fun newInstance() = ImportSubscriptionsFragment().apply {}
}
}
@@ -610,7 +610,7 @@ class VideoDetailView : ConstraintLayout {
}
val _trackingUpdateTimeLock = Object();
val _trackingUpdateInterval = 3000;
val _trackingUpdateInterval = 2500;
var _trackingLastUpdateTime = System.currentTimeMillis();
var _trackingLastPosition: Long = 0;
var _trackingLastVideo: IPlatformVideoDetails? = null;
@@ -53,10 +53,12 @@ class Subscription {
this.channel = channel;
}
fun shouldFetchVideos() = true;
fun shouldFetchVideos() = doFetchVideos &&
(lastVideo.getNowDiffDays() < 30 || lastVideoUpdate.getNowDiffDays() >= 1) &&
(lastVideo.getNowDiffDays() < 180 || lastVideoUpdate.getNowDiffDays() >= 3);
fun shouldFetchStreams() = doFetchStreams && lastLiveStream.getNowDiffDays() < 7;
fun shouldFetchLiveStreams() = doFetchLive && lastLiveStream.getNowDiffDays() < 14;
fun shouldFetchPosts() = doFetchPosts && lastPost.getNowDiffDays() < 2;
fun shouldFetchPosts() = doFetchPosts && lastPost.getNowDiffDays() < 5;
fun getClient() = StatePlatform.instance.getChannelClientOrNull(channel.url);
@@ -77,7 +77,11 @@ class StateSubscriptions {
val onSubscriptionsChanged = Event2<List<Subscription>, Boolean>();
fun getOldestUpdateTime(): OffsetDateTime {
return getSubscriptions().minOf { it.lastVideoUpdate };
val subs = getSubscriptions();
if(subs.size == 0)
return OffsetDateTime.now();
else
return subs.minOf { it.lastVideoUpdate };
}
fun getGlobalSubscriptionProgress(): Pair<Int, Int> {
return Pair(_lastGlobalSubscriptionProgress, _lastGlobalSubscriptionTotal);
@@ -85,21 +85,21 @@ class SmartSubscriptionAlgorithm(
ResultCapabilities.TYPE_STREAMS -> sub.lastLiveStream;
ResultCapabilities.TYPE_LIVE -> sub.lastLiveStream;
ResultCapabilities.TYPE_POSTS -> sub.lastPost;
else -> sub.lastVideo; //TODO: minimum of all
else -> sub.lastVideo; //TODO: minimum of all?
};
val lastUpdate = when(type) {
ResultCapabilities.TYPE_VIDEOS -> sub.lastVideoUpdate;
ResultCapabilities.TYPE_STREAMS -> sub.lastLiveStreamUpdate;
ResultCapabilities.TYPE_LIVE -> sub.lastLiveStreamUpdate;
ResultCapabilities.TYPE_POSTS -> sub.lastPostUpdate;
else -> sub.lastVideoUpdate; //TODO: minimum of all
else -> sub.lastVideoUpdate; //TODO: minimum of all?
};
val interval = when(type) {
ResultCapabilities.TYPE_VIDEOS -> sub.uploadInterval;
ResultCapabilities.TYPE_STREAMS -> sub.uploadStreamInterval;
ResultCapabilities.TYPE_LIVE -> sub.uploadStreamInterval;
ResultCapabilities.TYPE_POSTS -> sub.uploadPostInterval;
else -> sub.uploadInterval; //TODO: minimum of all
else -> sub.uploadInterval; //TODO: minimum of all?
};
val lastItemDaysAgo = lastItem.getNowDiffHours();
val lastUpdateHoursAgo = lastUpdate.getNowDiffHours();
@@ -54,7 +54,7 @@ abstract class SubscriptionsTaskFetchAlgorithm(
val clientTaskCount = clientTasks.value.filter { !it.fromCache }.size;
val clientCacheCount = clientTasks.value.size - clientTaskCount;
if(clientCacheCount > 0) {
UIDialogs.toast("[${clientTasks.key.name}] only updating ${clientTaskCount} most urgent channels. (${clientCacheCount} cached)");
UIDialogs.toast("[${clientTasks.key.name}] only updating ${clientTaskCount} most urgent channels (rqs). (${clientCacheCount} cached)");
}
}
@@ -14,6 +14,7 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> {
private val _confirmationMessage: String;
var onClick = Event1<Subscription>();
var onSettings = Event1<Subscription>();
var sortBy: Int = 3
set(value) {
field = value;
@@ -33,12 +34,16 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): SubscriptionViewHolder {
val holder = SubscriptionViewHolder(viewGroup);
holder.onClick.subscribe(onClick::emit);
holder.onSettings.subscribe(onSettings::emit);
holder.onTrash.subscribe {
val sub = holder.subscription ?: return@subscribe;
UIDialogs.showConfirmationDialog(_inflater.context, _confirmationMessage, {
StateSubscriptions.instance.removeSubscription(sub.channel.url);
});
};
holder.onSettings.subscribe {
onSettings.emit(it);
};
return holder;
}
@@ -49,10 +54,10 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> {
private fun updateDataset() {
_sortedDataset = when (sortBy) {
0 -> StateSubscriptions.instance.getSubscriptions().sortedBy({ u -> u.channel.name })
1 -> StateSubscriptions.instance.getSubscriptions().sortedByDescending({ u -> u.channel.name })
2 -> StateSubscriptions.instance.getSubscriptions().sortedBy { it.playbackViews }
3 -> StateSubscriptions.instance.getSubscriptions().sortedByDescending { it.playbackViews }
0 -> StateSubscriptions.instance.getSubscriptions().sortedBy({ u -> u.channel.name.lowercase() })
1 -> StateSubscriptions.instance.getSubscriptions().sortedByDescending({ u -> u.channel.name.lowercase() })
2 -> StateSubscriptions.instance.getSubscriptions().sortedBy { it.playbackViews * VIEW_PRIORITY + it.playbackSeconds }
3 -> StateSubscriptions.instance.getSubscriptions().sortedByDescending { it.playbackViews * VIEW_PRIORITY + it.playbackSeconds }
4 -> StateSubscriptions.instance.getSubscriptions().sortedBy { it.playbackSeconds }
5 -> StateSubscriptions.instance.getSubscriptions().sortedByDescending { it.playbackSeconds }
else -> throw IllegalStateException("Invalid sorting algorithm selected.");
@@ -60,4 +65,9 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> {
notifyDataSetChanged();
}
companion object {
val VIEW_PRIORITY = 36000 * 3;
}
}
@@ -10,6 +10,7 @@ import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.polycentric.PolycentricCache
import com.futo.platformplayer.R
import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.models.Subscription
@@ -18,6 +19,7 @@ import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.dp
import com.futo.platformplayer.selectBestImage
import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.toHumanBytesSpeed
import com.futo.platformplayer.toHumanTimeIndicator
import com.futo.platformplayer.views.others.CreatorThumbnail
@@ -29,6 +31,7 @@ class SubscriptionViewHolder : ViewHolder {
private val _textName: TextView;
private val _creatorThumbnail: CreatorThumbnail;
private val _buttonTrash: ImageButton;
private val _buttonSettings: ImageButton;
private val _platformIndicator : PlatformIndicator;
private val _textMeta: TextView;
@@ -45,6 +48,7 @@ class SubscriptionViewHolder : ViewHolder {
var onClick = Event1<Subscription>();
var onTrash = Event0();
var onSettings = Event1<Subscription>();
constructor(viewGroup: ViewGroup) : super(LayoutInflater.from(viewGroup.context).inflate(R.layout.list_subscription, viewGroup, false)) {
_layoutSubscription = itemView.findViewById(R.id.layout_subscription);
@@ -52,6 +56,7 @@ class SubscriptionViewHolder : ViewHolder {
_textMeta = itemView.findViewById(R.id.text_meta);
_creatorThumbnail = itemView.findViewById(R.id.creator_thumbnail);
_buttonTrash = itemView.findViewById(R.id.button_trash);
_buttonSettings = itemView.findViewById(R.id.button_settings);
_platformIndicator = itemView.findViewById(R.id.platform);
_layoutSubscription.setOnClickListener {
@@ -64,6 +69,11 @@ class SubscriptionViewHolder : ViewHolder {
_buttonTrash.setOnClickListener {
onTrash.emit();
};
_buttonSettings.setOnClickListener {
subscription?.let {
onSettings.emit(it);
};
}
}
fun bind(sub: Subscription) {
@@ -36,6 +36,7 @@ class SubscribeButton : LinearLayout {
} else { null };
val onSubscribed = Event1<Subscription>();
val onUnSubscribed = Event1<String>();
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
@@ -82,6 +83,7 @@ class SubscribeButton : LinearLayout {
if (removed != null)
UIDialogs.toast(context, context.getString(R.string.unsubscribed_from) + removed.channel.name);
setIsSubscribed(false);
onUnSubscribed.emit(url);
}
fun setSubscribeChannel(url: String) {
@@ -25,7 +25,7 @@ class SubscriptionBar : LinearLayout {
constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
inflate(context, R.layout.view_subscription_bar, this);
val subscriptions = StateSubscriptions.instance.getSubscriptions().sortedByDescending { it.playbackViews };
val subscriptions = StateSubscriptions.instance.getSubscriptions().sortedByDescending { it.playbackSeconds };
_adapterView = findViewById<RecyclerView>(R.id.recycler_creators).asAny(subscriptions, orientation = RecyclerView.HORIZONTAL) {
it.onClick.subscribe { c ->
onClickChannel.emit(c.channel);
@@ -55,4 +55,11 @@
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<FrameLayout
android:id="@+id/overlay_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="100dp"
android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
+18 -7
View File
@@ -56,13 +56,24 @@
</LinearLayout>
</LinearLayout>
<ImageButton
android:id="@+id/button_settings"
android:layout_width="50dp"
android:layout_height="40dp"
app:srcCompat="@drawable/ic_settings"
android:scaleType="fitCenter"
android:paddingStart="5dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingEnd="0dp" />
<ImageButton
android:id="@+id/button_trash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_trash_18dp"
android:paddingStart="20dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:paddingEnd="20dp" />
android:layout_width="60dp"
android:layout_height="40dp"
app:srcCompat="@drawable/ic_trash"
android:scaleType="fitCenter"
android:paddingStart="5dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingEnd="5dp" />
</LinearLayout>