mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-13 11:39:49 +00:00
Merge branch 'master' into small-hls-fixes
This commit is contained in:
commit
418f4a6075
21 changed files with 143 additions and 74 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -100,3 +100,9 @@
|
||||||
[submodule "app/src/unstable/assets/sources/curiositystream"]
|
[submodule "app/src/unstable/assets/sources/curiositystream"]
|
||||||
path = app/src/unstable/assets/sources/curiositystream
|
path = app/src/unstable/assets/sources/curiositystream
|
||||||
url = ../plugins/curiositystream.git
|
url = ../plugins/curiositystream.git
|
||||||
|
[submodule "app/src/unstable/assets/sources/crunchyroll"]
|
||||||
|
path = app/src/unstable/assets/sources/crunchyroll
|
||||||
|
url = ../plugins/crunchyroll.git
|
||||||
|
[submodule "app/src/stable/assets/sources/crunchyroll"]
|
||||||
|
path = app/src/stable/assets/sources/crunchyroll
|
||||||
|
url = ../plugins/crunchyroll.git
|
||||||
|
|
|
@ -198,6 +198,7 @@ dependencies {
|
||||||
implementation 'com.google.android.flexbox:flexbox:3.0.0'
|
implementation 'com.google.android.flexbox:flexbox:3.0.0'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
implementation fileTree(dir: 'aar', include: ['*.aar'])
|
implementation fileTree(dir: 'aar', include: ['*.aar'])
|
||||||
|
implementation 'com.arthenica:smart-exception-java:0.2.1'
|
||||||
implementation 'org.jetbrains.kotlin:kotlin-reflect:1.9.0'
|
implementation 'org.jetbrains.kotlin:kotlin-reflect:1.9.0'
|
||||||
implementation 'com.github.dhaval2404:imagepicker:2.1'
|
implementation 'com.github.dhaval2404:imagepicker:2.1'
|
||||||
implementation 'com.google.zxing:core:3.4.1'
|
implementation 'com.google.zxing:core:3.4.1'
|
||||||
|
|
|
@ -1123,8 +1123,8 @@ class UISlideOverlays {
|
||||||
return SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.add_to), null, true, items).apply { show() };
|
return SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.add_to), null, true, items).apply { show() };
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showFiltersOverlay(lifecycleScope: CoroutineScope, container: ViewGroup, enabledClientsIds: List<String>, filterValues: HashMap<String, List<String>>, isChannelSearch: Boolean = false): SlideUpMenuFilters {
|
fun showFiltersOverlay(lifecycleScope: CoroutineScope, container: ViewGroup, enabledClientsIds: List<String>, filterValues: HashMap<String, List<String>>): SlideUpMenuFilters {
|
||||||
val overlay = SlideUpMenuFilters(lifecycleScope, container, enabledClientsIds, filterValues, isChannelSearch);
|
val overlay = SlideUpMenuFilters(lifecycleScope, container, enabledClientsIds, filterValues);
|
||||||
overlay.show();
|
overlay.show();
|
||||||
return overlay;
|
return overlay;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
@ -25,6 +26,7 @@ import com.futo.platformplayer.api.media.structures.MultiPager
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.constructs.Event2
|
import com.futo.platformplayer.constructs.Event2
|
||||||
import com.futo.platformplayer.constructs.TaskHandler
|
import com.futo.platformplayer.constructs.TaskHandler
|
||||||
|
import com.futo.platformplayer.dp
|
||||||
import com.futo.platformplayer.engine.exceptions.PluginException
|
import com.futo.platformplayer.engine.exceptions.PluginException
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
||||||
import com.futo.platformplayer.exceptions.ChannelException
|
import com.futo.platformplayer.exceptions.ChannelException
|
||||||
|
@ -32,9 +34,11 @@ import com.futo.platformplayer.fragment.mainactivity.main.FeedView
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.states.StateCache
|
import com.futo.platformplayer.states.StateCache
|
||||||
import com.futo.platformplayer.states.StatePlatform
|
import com.futo.platformplayer.states.StatePlatform
|
||||||
|
import com.futo.platformplayer.states.StatePlugins
|
||||||
import com.futo.platformplayer.states.StatePolycentric
|
import com.futo.platformplayer.states.StatePolycentric
|
||||||
import com.futo.platformplayer.states.StateSubscriptions
|
import com.futo.platformplayer.states.StateSubscriptions
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
|
import com.futo.platformplayer.views.SearchView
|
||||||
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
|
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||||
import com.futo.platformplayer.views.adapters.feedtypes.PreviewContentListAdapter
|
import com.futo.platformplayer.views.adapters.feedtypes.PreviewContentListAdapter
|
||||||
|
@ -54,6 +58,8 @@ class ChannelContentsFragment(private val subType: String? = null) : Fragment(),
|
||||||
private var _results: ArrayList<IPlatformContent> = arrayListOf();
|
private var _results: ArrayList<IPlatformContent> = arrayListOf();
|
||||||
private var _adapterResults: InsertedViewAdapterWithLoader<ContentPreviewViewHolder>? = null;
|
private var _adapterResults: InsertedViewAdapterWithLoader<ContentPreviewViewHolder>? = null;
|
||||||
private var _lastPolycentricProfile: PolycentricProfile? = null;
|
private var _lastPolycentricProfile: PolycentricProfile? = null;
|
||||||
|
private var _query: String? = null
|
||||||
|
private var _searchView: SearchView? = null
|
||||||
|
|
||||||
val onContentClicked = Event2<IPlatformContent, Long>();
|
val onContentClicked = Event2<IPlatformContent, Long>();
|
||||||
val onContentUrlClicked = Event2<String, ContentType>();
|
val onContentUrlClicked = Event2<String, ContentType>();
|
||||||
|
@ -68,17 +74,32 @@ class ChannelContentsFragment(private val subType: String? = null) : Fragment(),
|
||||||
private fun getContentPager(channel: IPlatformChannel): IPager<IPlatformContent> {
|
private fun getContentPager(channel: IPlatformChannel): IPager<IPlatformContent> {
|
||||||
Logger.i(TAG, "getContentPager");
|
Logger.i(TAG, "getContentPager");
|
||||||
|
|
||||||
val lastPolycentricProfile = _lastPolycentricProfile;
|
var pager: IPager<IPlatformContent>? = null
|
||||||
var pager: IPager<IPlatformContent>? = null;
|
val query = _query
|
||||||
if (lastPolycentricProfile != null && StatePolycentric.instance.enabled)
|
if (!query.isNullOrBlank()) {
|
||||||
pager =
|
if(subType != null) {
|
||||||
StatePolycentric.instance.getChannelContent(lifecycleScope, lastPolycentricProfile, type = subType);
|
Logger.i(TAG, "StatePlatform.instance.searchChannel(channel.url = ${channel.url}, query = ${query}, subType = ${subType})")
|
||||||
|
pager = StatePlatform.instance.searchChannel(channel.url, query, subType);
|
||||||
|
} else {
|
||||||
|
Logger.i(TAG, "StatePlatform.instance.searchChannel(channel.url = ${channel.url}, query = ${query})")
|
||||||
|
pager = StatePlatform.instance.searchChannel(channel.url, query);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val lastPolycentricProfile = _lastPolycentricProfile;
|
||||||
|
if (lastPolycentricProfile != null && StatePolycentric.instance.enabled) {
|
||||||
|
pager = StatePolycentric.instance.getChannelContent(lifecycleScope, lastPolycentricProfile, type = subType);
|
||||||
|
Logger.i(TAG, "StatePolycentric.instance.getChannelContent(lifecycleScope, lastPolycentricProfile, type = ${subType})")
|
||||||
|
}
|
||||||
|
|
||||||
if(pager == null) {
|
if(pager == null) {
|
||||||
if(subType != null)
|
if(subType != null) {
|
||||||
pager = StatePlatform.instance.getChannelContent(channel.url, subType);
|
pager = StatePlatform.instance.getChannelContent(channel.url, subType);
|
||||||
else
|
Logger.i(TAG, "StatePlatform.instance.getChannelContent(channel.url = ${channel.url}, subType = ${subType})")
|
||||||
pager = StatePlatform.instance.getChannelContent(channel.url);
|
} else {
|
||||||
|
pager = StatePlatform.instance.getChannelContent(channel.url);
|
||||||
|
Logger.i(TAG, "StatePlatform.instance.getChannelContent(channel.url = ${channel.url})")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return pager;
|
return pager;
|
||||||
}
|
}
|
||||||
|
@ -145,19 +166,44 @@ class ChannelContentsFragment(private val subType: String? = null) : Fragment(),
|
||||||
|
|
||||||
_taskLoadVideos.cancel();
|
_taskLoadVideos.cancel();
|
||||||
|
|
||||||
|
_query = null
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
updateSearchViewVisibility()
|
||||||
_results.clear();
|
_results.clear();
|
||||||
_adapterResults?.notifyDataSetChanged();
|
_adapterResults?.notifyDataSetChanged();
|
||||||
|
|
||||||
loadInitial();
|
loadInitial();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateSearchViewVisibility() {
|
||||||
|
val client = _channel?.id?.pluginId?.let { StatePlatform.instance.getClientOrNull(it) }
|
||||||
|
Logger.i(TAG, "_searchView.visible = ${client?.capabilities?.hasSearchChannelContents == true}")
|
||||||
|
_searchView?.visibility = if (client?.capabilities?.hasSearchChannelContents == true) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setQuery(query: String) {
|
||||||
|
_query = query
|
||||||
|
_taskLoadVideos.cancel()
|
||||||
|
_results.clear()
|
||||||
|
_adapterResults?.notifyDataSetChanged()
|
||||||
|
loadInitial()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
val view = inflater.inflate(R.layout.fragment_channel_videos, container, false);
|
val view = inflater.inflate(R.layout.fragment_channel_videos, container, false);
|
||||||
|
|
||||||
|
_query = null
|
||||||
_recyclerResults = view.findViewById(R.id.recycler_videos);
|
_recyclerResults = view.findViewById(R.id.recycler_videos);
|
||||||
|
|
||||||
_adapterResults = PreviewContentListAdapter(view.context, FeedStyle.THUMBNAIL, _results, null, Settings.instance.channel.progressBar).apply {
|
val searchView = SearchView(requireContext()).apply { layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT) }.apply {
|
||||||
|
onEnter.subscribe {
|
||||||
|
setQuery(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_searchView = searchView
|
||||||
|
updateSearchViewVisibility()
|
||||||
|
|
||||||
|
_adapterResults = PreviewContentListAdapter(view.context, FeedStyle.THUMBNAIL, _results, null, Settings.instance.channel.progressBar, viewsToPrepend = arrayListOf(searchView)).apply {
|
||||||
this.onContentUrlClicked.subscribe(this@ChannelContentsFragment.onContentUrlClicked::emit);
|
this.onContentUrlClicked.subscribe(this@ChannelContentsFragment.onContentUrlClicked::emit);
|
||||||
this.onUrlClicked.subscribe(this@ChannelContentsFragment.onUrlClicked::emit);
|
this.onUrlClicked.subscribe(this@ChannelContentsFragment.onUrlClicked::emit);
|
||||||
this.onContentClicked.subscribe(this@ChannelContentsFragment.onContentClicked::emit);
|
this.onContentClicked.subscribe(this@ChannelContentsFragment.onContentClicked::emit);
|
||||||
|
@ -174,6 +220,7 @@ class ChannelContentsFragment(private val subType: String? = null) : Fragment(),
|
||||||
_recyclerResults?.layoutManager = _glmVideo;
|
_recyclerResults?.layoutManager = _glmVideo;
|
||||||
_recyclerResults?.addOnScrollListener(_scrollListener);
|
_recyclerResults?.addOnScrollListener(_scrollListener);
|
||||||
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +229,8 @@ class ChannelContentsFragment(private val subType: String? = null) : Fragment(),
|
||||||
_recyclerResults?.removeOnScrollListener(_scrollListener);
|
_recyclerResults?.removeOnScrollListener(_scrollListener);
|
||||||
_recyclerResults = null;
|
_recyclerResults = null;
|
||||||
_pager = null;
|
_pager = null;
|
||||||
|
_query = null
|
||||||
|
_searchView = null
|
||||||
|
|
||||||
_taskLoadVideos.cancel();
|
_taskLoadVideos.cancel();
|
||||||
_nextPageHandler.cancel();
|
_nextPageHandler.cancel();
|
||||||
|
@ -304,6 +353,7 @@ class ChannelContentsFragment(private val subType: String? = null) : Fragment(),
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadInitial() {
|
private fun loadInitial() {
|
||||||
|
Logger.i(TAG, "loadInitial")
|
||||||
val channel: IPlatformChannel = _channel ?: return;
|
val channel: IPlatformChannel = _channel ?: return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
_taskLoadVideos.run(channel);
|
_taskLoadVideos.run(channel);
|
||||||
|
|
|
@ -425,17 +425,15 @@ class ChannelFragment : MainFragment() {
|
||||||
_fragment.lifecycleScope.launch(Dispatchers.IO) {
|
_fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val plugin = StatePlatform.instance.getChannelClientOrNull(channel.url)
|
val plugin = StatePlatform.instance.getChannelClientOrNull(channel.url)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
if (plugin != null && plugin.capabilities.hasSearchChannelContents) {
|
buttons.add(Pair(R.drawable.ic_search) {
|
||||||
buttons.add(Pair(R.drawable.ic_search) {
|
_fragment.navigate<SuggestionsFragment>(
|
||||||
_fragment.navigate<SuggestionsFragment>(
|
SuggestionsFragmentData(
|
||||||
SuggestionsFragmentData(
|
"", SearchType.VIDEO
|
||||||
"", SearchType.VIDEO, channel.url
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
})
|
)
|
||||||
|
})
|
||||||
|
_fragment.topBar?.assume<NavigationTopBarFragment>()?.setMenuItems(buttons)
|
||||||
|
|
||||||
_fragment.topBar?.assume<NavigationTopBarFragment>()?.setMenuItems(buttons)
|
|
||||||
}
|
|
||||||
if(plugin != null && plugin.capabilities.hasGetChannelCapabilities) {
|
if(plugin != null && plugin.capabilities.hasGetChannelCapabilities) {
|
||||||
if(plugin.getChannelCapabilities()?.types?.contains(ResultCapabilities.TYPE_SHORTS) ?: false &&
|
if(plugin.getChannelCapabilities()?.types?.contains(ResultCapabilities.TYPE_SHORTS) ?: false &&
|
||||||
!(_viewPager.adapter as ChannelViewPagerAdapter).containsItem(ChannelTab.SHORTS.ordinal.toLong())) {
|
!(_viewPager.adapter as ChannelViewPagerAdapter).containsItem(ChannelTab.SHORTS.ordinal.toLong())) {
|
||||||
|
|
|
@ -89,7 +89,6 @@ class ContentSearchResultsFragment : MainFragment() {
|
||||||
private var _sortBy: String? = null;
|
private var _sortBy: String? = null;
|
||||||
private var _filterValues: HashMap<String, List<String>> = hashMapOf();
|
private var _filterValues: HashMap<String, List<String>> = hashMapOf();
|
||||||
private var _enabledClientIds: List<String>? = null;
|
private var _enabledClientIds: List<String>? = null;
|
||||||
private var _channelUrl: String? = null;
|
|
||||||
private var _searchType: SearchType? = null;
|
private var _searchType: SearchType? = null;
|
||||||
|
|
||||||
private val _taskSearch: TaskHandler<String, IPager<IPlatformContent>>;
|
private val _taskSearch: TaskHandler<String, IPager<IPlatformContent>>;
|
||||||
|
@ -98,17 +97,12 @@ class ContentSearchResultsFragment : MainFragment() {
|
||||||
constructor(fragment: ContentSearchResultsFragment, inflater: LayoutInflater) : super(fragment, inflater) {
|
constructor(fragment: ContentSearchResultsFragment, inflater: LayoutInflater) : super(fragment, inflater) {
|
||||||
_taskSearch = TaskHandler<String, IPager<IPlatformContent>>({fragment.lifecycleScope}, { query ->
|
_taskSearch = TaskHandler<String, IPager<IPlatformContent>>({fragment.lifecycleScope}, { query ->
|
||||||
Logger.i(TAG, "Searching for: $query")
|
Logger.i(TAG, "Searching for: $query")
|
||||||
val channelUrl = _channelUrl;
|
when (_searchType)
|
||||||
if (channelUrl != null) {
|
{
|
||||||
StatePlatform.instance.searchChannel(channelUrl, query, null, _sortBy, _filterValues, _enabledClientIds)
|
SearchType.VIDEO -> StatePlatform.instance.searchRefresh(fragment.lifecycleScope, query, null, _sortBy, _filterValues, _enabledClientIds)
|
||||||
} else {
|
SearchType.CREATOR -> StatePlatform.instance.searchChannelsAsContent(query)
|
||||||
when (_searchType)
|
SearchType.PLAYLIST -> StatePlatform.instance.searchPlaylist(query)
|
||||||
{
|
else -> throw Exception("Search type must be specified")
|
||||||
SearchType.VIDEO -> StatePlatform.instance.searchRefresh(fragment.lifecycleScope, query, null, _sortBy, _filterValues, _enabledClientIds)
|
|
||||||
SearchType.CREATOR -> StatePlatform.instance.searchChannelsAsContent(query)
|
|
||||||
SearchType.PLAYLIST -> StatePlatform.instance.searchPlaylist(query)
|
|
||||||
else -> throw Exception("Search type must be specified")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.success { loadedResult(it); }.exception<ScriptCaptchaRequiredException> { }
|
.success { loadedResult(it); }.exception<ScriptCaptchaRequiredException> { }
|
||||||
|
@ -147,7 +141,6 @@ class ContentSearchResultsFragment : MainFragment() {
|
||||||
fun onShown(parameter: Any?) {
|
fun onShown(parameter: Any?) {
|
||||||
if(parameter is SuggestionsFragmentData) {
|
if(parameter is SuggestionsFragmentData) {
|
||||||
setQuery(parameter.query, false);
|
setQuery(parameter.query, false);
|
||||||
setChannelUrl(parameter.channelUrl, false);
|
|
||||||
setSearchType(parameter.searchType, false)
|
setSearchType(parameter.searchType, false)
|
||||||
|
|
||||||
fragment.topBar?.apply {
|
fragment.topBar?.apply {
|
||||||
|
@ -164,7 +157,7 @@ class ContentSearchResultsFragment : MainFragment() {
|
||||||
onFilterClick.subscribe(this) {
|
onFilterClick.subscribe(this) {
|
||||||
_overlayContainer.let {
|
_overlayContainer.let {
|
||||||
val filterValuesCopy = HashMap(_filterValues);
|
val filterValuesCopy = HashMap(_filterValues);
|
||||||
val filtersOverlay = UISlideOverlays.showFiltersOverlay(lifecycleScope, it, _enabledClientIds!!, filterValuesCopy, _channelUrl != null);
|
val filtersOverlay = UISlideOverlays.showFiltersOverlay(lifecycleScope, it, _enabledClientIds!!, filterValuesCopy);
|
||||||
filtersOverlay.onOK.subscribe { enabledClientIds, changed ->
|
filtersOverlay.onOK.subscribe { enabledClientIds, changed ->
|
||||||
if (changed) {
|
if (changed) {
|
||||||
setFilterValues(filtersOverlay.commonCapabilities, filterValuesCopy);
|
setFilterValues(filtersOverlay.commonCapabilities, filterValuesCopy);
|
||||||
|
@ -211,11 +204,7 @@ class ContentSearchResultsFragment : MainFragment() {
|
||||||
|
|
||||||
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val commonCapabilities =
|
val commonCapabilities = StatePlatform.instance.getCommonSearchCapabilities(StatePlatform.instance.getEnabledClients().map { it.id });
|
||||||
if(_channelUrl == null)
|
|
||||||
StatePlatform.instance.getCommonSearchCapabilities(StatePlatform.instance.getEnabledClients().map { it.id });
|
|
||||||
else
|
|
||||||
StatePlatform.instance.getCommonSearchChannelContentsCapabilities(StatePlatform.instance.getEnabledClients().map { it.id });
|
|
||||||
val sorts = commonCapabilities?.sorts ?: listOf();
|
val sorts = commonCapabilities?.sorts ?: listOf();
|
||||||
if (sorts.size > 1) {
|
if (sorts.size > 1) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
@ -282,15 +271,6 @@ class ContentSearchResultsFragment : MainFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setChannelUrl(channelUrl: String?, updateResults: Boolean = true) {
|
|
||||||
_channelUrl = channelUrl;
|
|
||||||
|
|
||||||
if (updateResults) {
|
|
||||||
clearResults();
|
|
||||||
loadResults();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setSearchType(searchType: SearchType, updateResults: Boolean = true) {
|
private fun setSearchType(searchType: SearchType, updateResults: Boolean = true) {
|
||||||
_searchType = searchType
|
_searchType = searchType
|
||||||
|
|
||||||
|
|
|
@ -217,7 +217,7 @@ class PlaylistsFragment : MainFragment() {
|
||||||
var playlistsToReturn = pls;
|
var playlistsToReturn = pls;
|
||||||
if(!_listPlaylistsSearch.text.isNullOrEmpty())
|
if(!_listPlaylistsSearch.text.isNullOrEmpty())
|
||||||
playlistsToReturn = playlistsToReturn.filter { it.name.contains(_listPlaylistsSearch.text, true) };
|
playlistsToReturn = playlistsToReturn.filter { it.name.contains(_listPlaylistsSearch.text, true) };
|
||||||
if(!_ordering.value.isNullOrEmpty()){
|
if(!_ordering.value.isNullOrEmpty()) {
|
||||||
playlistsToReturn = when(_ordering.value){
|
playlistsToReturn = when(_ordering.value){
|
||||||
"nameAsc" -> playlistsToReturn.sortedBy { it.name.lowercase() }
|
"nameAsc" -> playlistsToReturn.sortedBy { it.name.lowercase() }
|
||||||
"nameDesc" -> playlistsToReturn.sortedByDescending { it.name.lowercase() };
|
"nameDesc" -> playlistsToReturn.sortedByDescending { it.name.lowercase() };
|
||||||
|
|
|
@ -21,7 +21,7 @@ import com.futo.platformplayer.views.adapters.SearchSuggestionAdapter
|
||||||
import com.futo.platformplayer.views.others.RadioGroupView
|
import com.futo.platformplayer.views.others.RadioGroupView
|
||||||
import com.futo.platformplayer.views.others.TagsView
|
import com.futo.platformplayer.views.others.TagsView
|
||||||
|
|
||||||
data class SuggestionsFragmentData(val query: String, val searchType: SearchType, val channelUrl: String? = null);
|
data class SuggestionsFragmentData(val query: String, val searchType: SearchType);
|
||||||
|
|
||||||
class SuggestionsFragment : MainFragment {
|
class SuggestionsFragment : MainFragment {
|
||||||
override val isMainView : Boolean = true;
|
override val isMainView : Boolean = true;
|
||||||
|
@ -34,7 +34,6 @@ class SuggestionsFragment : MainFragment {
|
||||||
private val _suggestions: ArrayList<String> = ArrayList();
|
private val _suggestions: ArrayList<String> = ArrayList();
|
||||||
private var _query: String? = null;
|
private var _query: String? = null;
|
||||||
private var _searchType: SearchType = SearchType.VIDEO;
|
private var _searchType: SearchType = SearchType.VIDEO;
|
||||||
private var _channelUrl: String? = null;
|
|
||||||
|
|
||||||
private val _adapterSuggestions = SearchSuggestionAdapter(_suggestions);
|
private val _adapterSuggestions = SearchSuggestionAdapter(_suggestions);
|
||||||
|
|
||||||
|
@ -52,7 +51,7 @@ class SuggestionsFragment : MainFragment {
|
||||||
_adapterSuggestions.onClicked.subscribe { suggestion ->
|
_adapterSuggestions.onClicked.subscribe { suggestion ->
|
||||||
val storage = FragmentedStorage.get<SearchHistoryStorage>();
|
val storage = FragmentedStorage.get<SearchHistoryStorage>();
|
||||||
storage.add(suggestion);
|
storage.add(suggestion);
|
||||||
navigate<ContentSearchResultsFragment>(SuggestionsFragmentData(suggestion, _searchType, _channelUrl));
|
navigate<ContentSearchResultsFragment>(SuggestionsFragmentData(suggestion, _searchType));
|
||||||
}
|
}
|
||||||
_adapterSuggestions.onRemove.subscribe { suggestion ->
|
_adapterSuggestions.onRemove.subscribe { suggestion ->
|
||||||
val index = _suggestions.indexOf(suggestion);
|
val index = _suggestions.indexOf(suggestion);
|
||||||
|
@ -109,10 +108,8 @@ class SuggestionsFragment : MainFragment {
|
||||||
|
|
||||||
if (parameter is SuggestionsFragmentData) {
|
if (parameter is SuggestionsFragmentData) {
|
||||||
_searchType = parameter.searchType;
|
_searchType = parameter.searchType;
|
||||||
_channelUrl = parameter.channelUrl;
|
|
||||||
} else if (parameter is SearchType) {
|
} else if (parameter is SearchType) {
|
||||||
_searchType = parameter;
|
_searchType = parameter;
|
||||||
_channelUrl = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_radioGroupView?.setOptions(listOf(Pair("Media", SearchType.VIDEO), Pair("Creators", SearchType.CREATOR), Pair("Playlists", SearchType.PLAYLIST)), listOf(_searchType), false, true)
|
_radioGroupView?.setOptions(listOf(Pair("Media", SearchType.VIDEO), Pair("Creators", SearchType.CREATOR), Pair("Playlists", SearchType.PLAYLIST)), listOf(_searchType), false, true)
|
||||||
|
@ -135,7 +132,7 @@ class SuggestionsFragment : MainFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
navigate<ContentSearchResultsFragment>(SuggestionsFragmentData(it, _searchType, _channelUrl));
|
navigate<ContentSearchResultsFragment>(SuggestionsFragmentData(it, _searchType));
|
||||||
};
|
};
|
||||||
|
|
||||||
onTextChange.subscribe(this) {
|
onTextChange.subscribe(this) {
|
||||||
|
|
|
@ -2680,9 +2680,10 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
onChannelClicked.subscribe {
|
onChannelClicked.subscribe {
|
||||||
if(it.url.isNotBlank())
|
if(it.url.isNotBlank()) {
|
||||||
|
fragment.minimizeVideoDetail()
|
||||||
fragment.navigate<ChannelFragment>(it)
|
fragment.navigate<ChannelFragment>(it)
|
||||||
else
|
} else
|
||||||
UIDialogs.appToast("No author url present");
|
UIDialogs.appToast("No author url present");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,6 @@ class SearchTopBarFragment : TopFragment() {
|
||||||
} else if (parameter is SuggestionsFragmentData) {
|
} else if (parameter is SuggestionsFragmentData) {
|
||||||
this.setText(parameter.query);
|
this.setText(parameter.query);
|
||||||
_searchType = parameter.searchType;
|
_searchType = parameter.searchType;
|
||||||
_channelUrl = parameter.channelUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(currentMain is SuggestionsFragment)
|
if(currentMain is SuggestionsFragment)
|
||||||
|
@ -114,7 +113,7 @@ class SearchTopBarFragment : TopFragment() {
|
||||||
fun clear() {
|
fun clear() {
|
||||||
_editSearch?.text?.clear();
|
_editSearch?.text?.clear();
|
||||||
if (currentMain !is SuggestionsFragment) {
|
if (currentMain !is SuggestionsFragment) {
|
||||||
navigate<SuggestionsFragment>(SuggestionsFragmentData("", _searchType, _channelUrl), false);
|
navigate<SuggestionsFragment>(SuggestionsFragmentData("", _searchType), false);
|
||||||
} else {
|
} else {
|
||||||
onSearch.emit("");
|
onSearch.emit("");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package com.futo.platformplayer.views
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
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.ImageButton
|
||||||
|
@ -30,9 +32,26 @@ class SearchView : FrameLayout {
|
||||||
textSearch = findViewById(R.id.edit_search)
|
textSearch = findViewById(R.id.edit_search)
|
||||||
buttonClear = findViewById(R.id.button_clear_search)
|
buttonClear = findViewById(R.id.button_clear_search)
|
||||||
|
|
||||||
buttonClear.setOnClickListener { textSearch.text = "" };
|
buttonClear.setOnClickListener {
|
||||||
|
textSearch.text = ""
|
||||||
|
textSearch?.clearFocus()
|
||||||
|
(context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(textSearch.windowToken, 0)
|
||||||
|
onSearchChanged.emit("")
|
||||||
|
onEnter.emit("")
|
||||||
|
}
|
||||||
|
textSearch.setOnEditorActionListener { _, i, _ ->
|
||||||
|
if (i == EditorInfo.IME_ACTION_DONE) {
|
||||||
|
textSearch?.clearFocus()
|
||||||
|
(context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(textSearch.windowToken, 0)
|
||||||
|
onEnter.emit(textSearch.text.toString())
|
||||||
|
return@setOnEditorActionListener true
|
||||||
|
}
|
||||||
|
return@setOnEditorActionListener false
|
||||||
|
|
||||||
|
}
|
||||||
textSearch.addTextChangedListener {
|
textSearch.addTextChangedListener {
|
||||||
onSearchChanged.emit(it.toString());
|
buttonClear.visibility = if ((it?.length ?: 0) > 0) View.VISIBLE else View.GONE
|
||||||
|
onSearchChanged.emit(it.toString())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,17 +28,14 @@ class SlideUpMenuFilters {
|
||||||
private var _changed: Boolean = false;
|
private var _changed: Boolean = false;
|
||||||
private val _lifecycleScope: CoroutineScope;
|
private val _lifecycleScope: CoroutineScope;
|
||||||
|
|
||||||
private var _isChannelSearch = false;
|
|
||||||
|
|
||||||
var commonCapabilities: ResultCapabilities? = null;
|
var commonCapabilities: ResultCapabilities? = null;
|
||||||
|
|
||||||
|
|
||||||
constructor(lifecycleScope: CoroutineScope, container: ViewGroup, enabledClientsIds: List<String>, filterValues: HashMap<String, List<String>>, isChannelSearch: Boolean = false) {
|
constructor(lifecycleScope: CoroutineScope, container: ViewGroup, enabledClientsIds: List<String>, filterValues: HashMap<String, List<String>>) {
|
||||||
_lifecycleScope = lifecycleScope;
|
_lifecycleScope = lifecycleScope;
|
||||||
_container = container;
|
_container = container;
|
||||||
_enabledClientsIds = enabledClientsIds;
|
_enabledClientsIds = enabledClientsIds;
|
||||||
_filterValues = filterValues;
|
_filterValues = filterValues;
|
||||||
_isChannelSearch = isChannelSearch;
|
|
||||||
_slideUpMenuOverlay = SlideUpMenuOverlay(_container.context, _container, container.context.getString(R.string.filters), container.context.getString(R.string.done), true, listOf());
|
_slideUpMenuOverlay = SlideUpMenuOverlay(_container.context, _container, container.context.getString(R.string.filters), container.context.getString(R.string.done), true, listOf());
|
||||||
_slideUpMenuOverlay.onOK.subscribe {
|
_slideUpMenuOverlay.onOK.subscribe {
|
||||||
onOK.emit(_enabledClientsIds, _changed);
|
onOK.emit(_enabledClientsIds, _changed);
|
||||||
|
@ -51,10 +48,7 @@ class SlideUpMenuFilters {
|
||||||
private fun updateCommonCapabilities() {
|
private fun updateCommonCapabilities() {
|
||||||
_lifecycleScope.launch(Dispatchers.IO) {
|
_lifecycleScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val caps = if(!_isChannelSearch)
|
val caps = StatePlatform.instance.getCommonSearchCapabilities(_enabledClientsIds);
|
||||||
StatePlatform.instance.getCommonSearchCapabilities(_enabledClientsIds);
|
|
||||||
else
|
|
||||||
StatePlatform.instance.getCommonSearchChannelContentsCapabilities(_enabledClientsIds);
|
|
||||||
synchronized(_filterValues) {
|
synchronized(_filterValues) {
|
||||||
if (caps != null) {
|
if (caps != null) {
|
||||||
val keysToRemove = arrayListOf<String>();
|
val keysToRemove = arrayListOf<String>();
|
||||||
|
|
|
@ -531,6 +531,8 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
fun setLoopVisible(visible: Boolean) {
|
fun setLoopVisible(visible: Boolean) {
|
||||||
_control_loop.visibility = if (visible) View.VISIBLE else View.GONE;
|
_control_loop.visibility = if (visible) View.VISIBLE else View.GONE;
|
||||||
_control_loop_fullscreen.visibility = if (visible) View.VISIBLE else View.GONE;
|
_control_loop_fullscreen.visibility = if (visible) View.VISIBLE else View.GONE;
|
||||||
|
if (StatePlayer.instance.loopVideo && !visible)
|
||||||
|
StatePlayer.instance.loopVideo = false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopAllGestures() {
|
fun stopAllGestures() {
|
||||||
|
|
16
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
16
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:pathData="m54,75 l25,-38h-50z"
|
||||||
|
android:strokeWidth=".83"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="m34,64c1,-1.1 3.6,-3.7 5.7,-5 2,-1.3 4.4,-5 5.4,-6.6 2.9,-4.3 9.4,-13 12,-15 0.5,-0.39 1.2,-1 1.5,-1.3 1,-2.3 4.6,-6.3 10,-3.5 0.5,-0.17 1.7,-0.21 2.3,-0.21 -0.44,0.3 -1.4,1.2 -1.5,2.3 -0.45,3.1 -2.1,4.9 -2.9,5.4 -0.56,3.6 -1.3,6.7 -3,7.8l1.5,2.7c2.3,2.4 7.1,7.7 7.8,9.3 -2.2,-0.73 -3.7,-1.4 -4.1,-1.7l4.1,5.7c-2.4,-0.19 -7.9,-1.8 -10,-6.6 0.95,2.6 1.9,5.8 2.2,7.1 -1.3,-1.1 -4.1,-4.2 -4.9,-8 0.22,3.7 0.19,6.4 0.14,7.3 -0.63,-0.58 -2.1,-2.4 -2.9,-5v4.3c-0.94,-1.3 -2.8,-4.5 -3,-6.9 0.16,2.7 0.06,4 -0.01,4.3l-3.6,-3.4c-0.95,0.51 -3.5,1.7 -6.1,2.2 -1.8,1.5 -4,5.3 -4.8,7v-2.2l-2.4,2.4 0.84,-2.5 -1.5,1.3c-0.35,0.21 -1.2,0.63 -1.6,0.63 0.17,-0.39 0.49,-0.82 0.63,-0.98l-2,0.77c0.23,-0.68 0.96,-2.2 2,-2.7 -1.5,0.56 -2,0.7 -2.2,0.7z"
|
||||||
|
android:strokeWidth=".2"/>
|
||||||
|
</vector>
|
|
@ -233,7 +233,7 @@
|
||||||
android:isScrollContainer="true"
|
android:isScrollContainer="true"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
android:maxHeight="200dp"
|
android:maxHeight="200dp"
|
||||||
android:text="An error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurredAn error has occurred" />
|
android:text="An error has occurred" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -2,4 +2,5 @@
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
|
@ -2,4 +2,5 @@
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
1
app/src/stable/assets/sources/crunchyroll
Submodule
1
app/src/stable/assets/sources/crunchyroll
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 1aa91f216c0a87604aed1669b63b7830e4288630
|
|
@ -15,7 +15,8 @@
|
||||||
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json",
|
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json",
|
||||||
"89ae4889-0420-4d16-ad6c-19c776b28f99": "sources/apple-podcasts/ApplePodcastsConfig.json",
|
"89ae4889-0420-4d16-ad6c-19c776b28f99": "sources/apple-podcasts/ApplePodcastsConfig.json",
|
||||||
"8d029a7f-5507-4e36-8bd8-c19a3b77d383": "sources/tedtalks/TedTalksConfig.json",
|
"8d029a7f-5507-4e36-8bd8-c19a3b77d383": "sources/tedtalks/TedTalksConfig.json",
|
||||||
"273b6523-5438-44e2-9f5d-78e0325a8fd9": "sources/curiositystream/CuriosityStreamConfig.json"
|
"273b6523-5438-44e2-9f5d-78e0325a8fd9": "sources/curiositystream/CuriosityStreamConfig.json",
|
||||||
|
"9bb33039-8580-48d4-9849-21319ae845a4": "sources/crunchyroll/CrunchyrollConfig.json"
|
||||||
},
|
},
|
||||||
"SOURCES_EMBEDDED_DEFAULT": [
|
"SOURCES_EMBEDDED_DEFAULT": [
|
||||||
"35ae969a-a7db-11ed-afa1-0242ac120002"
|
"35ae969a-a7db-11ed-afa1-0242ac120002"
|
||||||
|
|
1
app/src/unstable/assets/sources/crunchyroll
Submodule
1
app/src/unstable/assets/sources/crunchyroll
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 1aa91f216c0a87604aed1669b63b7830e4288630
|
|
@ -15,7 +15,8 @@
|
||||||
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json",
|
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json",
|
||||||
"89ae4889-0420-4d16-ad6c-19c776b28f99": "sources/apple-podcasts/ApplePodcastsConfig.json",
|
"89ae4889-0420-4d16-ad6c-19c776b28f99": "sources/apple-podcasts/ApplePodcastsConfig.json",
|
||||||
"8d029a7f-5507-4e36-8bd8-c19a3b77d383": "sources/tedtalks/TedTalksConfig.json",
|
"8d029a7f-5507-4e36-8bd8-c19a3b77d383": "sources/tedtalks/TedTalksConfig.json",
|
||||||
"273b6523-5438-44e2-9f5d-78e0325a8fd9": "sources/curiositystream/CuriosityStreamConfig.json"
|
"273b6523-5438-44e2-9f5d-78e0325a8fd9": "sources/curiositystream/CuriosityStreamConfig.json",
|
||||||
|
"9bb33039-8580-48d4-9849-21319ae845a4": "sources/crunchyroll/CrunchyrollConfig.json"
|
||||||
},
|
},
|
||||||
"SOURCES_EMBEDDED_DEFAULT": [
|
"SOURCES_EMBEDDED_DEFAULT": [
|
||||||
"35ae969a-a7db-11ed-afa1-0242ac120002"
|
"35ae969a-a7db-11ed-afa1-0242ac120002"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue