mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-09 01:29:25 +00:00
Working subscription groups and image pickers
This commit is contained in:
parent
02292fed04
commit
4930ea8183
24 changed files with 689 additions and 67 deletions
|
@ -248,20 +248,23 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
return FeedStyle.THUMBNAIL;
|
return FeedStyle.THUMBNAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 5)
|
@FormField(R.string.show_subscription_group, FieldForm.TOGGLE, R.string.show_subscription_group_description, 5)
|
||||||
|
var showSubscriptionGroups: Boolean = true;
|
||||||
|
|
||||||
|
@FormField(R.string.preview_feed_items, FieldForm.TOGGLE, R.string.preview_feed_items_description, 6)
|
||||||
var previewFeedItems: Boolean = true;
|
var previewFeedItems: Boolean = true;
|
||||||
|
|
||||||
@FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6)
|
@FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 7)
|
||||||
var progressBar: Boolean = true;
|
var progressBar: Boolean = true;
|
||||||
|
|
||||||
@FormField(R.string.fetch_on_app_boot, FieldForm.TOGGLE, R.string.shortly_after_opening_the_app_start_fetching_subscriptions, 7)
|
@FormField(R.string.fetch_on_app_boot, FieldForm.TOGGLE, R.string.shortly_after_opening_the_app_start_fetching_subscriptions, 8)
|
||||||
@Serializable(with = FlexibleBooleanSerializer::class)
|
@Serializable(with = FlexibleBooleanSerializer::class)
|
||||||
var fetchOnAppBoot: Boolean = true;
|
var fetchOnAppBoot: Boolean = true;
|
||||||
|
|
||||||
@FormField(R.string.fetch_on_tab_opened, FieldForm.TOGGLE, R.string.fetch_on_tab_opened_description, 8)
|
@FormField(R.string.fetch_on_tab_opened, FieldForm.TOGGLE, R.string.fetch_on_tab_opened_description, 9)
|
||||||
var fetchOnTabOpen: Boolean = true;
|
var fetchOnTabOpen: Boolean = true;
|
||||||
|
|
||||||
@FormField(R.string.background_update, FieldForm.DROPDOWN, R.string.experimental_background_update_for_subscriptions_cache, 9)
|
@FormField(R.string.background_update, FieldForm.DROPDOWN, R.string.experimental_background_update_for_subscriptions_cache, 10)
|
||||||
@DropdownFieldOptionsId(R.array.background_interval)
|
@DropdownFieldOptionsId(R.array.background_interval)
|
||||||
var subscriptionsBackgroundUpdateInterval: Int = 0;
|
var subscriptionsBackgroundUpdateInterval: Int = 0;
|
||||||
|
|
||||||
|
@ -277,7 +280,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@FormField(R.string.subscription_concurrency, FieldForm.DROPDOWN, R.string.specify_how_many_threads_are_used_to_fetch_channels, 10)
|
@FormField(R.string.subscription_concurrency, FieldForm.DROPDOWN, R.string.specify_how_many_threads_are_used_to_fetch_channels, 11)
|
||||||
@DropdownFieldOptionsId(R.array.thread_count)
|
@DropdownFieldOptionsId(R.array.thread_count)
|
||||||
var subscriptionConcurrency: Int = 3;
|
var subscriptionConcurrency: Int = 3;
|
||||||
|
|
||||||
|
@ -285,17 +288,17 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
return threadIndexToCount(subscriptionConcurrency);
|
return threadIndexToCount(subscriptionConcurrency);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FormField(R.string.show_watch_metrics, FieldForm.TOGGLE, R.string.show_watch_metrics_description, 11)
|
@FormField(R.string.show_watch_metrics, FieldForm.TOGGLE, R.string.show_watch_metrics_description, 12)
|
||||||
var showWatchMetrics: Boolean = false;
|
var showWatchMetrics: Boolean = false;
|
||||||
|
|
||||||
@FormField(R.string.track_playtime_locally, FieldForm.TOGGLE, R.string.track_playtime_locally_description, 12)
|
@FormField(R.string.track_playtime_locally, FieldForm.TOGGLE, R.string.track_playtime_locally_description, 13)
|
||||||
var allowPlaytimeTracking: Boolean = true;
|
var allowPlaytimeTracking: Boolean = true;
|
||||||
|
|
||||||
|
|
||||||
@FormField(R.string.always_reload_from_cache, FieldForm.TOGGLE, R.string.always_reload_from_cache_description, 13)
|
@FormField(R.string.always_reload_from_cache, FieldForm.TOGGLE, R.string.always_reload_from_cache_description, 14)
|
||||||
var alwaysReloadFromCache: Boolean = false;
|
var alwaysReloadFromCache: Boolean = false;
|
||||||
|
|
||||||
@FormField(R.string.clear_channel_cache, FieldForm.BUTTON, R.string.clear_channel_cache_description, 14)
|
@FormField(R.string.clear_channel_cache, FieldForm.BUTTON, R.string.clear_channel_cache_description, 15)
|
||||||
fun clearChannelCache() {
|
fun clearChannelCache() {
|
||||||
UIDialogs.toast(SettingsActivity.getActivity()!!, "Started clearing..");
|
UIDialogs.toast(SettingsActivity.getActivity()!!, "Started clearing..");
|
||||||
StateCache.instance.clear();
|
StateCache.instance.clear();
|
||||||
|
|
|
@ -103,14 +103,14 @@ class UISlideOverlays {
|
||||||
}, false) else null,
|
}, false) else null,
|
||||||
if(capabilities.hasType(ResultCapabilities.TYPE_POSTS)) SlideUpMenuItem(container.context, R.drawable.ic_chat, "Posts", "Check for posts", "fetchPosts", {
|
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;
|
subscription.doFetchPosts = menu?.selectOption(null, "fetchPosts", true, true) ?: subscription.doFetchPosts;
|
||||||
}, false) else null,
|
}, false) else null/*,,
|
||||||
|
|
||||||
SlideUpMenuGroup(container.context, "Actions",
|
SlideUpMenuGroup(container.context, "Actions",
|
||||||
"Various things you can do with this subscription",
|
"Various things you can do with this subscription",
|
||||||
-1, listOf()),
|
-1, listOf())
|
||||||
SlideUpMenuItem(container.context, R.drawable.ic_list, "Add to Group", "", "btnAddToGroup", {
|
SlideUpMenuItem(container.context, R.drawable.ic_list, "Add to Group", "", "btnAddToGroup", {
|
||||||
showCreateSubscriptionGroup(container, subscription.channel);
|
showCreateSubscriptionGroup(container, subscription.channel);
|
||||||
}, false)
|
}, false)*/
|
||||||
).filterNotNull());
|
).filterNotNull());
|
||||||
|
|
||||||
menu = SlideUpMenuOverlay(container.context, container, "Subscription Settings", null, true, items);
|
menu = SlideUpMenuOverlay(container.context, container, "Subscription Settings", null, true, items);
|
||||||
|
@ -531,7 +531,7 @@ class UISlideOverlays {
|
||||||
return overlay;
|
return overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showCreateSubscriptionGroup(container: ViewGroup, initialChannel: IPlatformChannel?, onCreate: ((String) -> Unit)? = null): SlideUpMenuOverlay {
|
fun showCreateSubscriptionGroup(container: ViewGroup, initialChannel: IPlatformChannel? = null, onCreate: ((String) -> Unit)? = null): SlideUpMenuOverlay {
|
||||||
val nameInput = SlideUpMenuTextInput(container.context, container.context.getString(R.string.name));
|
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);
|
val addSubGroupOverlay = SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.create_new_subgroup), container.context.getString(R.string.ok), false, nameInput);
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
lateinit var _fragImportPlaylists: ImportPlaylistsFragment;
|
lateinit var _fragImportPlaylists: ImportPlaylistsFragment;
|
||||||
lateinit var _fragBuy: BuyFragment;
|
lateinit var _fragBuy: BuyFragment;
|
||||||
lateinit var _fragSubGroup: SubscriptionGroupFragment;
|
lateinit var _fragSubGroup: SubscriptionGroupFragment;
|
||||||
|
lateinit var _fragSubGroupList: SubscriptionGroupListFragment;
|
||||||
|
|
||||||
lateinit var _fragBrowser: BrowserFragment;
|
lateinit var _fragBrowser: BrowserFragment;
|
||||||
|
|
||||||
|
@ -238,6 +239,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
_fragImportPlaylists = ImportPlaylistsFragment.newInstance();
|
_fragImportPlaylists = ImportPlaylistsFragment.newInstance();
|
||||||
_fragBuy = BuyFragment.newInstance();
|
_fragBuy = BuyFragment.newInstance();
|
||||||
_fragSubGroup = SubscriptionGroupFragment.newInstance();
|
_fragSubGroup = SubscriptionGroupFragment.newInstance();
|
||||||
|
_fragSubGroupList = SubscriptionGroupListFragment.newInstance();
|
||||||
|
|
||||||
_fragBrowser = BrowserFragment.newInstance();
|
_fragBrowser = BrowserFragment.newInstance();
|
||||||
|
|
||||||
|
@ -320,6 +322,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
_fragImportSubscriptions.topBar = _fragTopBarImport;
|
_fragImportSubscriptions.topBar = _fragTopBarImport;
|
||||||
_fragImportPlaylists.topBar = _fragTopBarImport;
|
_fragImportPlaylists.topBar = _fragTopBarImport;
|
||||||
_fragSubGroup.topBar = _fragTopBarNavigation;
|
_fragSubGroup.topBar = _fragTopBarNavigation;
|
||||||
|
_fragSubGroupList.topBar = _fragTopBarAdd;
|
||||||
|
|
||||||
_fragBrowser.topBar = _fragTopBarNavigation;
|
_fragBrowser.topBar = _fragTopBarNavigation;
|
||||||
|
|
||||||
|
@ -987,6 +990,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
BrowserFragment::class -> _fragBrowser as T;
|
BrowserFragment::class -> _fragBrowser as T;
|
||||||
BuyFragment::class -> _fragBuy as T;
|
BuyFragment::class -> _fragBuy as T;
|
||||||
SubscriptionGroupFragment::class -> _fragSubGroup as T;
|
SubscriptionGroupFragment::class -> _fragSubGroup as T;
|
||||||
|
SubscriptionGroupListFragment::class -> _fragSubGroupList as T;
|
||||||
else -> throw IllegalArgumentException("Fragment type ${T::class.java.name} is not available in MainActivity");
|
else -> throw IllegalArgumentException("Fragment type ${T::class.java.name} is not available in MainActivity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,10 +55,10 @@ class ManageTabsActivity : AppCompatActivity() {
|
||||||
Settings.instance.save()
|
Settings.instance.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
val items = Settings.instance.tabs.mapNotNull {
|
val items = ArrayList(Settings.instance.tabs.mapNotNull {
|
||||||
val buttonDefinition = MenuBottomBarFragment.buttonDefinitions.find { d -> it.id == d.id } ?: return@mapNotNull null
|
val buttonDefinition = MenuBottomBarFragment.buttonDefinitions.find { d -> it.id == d.id } ?: return@mapNotNull null
|
||||||
TabViewHolderData(buttonDefinition, it.enabled)
|
TabViewHolderData(buttonDefinition, it.enabled)
|
||||||
};
|
});
|
||||||
|
|
||||||
_listTabs = _recyclerTabs.asAny(items) {
|
_listTabs = _recyclerTabs.asAny(items) {
|
||||||
it.onDragDrop.subscribe { vh ->
|
it.onDragDrop.subscribe { vh ->
|
||||||
|
|
|
@ -348,6 +348,7 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
||||||
ButtonDefinition(5, R.drawable.ic_history, R.drawable.ic_history, R.string.history, canToggle = false, { it.currentMain is HistoryFragment }, { it.navigate<HistoryFragment>() }),
|
ButtonDefinition(5, R.drawable.ic_history, R.drawable.ic_history, R.string.history, canToggle = false, { it.currentMain is HistoryFragment }, { it.navigate<HistoryFragment>() }),
|
||||||
ButtonDefinition(6, R.drawable.ic_download, R.drawable.ic_download, R.string.downloads, canToggle = false, { it.currentMain is DownloadsFragment }, { it.navigate<DownloadsFragment>() }),
|
ButtonDefinition(6, R.drawable.ic_download, R.drawable.ic_download, R.string.downloads, canToggle = false, { it.currentMain is DownloadsFragment }, { it.navigate<DownloadsFragment>() }),
|
||||||
ButtonDefinition(8, R.drawable.ic_chat, R.drawable.ic_chat_filled, R.string.comments, canToggle = true, { it.currentMain is CommentsFragment }, { it.navigate<CommentsFragment>() }),
|
ButtonDefinition(8, R.drawable.ic_chat, R.drawable.ic_chat_filled, R.string.comments, canToggle = true, { it.currentMain is CommentsFragment }, { it.navigate<CommentsFragment>() }),
|
||||||
|
ButtonDefinition(9, R.drawable.ic_subscriptions, R.drawable.ic_subscriptions_filled, R.string.subscription_group_menu, canToggle = true, { it.currentMain is SubscriptionGroupListFragment }, { it.navigate<SubscriptionGroupListFragment>() }),
|
||||||
ButtonDefinition(7, R.drawable.ic_settings, R.drawable.ic_settings, R.string.settings, canToggle = false, { false }, {
|
ButtonDefinition(7, R.drawable.ic_settings, R.drawable.ic_settings, R.string.settings, canToggle = false, { false }, {
|
||||||
val c = it.context ?: return@ButtonDefinition;
|
val c = it.context ?: return@ButtonDefinition;
|
||||||
Logger.i(TAG, "settings preventPictureInPicture()");
|
Logger.i(TAG, "settings preventPictureInPicture()");
|
||||||
|
|
|
@ -10,6 +10,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageButton
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
@ -48,7 +49,7 @@ class SubscriptionGroupFragment : MainFragment() {
|
||||||
super.onShownWithView(parameter, isBack);
|
super.onShownWithView(parameter, isBack);
|
||||||
|
|
||||||
if(parameter is SubscriptionGroup)
|
if(parameter is SubscriptionGroup)
|
||||||
_view?.setGroup(parameter);
|
_view?.setGroup(StateSubscriptionGroups.instance.getSubscriptionGroup(parameter.id) ?: parameter);
|
||||||
else
|
else
|
||||||
_view?.setGroup(null);
|
_view?.setGroup(null);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +78,8 @@ class SubscriptionGroupFragment : MainFragment() {
|
||||||
|
|
||||||
private val _textGroupMeta: TextView;
|
private val _textGroupMeta: TextView;
|
||||||
|
|
||||||
private val _buttonSettings: ImageView;
|
private val _buttonSettings: ImageButton;
|
||||||
|
private val _buttonDelete: ImageButton;
|
||||||
|
|
||||||
private val _enabledCreators: ArrayList<IPlatformChannel> = arrayListOf();
|
private val _enabledCreators: ArrayList<IPlatformChannel> = arrayListOf();
|
||||||
private val _disabledCreators: ArrayList<IPlatformChannel> = arrayListOf();
|
private val _disabledCreators: ArrayList<IPlatformChannel> = arrayListOf();
|
||||||
|
@ -107,6 +109,7 @@ class SubscriptionGroupFragment : MainFragment() {
|
||||||
_buttonEditImage = findViewById(R.id.button_edit_image);
|
_buttonEditImage = findViewById(R.id.button_edit_image);
|
||||||
_textGroupMeta = findViewById(R.id.text_group_meta);
|
_textGroupMeta = findViewById(R.id.text_group_meta);
|
||||||
_buttonSettings = findViewById(R.id.button_settings);
|
_buttonSettings = findViewById(R.id.button_settings);
|
||||||
|
_buttonDelete = findViewById(R.id.button_delete);
|
||||||
_imageGroup.setBackgroundColor(Color.GRAY);
|
_imageGroup.setBackgroundColor(Color.GRAY);
|
||||||
|
|
||||||
val dp6 = 6.dp(resources);
|
val dp6 = 6.dp(resources);
|
||||||
|
@ -147,6 +150,16 @@ class SubscriptionGroupFragment : MainFragment() {
|
||||||
_buttonEditImage.setOnClickListener {
|
_buttonEditImage.setOnClickListener {
|
||||||
_group?.let { editImage(it) }
|
_group?.let { editImage(it) }
|
||||||
};
|
};
|
||||||
|
_buttonSettings.setOnClickListener {
|
||||||
|
|
||||||
|
}
|
||||||
|
_buttonDelete.setOnClickListener {
|
||||||
|
_group?.let {
|
||||||
|
StateSubscriptionGroups.instance.deleteSubscriptionGroup(it.id);
|
||||||
|
};
|
||||||
|
fragment.close(true);
|
||||||
|
}
|
||||||
|
_buttonSettings.visibility = View.GONE;
|
||||||
|
|
||||||
_searchBar.onSearchChanged.subscribe {
|
_searchBar.onSearchChanged.subscribe {
|
||||||
filterCreators();
|
filterCreators();
|
||||||
|
@ -255,8 +268,10 @@ class SubscriptionGroupFragment : MainFragment() {
|
||||||
_recyclerCreatorsEnabled.adapter.notifyItemInserted(_enabledCreatorsFiltered.size - 1);
|
_recyclerCreatorsEnabled.adapter.notifyItemInserted(_enabledCreatorsFiltered.size - 1);
|
||||||
|
|
||||||
_group?.let {
|
_group?.let {
|
||||||
it.urls.remove(channel.url);
|
if(!it.urls.contains(channel.url)) {
|
||||||
save();
|
it.urls.add(channel.url);
|
||||||
|
save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateMeta();
|
updateMeta();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
package com.futo.platformplayer.fragment.mainactivity.main
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.webkit.CookieManager
|
||||||
|
import android.webkit.WebResourceRequest
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.webkit.WebViewClient
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.UISlideOverlays
|
||||||
|
import com.futo.platformplayer.activities.AddSourceOptionsActivity
|
||||||
|
import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment
|
||||||
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import com.futo.platformplayer.models.SubscriptionGroup
|
||||||
|
import com.futo.platformplayer.states.StateSubscriptionGroups
|
||||||
|
import com.futo.platformplayer.views.AnyAdapterView
|
||||||
|
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||||
|
import com.futo.platformplayer.views.adapters.ItemMoveCallback
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.SubscriptionGroupListViewHolder
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.Collections
|
||||||
|
|
||||||
|
class SubscriptionGroupListFragment : MainFragment() {
|
||||||
|
override val isMainView : Boolean = true;
|
||||||
|
override val isTab: Boolean = true;
|
||||||
|
override val hasBottomBar: Boolean get() = true;
|
||||||
|
|
||||||
|
private var _touchHelper: ItemTouchHelper? = null;
|
||||||
|
|
||||||
|
private var _subs: ArrayList<SubscriptionGroup> = arrayListOf();
|
||||||
|
private var _list: AnyAdapterView<SubscriptionGroup, SubscriptionGroupListViewHolder>? = null;
|
||||||
|
private var _overlay: FrameLayout? = null;
|
||||||
|
|
||||||
|
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
val view = inflater.inflate(R.layout.fragment_subscriptions_group_list, container, false);
|
||||||
|
_overlay = view.findViewById(R.id.overlay);
|
||||||
|
val recycler = view.findViewById<RecyclerView>(R.id.list);
|
||||||
|
val callback = ItemMoveCallback();
|
||||||
|
_touchHelper = ItemTouchHelper(callback);
|
||||||
|
_touchHelper?.attachToRecyclerView(recycler);
|
||||||
|
|
||||||
|
_subs.clear();
|
||||||
|
_subs.addAll(StateSubscriptionGroups.instance.getSubscriptionGroups().sortedBy { it.priority });
|
||||||
|
_list = recycler.asAny(_subs, RecyclerView.VERTICAL){
|
||||||
|
it.onClick.subscribe {
|
||||||
|
navigate<SubscriptionGroupFragment>(it);
|
||||||
|
};
|
||||||
|
it.onSettings.subscribe {
|
||||||
|
|
||||||
|
};
|
||||||
|
it.onDelete.subscribe { group ->
|
||||||
|
val loc = _subs.indexOf(group);
|
||||||
|
_subs.remove(group);
|
||||||
|
_list?.adapter?.notifyItemRangeRemoved(loc);
|
||||||
|
StateSubscriptionGroups.instance.deleteSubscriptionGroup(group.id);
|
||||||
|
};
|
||||||
|
it.onDragDrop.subscribe {
|
||||||
|
_touchHelper?.startDrag(it);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
callback.onRowMoved.subscribe(::groupMoved);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun groupMoved(fromPosition: Int, toPosition: Int) {
|
||||||
|
Logger.i("SubscriptionGroupListFragment", "Moved ${fromPosition} to ${toPosition}");
|
||||||
|
synchronized(_subs) {
|
||||||
|
if (fromPosition < toPosition) {
|
||||||
|
for (i in fromPosition until toPosition) {
|
||||||
|
Collections.swap(_subs, i, i + 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i in fromPosition downTo toPosition + 1) {
|
||||||
|
Collections.swap(_subs, i, i - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_list?.adapter?.notifyItemMoved(fromPosition, toPosition);
|
||||||
|
|
||||||
|
synchronized(_subs) {
|
||||||
|
for(i in 0 until _subs.size) {
|
||||||
|
val sub = _subs[i];
|
||||||
|
if(sub.priority != i) {
|
||||||
|
sub.priority = i;
|
||||||
|
StateSubscriptionGroups.instance.updateSubscriptionGroup(sub, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onShownWithView(parameter: Any?, isBack: Boolean) {
|
||||||
|
super.onShownWithView(parameter, isBack);
|
||||||
|
|
||||||
|
updateGroups();
|
||||||
|
|
||||||
|
StateSubscriptionGroups.instance.onGroupsChanged.subscribe(this) {
|
||||||
|
updateGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(topBar is AddTopBarFragment)
|
||||||
|
(topBar as AddTopBarFragment).onAdd.subscribe {
|
||||||
|
_overlay?.let {
|
||||||
|
UISlideOverlays.showCreateSubscriptionGroup(it)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateGroups() {
|
||||||
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
_subs.clear();
|
||||||
|
_subs.addAll(StateSubscriptionGroups.instance.getSubscriptionGroups().sortedBy { it.priority });
|
||||||
|
_list?.adapter?.notifyContentChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onHide() {
|
||||||
|
super.onHide();
|
||||||
|
StateSubscriptionGroups.instance.onGroupsChanged.remove(this);
|
||||||
|
|
||||||
|
if(topBar is AddTopBarFragment)
|
||||||
|
(topBar as AddTopBarFragment).onAdd.remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = SubscriptionGroupListFragment().apply {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import com.futo.platformplayer.exceptions.ChannelException
|
||||||
import com.futo.platformplayer.exceptions.RateLimitException
|
import com.futo.platformplayer.exceptions.RateLimitException
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.models.SearchType
|
import com.futo.platformplayer.models.SearchType
|
||||||
|
import com.futo.platformplayer.models.SubscriptionGroup
|
||||||
import com.futo.platformplayer.states.StateApp
|
import com.futo.platformplayer.states.StateApp
|
||||||
import com.futo.platformplayer.states.StateCache
|
import com.futo.platformplayer.states.StateCache
|
||||||
import com.futo.platformplayer.states.StatePlatform
|
import com.futo.platformplayer.states.StatePlatform
|
||||||
|
@ -99,6 +100,8 @@ class SubscriptionsFeedFragment : MainFragment() {
|
||||||
class SubscriptionsFeedView : ContentFeedView<SubscriptionsFeedFragment> {
|
class SubscriptionsFeedView : ContentFeedView<SubscriptionsFeedFragment> {
|
||||||
override val shouldShowTimeBar: Boolean get() = Settings.instance.subscriptions.progressBar
|
override val shouldShowTimeBar: Boolean get() = Settings.instance.subscriptions.progressBar
|
||||||
|
|
||||||
|
private var _subGroup: SubscriptionGroup? = null;
|
||||||
|
|
||||||
constructor(fragment: SubscriptionsFeedFragment, inflater: LayoutInflater, cachedRecyclerData: RecyclerData<InsertedViewAdapterWithLoader<ContentPreviewViewHolder>, LinearLayoutManager, IPager<IPlatformContent>, IPlatformContent, IPlatformContent, InsertedViewHolder<ContentPreviewViewHolder>>? = null) : super(fragment, inflater, cachedRecyclerData) {
|
constructor(fragment: SubscriptionsFeedFragment, inflater: LayoutInflater, cachedRecyclerData: RecyclerData<InsertedViewAdapterWithLoader<ContentPreviewViewHolder>, LinearLayoutManager, IPager<IPlatformContent>, IPlatformContent, IPlatformContent, InsertedViewHolder<ContentPreviewViewHolder>>? = null) : super(fragment, inflater, cachedRecyclerData) {
|
||||||
Logger.i(TAG, "SubscriptionsFeedFragment constructor()");
|
Logger.i(TAG, "SubscriptionsFeedFragment constructor()");
|
||||||
StateSubscriptions.instance.onGlobalSubscriptionsUpdateProgress.subscribe(this) { progress, total ->
|
StateSubscriptions.instance.onGlobalSubscriptionsUpdateProgress.subscribe(this) { progress, total ->
|
||||||
|
@ -254,11 +257,17 @@ class SubscriptionsFeedFragment : MainFragment() {
|
||||||
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
};
|
};
|
||||||
_subscriptionBar?.onClickChannel?.subscribe { c -> fragment.navigate<ChannelFragment>(c); };
|
_subscriptionBar?.onClickChannel?.subscribe { c -> fragment.navigate<ChannelFragment>(c); };
|
||||||
_subscriptionBar?.onClickGroup?.subscribe { g ->
|
_subscriptionBar?.onToggleGroup?.subscribe { g ->
|
||||||
|
if(g is SubscriptionGroup.Add)
|
||||||
|
UISlideOverlays.showCreateSubscriptionGroup(_overlayContainer);
|
||||||
|
else {
|
||||||
|
_subGroup = g;
|
||||||
|
loadCache(); //TODO: Proper subset update
|
||||||
|
}
|
||||||
};
|
};
|
||||||
_subscriptionBar?.onHoldGroup?.subscribe { g ->
|
_subscriptionBar?.onHoldGroup?.subscribe { g ->
|
||||||
fragment.navigate<SubscriptionGroupFragment>(g);
|
if(g !is SubscriptionGroup.Add)
|
||||||
|
fragment.navigate<SubscriptionGroupFragment>(g);
|
||||||
};
|
};
|
||||||
|
|
||||||
synchronized(_filterLock) {
|
synchronized(_filterLock) {
|
||||||
|
@ -294,9 +303,15 @@ class SubscriptionsFeedFragment : MainFragment() {
|
||||||
|
|
||||||
override fun filterResults(results: List<IPlatformContent>): List<IPlatformContent> {
|
override fun filterResults(results: List<IPlatformContent>): List<IPlatformContent> {
|
||||||
val nowSoon = OffsetDateTime.now().plusMinutes(5);
|
val nowSoon = OffsetDateTime.now().plusMinutes(5);
|
||||||
|
val filterGroup = _subGroup;
|
||||||
return results.filter {
|
return results.filter {
|
||||||
val allowedContentType = _filterSettings.allowContentTypes.contains(if(it.contentType == ContentType.NESTED_VIDEO || it.contentType == ContentType.LOCKED) ContentType.MEDIA else it.contentType);
|
val allowedContentType = _filterSettings.allowContentTypes.contains(if(it.contentType == ContentType.NESTED_VIDEO || it.contentType == ContentType.LOCKED) ContentType.MEDIA else it.contentType);
|
||||||
|
|
||||||
|
//TODO: Check against a sub cache
|
||||||
|
if(filterGroup != null && !filterGroup.urls.contains(it.author.url))
|
||||||
|
return@filter false;
|
||||||
|
|
||||||
|
|
||||||
if(it.datetime?.isAfter(nowSoon) == true) {
|
if(it.datetime?.isAfter(nowSoon) == true) {
|
||||||
if(!_filterSettings.allowPlanned)
|
if(!_filterSettings.allowPlanned)
|
||||||
return@filter false;
|
return@filter false;
|
||||||
|
|
|
@ -3,13 +3,31 @@ package com.futo.platformplayer.models
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
class SubscriptionGroup {
|
open class SubscriptionGroup {
|
||||||
val id: String = UUID.randomUUID().toString();
|
var id: String = UUID.randomUUID().toString();
|
||||||
var name: String;
|
var name: String;
|
||||||
var image: ImageVariable? = null;
|
var image: ImageVariable? = null;
|
||||||
var urls: MutableList<String> = mutableListOf();
|
var urls: MutableList<String> = mutableListOf();
|
||||||
|
var priority: Int = 99;
|
||||||
|
|
||||||
constructor(name: String) {
|
constructor(name: String) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
constructor(parent: SubscriptionGroup) {
|
||||||
|
this.id = parent.id;
|
||||||
|
this.name = parent.name;
|
||||||
|
this.image = parent.image;
|
||||||
|
this.urls = parent.urls;
|
||||||
|
this.priority = parent.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Selectable(parent: SubscriptionGroup, isSelected: Boolean = false): SubscriptionGroup(parent) {
|
||||||
|
var selected: Boolean = isSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Add: SubscriptionGroup("+") {
|
||||||
|
init {
|
||||||
|
urls.add("+");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -133,6 +133,7 @@ class StateApp {
|
||||||
|
|
||||||
//Files
|
//Files
|
||||||
private var _tempDirectory: File? = null;
|
private var _tempDirectory: File? = null;
|
||||||
|
private var _persistentDirectory: File? = null;
|
||||||
|
|
||||||
|
|
||||||
//AutoRotate
|
//AutoRotate
|
||||||
|
@ -165,6 +166,16 @@ class StateApp {
|
||||||
return File(_tempDirectory, name);
|
return File(_tempDirectory, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getPersistFile(extension: String? = null): File {
|
||||||
|
val name = UUID.randomUUID().toString() +
|
||||||
|
if(extension != null)
|
||||||
|
".${extension}"
|
||||||
|
else
|
||||||
|
"";
|
||||||
|
|
||||||
|
return File(_persistentDirectory, name);
|
||||||
|
}
|
||||||
|
|
||||||
fun getCurrentSystemAutoRotate(): Boolean {
|
fun getCurrentSystemAutoRotate(): Boolean {
|
||||||
_context?.let {
|
_context?.let {
|
||||||
systemAutoRotate = android.provider.Settings.System.getInt(
|
systemAutoRotate = android.provider.Settings.System.getInt(
|
||||||
|
@ -290,6 +301,10 @@ class StateApp {
|
||||||
_tempDirectory?.deleteRecursively();
|
_tempDirectory?.deleteRecursively();
|
||||||
}
|
}
|
||||||
_tempDirectory?.mkdirs();
|
_tempDirectory?.mkdirs();
|
||||||
|
_persistentDirectory = File(context.filesDir, "persist");
|
||||||
|
if(_persistentDirectory?.exists() == false) {
|
||||||
|
_persistentDirectory?.mkdirs();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,19 +51,25 @@ class StateSubscriptionGroups {
|
||||||
.withUnique { it.id }
|
.withUnique { it.id }
|
||||||
.load();
|
.load();
|
||||||
|
|
||||||
|
val onGroupsChanged = Event0();
|
||||||
|
|
||||||
fun getSubscriptionGroup(id: String): SubscriptionGroup? {
|
fun getSubscriptionGroup(id: String): SubscriptionGroup? {
|
||||||
return _subGroups.findItem { it.id == id };
|
return _subGroups.findItem { it.id == id };
|
||||||
}
|
}
|
||||||
fun getSubscriptionGroups(): List<SubscriptionGroup> {
|
fun getSubscriptionGroups(): List<SubscriptionGroup> {
|
||||||
return _subGroups.getItems();
|
return _subGroups.getItems();
|
||||||
}
|
}
|
||||||
fun updateSubscriptionGroup(subGroup: SubscriptionGroup) {
|
fun updateSubscriptionGroup(subGroup: SubscriptionGroup, preventNotify: Boolean = false) {
|
||||||
_subGroups.save(subGroup);
|
_subGroups.save(subGroup);
|
||||||
|
if(!preventNotify)
|
||||||
|
onGroupsChanged.emit();
|
||||||
}
|
}
|
||||||
fun deleteSubscriptionGroup(id: String){
|
fun deleteSubscriptionGroup(id: String){
|
||||||
val group = getSubscriptionGroup(id);
|
val group = getSubscriptionGroup(id);
|
||||||
if(group != null)
|
if(group != null) {
|
||||||
_subGroups.delete(group);
|
_subGroups.delete(group);
|
||||||
|
onGroupsChanged.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,10 @@ class AnyAdapterView<I, T>(view: RecyclerView, adapter: BaseAnyAdapter<I, T, T>,
|
||||||
where T : AnyAdapter.AnyViewHolder<I>{
|
where T : AnyAdapter.AnyViewHolder<I>{
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/*
|
||||||
inline fun <I, reified T : AnyAdapter.AnyViewHolder<I>> RecyclerView.asAny(list: List<I>, orientation: Int = RecyclerView.VERTICAL, reversed: Boolean = false, noinline onCreate: ((T)->Unit)? = null): AnyAdapterView<I, T> {
|
inline fun <I, reified T : AnyAdapter.AnyViewHolder<I>> RecyclerView.asAny(list: List<I>, orientation: Int = RecyclerView.VERTICAL, reversed: Boolean = false, noinline onCreate: ((T)->Unit)? = null): AnyAdapterView<I, T> {
|
||||||
return asAny(ArrayList(list), orientation, reversed, onCreate);
|
return asAny(ArrayList(list), orientation, reversed, onCreate);
|
||||||
}
|
}*/
|
||||||
inline fun <I, reified T : AnyAdapter.AnyViewHolder<I>> RecyclerView.asAny(list: ArrayList<I>, orientation: Int = RecyclerView.VERTICAL, reversed: Boolean = false, noinline onCreate: ((T)->Unit)? = null): AnyAdapterView<I, T> {
|
inline fun <I, reified T : AnyAdapter.AnyViewHolder<I>> RecyclerView.asAny(list: ArrayList<I>, orientation: Int = RecyclerView.VERTICAL, reversed: Boolean = false, noinline onCreate: ((T)->Unit)? = null): AnyAdapterView<I, T> {
|
||||||
return AnyAdapterView(this, AnyAdapter.create(list, onCreate), orientation, reversed);
|
return AnyAdapterView(this, AnyAdapter.create(list, onCreate), orientation, reversed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.futo.platformplayer.views.adapters
|
package com.futo.platformplayer.views.adapters
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
|
@ -50,6 +51,7 @@ open class BaseAnyAdapter<I, T : AnyAdapter.AnyViewHolder<I>, IT : ViewHolder> {
|
||||||
cb(item);
|
cb(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
fun notifyContentChanged() {
|
fun notifyContentChanged() {
|
||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.futo.platformplayer.views.adapters.viewholders
|
package com.futo.platformplayer.views.adapters.viewholders
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
@ -41,12 +42,12 @@ class SubscriptionGroupBarViewHolder(private val _viewGroup: ViewGroup) : AnyAda
|
||||||
.setAllCorners(CornerFamily.ROUNDED, dp6.toFloat())
|
.setAllCorners(CornerFamily.ROUNDED, dp6.toFloat())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
_viewGroup.setOnClickListener {
|
_view.setOnClickListener {
|
||||||
_group?.let {
|
_group?.let {
|
||||||
onClick.emit(it);
|
onClick.emit(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_viewGroup.setOnLongClickListener {
|
_view.setOnLongClickListener {
|
||||||
_group?.let {
|
_group?.let {
|
||||||
onClickLong.emit(it);
|
onClickLong.emit(it);
|
||||||
}
|
}
|
||||||
|
@ -59,9 +60,18 @@ class SubscriptionGroupBarViewHolder(private val _viewGroup: ViewGroup) : AnyAda
|
||||||
val img = value.image;
|
val img = value.image;
|
||||||
if(img != null)
|
if(img != null)
|
||||||
img.setImageView(_image)
|
img.setImageView(_image)
|
||||||
else
|
else {
|
||||||
_image.setImageResource(0);
|
_image.setImageResource(0);
|
||||||
|
|
||||||
|
if(value is SubscriptionGroup.Add)
|
||||||
|
_image.setBackgroundColor(Color.DKGRAY);
|
||||||
|
}
|
||||||
_textSubGroup.text = value.name;
|
_textSubGroup.text = value.name;
|
||||||
|
|
||||||
|
if(value is SubscriptionGroup.Selectable && value.selected)
|
||||||
|
_view.setBackgroundColor(_view.context.resources.getColor(R.color.colorPrimary, null));
|
||||||
|
else
|
||||||
|
_view.setBackgroundColor(_view.context.resources.getColor(R.color.transparent, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
package com.futo.platformplayer.views.adapters.viewholders
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.api.media.PlatformID
|
||||||
|
import com.futo.platformplayer.api.media.models.channels.SerializedChannel
|
||||||
|
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.Subscription
|
||||||
|
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.adapters.ItemMoveCallback
|
||||||
|
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 SubscriptionGroupListViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<SubscriptionGroup>(
|
||||||
|
LayoutInflater.from(_viewGroup.context).inflate(R.layout.list_subscription_group, _viewGroup, false)) {
|
||||||
|
private var _group: SubscriptionGroup? = null;
|
||||||
|
|
||||||
|
private val _thumb: ImageView;
|
||||||
|
private val _image: ShapeableImageView;
|
||||||
|
private val _textSubGroup: TextView;
|
||||||
|
private val _textSubGroupMeta: TextView;
|
||||||
|
|
||||||
|
private val _buttonSettings: ImageButton;
|
||||||
|
private val _buttonDelete: ImageButton;
|
||||||
|
|
||||||
|
val onClick = Event1<SubscriptionGroup>();
|
||||||
|
val onSettings = Event1<SubscriptionGroup>();
|
||||||
|
val onDelete = Event1<SubscriptionGroup>();
|
||||||
|
val onDragDrop = Event1<RecyclerView.ViewHolder>();
|
||||||
|
|
||||||
|
init {
|
||||||
|
_thumb = _view.findViewById(R.id.thumb);
|
||||||
|
_image = _view.findViewById(R.id.image);
|
||||||
|
_textSubGroup = _view.findViewById(R.id.text_sub_group);
|
||||||
|
_textSubGroupMeta = _view.findViewById(R.id.text_sub_group_meta);
|
||||||
|
_buttonSettings = _view.findViewById(R.id.button_settings);
|
||||||
|
_buttonDelete = _view.findViewById(R.id.button_trash);
|
||||||
|
|
||||||
|
val dp6 = 6.dp(_view.resources);
|
||||||
|
_image.shapeAppearanceModel = ShapeAppearanceModel.builder()
|
||||||
|
.setAllCorners(CornerFamily.ROUNDED, dp6.toFloat())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
_view.setOnClickListener {
|
||||||
|
_group?.let {
|
||||||
|
onClick.emit(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_thumb.setOnTouchListener { _, event ->
|
||||||
|
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||||
|
onDragDrop.emit(this);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
_buttonSettings.setOnClickListener {
|
||||||
|
_group?.let {
|
||||||
|
onSettings.emit(it);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_buttonDelete.setOnClickListener {
|
||||||
|
_group?.let {
|
||||||
|
onDelete.emit(it);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(value: SubscriptionGroup) {
|
||||||
|
_group = value;
|
||||||
|
val img = value.image;
|
||||||
|
if(img != null)
|
||||||
|
img.setImageView(_image)
|
||||||
|
else {
|
||||||
|
_image.setImageResource(0);
|
||||||
|
|
||||||
|
if(value is SubscriptionGroup.Add)
|
||||||
|
_image.setBackgroundColor(Color.DKGRAY);
|
||||||
|
}
|
||||||
|
_textSubGroup.text = value.name;
|
||||||
|
_textSubGroupMeta.text = "${value.urls.size} subscriptions";
|
||||||
|
|
||||||
|
if(value is SubscriptionGroup.Selectable && value.selected)
|
||||||
|
_view.setBackgroundColor(_view.context.resources.getColor(R.color.colorPrimary, null));
|
||||||
|
else
|
||||||
|
_view.setBackgroundColor(_view.context.resources.getColor(R.color.transparent, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "SubscriptionGroupBarViewHolder";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package com.futo.platformplayer.views.overlays
|
package com.futo.platformplayer.views.overlays
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
@ -12,12 +13,16 @@ import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.net.toFile
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
import com.futo.platformplayer.activities.IWithResultLauncher
|
import com.futo.platformplayer.activities.IWithResultLauncher
|
||||||
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||||
import com.futo.platformplayer.constructs.Event0
|
import com.futo.platformplayer.constructs.Event0
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
@ -31,13 +36,17 @@ import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.CreatorBarViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.CreatorBarViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.SelectableCreatorBarViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.SelectableCreatorBarViewHolder
|
||||||
import com.futo.platformplayer.views.buttons.BigButton
|
import com.futo.platformplayer.views.buttons.BigButton
|
||||||
|
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||||
import com.google.android.flexbox.FlexboxLayout
|
import com.google.android.flexbox.FlexboxLayout
|
||||||
import com.google.android.material.imageview.ShapeableImageView
|
import com.google.android.material.imageview.ShapeableImageView
|
||||||
import com.google.android.material.shape.CornerFamily
|
import com.google.android.material.shape.CornerFamily
|
||||||
import com.google.android.material.shape.ShapeAppearanceModel
|
import com.google.android.material.shape.ShapeAppearanceModel
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class ImageVariableOverlay: ConstraintLayout {
|
class ImageVariableOverlay: ConstraintLayout {
|
||||||
private val _buttonGallery: BigButton;
|
private val _buttonGallery: BigButton;
|
||||||
|
private val _imageGallerySelected: ImageView;
|
||||||
|
private val _imageGallerySelectedContainer: LinearLayout;
|
||||||
private val _buttonSelect: Button;
|
private val _buttonSelect: Button;
|
||||||
private val _topbar: OverlayTopbar;
|
private val _topbar: OverlayTopbar;
|
||||||
private val _recyclerPresets: AnyAdapterView<PresetImage, PresetViewHolder>;
|
private val _recyclerPresets: AnyAdapterView<PresetImage, PresetViewHolder>;
|
||||||
|
@ -53,6 +62,7 @@ class ImageVariableOverlay: ConstraintLayout {
|
||||||
);
|
);
|
||||||
|
|
||||||
private var _selected: ImageVariable? = null;
|
private var _selected: ImageVariable? = null;
|
||||||
|
private var _selectedFile: String? = null;
|
||||||
|
|
||||||
val onSelected = Event1<ImageVariable>();
|
val onSelected = Event1<ImageVariable>();
|
||||||
val onClose = Event0();
|
val onClose = Event0();
|
||||||
|
@ -74,6 +84,8 @@ class ImageVariableOverlay: ConstraintLayout {
|
||||||
inflate(context, R.layout.overlay_image_variable, this);
|
inflate(context, R.layout.overlay_image_variable, this);
|
||||||
_topbar = findViewById(R.id.topbar);
|
_topbar = findViewById(R.id.topbar);
|
||||||
_buttonGallery = findViewById(R.id.button_gallery);
|
_buttonGallery = findViewById(R.id.button_gallery);
|
||||||
|
_imageGallerySelected = findViewById(R.id.gallery_selected);
|
||||||
|
_imageGallerySelectedContainer = findViewById(R.id.gallery_selected_container);
|
||||||
_buttonSelect = findViewById(R.id.button_select);
|
_buttonSelect = findViewById(R.id.button_select);
|
||||||
_recyclerPresets = findViewById<RecyclerView>(R.id.recycler_presets).asAny(_presets, RecyclerView.HORIZONTAL) {
|
_recyclerPresets = findViewById<RecyclerView>(R.id.recycler_presets).asAny(_presets, RecyclerView.HORIZONTAL) {
|
||||||
it.onClick.subscribe {
|
it.onClick.subscribe {
|
||||||
|
@ -97,23 +109,37 @@ class ImageVariableOverlay: ConstraintLayout {
|
||||||
this.orientation = LinearLayoutManager.VERTICAL;
|
this.orientation = LinearLayoutManager.VERTICAL;
|
||||||
};
|
};
|
||||||
|
|
||||||
_buttonGallery.setOnClickListener {
|
_buttonGallery.onClick.subscribe {
|
||||||
val context = StateApp.instance.contextOrNull;
|
val context = StateApp.instance.contextOrNull;
|
||||||
if(context is IWithResultLauncher) {
|
if(context is IWithResultLauncher && context is MainActivity) {
|
||||||
val intent = Intent();
|
ImagePicker.with(context)
|
||||||
intent.setType("image/*");
|
.compress(512)
|
||||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
.maxResultSize(750, 500)
|
||||||
|
.createIntent {
|
||||||
context.launchForResult(intent, 888) {
|
context.launchForResult(it, 888) {
|
||||||
if(it.resultCode == 888) {
|
if(it.resultCode == Activity.RESULT_OK) {
|
||||||
val url = it.data?.data ?: return@launchForResult;
|
cleanupLastFile();
|
||||||
//TODO: Write to local storage
|
val fileUri = it.data?.data;
|
||||||
_selected = ImageVariable(url.toString());
|
if(fileUri != null) {
|
||||||
updateSelected();
|
val file = fileUri.toFile();
|
||||||
}
|
val ext = file.extension;
|
||||||
};
|
val persistFile = StateApp.instance.getPersistFile(ext);
|
||||||
|
file.copyTo(persistFile);
|
||||||
|
_selectedFile = persistFile.toUri().toString();
|
||||||
|
_selected = ImageVariable(_selectedFile);
|
||||||
|
updateSelected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
_imageGallerySelectedContainer.setOnClickListener {
|
||||||
|
if(_selectedFile != null) {
|
||||||
|
_selected = ImageVariable(_selectedFile);
|
||||||
|
updateSelected();
|
||||||
|
}
|
||||||
|
}
|
||||||
_buttonSelect.setOnClickListener {
|
_buttonSelect.setOnClickListener {
|
||||||
_selected?.let {
|
_selected?.let {
|
||||||
select(it);
|
select(it);
|
||||||
|
@ -133,13 +159,38 @@ class ImageVariableOverlay: ConstraintLayout {
|
||||||
_creators.forEach { p -> p.active = p.channel.thumbnail == url };
|
_creators.forEach { p -> p.active = p.channel.thumbnail == url };
|
||||||
_recyclerCreators.notifyContentChanged();
|
_recyclerCreators.notifyContentChanged();
|
||||||
|
|
||||||
|
if(_selectedFile != null) {
|
||||||
|
_imageGallerySelectedContainer.visibility = View.VISIBLE;
|
||||||
|
Glide.with(_imageGallerySelected)
|
||||||
|
.load(_selectedFile)
|
||||||
|
.into(_imageGallerySelected);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_imageGallerySelectedContainer.visibility = View.GONE;
|
||||||
|
|
||||||
|
if(_selected?.url == _selectedFile)
|
||||||
|
_imageGallerySelectedContainer.setBackgroundColor(resources.getColor(R.color.colorPrimary, null));
|
||||||
|
else
|
||||||
|
_imageGallerySelectedContainer.setBackgroundColor(resources.getColor(R.color.transparent, null));
|
||||||
|
|
||||||
if(_selected != null)
|
if(_selected != null)
|
||||||
_buttonSelect.alpha = 1f;
|
_buttonSelect.alpha = 1f;
|
||||||
else
|
else
|
||||||
_buttonSelect.alpha = 0.5f;
|
_buttonSelect.alpha = 0.5f;
|
||||||
}
|
}
|
||||||
|
fun cleanupLastFile() {
|
||||||
|
_selectedFile?.let {
|
||||||
|
val file = File(it);
|
||||||
|
if(file.exists())
|
||||||
|
file.delete();
|
||||||
|
_selectedFile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun select(variable: ImageVariable) {
|
fun select(variable: ImageVariable) {
|
||||||
|
if(_selected?.url != _selectedFile)
|
||||||
|
cleanupLastFile();
|
||||||
onSelected.emit(variable);
|
onSelected.emit(variable);
|
||||||
onClose.emit();
|
onClose.emit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,11 @@ package com.futo.platformplayer.views.subscriptions
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.RecyclerView.Recycler
|
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.Settings
|
||||||
import com.futo.platformplayer.api.media.models.channels.SerializedChannel
|
import com.futo.platformplayer.api.media.models.channels.SerializedChannel
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.models.Subscription
|
import com.futo.platformplayer.models.Subscription
|
||||||
|
@ -17,35 +19,97 @@ import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||||
import com.futo.platformplayer.views.others.ToggleTagView
|
import com.futo.platformplayer.views.others.ToggleTagView
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.SubscriptionBarViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.SubscriptionBarViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.SubscriptionGroupBarViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.SubscriptionGroupBarViewHolder
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class SubscriptionBar : LinearLayout {
|
class SubscriptionBar : LinearLayout {
|
||||||
private var _adapterView: AnyAdapterView<Subscription, SubscriptionBarViewHolder>? = null;
|
private var _adapterView: AnyAdapterView<Subscription, SubscriptionBarViewHolder>? = null;
|
||||||
private var _subGroups: AnyAdapterView<SubscriptionGroup, SubscriptionGroupBarViewHolder>
|
private var _subGroups: AnyAdapterView<SubscriptionGroup, SubscriptionGroupBarViewHolder>
|
||||||
private val _tagsContainer: LinearLayout;
|
private val _tagsContainer: LinearLayout;
|
||||||
|
|
||||||
|
private val _groups: ArrayList<SubscriptionGroup>;
|
||||||
|
private var _group: SubscriptionGroup? = null;
|
||||||
|
|
||||||
val onClickChannel = Event1<SerializedChannel>();
|
val onClickChannel = Event1<SerializedChannel>();
|
||||||
val onClickGroup = Event1<SubscriptionGroup>();
|
val onToggleGroup = Event1<SubscriptionGroup?>();
|
||||||
val onHoldGroup = Event1<SubscriptionGroup>();
|
val onHoldGroup = Event1<SubscriptionGroup>();
|
||||||
|
|
||||||
|
override fun onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
StateSubscriptionGroups.instance.onGroupsChanged.subscribe(this) {
|
||||||
|
findViewTreeLifecycleOwner()?.lifecycleScope?.launch(Dispatchers.Main) {
|
||||||
|
reloadGroups();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromWindow() {
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
StateSubscriptionGroups.instance.onGroupsChanged.remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
|
constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
|
||||||
inflate(context, R.layout.view_subscription_bar, this);
|
inflate(context, R.layout.view_subscription_bar, this);
|
||||||
|
|
||||||
val subscriptions = StateSubscriptions.instance.getSubscriptions().sortedByDescending { it.playbackSeconds };
|
val subscriptions = ArrayList(StateSubscriptions.instance.getSubscriptions().sortedByDescending { it.playbackSeconds });
|
||||||
_adapterView = findViewById<RecyclerView>(R.id.recycler_creators).asAny(subscriptions, orientation = RecyclerView.HORIZONTAL) {
|
_adapterView = findViewById<RecyclerView>(R.id.recycler_creators).asAny(subscriptions, orientation = RecyclerView.HORIZONTAL) {
|
||||||
it.onClick.subscribe { c ->
|
it.onClick.subscribe { c ->
|
||||||
onClickChannel.emit(c.channel);
|
onClickChannel.emit(c.channel);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
val subgroups = StateSubscriptionGroups.instance.getSubscriptionGroups();
|
_groups = ArrayList(getGroups());
|
||||||
_subGroups = findViewById<RecyclerView>(R.id.recycler_subgroups).asAny(subgroups, orientation = RecyclerView.HORIZONTAL) {
|
_subGroups = findViewById<RecyclerView>(R.id.recycler_subgroups).asAny(_groups, orientation = RecyclerView.HORIZONTAL) {
|
||||||
it.onClick.subscribe(onClickGroup::emit);
|
it.onClick.subscribe(::groupClicked);
|
||||||
it.onClickLong.subscribe(onHoldGroup::emit);
|
it.onClickLong.subscribe { g ->
|
||||||
|
onHoldGroup.emit(g);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_tagsContainer = findViewById(R.id.container_tags);
|
_tagsContainer = findViewById(R.id.container_tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun groupClicked(g: SubscriptionGroup) {
|
||||||
|
if(g is SubscriptionGroup.Add) {
|
||||||
|
onToggleGroup.emit(g);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
val isSame = _group == g;
|
||||||
|
_group?.let {
|
||||||
|
if (it is SubscriptionGroup.Selectable) {
|
||||||
|
it.selected = false;
|
||||||
|
val index = _groups.indexOf(it);
|
||||||
|
if (index >= 0)
|
||||||
|
_subGroups.notifyContentChanged(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isSame) {
|
||||||
|
_group = null;
|
||||||
|
onToggleGroup.emit(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_group = g;
|
||||||
|
if(g is SubscriptionGroup.Selectable)
|
||||||
|
g.selected = true;
|
||||||
|
_subGroups.notifyContentChanged(_groups.indexOf(g));
|
||||||
|
onToggleGroup.emit(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reloadGroups() {
|
||||||
|
val results = getGroups();
|
||||||
|
_groups.clear();
|
||||||
|
_groups.addAll(results);
|
||||||
|
_subGroups.notifyContentChanged();
|
||||||
|
}
|
||||||
|
private fun getGroups(): List<SubscriptionGroup> {
|
||||||
|
return if(Settings.instance.subscriptions.showSubscriptionGroups)
|
||||||
|
(StateSubscriptionGroups.instance.getSubscriptionGroups()
|
||||||
|
.sortedBy { it.priority }
|
||||||
|
.map { SubscriptionGroup.Selectable(it, it.id == _group?.id) } +
|
||||||
|
listOf(SubscriptionGroup.Add()));
|
||||||
|
else listOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun setToggles(vararg buttons: Toggle) {
|
fun setToggles(vararg buttons: Toggle) {
|
||||||
_tagsContainer.removeAllViews();
|
_tagsContainer.removeAllViews();
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/colorPrimary" />
|
||||||
|
<corners android:radius="1dp" />
|
||||||
|
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||||
|
</shape>
|
|
@ -26,19 +26,37 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#AA000000">
|
android:background="#AA000000">
|
||||||
<ImageButton
|
<LinearLayout
|
||||||
android:id="@+id/button_settings"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="50dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="50dp"
|
|
||||||
android:layout_marginLeft="5dp"
|
|
||||||
android:layout_marginRight="5dp"
|
|
||||||
android:src="@drawable/ic_settings"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
android:padding="10dp"
|
android:orientation="horizontal">
|
||||||
android:background="@color/transparent"
|
<ImageButton
|
||||||
android:visibility="invisible"
|
android:id="@+id/button_delete"
|
||||||
android:scaleType="fitCenter" />
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="0dp"
|
||||||
|
android:src="@drawable/ic_trash"
|
||||||
|
app:tint="#CC0000"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:scaleType="fitCenter" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_settings"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
|
android:src="@drawable/ic_settings"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:scaleType="fitCenter" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
android:id="@+id/image_group"
|
android:id="@+id/image_group"
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
99
app/src/main/res/layout/list_subscription_group.xml
Normal file
99
app/src/main/res/layout/list_subscription_group.xml
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<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="match_parent"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:layout_margin="2dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:id="@+id/root">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/thumb"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="12dp"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:src="@drawable/ic_dragdrop_white"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/image"
|
||||||
|
android:layout_width="75dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/thumb"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/xp_book" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/image"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/buttons"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<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:textColor="@color/white"
|
||||||
|
android:textAlignment="textStart"
|
||||||
|
android:text="News" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_sub_group_meta"
|
||||||
|
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="10dp"
|
||||||
|
android:textColor="@color/gray_ac"
|
||||||
|
android:textAlignment="textStart"
|
||||||
|
android:text="News" />
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/buttons"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_trash"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginRight="10dp"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
app:tint="@color/pastel_red"
|
||||||
|
android:src="@drawable/ic_trash"
|
||||||
|
/>
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_settings"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:src="@drawable/ic_settings"
|
||||||
|
android:visibility="gone"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -26,8 +26,24 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/gallery_selected_container"
|
||||||
|
android:layout_width="150dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginRight="10dp">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/gallery_selected"
|
||||||
|
android:layout_width="150dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:scaleType="centerCrop" />
|
||||||
|
</LinearLayout>
|
||||||
<com.futo.platformplayer.views.buttons.BigButton
|
<com.futo.platformplayer.views.buttons.BigButton
|
||||||
android:id="@+id/button_gallery"
|
android:id="@+id/button_gallery"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -41,6 +57,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="10dp"
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginRight="10dp"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -71,6 +88,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:layout_marginLeft="10dp"
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginRight="10dp"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<TextView
|
<TextView
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="75dp"
|
android:layout_width="78dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="54dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:layout_margin="4dp"
|
android:padding="2dp"
|
||||||
|
android:layout_margin="2dp"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:id="@+id/root">
|
android:id="@+id/root">
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
android:id="@+id/image"
|
android:id="@+id/image"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
android:src="@drawable/xp_book" />
|
android:src="@drawable/xp_book" />
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -346,6 +346,9 @@
|
||||||
<string name="live_chat_webview">Live Chat Webview</string>
|
<string name="live_chat_webview">Live Chat Webview</string>
|
||||||
<string name="background_switch_audio">Switch to Audio in Background</string>
|
<string name="background_switch_audio">Switch to Audio in Background</string>
|
||||||
<string name="background_switch_audio_description">Optimize bandwidth usage by switching to audio-only stream in background if available, may cause stutter</string>
|
<string name="background_switch_audio_description">Optimize bandwidth usage by switching to audio-only stream in background if available, may cause stutter</string>
|
||||||
|
<string name="subscription_group_menu">Groups</string>
|
||||||
|
<string name="show_subscription_group">Show Subscription Groups</string>
|
||||||
|
<string name="show_subscription_group_description">If subscription groups should be shown above your subscriptions to filter</string>
|
||||||
<string name="preview_feed_items">Preview Feed Items</string>
|
<string name="preview_feed_items">Preview Feed Items</string>
|
||||||
<string name="preview_feed_items_description">When the preview feedstyle is used, if items should auto-preview when scrolling over them</string>
|
<string name="preview_feed_items_description">When the preview feedstyle is used, if items should auto-preview when scrolling over them</string>
|
||||||
<string name="log_level">Log Level</string>
|
<string name="log_level">Log Level</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue