mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-04-20 03:24:50 +00:00
Progress
This commit is contained in:
parent
27eb5aa6e1
commit
02292fed04
21 changed files with 587 additions and 60 deletions
|
@ -3,8 +3,10 @@ package com.futo.platformplayer
|
|||
import android.content.ContentResolver
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.futo.platformplayer.activities.MainActivity
|
||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
||||
import com.futo.platformplayer.api.media.models.ResultCapabilities
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
|
||||
import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantAudioUrlSource
|
||||
import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantVideoUrlSource
|
||||
|
@ -17,10 +19,13 @@ 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.SerializedPlatformVideo
|
||||
import com.futo.platformplayer.downloads.VideoLocal
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.SubscriptionGroupFragment
|
||||
import com.futo.platformplayer.helpers.VideoHelper
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.ImageVariable
|
||||
import com.futo.platformplayer.models.Playlist
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.models.SubscriptionGroup
|
||||
import com.futo.platformplayer.parsers.HLS
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StateDownloads
|
||||
|
@ -79,6 +84,7 @@ class UISlideOverlays {
|
|||
SlideUpMenuItem(container.context, R.drawable.ic_notifications, "Notifications", "", "notifications", {
|
||||
subscription.doNotifications = menu?.selectOption(null, "notifications", true, true) ?: subscription.doNotifications;
|
||||
}, false),
|
||||
|
||||
SlideUpMenuGroup(container.context, "Fetch Settings",
|
||||
"Depending on the platform you might not need to enable a type for it to be available.",
|
||||
-1, listOf()),
|
||||
|
@ -97,7 +103,15 @@ class UISlideOverlays {
|
|||
}, false) else null,
|
||||
if(capabilities.hasType(ResultCapabilities.TYPE_POSTS)) SlideUpMenuItem(container.context, R.drawable.ic_chat, "Posts", "Check for posts", "fetchPosts", {
|
||||
subscription.doFetchPosts = menu?.selectOption(null, "fetchPosts", true, true) ?: subscription.doFetchPosts;
|
||||
}, false) else null).filterNotNull());
|
||||
}, false) else null,
|
||||
|
||||
SlideUpMenuGroup(container.context, "Actions",
|
||||
"Various things you can do with this subscription",
|
||||
-1, listOf()),
|
||||
SlideUpMenuItem(container.context, R.drawable.ic_list, "Add to Group", "", "btnAddToGroup", {
|
||||
showCreateSubscriptionGroup(container, subscription.channel);
|
||||
}, false)
|
||||
).filterNotNull());
|
||||
|
||||
menu = SlideUpMenuOverlay(container.context, container, "Subscription Settings", null, true, items);
|
||||
|
||||
|
@ -135,6 +149,10 @@ class UISlideOverlays {
|
|||
}
|
||||
}
|
||||
|
||||
fun showAddToGroupOverlay(channel: IPlatformVideo, container: ViewGroup) {
|
||||
|
||||
}
|
||||
|
||||
fun showHlsPicker(video: IPlatformVideoDetails, source: Any, sourceUrl: String, container: ViewGroup): SlideUpMenuOverlay {
|
||||
val items = arrayListOf<View>(LoaderView(container.context))
|
||||
val slideUpMenuOverlay = SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.download_video), null, true, items)
|
||||
|
@ -513,6 +531,48 @@ class UISlideOverlays {
|
|||
return overlay;
|
||||
}
|
||||
|
||||
fun showCreateSubscriptionGroup(container: ViewGroup, initialChannel: IPlatformChannel?, onCreate: ((String) -> Unit)? = null): SlideUpMenuOverlay {
|
||||
val nameInput = SlideUpMenuTextInput(container.context, container.context.getString(R.string.name));
|
||||
val addSubGroupOverlay = SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.create_new_subgroup), container.context.getString(R.string.ok), false, nameInput);
|
||||
|
||||
addSubGroupOverlay.onOK.subscribe {
|
||||
val text = nameInput.text;
|
||||
if (text.isBlank()) {
|
||||
return@subscribe;
|
||||
}
|
||||
|
||||
addSubGroupOverlay.hide();
|
||||
nameInput.deactivate();
|
||||
nameInput.clear();
|
||||
if(onCreate == null)
|
||||
{
|
||||
//TODO: Do this better, temp
|
||||
StateApp.instance.contextOrNull?.let {
|
||||
if(it is MainActivity) {
|
||||
val subGroup = SubscriptionGroup(text);
|
||||
if(initialChannel != null) {
|
||||
subGroup.urls.add(initialChannel.url);
|
||||
if(initialChannel.thumbnail != null)
|
||||
subGroup.image = ImageVariable(initialChannel.thumbnail);
|
||||
}
|
||||
it.navigate(it.getFragment<SubscriptionGroupFragment>(), subGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
onCreate(text)
|
||||
};
|
||||
|
||||
addSubGroupOverlay.onCancel.subscribe {
|
||||
nameInput.deactivate();
|
||||
nameInput.clear();
|
||||
};
|
||||
|
||||
addSubGroupOverlay.show();
|
||||
nameInput.activate();
|
||||
|
||||
return addSubGroupOverlay
|
||||
}
|
||||
fun showCreatePlaylistOverlay(container: ViewGroup, onCreate: (String) -> Unit): SlideUpMenuOverlay {
|
||||
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);
|
||||
|
|
|
@ -36,6 +36,7 @@ import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFrag
|
|||
import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment
|
||||
import com.futo.platformplayer.listeners.OrientationManager
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.SubscriptionGroup
|
||||
import com.futo.platformplayer.models.UrlVideoWithTime
|
||||
import com.futo.platformplayer.states.*
|
||||
import com.futo.platformplayer.stores.FragmentedStorage
|
||||
|
@ -100,6 +101,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||
lateinit var _fragImportSubscriptions: ImportSubscriptionsFragment;
|
||||
lateinit var _fragImportPlaylists: ImportPlaylistsFragment;
|
||||
lateinit var _fragBuy: BuyFragment;
|
||||
lateinit var _fragSubGroup: SubscriptionGroupFragment;
|
||||
|
||||
lateinit var _fragBrowser: BrowserFragment;
|
||||
|
||||
|
@ -235,6 +237,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||
_fragImportSubscriptions = ImportSubscriptionsFragment.newInstance();
|
||||
_fragImportPlaylists = ImportPlaylistsFragment.newInstance();
|
||||
_fragBuy = BuyFragment.newInstance();
|
||||
_fragSubGroup = SubscriptionGroupFragment.newInstance();
|
||||
|
||||
_fragBrowser = BrowserFragment.newInstance();
|
||||
|
||||
|
@ -316,6 +319,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||
_fragDownloads.topBar = _fragTopBarGeneral;
|
||||
_fragImportSubscriptions.topBar = _fragTopBarImport;
|
||||
_fragImportPlaylists.topBar = _fragTopBarImport;
|
||||
_fragSubGroup.topBar = _fragTopBarNavigation;
|
||||
|
||||
_fragBrowser.topBar = _fragTopBarNavigation;
|
||||
|
||||
|
@ -982,6 +986,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||
ImportPlaylistsFragment::class -> _fragImportPlaylists as T;
|
||||
BrowserFragment::class -> _fragBrowser as T;
|
||||
BuyFragment::class -> _fragBuy as T;
|
||||
SubscriptionGroupFragment::class -> _fragSubGroup as T;
|
||||
else -> throw IllegalArgumentException("Fragment type ${T::class.java.name} is not available in MainActivity");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,29 +2,40 @@ package com.futo.platformplayer.fragment.mainactivity.main
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.UISlideOverlays
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.models.SubscriptionGroup
|
||||
import com.futo.platformplayer.states.StateSubscriptionGroups
|
||||
import com.futo.platformplayer.states.StateSubscriptions
|
||||
import com.futo.platformplayer.views.AnyAdapterView
|
||||
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||
import com.futo.platformplayer.views.SearchView
|
||||
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||
import com.futo.platformplayer.views.adapters.viewholders.CreatorBarViewHolder
|
||||
import com.futo.platformplayer.views.overlays.ImageVariableOverlay
|
||||
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuTextInput
|
||||
import com.google.android.material.imageview.ShapeableImageView
|
||||
import com.google.android.material.shape.CornerFamily
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
|
||||
class SubscriptionGroupFragment : MainFragment() {
|
||||
override val isMainView : Boolean = true;
|
||||
|
@ -60,7 +71,9 @@ class SubscriptionGroupFragment : MainFragment() {
|
|||
private val _textGroupTitleContainer: LinearLayout;
|
||||
private val _textGroupTitle: TextView;
|
||||
private val _imageGroup: ShapeableImageView;
|
||||
private val _imageGroupBackground: ImageView;
|
||||
private val _buttonEditImage: LinearLayout;
|
||||
private val _searchBar: SearchView;
|
||||
|
||||
private val _textGroupMeta: TextView;
|
||||
|
||||
|
@ -68,6 +81,8 @@ class SubscriptionGroupFragment : MainFragment() {
|
|||
|
||||
private val _enabledCreators: ArrayList<IPlatformChannel> = arrayListOf();
|
||||
private val _disabledCreators: ArrayList<IPlatformChannel> = arrayListOf();
|
||||
private val _enabledCreatorsFiltered: ArrayList<IPlatformChannel> = arrayListOf();
|
||||
private val _disabledCreatorsFiltered: ArrayList<IPlatformChannel> = arrayListOf();
|
||||
|
||||
private val _containerEnabled: LinearLayout;
|
||||
private val _containerDisabled: LinearLayout;
|
||||
|
@ -79,39 +94,53 @@ class SubscriptionGroupFragment : MainFragment() {
|
|||
|
||||
private var _group: SubscriptionGroup? = null;
|
||||
|
||||
private val _editNameOverlayField: SlideUpMenuTextInput;
|
||||
|
||||
|
||||
constructor(context: Context, fragment: SubscriptionGroupFragment): super(context) {
|
||||
inflate(context, R.layout.fragment_subscriptions_group, this);
|
||||
_fragment = fragment;
|
||||
|
||||
_editNameOverlayField = SlideUpMenuTextInput(context, "Group name");
|
||||
|
||||
_overlay = findViewById(R.id.overlay);
|
||||
_searchBar = findViewById(R.id.search_bar);
|
||||
_textGroupTitleContainer = findViewById(R.id.text_group_title_container);
|
||||
_textGroupTitle = findViewById(R.id.text_group_title);
|
||||
_imageGroup = findViewById(R.id.image_group);
|
||||
_imageGroupBackground = findViewById(R.id.group_image_background);
|
||||
_buttonEditImage = findViewById(R.id.button_edit_image);
|
||||
_textGroupMeta = findViewById(R.id.text_group_meta);
|
||||
_buttonSettings = findViewById(R.id.button_settings);
|
||||
_imageGroup.setBackgroundColor(Color.GRAY);
|
||||
|
||||
val dp6 = 6.dp(resources);
|
||||
_imageGroup.shapeAppearanceModel = ShapeAppearanceModel.builder()
|
||||
.setAllCorners(CornerFamily.ROUNDED, dp6.toFloat())
|
||||
.build()
|
||||
|
||||
_containerEnabled = findViewById(R.id.container_enabled);
|
||||
_containerDisabled = findViewById(R.id.container_disabled);
|
||||
_recyclerCreatorsEnabled = findViewById<RecyclerView>(R.id.recycler_creators_enabled).asAny(_enabledCreators) {
|
||||
_recyclerCreatorsEnabled = findViewById<RecyclerView>(R.id.recycler_creators_enabled).asAny(_enabledCreatorsFiltered) {
|
||||
it.itemView.setPadding(0, dp6, 0, dp6);
|
||||
it.onClick.subscribe { channel ->
|
||||
disableCreator(channel);
|
||||
};
|
||||
}
|
||||
_recyclerCreatorsDisabled = findViewById<RecyclerView>(R.id.recycler_creators_disabled).asAny(_disabledCreators) {
|
||||
_recyclerCreatorsDisabled = findViewById<RecyclerView>(R.id.recycler_creators_disabled).asAny(_disabledCreatorsFiltered) {
|
||||
it.itemView.setPadding(0, dp6, 0, dp6);
|
||||
it.onClick.subscribe { channel ->
|
||||
enableCreator(channel);
|
||||
};
|
||||
}
|
||||
_recyclerCreatorsEnabled.view.layoutManager = GridLayoutManager(context, 5).apply {
|
||||
this.orientation = LinearLayoutManager.VERTICAL;
|
||||
};
|
||||
_recyclerCreatorsDisabled.view.layoutManager = GridLayoutManager(context, 5).apply {
|
||||
this.orientation = LinearLayoutManager.VERTICAL;
|
||||
};
|
||||
|
||||
_textGroupTitleContainer.setOnClickListener {
|
||||
_group?.let { editName(it) };
|
||||
};
|
||||
_textGroupMeta.setOnClickListener {
|
||||
_group?.let { editName(it) };
|
||||
};
|
||||
_imageGroup.setOnClickListener {
|
||||
_group?.let { editImage(it) };
|
||||
};
|
||||
|
@ -119,29 +148,52 @@ class SubscriptionGroupFragment : MainFragment() {
|
|||
_group?.let { editImage(it) }
|
||||
};
|
||||
|
||||
_searchBar.onSearchChanged.subscribe {
|
||||
filterCreators();
|
||||
}
|
||||
|
||||
setGroup(null);
|
||||
}
|
||||
|
||||
fun save() {
|
||||
_group?.let {
|
||||
StateSubscriptionGroups.instance.updateSubscriptionGroup(it);
|
||||
};
|
||||
}
|
||||
|
||||
fun editName(group: SubscriptionGroup) {
|
||||
val editView = SlideUpMenuTextInput(context, "Group name");
|
||||
editView.text = group.name;
|
||||
UISlideOverlays.showOverlay(_overlay, "Edit name", "Save", {
|
||||
val text = _editNameOverlayField.text;
|
||||
editView.deactivate();
|
||||
val text = editView.text;
|
||||
if(!text.isNullOrEmpty()) {
|
||||
group.name = text;
|
||||
_textGroupTitle.text = text;
|
||||
//TODO: Save
|
||||
save();
|
||||
}
|
||||
}, _editNameOverlayField);
|
||||
}, editView).onCancel.subscribe {
|
||||
editView.deactivate();
|
||||
}
|
||||
editView.activate();
|
||||
}
|
||||
fun editImage(group: SubscriptionGroup) {
|
||||
val overlay = ImageVariableOverlay(context);
|
||||
val view = UISlideOverlays.showOverlay(_overlay, "Temp", null, {},
|
||||
overlay);
|
||||
val overlay = ImageVariableOverlay(context, _enabledCreators.map { it.url });
|
||||
_overlay.removeAllViews();
|
||||
_overlay.addView(overlay);
|
||||
_overlay.alpha = 0f
|
||||
_overlay.visibility = View.VISIBLE;
|
||||
_overlay.animate().alpha(1f).setDuration(300).start();
|
||||
overlay.onSelected.subscribe {
|
||||
view.hide(true);
|
||||
group.image = it;
|
||||
it.setImageView(_imageGroup);
|
||||
//TODO: Save
|
||||
it.setImageView(_imageGroupBackground);
|
||||
save();
|
||||
};
|
||||
overlay.onClose.subscribe {
|
||||
_overlay.visibility = View.GONE;
|
||||
overlay.removeAllViews();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,10 +202,14 @@ class SubscriptionGroupFragment : MainFragment() {
|
|||
_textGroupTitle.text = group?.name;
|
||||
|
||||
val image = group?.image;
|
||||
if(image != null)
|
||||
if(image != null) {
|
||||
image.setImageView(_imageGroupBackground);
|
||||
image.setImageView(_imageGroup);
|
||||
else
|
||||
}
|
||||
else {
|
||||
_imageGroupBackground.setImageResource(0);
|
||||
_imageGroup.setImageResource(0);
|
||||
}
|
||||
updateMeta();
|
||||
reloadCreators(group);
|
||||
}
|
||||
|
@ -169,43 +225,63 @@ class SubscriptionGroupFragment : MainFragment() {
|
|||
_enabledCreators.addAll(subs.filter { urls.contains(it.url) });
|
||||
_disabledCreators.addAll(subs.filter { !urls.contains(it.url) });
|
||||
}
|
||||
filterCreators();
|
||||
}
|
||||
|
||||
private fun filterCreators() {
|
||||
val query = _searchBar.textSearch.text.toString().lowercase();
|
||||
val filteredEnabled = _enabledCreators.filter { it.name.lowercase().contains(query) };
|
||||
val filteredDisabled = _disabledCreators.filter { it.name.lowercase().contains(query) };
|
||||
|
||||
//Optimize
|
||||
_enabledCreatorsFiltered.clear();
|
||||
_enabledCreatorsFiltered.addAll(filteredEnabled);
|
||||
_disabledCreatorsFiltered.clear();
|
||||
_disabledCreatorsFiltered.addAll(filteredDisabled);
|
||||
|
||||
_recyclerCreatorsEnabled.notifyContentChanged();
|
||||
_recyclerCreatorsDisabled.notifyContentChanged();
|
||||
}
|
||||
|
||||
private fun enableCreator(channel: IPlatformChannel) {
|
||||
val index = _disabledCreators.indexOf(channel);
|
||||
val index = _disabledCreatorsFiltered.indexOf(channel);
|
||||
if (index >= 0) {
|
||||
_disabledCreators.removeAt(index)
|
||||
_disabledCreators.remove(channel)
|
||||
_disabledCreatorsFiltered.remove(channel);
|
||||
_recyclerCreatorsDisabled.adapter.notifyItemRangeRemoved(index);
|
||||
|
||||
_enabledCreators.add(channel);
|
||||
_recyclerCreatorsEnabled.adapter.notifyItemInserted(_enabledCreators.size - 1);
|
||||
_enabledCreatorsFiltered.add(channel);
|
||||
_recyclerCreatorsEnabled.adapter.notifyItemInserted(_enabledCreatorsFiltered.size - 1);
|
||||
|
||||
_group?.let {
|
||||
it.urls.remove(channel.url);
|
||||
//TODO: Save
|
||||
save();
|
||||
}
|
||||
updateMeta();
|
||||
}
|
||||
}
|
||||
private fun disableCreator(channel: IPlatformChannel) {
|
||||
val index = _disabledCreators.indexOf(channel);
|
||||
val index = _enabledCreatorsFiltered.indexOf(channel);
|
||||
if (index >= 0) {
|
||||
_disabledCreators.removeAt(index)
|
||||
_recyclerCreatorsDisabled.adapter.notifyItemRangeRemoved(index);
|
||||
_enabledCreators.remove(channel)
|
||||
_enabledCreatorsFiltered.removeAt(index);
|
||||
_recyclerCreatorsEnabled.adapter.notifyItemRangeRemoved(index);
|
||||
|
||||
_enabledCreators.add(channel);
|
||||
_recyclerCreatorsEnabled.adapter.notifyItemInserted(_enabledCreators.size - 1);
|
||||
_disabledCreators.add(channel);
|
||||
_disabledCreatorsFiltered.add(channel);
|
||||
_recyclerCreatorsDisabled.adapter.notifyItemInserted(_disabledCreatorsFiltered.size - 1);
|
||||
|
||||
_group?.let {
|
||||
it.urls.remove(channel.url);
|
||||
//TODO: Save
|
||||
save();
|
||||
}
|
||||
updateMeta();
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMeta() {
|
||||
_textGroupMeta.text = "${_group?.urls?.size} creators";
|
||||
_textGroupMeta.text = "${_enabledCreators.size} creators";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -254,6 +254,12 @@ class SubscriptionsFeedFragment : MainFragment() {
|
|||
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
};
|
||||
_subscriptionBar?.onClickChannel?.subscribe { c -> fragment.navigate<ChannelFragment>(c); };
|
||||
_subscriptionBar?.onClickGroup?.subscribe { g ->
|
||||
|
||||
};
|
||||
_subscriptionBar?.onHoldGroup?.subscribe { g ->
|
||||
fragment.navigate<SubscriptionGroupFragment>(g);
|
||||
};
|
||||
|
||||
synchronized(_filterLock) {
|
||||
_subscriptionBar?.setToggles(
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package com.futo.platformplayer.states
|
||||
|
||||
import com.futo.platformplayer.Settings
|
||||
import com.futo.platformplayer.UIDialogs
|
||||
import com.futo.platformplayer.api.media.models.ResultCapabilities
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.api.media.models.channels.SerializedChannel
|
||||
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
||||
import com.futo.platformplayer.api.media.platforms.js.JSClient
|
||||
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
|
||||
import com.futo.platformplayer.api.media.structures.*
|
||||
import com.futo.platformplayer.api.media.structures.ReusablePager.Companion.asReusable
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.constructs.Event2
|
||||
import com.futo.platformplayer.engine.exceptions.PluginException
|
||||
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
||||
import com.futo.platformplayer.engine.exceptions.ScriptCriticalException
|
||||
import com.futo.platformplayer.exceptions.ChannelException
|
||||
import com.futo.platformplayer.findNonRuntimeException
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
|
||||
import com.futo.platformplayer.getNowDiffDays
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.models.SubscriptionGroup
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.resolveChannelUrl
|
||||
import com.futo.platformplayer.stores.FragmentedStorage
|
||||
import com.futo.platformplayer.stores.SubscriptionStorage
|
||||
import com.futo.platformplayer.stores.v2.ReconstructStore
|
||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
||||
import com.futo.platformplayer.subscription.SubscriptionFetchAlgorithm
|
||||
import com.futo.platformplayer.subscription.SubscriptionFetchAlgorithms
|
||||
import kotlinx.coroutines.*
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
import java.util.concurrent.ForkJoinTask
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.streams.asSequence
|
||||
import kotlin.streams.toList
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
/***
|
||||
* Used to maintain subscription groups
|
||||
*/
|
||||
class StateSubscriptionGroups {
|
||||
private val _subGroups = FragmentedStorage.storeJson<SubscriptionGroup>("subscription_groups")
|
||||
.withUnique { it.id }
|
||||
.load();
|
||||
|
||||
fun getSubscriptionGroup(id: String): SubscriptionGroup? {
|
||||
return _subGroups.findItem { it.id == id };
|
||||
}
|
||||
fun getSubscriptionGroups(): List<SubscriptionGroup> {
|
||||
return _subGroups.getItems();
|
||||
}
|
||||
fun updateSubscriptionGroup(subGroup: SubscriptionGroup) {
|
||||
_subGroups.save(subGroup);
|
||||
}
|
||||
fun deleteSubscriptionGroup(id: String){
|
||||
val group = getSubscriptionGroup(id);
|
||||
if(group != null)
|
||||
_subGroups.delete(group);
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val TAG = "StateSubscriptionGroups";
|
||||
const val VERSION = 1;
|
||||
|
||||
private var _instance : StateSubscriptionGroups? = null;
|
||||
val instance : StateSubscriptionGroups
|
||||
get(){
|
||||
if(_instance == null)
|
||||
_instance = StateSubscriptionGroups();
|
||||
return _instance!!;
|
||||
};
|
||||
|
||||
fun finish() {
|
||||
_instance?.let {
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,9 @@ package com.futo.platformplayer.views.adapters
|
|||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Filter
|
||||
import android.widget.Filterable
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import java.lang.reflect.Constructor
|
||||
|
@ -116,7 +119,6 @@ class AnyAdapter<I, T : AnyAdapter.AnyViewHolder<I>> : BaseAnyAdapter<I, T, T> {
|
|||
private class Adapter<I, T : AnyViewHolder<I>> : RecyclerView.Adapter<T> {
|
||||
private val _parent: AnyAdapter<I, T>;
|
||||
|
||||
|
||||
constructor(parentAdapter: AnyAdapter<I, T>) {
|
||||
_parent = parentAdapter;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.futo.platformplayer.views.adapters.viewholders
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
|
@ -86,4 +87,78 @@ class CreatorBarViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyVi
|
|||
companion object {
|
||||
private const val TAG = "CreatorBarViewHolder";
|
||||
}
|
||||
}
|
||||
class SelectableCreatorBarViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<SelectableCreatorBarViewHolder.Selectable>(
|
||||
LayoutInflater.from(_viewGroup.context).inflate(R.layout.view_subscription_bar_icon, _viewGroup, false)) {
|
||||
|
||||
private val _creatorThumbnail: CreatorThumbnail;
|
||||
private val _name: TextView;
|
||||
private var _channel: Selectable? = null;
|
||||
|
||||
val onClick = Event1<Selectable>();
|
||||
|
||||
private val _taskLoadProfile = TaskHandler<PlatformID, PolycentricCache.CachedPolycentricProfile?>(
|
||||
StateApp.instance.scopeGetter,
|
||||
{ PolycentricCache.instance.getProfileAsync(it) })
|
||||
.success { onProfileLoaded(it, true) }
|
||||
.exception<Throwable> {
|
||||
Logger.w(TAG, "Failed to load profile.", it);
|
||||
};
|
||||
|
||||
init {
|
||||
_creatorThumbnail = _view.findViewById(R.id.creator_thumbnail);
|
||||
_name = _view.findViewById(R.id.text_channel_name);
|
||||
_view.findViewById<LinearLayout>(R.id.root).setOnClickListener {
|
||||
val s = _channel ?: return@setOnClickListener;
|
||||
onClick.emit(s);
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(value: Selectable) {
|
||||
_taskLoadProfile.cancel();
|
||||
|
||||
_channel = value;
|
||||
|
||||
if(value.active)
|
||||
_view.setBackgroundColor(_view.context.resources.getColor(R.color.colorPrimaryDark, null))
|
||||
else
|
||||
_view.setBackgroundColor(_view.context.resources.getColor(R.color.transparent, null))
|
||||
|
||||
_creatorThumbnail.setThumbnail(value.channel.thumbnail, false);
|
||||
_name.text = value.channel.name;
|
||||
|
||||
val cachedProfile = PolycentricCache.instance.getCachedProfile(value.channel.url, true);
|
||||
if (cachedProfile != null) {
|
||||
onProfileLoaded(cachedProfile, false);
|
||||
if (cachedProfile.expired) {
|
||||
_taskLoadProfile.run(value.channel.id);
|
||||
}
|
||||
} else {
|
||||
_taskLoadProfile.run(value.channel.id);
|
||||
}
|
||||
}
|
||||
|
||||
private fun onProfileLoaded(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||
val dp_55 = 55.dp(itemView.context.resources)
|
||||
val profile = cachedPolycentricProfile?.profile;
|
||||
val avatar = profile?.systemState?.avatar?.selectBestImage(dp_55 * dp_55)
|
||||
?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) };
|
||||
|
||||
if (avatar != null) {
|
||||
_creatorThumbnail.setThumbnail(avatar, animate);
|
||||
} else {
|
||||
_creatorThumbnail.setThumbnail(_channel?.channel?.thumbnail, animate);
|
||||
_creatorThumbnail.setHarborAvailable(profile != null, animate);
|
||||
}
|
||||
|
||||
if (profile != null) {
|
||||
_name.text = profile.systemState.username;
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CreatorBarViewHolder";
|
||||
}
|
||||
|
||||
data class Selectable(var channel: IPlatformChannel, var active: Boolean)
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package com.futo.platformplayer.views.adapters.viewholders
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.api.media.PlatformID
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.constructs.TaskHandler
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.SubscriptionGroup
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.selectBestImage
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||
import com.futo.platformplayer.views.others.CreatorThumbnail
|
||||
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
||||
import com.google.android.material.imageview.ShapeableImageView
|
||||
import com.google.android.material.shape.CornerFamily
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
|
||||
class SubscriptionGroupBarViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<SubscriptionGroup>(
|
||||
LayoutInflater.from(_viewGroup.context).inflate(R.layout.view_subscription_group_bar, _viewGroup, false)) {
|
||||
private var _group: SubscriptionGroup? = null;
|
||||
|
||||
private val _image: ShapeableImageView;
|
||||
private val _textSubGroup: TextView;
|
||||
|
||||
val onClick = Event1<SubscriptionGroup>();
|
||||
val onClickLong = Event1<SubscriptionGroup>();
|
||||
|
||||
init {
|
||||
_image = _view.findViewById(R.id.image);
|
||||
_textSubGroup = _view.findViewById(R.id.text_sub_group);
|
||||
|
||||
val dp6 = 6.dp(_view.resources);
|
||||
_image.shapeAppearanceModel = ShapeAppearanceModel.builder()
|
||||
.setAllCorners(CornerFamily.ROUNDED, dp6.toFloat())
|
||||
.build()
|
||||
|
||||
_viewGroup.setOnClickListener {
|
||||
_group?.let {
|
||||
onClick.emit(it);
|
||||
}
|
||||
}
|
||||
_viewGroup.setOnLongClickListener {
|
||||
_group?.let {
|
||||
onClickLong.emit(it);
|
||||
}
|
||||
true;
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(value: SubscriptionGroup) {
|
||||
_group = value;
|
||||
val img = value.image;
|
||||
if(img != null)
|
||||
img.setImageView(_image)
|
||||
else
|
||||
_image.setImageResource(0);
|
||||
_textSubGroup.text = value.name;
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SubscriptionGroupBarViewHolder";
|
||||
}
|
||||
}
|
|
@ -6,62 +6,96 @@ import android.graphics.Color
|
|||
import android.graphics.drawable.shapes.Shape
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.UIDialogs
|
||||
import com.futo.platformplayer.activities.IWithResultLauncher
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.dp
|
||||
import com.futo.platformplayer.models.ImageVariable
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StateSubscriptions
|
||||
import com.futo.platformplayer.views.AnyAdapterView
|
||||
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||
import com.futo.platformplayer.views.adapters.viewholders.CreatorBarViewHolder
|
||||
import com.futo.platformplayer.views.adapters.viewholders.SelectableCreatorBarViewHolder
|
||||
import com.futo.platformplayer.views.buttons.BigButton
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.material.imageview.ShapeableImageView
|
||||
import com.google.android.material.shape.CornerFamily
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
|
||||
class ImageVariableOverlay: ConstraintLayout {
|
||||
private val _buttonGallery: BigButton;
|
||||
private val _buttonSelect: Button;
|
||||
private val _recyclerPresets: AnyAdapterView<Int, PresetViewHolder>;
|
||||
private val _recyclerCreators: AnyAdapterView<IPlatformChannel, CreatorBarViewHolder>;
|
||||
private val _topbar: OverlayTopbar;
|
||||
private val _recyclerPresets: AnyAdapterView<PresetImage, PresetViewHolder>;
|
||||
private val _recyclerCreators: AnyAdapterView<SelectableCreatorBarViewHolder.Selectable, SelectableCreatorBarViewHolder>;
|
||||
|
||||
private val _creators: ArrayList<IPlatformChannel> = arrayListOf();
|
||||
private val _presets: ArrayList<Int> = arrayListOf();
|
||||
private val _creators: ArrayList<SelectableCreatorBarViewHolder.Selectable> = arrayListOf();
|
||||
private val _presets: ArrayList<PresetImage> = arrayListOf(
|
||||
PresetImage(R.drawable.xp_book, false),
|
||||
PresetImage(R.drawable.xp_forest, false),
|
||||
PresetImage(R.drawable.xp_laptop, false),
|
||||
PresetImage(R.drawable.xp_controller, false),
|
||||
PresetImage(R.drawable.xp_code, false),
|
||||
);
|
||||
|
||||
private var _selected: ImageVariable? = null;
|
||||
|
||||
val onSelected = Event1<ImageVariable>();
|
||||
val onClose = Event0();
|
||||
|
||||
constructor(context: Context): super(context) {
|
||||
inflate(context, R.layout.overlay_image_variable, this);
|
||||
}
|
||||
constructor(context: Context, attrs: AttributeSet?): super(context, attrs) {
|
||||
inflate(context, R.layout.overlay_image_variable, this);
|
||||
constructor(context: Context, creatorFilters: List<String>? = null): super(context) {
|
||||
val subs = StateSubscriptions.instance.getSubscriptions();
|
||||
if(creatorFilters != null) {
|
||||
_creators.addAll(subs
|
||||
.filter { creatorFilters.contains(it.channel.url) }
|
||||
.map { SelectableCreatorBarViewHolder.Selectable(it.channel, false) });
|
||||
}
|
||||
else
|
||||
_creators.addAll(subs
|
||||
.map { SelectableCreatorBarViewHolder.Selectable(it.channel, false) });
|
||||
_recyclerCreators.notifyContentChanged();
|
||||
}
|
||||
constructor(context: Context, attrs: AttributeSet?): super(context, attrs) { }
|
||||
init {
|
||||
inflate(context, R.layout.overlay_image_variable, this);
|
||||
_topbar = findViewById(R.id.topbar);
|
||||
_buttonGallery = findViewById(R.id.button_gallery);
|
||||
_buttonSelect = findViewById(R.id.button_select);
|
||||
_recyclerPresets = findViewById<RecyclerView>(R.id.recycler_presets).asAny(_presets, RecyclerView.HORIZONTAL) {
|
||||
it.onClick.subscribe {
|
||||
_selected = ImageVariable(null, it);
|
||||
_selected = ImageVariable(null, it.id);
|
||||
updateSelected();
|
||||
};
|
||||
};
|
||||
val dp6 = 6.dp(resources);
|
||||
_recyclerCreators = findViewById<RecyclerView>(R.id.recycler_creators).asAny(_creators, RecyclerView.HORIZONTAL) { creatorView ->
|
||||
creatorView.itemView.setPadding(0, dp6, 0, dp6);
|
||||
creatorView.onClick.subscribe {
|
||||
if(it.thumbnail == null) {
|
||||
if(it.channel.thumbnail == null) {
|
||||
UIDialogs.toast(context, "No thumbnail found");
|
||||
return@subscribe;
|
||||
}
|
||||
_selected = ImageVariable(it.thumbnail);
|
||||
_selected = ImageVariable(it.channel.thumbnail);
|
||||
updateSelected();
|
||||
};
|
||||
};
|
||||
_recyclerCreators.view.layoutManager = GridLayoutManager(context, 5).apply {
|
||||
this.orientation = LinearLayoutManager.VERTICAL;
|
||||
};
|
||||
|
||||
_buttonGallery.setOnClickListener {
|
||||
val context = StateApp.instance.contextOrNull;
|
||||
|
@ -85,9 +119,20 @@ class ImageVariableOverlay: ConstraintLayout {
|
|||
select(it);
|
||||
}
|
||||
};
|
||||
_topbar.onClose.subscribe {
|
||||
onClose.emit();
|
||||
}
|
||||
updateSelected();
|
||||
}
|
||||
|
||||
fun updateSelected() {
|
||||
val id = _selected?.resId;
|
||||
val url = _selected?.url;
|
||||
_presets.forEach { p -> p.active = p.id == id };
|
||||
_recyclerPresets.notifyContentChanged();
|
||||
_creators.forEach { p -> p.active = p.channel.thumbnail == url };
|
||||
_recyclerCreators.notifyContentChanged();
|
||||
|
||||
if(_selected != null)
|
||||
_buttonSelect.alpha = 1f;
|
||||
else
|
||||
|
@ -96,23 +141,46 @@ class ImageVariableOverlay: ConstraintLayout {
|
|||
|
||||
fun select(variable: ImageVariable) {
|
||||
onSelected.emit(variable);
|
||||
onClose.emit();
|
||||
}
|
||||
|
||||
class PresetViewHolder(context: Context) : AnyAdapter.AnyViewHolder<Int>(ShapeableImageView(context)) {
|
||||
private val view = _view as ShapeableImageView;
|
||||
class PresetViewHolder(viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<PresetImage>(LinearLayout(viewGroup.context)) {
|
||||
private val view = _view as LinearLayout;
|
||||
private val imageView = ShapeableImageView(viewGroup.context);
|
||||
|
||||
private var value: Int = 0;
|
||||
private var value: PresetImage = PresetImage(0, false);
|
||||
|
||||
val onClick = Event1<Int>();
|
||||
val onClick = Event1<PresetImage>();
|
||||
init {
|
||||
view.setOnClickListener {
|
||||
view.addView(imageView);
|
||||
val dp2 = 2.dp(viewGroup.context.resources);
|
||||
val dp6 = 6.dp(viewGroup.context.resources);
|
||||
view.setPadding(dp2, dp2, dp2, dp2);
|
||||
imageView.setOnClickListener {
|
||||
onClick.emit(value);
|
||||
}
|
||||
imageView.layoutParams = LinearLayout.LayoutParams(110.dp(viewGroup.context.resources), 70.dp(viewGroup.context.resources)).apply {
|
||||
//this.rightMargin = dp6
|
||||
}
|
||||
imageView.scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
imageView.shapeAppearanceModel = ShapeAppearanceModel.builder()
|
||||
.setAllCorners(CornerFamily.ROUNDED, dp6.toFloat())
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun bind(value: Int) {
|
||||
view.setImageResource(value);
|
||||
override fun bind(value: PresetImage) {
|
||||
imageView.setImageResource(value.id);
|
||||
this.value = value;
|
||||
setActive(value.active);
|
||||
}
|
||||
|
||||
fun setActive(active: Boolean) {
|
||||
if(active)
|
||||
_view.setBackgroundColor(view.context.resources.getColor(R.color.colorPrimary, null));
|
||||
else
|
||||
_view.setBackgroundColor(view.context.resources.getColor(R.color.transparent, null));
|
||||
}
|
||||
}
|
||||
|
||||
data class PresetImage(var id: Int, var active: Boolean);
|
||||
}
|
|
@ -18,7 +18,8 @@ class SlideUpMenuTextInput : LinearLayout {
|
|||
private lateinit var _editText: EditText;
|
||||
private lateinit var _inputMethodManager: InputMethodManager;
|
||||
|
||||
val text: String get() = _editText.text.toString();
|
||||
var text: String get() = _editText.text.toString()
|
||||
set(v: String) = _editText.setText(v);
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet? = null): super(context, attrs) {
|
||||
init();
|
||||
|
|
|
@ -4,21 +4,28 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.Recycler
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.api.media.models.channels.SerializedChannel
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.models.Subscription
|
||||
import com.futo.platformplayer.models.SubscriptionGroup
|
||||
import com.futo.platformplayer.states.StateSubscriptionGroups
|
||||
import com.futo.platformplayer.states.StateSubscriptions
|
||||
import com.futo.platformplayer.views.AnyAdapterView
|
||||
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||
import com.futo.platformplayer.views.others.ToggleTagView
|
||||
import com.futo.platformplayer.views.adapters.viewholders.SubscriptionBarViewHolder
|
||||
import com.futo.platformplayer.views.adapters.viewholders.SubscriptionGroupBarViewHolder
|
||||
|
||||
class SubscriptionBar : LinearLayout {
|
||||
private var _adapterView: AnyAdapterView<Subscription, SubscriptionBarViewHolder>? = null;
|
||||
private var _subGroups: AnyAdapterView<SubscriptionGroup, SubscriptionGroupBarViewHolder>
|
||||
private val _tagsContainer: LinearLayout;
|
||||
|
||||
val onClickChannel = Event1<SerializedChannel>();
|
||||
val onClickGroup = Event1<SubscriptionGroup>();
|
||||
val onHoldGroup = Event1<SubscriptionGroup>();
|
||||
|
||||
|
||||
|
||||
|
@ -31,6 +38,11 @@ class SubscriptionBar : LinearLayout {
|
|||
onClickChannel.emit(c.channel);
|
||||
};
|
||||
};
|
||||
val subgroups = StateSubscriptionGroups.instance.getSubscriptionGroups();
|
||||
_subGroups = findViewById<RecyclerView>(R.id.recycler_subgroups).asAny(subgroups, orientation = RecyclerView.HORIZONTAL) {
|
||||
it.onClick.subscribe(onClickGroup::emit);
|
||||
it.onClickLong.subscribe(onHoldGroup::emit);
|
||||
}
|
||||
_tagsContainer = findViewById(R.id.container_tags);
|
||||
}
|
||||
|
||||
|
|
BIN
app/src/main/res/drawable/xp_code.jpg
Normal file
BIN
app/src/main/res/drawable/xp_code.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 544 KiB |
BIN
app/src/main/res/drawable/xp_controller.jpg
Normal file
BIN
app/src/main/res/drawable/xp_controller.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 986 KiB |
BIN
app/src/main/res/drawable/xp_forest.jpg
Normal file
BIN
app/src/main/res/drawable/xp_forest.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 MiB |
BIN
app/src/main/res/drawable/xp_laptop.jpg
Normal file
BIN
app/src/main/res/drawable/xp_laptop.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 637 KiB |
|
@ -12,23 +12,32 @@
|
|||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="150dp"
|
||||
android:background="#222222">
|
||||
android:background="#AAAAAA">
|
||||
<ImageView
|
||||
android:id="@+id/group_image_background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/xp_book"
|
||||
android:scaleType="centerCrop" />
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#55000000">
|
||||
android:background="#AA000000">
|
||||
<ImageButton
|
||||
android:id="@+id/button_settings"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:src="@drawable/ic_settings"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:padding="5dp"
|
||||
android:padding="10dp"
|
||||
android:background="@color/transparent"
|
||||
android:visibility="invisible"
|
||||
android:scaleType="fitCenter" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
|
@ -57,6 +66,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="5dp"
|
||||
android:clickable="false"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="@color/transparent"
|
||||
android:src="@drawable/ic_edit"/>
|
||||
|
@ -102,7 +112,7 @@
|
|||
android:text="42 creators" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<com.futo.platformplayer.views.SearchView
|
||||
android:id="@+id/search_bar"
|
||||
|
@ -116,8 +126,8 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp">
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="0dp">
|
||||
<LinearLayout
|
||||
android:id="@+id/container_enabled"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -130,6 +140,8 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:textColor="@color/white"
|
||||
android:fontFamily="@font/inter_light"
|
||||
android:text="@string/enabled" />
|
||||
|
@ -138,6 +150,8 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:textColor="@color/gray_ac"
|
||||
android:fontFamily="@font/inter_extra_light"
|
||||
android:text="@string/these_creators_in_group" />
|
||||
|
@ -146,6 +160,9 @@
|
|||
android:id="@+id/recycler_creators_enabled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp" />
|
||||
</LinearLayout>
|
||||
|
@ -161,6 +178,8 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:textColor="@color/white"
|
||||
android:fontFamily="@font/inter_light"
|
||||
android:text="@string/disabled" />
|
||||
|
@ -169,6 +188,8 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:textColor="@color/gray_ac"
|
||||
android:fontFamily="@font/inter_extra_light"
|
||||
android:text="@string/these_creators_not_in_group" />
|
||||
|
@ -177,6 +198,8 @@
|
|||
android:id="@+id/recycler_creators_disabled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp" />
|
||||
</LinearLayout>
|
||||
|
|
|
@ -88,6 +88,7 @@
|
|||
android:text="Pick a creator as group image" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_creators"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
|
|
@ -23,4 +23,9 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" />
|
||||
</ScrollView>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_subgroups"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" />
|
||||
</LinearLayout>
|
||||
|
|
34
app/src/main/res/layout/view_subscription_group_bar.xml
Normal file
34
app/src/main/res/layout/view_subscription_group_bar.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="75dp"
|
||||
android:layout_height="50dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_margin="4dp"
|
||||
android:clickable="true"
|
||||
android:id="@+id/root">
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/xp_book" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#99000000"
|
||||
android:gravity="center">
|
||||
<TextView
|
||||
android:id="@+id/text_sub_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
android:textSize="12dp"
|
||||
android:textAlignment="center"
|
||||
android:text="News" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -2,7 +2,7 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="27dp"
|
||||
android:layout_height="32dp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"
|
||||
android:background="@drawable/background_pill"
|
||||
|
|
|
@ -564,6 +564,7 @@
|
|||
<string name="playlist_copied_as_local_playlist">Playlist copied as local playlist</string>
|
||||
<string name="are_you_sure_you_want_to_delete_the_downloaded_videos">Are you sure you want to delete the downloaded videos?</string>
|
||||
<string name="create_new_playlist">Create new playlist</string>
|
||||
<string name="create_new_subgroup">Create new subscription group</string>
|
||||
<string name="expected_media_content_found">Expected media content, found</string>
|
||||
<string name="failed_to_load_post">Failed to load post.</string>
|
||||
<string name="replies">replies</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue