mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-16 04:52:39 +02:00
Even more library work
This commit is contained in:
@@ -8,6 +8,7 @@ import com.caoccao.javet.values.reference.V8ValueError
|
|||||||
import com.caoccao.javet.values.reference.V8ValueObject
|
import com.caoccao.javet.values.reference.V8ValueObject
|
||||||
import com.caoccao.javet.values.reference.V8ValuePromise
|
import com.caoccao.javet.values.reference.V8ValuePromise
|
||||||
import com.futo.platformplayer.api.media.platforms.js.JSClient
|
import com.futo.platformplayer.api.media.platforms.js.JSClient
|
||||||
|
import com.futo.platformplayer.api.media.structures.IPager
|
||||||
import com.futo.platformplayer.engine.IV8PluginConfig
|
import com.futo.platformplayer.engine.IV8PluginConfig
|
||||||
import com.futo.platformplayer.engine.V8Plugin
|
import com.futo.platformplayer.engine.V8Plugin
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptExecutionException
|
import com.futo.platformplayer.engine.exceptions.ScriptExecutionException
|
||||||
@@ -387,4 +388,15 @@ suspend fun <T> Deferred<T>.awaitCancelConverted(): T {
|
|||||||
}
|
}
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> IPager<T>.toList(): List<T> {
|
||||||
|
val list = this.getResults().toMutableList();
|
||||||
|
|
||||||
|
while(this.hasMorePages()) {
|
||||||
|
this.nextPage();
|
||||||
|
list.addAll(this.getResults());
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.toList();
|
||||||
}
|
}
|
||||||
@@ -83,6 +83,7 @@ import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailFragment.St
|
|||||||
import com.futo.platformplayer.fragment.mainactivity.main.WatchLaterFragment
|
import com.futo.platformplayer.fragment.mainactivity.main.WatchLaterFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.WebDetailFragment
|
import com.futo.platformplayer.fragment.mainactivity.main.WebDetailFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment
|
import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment
|
||||||
|
import com.futo.platformplayer.fragment.mainactivity.topbar.FilesTopBarFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.topbar.GeneralTopBarFragment
|
import com.futo.platformplayer.fragment.mainactivity.topbar.GeneralTopBarFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.topbar.ImportTopBarFragment
|
import com.futo.platformplayer.fragment.mainactivity.topbar.ImportTopBarFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFragment
|
import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFragment
|
||||||
@@ -154,6 +155,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
lateinit var _fragTopBarNavigation: NavigationTopBarFragment;
|
lateinit var _fragTopBarNavigation: NavigationTopBarFragment;
|
||||||
lateinit var _fragTopBarImport: ImportTopBarFragment;
|
lateinit var _fragTopBarImport: ImportTopBarFragment;
|
||||||
lateinit var _fragTopBarAdd: AddTopBarFragment;
|
lateinit var _fragTopBarAdd: AddTopBarFragment;
|
||||||
|
lateinit var _fragTopBarFiles: FilesTopBarFragment;
|
||||||
|
|
||||||
//Frags BotBar
|
//Frags BotBar
|
||||||
lateinit var _fragBotBarMenu: MenuBottomBarFragment;
|
lateinit var _fragBotBarMenu: MenuBottomBarFragment;
|
||||||
@@ -332,6 +334,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
_fragTopBarNavigation = NavigationTopBarFragment.newInstance();
|
_fragTopBarNavigation = NavigationTopBarFragment.newInstance();
|
||||||
_fragTopBarImport = ImportTopBarFragment.newInstance();
|
_fragTopBarImport = ImportTopBarFragment.newInstance();
|
||||||
_fragTopBarAdd = AddTopBarFragment.newInstance();
|
_fragTopBarAdd = AddTopBarFragment.newInstance();
|
||||||
|
_fragTopBarFiles = FilesTopBarFragment.newInstance();
|
||||||
|
|
||||||
//BotBars
|
//BotBars
|
||||||
_fragBotBarMenu = MenuBottomBarFragment.newInstance();
|
_fragBotBarMenu = MenuBottomBarFragment.newInstance();
|
||||||
@@ -508,7 +511,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
_fragLibraryArtists.topBar = _fragTopBarNavigation;
|
_fragLibraryArtists.topBar = _fragTopBarNavigation;
|
||||||
_fragLibraryArtist.topBar = _fragTopBarNavigation;
|
_fragLibraryArtist.topBar = _fragTopBarNavigation;
|
||||||
_fragLibraryVideos.topBar = _fragTopBarNavigation;
|
_fragLibraryVideos.topBar = _fragTopBarNavigation;
|
||||||
_fragLibraryFiles.topBar = _fragTopBarNavigation;
|
_fragLibraryFiles.topBar = _fragTopBarFiles;
|
||||||
|
|
||||||
_fragBrowser.topBar = _fragTopBarNavigation;
|
_fragBrowser.topBar = _fragTopBarNavigation;
|
||||||
|
|
||||||
@@ -1288,6 +1291,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
VideoDetailFragment::class -> _fragVideoDetail as T;
|
VideoDetailFragment::class -> _fragVideoDetail as T;
|
||||||
MenuBottomBarFragment::class -> _fragBotBarMenu as T;
|
MenuBottomBarFragment::class -> _fragBotBarMenu as T;
|
||||||
GeneralTopBarFragment::class -> _fragTopBarGeneral as T;
|
GeneralTopBarFragment::class -> _fragTopBarGeneral as T;
|
||||||
|
FilesTopBarFragment::class -> _fragTopBarFiles as T;
|
||||||
SearchTopBarFragment::class -> _fragTopBarSearch as T;
|
SearchTopBarFragment::class -> _fragTopBarSearch as T;
|
||||||
CreatorsFragment::class -> _fragMainSubscriptions as T;
|
CreatorsFragment::class -> _fragMainSubscriptions as T;
|
||||||
CommentsFragment::class -> _fragMainComments as T;
|
CommentsFragment::class -> _fragMainComments as T;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import java.time.OffsetDateTime
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : LinearLayout where TPager : IPager<TResult>, TViewHolder : RecyclerView.ViewHolder, TFragment : MainFragment {
|
abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : LinearLayout where TPager : IPager<TResult>, TViewHolder : RecyclerView.ViewHolder, TFragment : MainFragment {
|
||||||
|
protected val _feedRoot: FrameLayout;
|
||||||
protected val _recyclerResults: RecyclerView;
|
protected val _recyclerResults: RecyclerView;
|
||||||
protected val _overlayContainer: FrameLayout;
|
protected val _overlayContainer: FrameLayout;
|
||||||
protected val _swipeRefresh: SwipeRefreshLayout;
|
protected val _swipeRefresh: SwipeRefreshLayout;
|
||||||
@@ -67,7 +68,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
|||||||
private var _sortByOptions: List<String>? = null;
|
private var _sortByOptions: List<String>? = null;
|
||||||
private var _activeTags: List<String>? = null;
|
private var _activeTags: List<String>? = null;
|
||||||
|
|
||||||
private var _nextPageHandler: TaskHandler<TPager, List<TResult>>;
|
private var _nextPageHandler: TaskHandler<TPager, Pair<TPager, List<TResult>>>;
|
||||||
val recyclerData: RecyclerData<InsertedViewAdapterWithLoader<TViewHolder>, GridLayoutManager, TPager, TResult, TConverted, InsertedViewHolder<TViewHolder>>;
|
val recyclerData: RecyclerData<InsertedViewAdapterWithLoader<TViewHolder>, GridLayoutManager, TPager, TResult, TConverted, InsertedViewHolder<TViewHolder>>;
|
||||||
|
|
||||||
val fragment: TFragment;
|
val fragment: TFragment;
|
||||||
@@ -80,6 +81,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
|||||||
this.fragment = fragment;
|
this.fragment = fragment;
|
||||||
inflater.inflate(R.layout.fragment_feed, this);
|
inflater.inflate(R.layout.fragment_feed, this);
|
||||||
|
|
||||||
|
_feedRoot = findViewById(R.id.feed_root);
|
||||||
_textCentered = findViewById(R.id.text_centered);
|
_textCentered = findViewById(R.id.text_centered);
|
||||||
_emptyPagerContainer = findViewById(R.id.empty_pager_container);
|
_emptyPagerContainer = findViewById(R.id.empty_pager_container);
|
||||||
_progressBar = findViewById(R.id.progress_bar);
|
_progressBar = findViewById(R.id.progress_bar);
|
||||||
@@ -135,23 +137,27 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
|||||||
|
|
||||||
_toolbarContentView = findViewById(R.id.container_toolbar_content);
|
_toolbarContentView = findViewById(R.id.container_toolbar_content);
|
||||||
|
|
||||||
_nextPageHandler = TaskHandler<TPager, List<TResult>>({fragment.lifecycleScope}, {
|
_nextPageHandler = TaskHandler<TPager, Pair<TPager, List<TResult>>>({fragment.lifecycleScope}, {
|
||||||
if (it is IAsyncPager<*>)
|
if (it is IAsyncPager<*>)
|
||||||
it.nextPageAsync();
|
it.nextPageAsync();
|
||||||
else
|
else
|
||||||
it.nextPage();
|
it.nextPage();
|
||||||
|
|
||||||
processPagerExceptions(it);
|
processPagerExceptions(it);
|
||||||
return@TaskHandler it.getResults();
|
return@TaskHandler Pair(it, it.getResults());
|
||||||
}).success {
|
}).success {
|
||||||
|
val pager = it.first;
|
||||||
|
val results = it.second
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
val posBefore = recyclerData.results.size;
|
val posBefore = recyclerData.results.size;
|
||||||
val filteredResults = filterResults(it);
|
val filteredResults = filterResults(results);
|
||||||
recyclerData.results.addAll(filteredResults);
|
recyclerData.results.addAll(filteredResults);
|
||||||
recyclerData.resultsUnfiltered.addAll(it);
|
recyclerData.resultsUnfiltered.addAll(results);
|
||||||
recyclerData.adapter.notifyItemRangeInserted(recyclerData.adapter.childToParentPosition(posBefore), filteredResults.size);
|
recyclerData.adapter.notifyItemRangeInserted(recyclerData.adapter.childToParentPosition(posBefore), filteredResults.size);
|
||||||
ensureEnoughContentVisible(filteredResults)
|
if(pager.hasMorePages())
|
||||||
|
ensureEnoughContentVisible(filteredResults)
|
||||||
}.exception<Throwable> {
|
}.exception<Throwable> {
|
||||||
Logger.w(TAG, "Failed to load next page.", it);
|
Logger.w(TAG, "Failed to load next page.", it);
|
||||||
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_next_page), it, {
|
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_next_page), it, {
|
||||||
@@ -390,6 +396,9 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
|||||||
protected fun finishRefreshLayoutLoader() {
|
protected fun finishRefreshLayoutLoader() {
|
||||||
_swipeRefresh.isRefreshing = false;
|
_swipeRefresh.isRefreshing = false;
|
||||||
}
|
}
|
||||||
|
protected fun disableRefreshLayout() {
|
||||||
|
_swipeRefresh.isEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
fun clearResults(){
|
fun clearResults(){
|
||||||
setPager(EmptyPager<TResult>() as TPager);
|
setPager(EmptyPager<TResult>() as TPager);
|
||||||
|
|||||||
+10
@@ -5,6 +5,9 @@ import android.os.Bundle
|
|||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.futo.platformplayer.UISlideOverlays
|
import com.futo.platformplayer.UISlideOverlays
|
||||||
@@ -12,6 +15,8 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
|||||||
import com.futo.platformplayer.api.media.structures.AdhocPager
|
import com.futo.platformplayer.api.media.structures.AdhocPager
|
||||||
import com.futo.platformplayer.api.media.structures.EmptyPager
|
import com.futo.platformplayer.api.media.structures.EmptyPager
|
||||||
import com.futo.platformplayer.api.media.structures.IPager
|
import com.futo.platformplayer.api.media.structures.IPager
|
||||||
|
import com.futo.platformplayer.dp
|
||||||
|
import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFragment
|
||||||
import com.futo.platformplayer.states.Album
|
import com.futo.platformplayer.states.Album
|
||||||
import com.futo.platformplayer.states.StateLibrary
|
import com.futo.platformplayer.states.StateLibrary
|
||||||
import com.futo.platformplayer.states.StatePlayer
|
import com.futo.platformplayer.states.StatePlayer
|
||||||
@@ -76,6 +81,11 @@ class LibraryAlbumFragment : MainFragment() {
|
|||||||
StatePlayer.instance.setPlaylist(playlist, focus = true, shuffle = true);
|
StatePlayer.instance.setPlaylist(playlist, focus = true, shuffle = true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
_feedRoot.updateLayoutParams<LinearLayout.LayoutParams> {
|
||||||
|
this.setMargins(0,-50.dp(resources),0,0)
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onShown(parameter: Any?) {
|
fun onShown(parameter: Any?) {
|
||||||
|
|||||||
+6
@@ -95,6 +95,7 @@ class LibraryAlbumsFragment : MainFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_toolbarContentView.addView(libraryTypeHeader);
|
_toolbarContentView.addView(libraryTypeHeader);
|
||||||
|
disableRefreshLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onShown() {
|
fun onShown() {
|
||||||
@@ -105,6 +106,11 @@ class LibraryAlbumsFragment : MainFragment() {
|
|||||||
setPager(AdhocPager<Album>({ listOf(); }, initialAlbums));
|
setPager(AdhocPager<Album>({ listOf(); }, initialAlbums));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun reload() {
|
||||||
|
super.reload();
|
||||||
|
finishRefreshLayoutLoader();
|
||||||
|
}
|
||||||
|
|
||||||
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<Album>): InsertedViewAdapterWithLoader<AlbumTileViewHolder> {
|
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<Album>): InsertedViewAdapterWithLoader<AlbumTileViewHolder> {
|
||||||
return InsertedViewAdapterWithLoader(context, arrayListOf(), arrayListOf(),
|
return InsertedViewAdapterWithLoader(context, arrayListOf(), arrayListOf(),
|
||||||
childCountGetter = { dataset.size },
|
childCountGetter = { dataset.size },
|
||||||
|
|||||||
+50
-3
@@ -52,6 +52,7 @@ import com.futo.platformplayer.states.StateSubscriptions
|
|||||||
import com.futo.platformplayer.views.FeedStyle
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.TrackViewHolder
|
||||||
import com.futo.platformplayer.views.others.CreatorThumbnail
|
import com.futo.platformplayer.views.others.CreatorThumbnail
|
||||||
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
|
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
|
||||||
import com.futo.platformplayer.views.subscriptions.SubscribeButton
|
import com.futo.platformplayer.views.subscriptions.SubscribeButton
|
||||||
@@ -125,7 +126,7 @@ class LibraryArtistFragment : MainFragment() {
|
|||||||
private val _onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {}
|
private val _onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
inflater.inflate(R.layout.fragment_channel, this)
|
inflater.inflate(R.layout.fragment_artist, this)
|
||||||
|
|
||||||
val tabs: TabLayout = findViewById(R.id.tabs)
|
val tabs: TabLayout = findViewById(R.id.tabs)
|
||||||
val viewPager: ViewPager2 = findViewById(R.id.view_pager)
|
val viewPager: ViewPager2 = findViewById(R.id.view_pager)
|
||||||
@@ -339,7 +340,7 @@ class LibraryArtistFragment : MainFragment() {
|
|||||||
_buttonSubscriptionSettings.visibility =
|
_buttonSubscriptionSettings.visibility =
|
||||||
if (_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE
|
if (_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE
|
||||||
_textChannel.text = channel.name
|
_textChannel.text = channel.name
|
||||||
_textChannelSub.text = ""
|
_textChannelSub.text = "${channel.countTracks} songs, ${channel.countAlbums} albums";
|
||||||
|
|
||||||
var supportsPlaylists = false;
|
var supportsPlaylists = false;
|
||||||
val playlistPosition = 1
|
val playlistPosition = 1
|
||||||
@@ -474,20 +475,66 @@ class LibraryArtistFragment : MainFragment() {
|
|||||||
_lastArtist = artist;
|
_lastArtist = artist;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class ArtistContentView : ContentFeedView<LibraryArtistFragment> {
|
class ArtistContentView : FeedView<LibraryArtistFragment, IPlatformContent, IPlatformVideo, IPager<IPlatformContent>, TrackViewHolder> {
|
||||||
override val feedStyle: FeedStyle = FeedStyle.THUMBNAIL; //R.layout.list_creator;
|
override val feedStyle: FeedStyle = FeedStyle.THUMBNAIL; //R.layout.list_creator;
|
||||||
|
|
||||||
|
protected var _artist: Artist? = null;
|
||||||
|
|
||||||
constructor(fragment: LibraryArtistFragment, inflater: LayoutInflater) : super(fragment, inflater) {
|
constructor(fragment: LibraryArtistFragment, inflater: LayoutInflater) : super(fragment, inflater) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setArtist(artist: Artist) {
|
fun setArtist(artist: Artist) {
|
||||||
|
this._artist = artist;
|
||||||
val tracks = artist.getAudioTracks();
|
val tracks = artist.getAudioTracks();
|
||||||
if(tracks.getResults().isEmpty())
|
if(tracks.getResults().isEmpty())
|
||||||
UIDialogs.appToast("No tracks found");
|
UIDialogs.appToast("No tracks found");
|
||||||
setPager(tracks);
|
setPager(tracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun filterResults(results: List<IPlatformContent>): List<IPlatformVideo> {
|
||||||
|
return results.filter { it is IPlatformVideo }.map { it as IPlatformVideo };
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<IPlatformVideo>): InsertedViewAdapterWithLoader<TrackViewHolder> {
|
||||||
|
return InsertedViewAdapterWithLoader(context, arrayListOf(), arrayListOf(),
|
||||||
|
childCountGetter = { dataset.size },
|
||||||
|
childViewHolderBinder = { viewHolder, position -> viewHolder.bind(dataset[position]); },
|
||||||
|
childViewHolderFactory = { viewGroup, _ ->
|
||||||
|
val holder = TrackViewHolder(viewGroup);
|
||||||
|
holder.onClick.subscribe { c ->
|
||||||
|
|
||||||
|
val playlist = _artist?.toPlaylist();
|
||||||
|
if (playlist != null) {
|
||||||
|
val index = playlist.videos.indexOf(c);
|
||||||
|
if (index == -1)
|
||||||
|
return@subscribe;
|
||||||
|
|
||||||
|
StatePlayer.instance.setPlaylist(playlist, index, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
holder.onOptions.subscribe {
|
||||||
|
if(it is IPlatformVideo)
|
||||||
|
UISlideOverlays.showVideoOptionsOverlay(it, _overlayContainer);
|
||||||
|
}
|
||||||
|
return@InsertedViewAdapterWithLoader holder;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createLayoutManager(recyclerResults: RecyclerView, context: Context): GridLayoutManager {
|
||||||
|
val glmResults = GridLayoutManager(context, 1)
|
||||||
|
|
||||||
|
_swipeRefresh.layoutParams = (_swipeRefresh.layoutParams as MarginLayoutParams?)?.apply {
|
||||||
|
rightMargin = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
8.0f,
|
||||||
|
context.resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
}
|
||||||
|
return glmResults
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateSpanCount(){ }
|
override fun updateSpanCount(){ }
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
@@ -97,6 +97,7 @@ class LibraryArtistsFragment : MainFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_toolbarContentView.addView(libraryTypeHeader);
|
_toolbarContentView.addView(libraryTypeHeader);
|
||||||
|
disableRefreshLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onShown() {
|
fun onShown() {
|
||||||
|
|||||||
+61
-62
@@ -18,12 +18,14 @@ import com.futo.platformplayer.api.media.structures.AdhocPager
|
|||||||
import com.futo.platformplayer.api.media.structures.EmptyPager
|
import com.futo.platformplayer.api.media.structures.EmptyPager
|
||||||
import com.futo.platformplayer.api.media.structures.IPager
|
import com.futo.platformplayer.api.media.structures.IPager
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
import com.futo.platformplayer.fragment.mainactivity.topbar.FilesTopBarFragment
|
||||||
import com.futo.platformplayer.states.FileEntry
|
import com.futo.platformplayer.states.FileEntry
|
||||||
import com.futo.platformplayer.states.StateLibrary
|
import com.futo.platformplayer.states.StateLibrary
|
||||||
import com.futo.platformplayer.views.FeedStyle
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
import com.futo.platformplayer.views.NoResultsView
|
import com.futo.platformplayer.views.NoResultsView
|
||||||
import com.futo.platformplayer.views.adapters.AnyAdapter
|
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.FileViewHolder
|
||||||
import com.futo.platformplayer.views.buttons.BigButton
|
import com.futo.platformplayer.views.buttons.BigButton
|
||||||
|
|
||||||
class LibraryFilesFragment : MainFragment() {
|
class LibraryFilesFragment : MainFragment() {
|
||||||
@@ -46,7 +48,7 @@ class LibraryFilesFragment : MainFragment() {
|
|||||||
|
|
||||||
override fun onShownWithView(parameter: Any?, isBack: Boolean) {
|
override fun onShownWithView(parameter: Any?, isBack: Boolean) {
|
||||||
super.onShownWithView(parameter, isBack);
|
super.onShownWithView(parameter, isBack);
|
||||||
view?.onShown();
|
view?.onShown(parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyMainView() {
|
override fun onDestroyMainView() {
|
||||||
@@ -65,30 +67,51 @@ class LibraryFilesFragment : MainFragment() {
|
|||||||
var buttonUp: BigButton? = null;
|
var buttonUp: BigButton? = null;
|
||||||
var buttonAdd: BigButton? = null;
|
var buttonAdd: BigButton? = null;
|
||||||
|
|
||||||
|
private var root: FileEntry? = null;
|
||||||
|
|
||||||
constructor(fragment: LibraryFilesFragment, inflater: LayoutInflater) : super(fragment, inflater) {
|
constructor(fragment: LibraryFilesFragment, inflater: LayoutInflater) : super(fragment, inflater) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onShown() {
|
fun onShown(parameter: Any? = null) {
|
||||||
|
this.root = if(parameter is FileEntry) parameter else null;
|
||||||
loadTop();
|
loadTop();
|
||||||
}
|
}
|
||||||
fun loadTop() {
|
fun loadTop() {
|
||||||
val initialDirectories = StateLibrary.instance.getFileDirectories();
|
var initialDirectories = listOf<FileEntry>();
|
||||||
if(initialDirectories.size == 0) {
|
if(root == null) {
|
||||||
setEmptyPager(true);
|
initialDirectories = StateLibrary.instance.getFileDirectories();
|
||||||
setPager(EmptyPager());
|
if (initialDirectories.size == 0) {
|
||||||
|
setEmptyPager(true);
|
||||||
|
setPager(EmptyPager());
|
||||||
|
buttonAdd?.let {
|
||||||
|
it.isVisible = false;
|
||||||
|
}
|
||||||
|
buttonUp?.let {
|
||||||
|
it.isVisible = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else
|
||||||
|
setEmptyPager(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
buttonAdd?.let {
|
buttonAdd?.let {
|
||||||
it.isVisible = false;
|
it.isVisible = false;
|
||||||
}
|
}
|
||||||
buttonUp?.let {
|
buttonUp?.let {
|
||||||
it.isVisible = false;
|
it.isVisible = false;
|
||||||
}
|
}
|
||||||
return;
|
initialDirectories = root?.getSubFiles() ?: listOf();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
setEmptyPager(false);
|
|
||||||
navStack.clear();
|
navStack.clear();
|
||||||
navStack.add(FileStack("", initialDirectories));
|
val entry = FileStack("", initialDirectories);
|
||||||
|
navStack.add(entry);
|
||||||
openDirectory(navStack.last());
|
openDirectory(navStack.last());
|
||||||
|
fragment.topBar?.let {
|
||||||
|
if(it is FilesTopBarFragment) {
|
||||||
|
it.setUpNavigate(null);
|
||||||
|
it.setTitle(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fun leaveDirectory() {
|
fun leaveDirectory() {
|
||||||
if(navStack.size > 1) {
|
if(navStack.size > 1) {
|
||||||
@@ -101,6 +124,12 @@ class LibraryFilesFragment : MainFragment() {
|
|||||||
if(addToStack)
|
if(addToStack)
|
||||||
navStack.add(stack);
|
navStack.add(stack);
|
||||||
|
|
||||||
|
fragment.topBar?.let {
|
||||||
|
if(it is FilesTopBarFragment) {
|
||||||
|
it.setTitle(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buttonAdd?.let {
|
buttonAdd?.let {
|
||||||
it.isVisible = navStack.size < 2
|
it.isVisible = navStack.size < 2
|
||||||
}
|
}
|
||||||
@@ -109,6 +138,21 @@ class LibraryFilesFragment : MainFragment() {
|
|||||||
}
|
}
|
||||||
setPager(AdhocPager<FileEntry>({ listOf(); }, stack.files));
|
setPager(AdhocPager<FileEntry>({ listOf(); }, stack.files));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
|
fragment.topBar?.let {
|
||||||
|
if(it is FilesTopBarFragment) {
|
||||||
|
if(navStack.size > 1)
|
||||||
|
it.setUpNavigate{
|
||||||
|
leaveDirectory();
|
||||||
|
};
|
||||||
|
else it.setUpNavigate(null);
|
||||||
|
it.setTitle(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBack() {
|
||||||
|
fragment.topBar?.view
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getEmptyPagerView(): View? {
|
override fun getEmptyPagerView(): View? {
|
||||||
@@ -116,14 +160,15 @@ class LibraryFilesFragment : MainFragment() {
|
|||||||
"To see files in Grayjay you have to add directories to view",
|
"To see files in Grayjay you have to add directories to view",
|
||||||
R.drawable.ic_library, listOf(
|
R.drawable.ic_library, listOf(
|
||||||
BigButton(context, "Add Directory", "Select a directory to add", R.drawable.ic_add, {
|
BigButton(context, "Add Directory", "Select a directory to add", R.drawable.ic_add, {
|
||||||
StateLibrary.instance.addFileDirectory {
|
StateLibrary.instance.addFileDirectory({
|
||||||
loadTop();
|
loadTop();
|
||||||
};
|
}, true);
|
||||||
})
|
})
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<FileEntry>): InsertedViewAdapterWithLoader<FileViewHolder> {
|
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<FileEntry>): InsertedViewAdapterWithLoader<FileViewHolder> {
|
||||||
|
/*
|
||||||
val buttonUp = BigButton(fragment.requireContext(), "Go up", "Go up a directory", R.drawable.ic_move_up) {
|
val buttonUp = BigButton(fragment.requireContext(), "Go up", "Go up a directory", R.drawable.ic_move_up) {
|
||||||
if(navStack.size > 1)
|
if(navStack.size > 1)
|
||||||
leaveDirectory();
|
leaveDirectory();
|
||||||
@@ -133,9 +178,10 @@ class LibraryFilesFragment : MainFragment() {
|
|||||||
loadTop();
|
loadTop();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.buttonUp = buttonUp;
|
*/
|
||||||
this.buttonAdd = buttonAdd;
|
//this.buttonUp = buttonUp;
|
||||||
return InsertedViewAdapterWithLoader(context, arrayListOf(buttonUp), arrayListOf(buttonAdd),
|
//this.buttonAdd = buttonAdd;
|
||||||
|
return InsertedViewAdapterWithLoader(context, arrayListOf(), arrayListOf(),
|
||||||
childCountGetter = { dataset.size },
|
childCountGetter = { dataset.size },
|
||||||
childViewHolderBinder = { viewHolder, position -> viewHolder.bind(dataset[position]); },
|
childViewHolderBinder = { viewHolder, position -> viewHolder.bind(dataset[position]); },
|
||||||
childViewHolderFactory = { viewGroup, _ ->
|
childViewHolderFactory = { viewGroup, _ ->
|
||||||
@@ -185,51 +231,4 @@ class LibraryFilesFragment : MainFragment() {
|
|||||||
val files: List<FileEntry>
|
val files: List<FileEntry>
|
||||||
)
|
)
|
||||||
|
|
||||||
class FileViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<FileEntry>(
|
|
||||||
LayoutInflater.from(_viewGroup.context).inflate(R.layout.list_file,
|
|
||||||
_viewGroup, false)) {
|
|
||||||
|
|
||||||
val onClick = Event1<FileEntry?>();
|
|
||||||
val onDelete = Event1<FileEntry?>();
|
|
||||||
|
|
||||||
protected var _file: FileEntry? = null;
|
|
||||||
protected val _imageThumbnail: ImageView
|
|
||||||
protected val _buttonDelete: ImageButton;
|
|
||||||
protected val _textName: TextView
|
|
||||||
protected val _textMetadata: TextView
|
|
||||||
|
|
||||||
init {
|
|
||||||
_imageThumbnail = _view.findViewById(R.id.image_thumbnail);
|
|
||||||
_textName = _view.findViewById(R.id.text_name);
|
|
||||||
_textMetadata = _view.findViewById(R.id.text_metadata);
|
|
||||||
_buttonDelete = _view.findViewById(R.id.button_delete);
|
|
||||||
|
|
||||||
_view.setOnClickListener { onClick.emit(_file) };
|
|
||||||
_buttonDelete.setOnClickListener { onDelete.emit(_file) }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun bind(file: FileEntry) {
|
|
||||||
_file = file;
|
|
||||||
_imageThumbnail?.let {
|
|
||||||
if(file.isDirectory)
|
|
||||||
it.setImageResource(R.drawable.ic_library);
|
|
||||||
else {
|
|
||||||
Glide.with(it)
|
|
||||||
.load(file.thumbnail)
|
|
||||||
.placeholder(R.drawable.placeholder_channel_thumbnail)
|
|
||||||
.into(it)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
_buttonDelete.isVisible = file.removable;
|
|
||||||
|
|
||||||
_textName.text = file.name;
|
|
||||||
if(file.isDirectory)
|
|
||||||
_textMetadata.text = "Directory";
|
|
||||||
else
|
|
||||||
_textMetadata.text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+9
-5
@@ -6,6 +6,7 @@ import android.os.Bundle
|
|||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
@@ -37,6 +38,7 @@ import com.futo.platformplayer.views.adapters.AnyAdapter
|
|||||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapter
|
import com.futo.platformplayer.views.adapters.InsertedViewAdapter
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.ArtistTileViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.ArtistTileViewHolder
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.FileViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.LocalVideoTileViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.LocalVideoTileViewHolder
|
||||||
import com.futo.platformplayer.views.buttons.BigButton
|
import com.futo.platformplayer.views.buttons.BigButton
|
||||||
|
|
||||||
@@ -146,7 +148,7 @@ class LibraryFragment : MainFragment() {
|
|||||||
|
|
||||||
val recycler: RecyclerView;
|
val recycler: RecyclerView;
|
||||||
|
|
||||||
val adapterFiles: AnyInsertedAdapterView<FileEntry,LibraryFilesFragment.FileViewHolder>;
|
val adapterFiles: AnyInsertedAdapterView<FileEntry, FileViewHolder>;
|
||||||
|
|
||||||
//var metaInfo: TextView;
|
//var metaInfo: TextView;
|
||||||
|
|
||||||
@@ -174,9 +176,9 @@ class LibraryFragment : MainFragment() {
|
|||||||
this.setMargins(0,0, 0, 0);
|
this.setMargins(0,0, 0, 0);
|
||||||
}
|
}
|
||||||
sectionFiles.setSection("Directories") {
|
sectionFiles.setSection("Directories") {
|
||||||
StateLibrary.instance.addFileDirectory {
|
StateLibrary.instance.addFileDirectory({
|
||||||
reloadFiles();
|
reloadFiles();
|
||||||
}
|
}, true)
|
||||||
}
|
}
|
||||||
sectionFiles.setNavIcon(R.drawable.ic_add);
|
sectionFiles.setNavIcon(R.drawable.ic_add);
|
||||||
//buttonFiles = findViewById<BigButton>(R.id.button_files);
|
//buttonFiles = findViewById<BigButton>(R.id.button_files);
|
||||||
@@ -231,13 +233,15 @@ class LibraryFragment : MainFragment() {
|
|||||||
val videos = StateLibrary.instance.getRecentVideos(null, 20);
|
val videos = StateLibrary.instance.getRecentVideos(null, 20);
|
||||||
adapterVideos.setData(videos);
|
adapterVideos.setData(videos);
|
||||||
|
|
||||||
adapterFiles = recycler.asAnyWithViews<FileEntry, LibraryFilesFragment.FileViewHolder>(
|
adapterFiles = recycler.asAnyWithViews<FileEntry, FileViewHolder>(
|
||||||
arrayListOf(
|
arrayListOf(
|
||||||
sectionArtists,
|
sectionArtists,
|
||||||
sectionAlbums,
|
sectionAlbums,
|
||||||
sectionVideos,
|
sectionVideos,
|
||||||
sectionFiles
|
sectionFiles
|
||||||
), arrayListOf(), RecyclerView.VERTICAL, false, {
|
),
|
||||||
|
arrayListOf(View(context).apply { this.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 20.dp(resources)) }),
|
||||||
|
RecyclerView.VERTICAL, false, {
|
||||||
it.onClick.subscribe {
|
it.onClick.subscribe {
|
||||||
if(it != null)
|
if(it != null)
|
||||||
fragment.navigate<LibraryFilesFragment>(it);
|
fragment.navigate<LibraryFilesFragment>(it);
|
||||||
|
|||||||
+210
@@ -0,0 +1,210 @@
|
|||||||
|
package com.futo.platformplayer.fragment.mainactivity.main
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.AdapterView
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.LinearLayout.GONE
|
||||||
|
import android.widget.LinearLayout.VISIBLE
|
||||||
|
import android.widget.Spinner
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.widget.addTextChangedListener
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.UISlideOverlays
|
||||||
|
import com.futo.platformplayer.api.media.models.PlatformAuthorLink
|
||||||
|
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
||||||
|
import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist
|
||||||
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
||||||
|
import com.futo.platformplayer.api.media.structures.AdhocPager
|
||||||
|
import com.futo.platformplayer.api.media.structures.IPager
|
||||||
|
import com.futo.platformplayer.constructs.Event0
|
||||||
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
import com.futo.platformplayer.dp
|
||||||
|
import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment
|
||||||
|
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||||
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import com.futo.platformplayer.states.Album
|
||||||
|
import com.futo.platformplayer.states.Artist
|
||||||
|
import com.futo.platformplayer.states.ArtistOrdering
|
||||||
|
import com.futo.platformplayer.states.FileEntry
|
||||||
|
import com.futo.platformplayer.states.StateLibrary
|
||||||
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
|
import com.futo.platformplayer.stores.StringStorage
|
||||||
|
import com.futo.platformplayer.views.AnyAdapterView
|
||||||
|
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||||
|
import com.futo.platformplayer.views.AnyInsertedAdapterView
|
||||||
|
import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithViews
|
||||||
|
import com.futo.platformplayer.views.FeedStyle
|
||||||
|
import com.futo.platformplayer.views.LibrarySection
|
||||||
|
import com.futo.platformplayer.views.LibraryTypeHeaderView
|
||||||
|
import com.futo.platformplayer.views.LibraryTypeHeaderView.SelectedType
|
||||||
|
import com.futo.platformplayer.views.PillV2
|
||||||
|
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||||
|
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||||
|
import com.futo.platformplayer.views.adapters.SubscriptionAdapter
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.ArtistTileViewHolder
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.FileViewHolder
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.LocalVideoTileViewHolder
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.SelectablePlaylist
|
||||||
|
import com.futo.platformplayer.views.adapters.viewholders.TrackViewHolder
|
||||||
|
import com.futo.platformplayer.views.others.CreatorThumbnail
|
||||||
|
import com.futo.platformplayer.views.platform.PlatformIndicator
|
||||||
|
|
||||||
|
class LibrarySearchFragment : MainFragment() {
|
||||||
|
override val isMainView : Boolean = true;
|
||||||
|
override val isTab: Boolean = true;
|
||||||
|
override val hasBottomBar: Boolean get() = true;
|
||||||
|
|
||||||
|
|
||||||
|
var view: FragView? = null;
|
||||||
|
|
||||||
|
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
val view = FragView(this);
|
||||||
|
this.view = view;
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onShown(parameter: Any?, isBack: Boolean) {
|
||||||
|
super.onShown(parameter, isBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onShownWithView(parameter: Any?, isBack: Boolean) {
|
||||||
|
super.onShownWithView(parameter, isBack);
|
||||||
|
view?.onShown();
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyMainView() {
|
||||||
|
view = null;
|
||||||
|
super.onDestroyMainView();
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = LibrarySearchFragment().apply {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FragView: ConstraintLayout {
|
||||||
|
val fragment: LibrarySearchFragment;
|
||||||
|
|
||||||
|
val pillArtist: PillV2;
|
||||||
|
val pillAlbums: PillV2;
|
||||||
|
val pillSongs: PillV2;
|
||||||
|
val pills: List<PillV2>;
|
||||||
|
|
||||||
|
val recycler: RecyclerView;
|
||||||
|
|
||||||
|
val adapterArtists: AnyAdapterView<Artist, LibraryArtistsFragment.ArtistViewHolder>;
|
||||||
|
val adapterSongs: AnyAdapterView<IPlatformContent, TrackViewHolder>;
|
||||||
|
val adapterAlbums: AnyAdapterView<Album, AlbumTileViewHolder>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
constructor(fragment: LibrarySearchFragment) : super(fragment.requireContext()) {
|
||||||
|
inflate(context, R.layout.fragview_library, this);
|
||||||
|
this.fragment = fragment;
|
||||||
|
|
||||||
|
pillArtist = findViewById(R.id.pill_artist);
|
||||||
|
pillAlbums = findViewById(R.id.pill_albums);
|
||||||
|
pillSongs = findViewById(R.id.pill_songs);
|
||||||
|
pills = listOf(pillArtist, pillAlbums, pillSongs);
|
||||||
|
|
||||||
|
pillArtist.onClick.subscribe {
|
||||||
|
pills.forEach { it.setIsEnabled(false) };
|
||||||
|
pillArtist.setIsEnabled(true);
|
||||||
|
loadArtists();
|
||||||
|
}
|
||||||
|
pillAlbums.onClick.subscribe {
|
||||||
|
pills.forEach { it.setIsEnabled(false) };
|
||||||
|
pillAlbums.setIsEnabled(true);
|
||||||
|
loadAlbums();
|
||||||
|
}
|
||||||
|
pillSongs.onClick.subscribe {
|
||||||
|
pills.forEach { it.setIsEnabled(false) };
|
||||||
|
pillSongs.setIsEnabled(true);
|
||||||
|
loadSongs();
|
||||||
|
}
|
||||||
|
|
||||||
|
recycler = findViewById(R.id.recycler);
|
||||||
|
adapterArtists = recycler.asAny<Artist, LibraryArtistsFragment.ArtistViewHolder>(RecyclerView.VERTICAL, false, {
|
||||||
|
it.onClick.subscribe {
|
||||||
|
if(it != null)
|
||||||
|
fragment.navigate<LibraryArtistFragment>(it);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
adapterAlbums = recycler.asAny<Album, AlbumTileViewHolder>(RecyclerView.VERTICAL, false, {
|
||||||
|
it.onClick.subscribe {
|
||||||
|
if(it != null)
|
||||||
|
fragment.navigate<LibraryAlbumFragment>(it);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
adapterSongs = recycler.asAny<IPlatformContent, TrackViewHolder>(RecyclerView.VERTICAL, false, {
|
||||||
|
it.onClick.subscribe {
|
||||||
|
if(it != null && it is IPlatformVideo)
|
||||||
|
fragment.navigate<VideoDetailFragment>(it);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fragment.topBar?.let {
|
||||||
|
if(it is SearchTopBarFragment) {
|
||||||
|
it.onSearch.subscribe {
|
||||||
|
search(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pillArtist.setIsEnabled(true);
|
||||||
|
loadArtists();
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadArtists(){
|
||||||
|
recycler.adapter = adapterArtists.adapter.adapter;
|
||||||
|
}
|
||||||
|
fun loadAlbums() {
|
||||||
|
recycler.adapter = adapterAlbums.adapter.adapter;
|
||||||
|
}
|
||||||
|
fun loadSongs() {
|
||||||
|
recycler.adapter = adapterSongs.adapter.adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun search(str: String) {
|
||||||
|
if(recycler.adapter == adapterArtists.adapter.adapter) {
|
||||||
|
if(str.isNullOrBlank())
|
||||||
|
adapterArtists.adapter.setData(listOf());
|
||||||
|
else
|
||||||
|
adapterArtists.setData(StateLibrary.instance.searchArtists(str));
|
||||||
|
}
|
||||||
|
else if(recycler.adapter == adapterAlbums.adapter.adapter) {
|
||||||
|
if(str.isNullOrBlank())
|
||||||
|
adapterAlbums.adapter.setData(listOf());
|
||||||
|
else
|
||||||
|
adapterAlbums.setData(StateLibrary.instance.searchAlbums(str));
|
||||||
|
}
|
||||||
|
else if(recycler.adapter == adapterSongs.adapter.adapter) {
|
||||||
|
if(str.isNullOrBlank())
|
||||||
|
adapterSongs.adapter.setData(listOf());
|
||||||
|
else
|
||||||
|
adapterSongs.setData(StateLibrary.instance.searchTracks(str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun onShown() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -90,6 +90,7 @@ class LibraryVideosFragment : MainFragment() {
|
|||||||
|
|
||||||
constructor(fragment: LibraryVideosFragment, inflater: LayoutInflater) : super(fragment, inflater) {
|
constructor(fragment: LibraryVideosFragment, inflater: LayoutInflater) : super(fragment, inflater) {
|
||||||
initializeToolbarContent();
|
initializeToolbarContent();
|
||||||
|
disableRefreshLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onShown() {
|
fun onShown() {
|
||||||
|
|||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
package com.futo.platformplayer.fragment.mainactivity.topbar
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.api.media.IPlatformClient
|
||||||
|
import com.futo.platformplayer.api.media.models.PlatformAuthorLink
|
||||||
|
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||||
|
import com.futo.platformplayer.fragment.mainactivity.main.LibraryFilesFragment
|
||||||
|
import com.futo.platformplayer.models.Playlist
|
||||||
|
import com.futo.platformplayer.states.FileEntry
|
||||||
|
import com.futo.platformplayer.views.casting.CastButton
|
||||||
|
import com.futo.polycentric.core.PolycentricProfile
|
||||||
|
|
||||||
|
class FilesTopBarFragment : TopFragment() {
|
||||||
|
private var _buttonBack: ImageButton? = null;
|
||||||
|
private var _buttonCast: CastButton? = null;
|
||||||
|
private var _textTitle: TextView? = null;
|
||||||
|
private var _menuItems: LinearLayout? = null;
|
||||||
|
|
||||||
|
private var _upHandle: (()->Unit)? = null;
|
||||||
|
|
||||||
|
override fun onShown(parameter: Any?) {
|
||||||
|
setTitle(parameter);
|
||||||
|
setMenuItems(listOf());
|
||||||
|
}
|
||||||
|
override fun onHide() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTitle(parameter: Any? = null) {
|
||||||
|
if(parameter is IPlatformChannel) {
|
||||||
|
_textTitle?.text = parameter.name;
|
||||||
|
} else if(parameter is PlatformAuthorLink) {
|
||||||
|
_textTitle?.text = parameter.name;
|
||||||
|
} else if (parameter is Playlist) {
|
||||||
|
_textTitle?.text = parameter.name;
|
||||||
|
} else if (parameter is String) {
|
||||||
|
_textTitle?.text = parameter;
|
||||||
|
} else if (parameter is IPlatformClient) {
|
||||||
|
_textTitle?.text = parameter.name;
|
||||||
|
} else if (parameter is PolycentricProfile) {
|
||||||
|
_textTitle?.text = parameter.systemState.username;
|
||||||
|
} else if(parameter is FileEntry) {
|
||||||
|
val treePrefix = "content://com.android.externalstorage.documents/tree/";
|
||||||
|
if(parameter.path.startsWith(treePrefix)) {
|
||||||
|
_textTitle?.text = parameter.path.substring(treePrefix.length - 1).replace("%3A", " ").replace("%2F", "/");
|
||||||
|
}
|
||||||
|
else if(parameter.path.isNullOrBlank())
|
||||||
|
_textTitle?.text = parameter.name;
|
||||||
|
else
|
||||||
|
_textTitle?.text = parameter.path;
|
||||||
|
}
|
||||||
|
else if(parameter is LibraryFilesFragment.FileStack) {
|
||||||
|
val treePrefix = "content://com.android.externalstorage.documents/tree/";
|
||||||
|
if(parameter.path.startsWith(treePrefix)) {
|
||||||
|
_textTitle?.text = parameter.path.substring(treePrefix.length - 1).replace("%3A", " ").replace("%2F", "/");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_textTitle?.text = parameter.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
val view = inflater.inflate(R.layout.fragment_files_top_bar, container, false);
|
||||||
|
|
||||||
|
val buttonBack: ImageButton = view.findViewById(R.id.button_back);
|
||||||
|
_textTitle = view.findViewById(R.id.text_title);
|
||||||
|
_menuItems = view.findViewById(R.id.menu_buttons)
|
||||||
|
|
||||||
|
buttonBack.setOnClickListener {
|
||||||
|
if(_upHandle != null)
|
||||||
|
_upHandle?.invoke();
|
||||||
|
else
|
||||||
|
closeSegment();
|
||||||
|
};
|
||||||
|
|
||||||
|
_buttonBack = buttonBack;
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUpNavigate(handle: (()->Unit)? = null) {
|
||||||
|
_upHandle = handle;
|
||||||
|
_buttonBack?.setImageResource(if(handle == null) R.drawable.ic_back_nav else R.drawable.ic_arrow_up);
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
|
||||||
|
_buttonBack?.setOnClickListener(null);
|
||||||
|
_buttonBack = null;
|
||||||
|
_buttonCast?.cleanup();
|
||||||
|
_buttonCast = null;
|
||||||
|
_textTitle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMenuItems(items: List<Pair<Int, ()->Unit>>) {
|
||||||
|
_menuItems?.removeAllViews();
|
||||||
|
|
||||||
|
val dp4 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4f, resources.displayMetrics).toInt();
|
||||||
|
val dp9 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 9f, resources.displayMetrics).toInt();
|
||||||
|
|
||||||
|
for(item in items) {
|
||||||
|
val compatImageItem = AppCompatImageView(requireContext());
|
||||||
|
compatImageItem.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
|
||||||
|
compatImageItem.setImageResource(item.first);
|
||||||
|
compatImageItem.setPadding(dp4, dp9, dp4, dp9);
|
||||||
|
compatImageItem.scaleType = ImageView.ScaleType.FIT_CENTER;
|
||||||
|
compatImageItem.setOnClickListener {
|
||||||
|
item.second.invoke();
|
||||||
|
};
|
||||||
|
|
||||||
|
_menuItems?.addView(compatImageItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = FilesTopBarFragment().apply { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -279,29 +279,52 @@ class StateApp {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun requestDirectoryAccess(activity: IWithResultLauncher, name: String, purpose: String? = null, path: Uri?, handle: (Uri?)->Unit)
|
fun requestDirectoryAccess(activity: IWithResultLauncher, name: String, purpose: String? = null, path: Uri?, handle: (Uri?)->Unit) {
|
||||||
|
return requestDirectoryAccess(activity, name, purpose, path, handle, false);
|
||||||
|
}
|
||||||
|
fun requestDirectoryAccess(activity: IWithResultLauncher, name: String, purpose: String? = null, path: Uri?, handle: (Uri?)->Unit, skipDialog: Boolean = false)
|
||||||
{
|
{
|
||||||
if(activity is Context)
|
if(activity is Context)
|
||||||
{
|
{
|
||||||
UIDialogs.showDialog(activity, R.drawable.ic_security, "Directory required for\n${name}", "Please select a directory for ${name}.\n${purpose}".trim(), null, 0,
|
if(skipDialog) {
|
||||||
UIDialogs.Action("Cancel", {}),
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||||
UIDialogs.Action("Ok", {
|
if(path != null)
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, path);
|
||||||
if(path != null)
|
intent.flags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, path);
|
.or(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
intent.flags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
.or(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||||
.or(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
.or(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||||
.or(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
||||||
.or(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
activity.launchForResult(intent, 99) {
|
||||||
|
if(it.resultCode == Activity.RESULT_OK) {
|
||||||
|
handle(it.data?.data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
UIDialogs.showDialogOk(context, R.drawable.ic_security_pred, "No access granted");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UIDialogs.showDialog(activity, R.drawable.ic_security, "Directory required for\n${name}", "Please select a directory for ${name}.\n${purpose}".trim(), null, 0,
|
||||||
|
UIDialogs.Action("Cancel", {}),
|
||||||
|
UIDialogs.Action("Ok", {
|
||||||
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||||
|
if(path != null)
|
||||||
|
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, path);
|
||||||
|
intent.flags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
.or(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
.or(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||||
|
.or(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||||
|
|
||||||
|
activity.launchForResult(intent, 99) {
|
||||||
|
if(it.resultCode == Activity.RESULT_OK) {
|
||||||
|
handle(it.data?.data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
UIDialogs.showDialogOk(context, R.drawable.ic_security_pred, "No access granted");
|
||||||
|
};
|
||||||
|
}, UIDialogs.ActionStyle.PRIMARY));
|
||||||
|
}
|
||||||
|
|
||||||
activity.launchForResult(intent, 99) {
|
|
||||||
if(it.resultCode == Activity.RESULT_OK) {
|
|
||||||
handle(it.data?.data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
UIDialogs.showDialogOk(context, R.drawable.ic_security_pred, "No access granted");
|
|
||||||
};
|
|
||||||
}, UIDialogs.ActionStyle.PRIMARY));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import com.futo.platformplayer.models.Playlist
|
|||||||
import com.futo.platformplayer.states.Album.Companion.TAG
|
import com.futo.platformplayer.states.Album.Companion.TAG
|
||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
import com.futo.platformplayer.stores.StringArrayStorage
|
import com.futo.platformplayer.stores.StringArrayStorage
|
||||||
|
import com.futo.platformplayer.toList
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
@@ -63,30 +64,50 @@ class StateLibrary {
|
|||||||
_files.remove(path);
|
_files.remove(path);
|
||||||
_files.save();
|
_files.save();
|
||||||
}
|
}
|
||||||
fun addFileDirectory(onAdded: ((entry: FileEntry) -> Unit)? = null): Boolean {
|
fun addFileDirectory(onAdded: ((entry: FileEntry) -> Unit)? = null, skipDialog: Boolean = false): Boolean {
|
||||||
if(!StateApp.instance.isMainActive)
|
if(!StateApp.instance.isMainActive)
|
||||||
return false;
|
return false;
|
||||||
val mainActivity = StateApp.instance.contextOrNull as MainActivity? ?: return false;
|
val mainActivity = StateApp.instance.contextOrNull as MainActivity? ?: return false;
|
||||||
|
|
||||||
StateApp.instance.requestDirectoryAccess(mainActivity, "Select Directory",
|
StateApp.instance.requestDirectoryAccess(mainActivity, "Select Directory",
|
||||||
"Select a directory you would like to make accessible to Grayjay", null, {
|
"Select a directory you would like to make accessible to Grayjay", null, {
|
||||||
if(it != null) {
|
if(it != null) {
|
||||||
mainActivity.contentResolver.takePersistableUriPermission(it, Intent.FLAG_GRANT_WRITE_URI_PERMISSION.or(Intent.FLAG_GRANT_READ_URI_PERMISSION));
|
mainActivity.contentResolver.takePersistableUriPermission(it, Intent.FLAG_GRANT_WRITE_URI_PERMISSION.or(Intent.FLAG_GRANT_READ_URI_PERMISSION));
|
||||||
try {
|
try {
|
||||||
val file = DocumentFile.fromTreeUri(mainActivity, it) ?: return@requestDirectoryAccess;
|
val file = DocumentFile.fromTreeUri(mainActivity, it) ?: return@requestDirectoryAccess;
|
||||||
val dir = FileEntry.fromFile(file);
|
val dir = FileEntry.fromFile(file);
|
||||||
_files.add(dir.path);
|
_files.add(dir.path);
|
||||||
_files.save();
|
_files.save();
|
||||||
onAdded?.invoke(dir);
|
onAdded?.invoke(dir);
|
||||||
|
}
|
||||||
|
catch(ex: Throwable) {
|
||||||
|
Logger.e(TAG, "Something went wrong converting requested directory", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(ex: Throwable) {
|
}, skipDialog);
|
||||||
Logger.e(TAG, "Something went wrong converting requested directory", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun searchTracks(str: String): List<IPlatformVideo> {
|
||||||
|
val resolver = StateApp.instance.contextOrNull?.contentResolver;
|
||||||
|
if(resolver == null) {
|
||||||
|
Logger.w(TAG, "Album contentResolver not found");
|
||||||
|
return listOf();
|
||||||
|
}
|
||||||
|
val cursor = resolver?.query(
|
||||||
|
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, StateLibrary.PROJECTION_MEDIA,
|
||||||
|
"LOWER(" + MediaStore.Audio.Media.DISPLAY_NAME + ") LIKE ? ", arrayOf(str.trim().lowercase()),
|
||||||
|
null) ?: return listOf();
|
||||||
|
cursor.moveToFirst();
|
||||||
|
val list = mutableListOf<IPlatformVideo>()
|
||||||
|
while(!cursor.isAfterLast) {
|
||||||
|
list.add(StateLibrary.audioFromCursor(cursor));
|
||||||
|
cursor.moveToNext();
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
fun getAlbums(): List<Album> {
|
fun getAlbums(): List<Album> {
|
||||||
return Album.getAlbums();
|
return Album.getAlbums();
|
||||||
}
|
}
|
||||||
@@ -96,6 +117,10 @@ class StateLibrary {
|
|||||||
return getAlbum(idLong);
|
return getAlbum(idLong);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
fun searchAlbums(str: String): List<Album> {
|
||||||
|
return Album.getAlbums("LOWER(" + MediaStore.Audio.Albums.ALBUM + ") LIKE ? ", arrayOf(str.trim().lowercase()));
|
||||||
|
}
|
||||||
|
|
||||||
fun getAlbum(id: Long): Album? {
|
fun getAlbum(id: Long): Album? {
|
||||||
return Album.getAlbum(id);
|
return Album.getAlbum(id);
|
||||||
}
|
}
|
||||||
@@ -109,6 +134,10 @@ class StateLibrary {
|
|||||||
return getArtist(idLong);
|
return getArtist(idLong);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
fun searchArtists(str: String): List<Artist> {
|
||||||
|
return Artist.getArtists(ArtistOrdering.TrackCount, "LOWER(" + MediaStore.Audio.Artists.ARTIST + ") LIKE ? ", arrayOf(str.trim().lowercase()));
|
||||||
|
}
|
||||||
|
|
||||||
fun getArtist(id: Long): Artist? {
|
fun getArtist(id: Long): Artist? {
|
||||||
return Artist.getArtist(id);
|
return Artist.getArtist(id);
|
||||||
}
|
}
|
||||||
@@ -401,6 +430,10 @@ class Artist {
|
|||||||
return Album.getArtistAlbums(id.toLongOrNull() ?: return listOf());
|
return Album.getArtistAlbums(id.toLongOrNull() ?: return listOf());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toPlaylist(tracks: List<IPlatformVideo>? = null): Playlist {
|
||||||
|
return Playlist(name, tracks?.map { SerializedPlatformVideo.fromVideo(it) } ?: getAudioTracks().toList().filter { it is IPlatformVideo }.map { SerializedPlatformVideo.fromVideo(it as IPlatformVideo) })
|
||||||
|
}
|
||||||
|
|
||||||
fun getAudioTracks(): IPager<IPlatformContent> {
|
fun getAudioTracks(): IPager<IPlatformContent> {
|
||||||
val idLong = id.toLongOrNull() ?: return EmptyPager();
|
val idLong = id.toLongOrNull() ?: return EmptyPager();
|
||||||
return AdhocPager({ listOf() }, getTracksPager(idLong));
|
return AdhocPager({ listOf() }, getTracksPager(idLong));
|
||||||
@@ -441,7 +474,7 @@ class Artist {
|
|||||||
return null;
|
return null;
|
||||||
return Artist.fromCursor(cursor);
|
return Artist.fromCursor(cursor);
|
||||||
}
|
}
|
||||||
fun getArtists(ordering: ArtistOrdering = ArtistOrdering.Alphabethic): List<Artist> {
|
fun getArtists(ordering: ArtistOrdering = ArtistOrdering.Alphabethic, query: String? = null, args: Array<String>? = null): List<Artist> {
|
||||||
val ordering = when(ordering) {
|
val ordering = when(ordering) {
|
||||||
ArtistOrdering.Alphabethic -> Artists.ARTIST + " ASC";
|
ArtistOrdering.Alphabethic -> Artists.ARTIST + " ASC";
|
||||||
ArtistOrdering.AlbumCount -> Artists.NUMBER_OF_ALBUMS + " DESC";
|
ArtistOrdering.AlbumCount -> Artists.NUMBER_OF_ALBUMS + " DESC";
|
||||||
@@ -450,8 +483,8 @@ class Artist {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val cursor = StateApp.instance.contextOrNull?.contentResolver?.query(Artists.EXTERNAL_CONTENT_URI, PROJECTION,
|
val cursor = StateApp.instance.contextOrNull?.contentResolver?.query(Artists.EXTERNAL_CONTENT_URI, PROJECTION,
|
||||||
null,
|
query,
|
||||||
null,
|
args,
|
||||||
ordering) ?: return listOf();
|
ordering) ?: return listOf();
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
val list = mutableListOf<Artist>()
|
val list = mutableListOf<Artist>()
|
||||||
@@ -557,14 +590,14 @@ class Album {
|
|||||||
return null;
|
return null;
|
||||||
return fromCursor(cursor);
|
return fromCursor(cursor);
|
||||||
}
|
}
|
||||||
fun getAlbums(): List<Album> {
|
fun getAlbums(query: String? = null, args: Array<String>? = null): List<Album> {
|
||||||
val resolver = StateApp.instance.contextOrNull?.contentResolver;
|
val resolver = StateApp.instance.contextOrNull?.contentResolver;
|
||||||
if(resolver == null) {
|
if(resolver == null) {
|
||||||
Logger.w(TAG, "Album contentResolver not found");
|
Logger.w(TAG, "Album contentResolver not found");
|
||||||
return listOf();
|
return listOf();
|
||||||
}
|
}
|
||||||
val cursor = resolver?.query(
|
val cursor = resolver?.query(
|
||||||
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, PROJECTION, null, null,
|
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, PROJECTION, query, args,
|
||||||
MediaStore.Audio.Albums.ALBUM + " ASC") ?: return listOf();
|
MediaStore.Audio.Albums.ALBUM + " ASC") ?: return listOf();
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
val list = mutableListOf<Album>()
|
val list = mutableListOf<Album>()
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import android.widget.TextView
|
|||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
import com.futo.platformplayer.models.ImageVariable
|
||||||
|
import com.futo.platformplayer.views.others.ToggleTagView
|
||||||
|
|
||||||
class PillV2: FrameLayout {
|
class PillV2: FrameLayout {
|
||||||
|
|
||||||
@@ -17,6 +19,26 @@ class PillV2: FrameLayout {
|
|||||||
|
|
||||||
val onClick = Event1<Boolean>();
|
val onClick = Event1<Boolean>();
|
||||||
|
|
||||||
|
constructor(context: Context, name: String, isActive: Boolean = false, action: (PillV2, Boolean)->Unit, actionLong: ((PillV2, Boolean)->Unit)? = null): super(context) {
|
||||||
|
inflate(context, R.layout.view_tag_v2, this);
|
||||||
|
root = findViewById(R.id.root);
|
||||||
|
text = findViewById(R.id.text_tag);
|
||||||
|
|
||||||
|
text.text = name;
|
||||||
|
setIsEnabled(isActive);
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
setIsEnabled(!isToggled);
|
||||||
|
onClick.emit(isToggled);
|
||||||
|
action(this, isToggled);
|
||||||
|
}
|
||||||
|
if(actionLong != null)
|
||||||
|
setOnLongClickListener {
|
||||||
|
actionLong(this, this.isToggled);
|
||||||
|
return@setOnLongClickListener true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor(context: Context, attr: AttributeSet? = null) : super(context, attr) {
|
constructor(context: Context, attr: AttributeSet? = null) : super(context, attr) {
|
||||||
inflate(context, R.layout.view_tag_v2, this);
|
inflate(context, R.layout.view_tag_v2, this);
|
||||||
root = findViewById(R.id.root);
|
root = findViewById(R.id.root);
|
||||||
|
|||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package com.futo.platformplayer.views.adapters.viewholders
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
import com.futo.platformplayer.states.FileEntry
|
||||||
|
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||||
|
|
||||||
|
|
||||||
|
class FileViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<FileEntry>(
|
||||||
|
LayoutInflater.from(_viewGroup.context).inflate(
|
||||||
|
R.layout.list_file,
|
||||||
|
_viewGroup, false)) {
|
||||||
|
|
||||||
|
val onClick = Event1<FileEntry?>();
|
||||||
|
val onDelete = Event1<FileEntry?>();
|
||||||
|
|
||||||
|
protected var _file: FileEntry? = null;
|
||||||
|
protected val _imageThumbnail: ImageView
|
||||||
|
protected val _buttonDelete: ImageButton;
|
||||||
|
protected val _textName: TextView
|
||||||
|
//protected val _textMetadata: TextView
|
||||||
|
|
||||||
|
init {
|
||||||
|
_imageThumbnail = _view.findViewById(R.id.image_thumbnail);
|
||||||
|
_textName = _view.findViewById(R.id.text_name);
|
||||||
|
//_textMetadata = _view.findViewById(R.id.text_metadata);
|
||||||
|
_buttonDelete = _view.findViewById(R.id.button_delete);
|
||||||
|
|
||||||
|
_view.setOnClickListener { onClick.emit(_file) };
|
||||||
|
_buttonDelete.setOnClickListener { onDelete.emit(_file) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun bind(file: FileEntry) {
|
||||||
|
_file = file;
|
||||||
|
_imageThumbnail?.let {
|
||||||
|
if(file.isDirectory)
|
||||||
|
it.setImageResource(R.drawable.ic_library);
|
||||||
|
else {
|
||||||
|
Glide.with(it)
|
||||||
|
.load(file.thumbnail)
|
||||||
|
.placeholder(R.drawable.ic_music)
|
||||||
|
.into(it)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_buttonDelete.isVisible = file.removable;
|
||||||
|
|
||||||
|
_textName.text = file.name;
|
||||||
|
//if(file.isDirectory)
|
||||||
|
// _textMetadata.text = "Directory";
|
||||||
|
//else
|
||||||
|
// _textMetadata.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M450,780L450,294.92L222.15,522.77L180,480L480,180L780,480L737.85,522.77L510,294.92L510,780L450,780Z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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="match_parent">
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:id="@+id/channel_coordinator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/black">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/app_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="170dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
app:elevation="0dp">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
|
android:id="@+id/toolbar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:contentScrim="@color/transparent"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed"
|
||||||
|
app:toolbarId="@+id/toolbar">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginBottom="40dp">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image_channel_banner"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/channel_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:background="@color/overlay">
|
||||||
|
<com.futo.platformplayer.views.others.CreatorThumbnail
|
||||||
|
android:id="@+id/creator_thumbnail"
|
||||||
|
android:background="@drawable/rounded_outline"
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="35dp"
|
||||||
|
android:contentDescription="@string/cd_creator_thumbnail"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_channel_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_regular"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
tools:text="CHANNEL NAME"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/creator_thumbnail"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/text_metadata"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/button_sub_settings" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_metadata"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14dp"
|
||||||
|
android:textColor="@color/gray_ac"
|
||||||
|
android:fontFamily="@font/inter_light"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
tools:text="17 videos"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/creator_thumbnail"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/button_sub_settings"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_sub_settings"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:contentDescription="@string/cd_button_settings"
|
||||||
|
android:layout_marginTop="3dp"
|
||||||
|
android:layout_marginRight="10dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/button_subscribe"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/button_subscribe"
|
||||||
|
android:src="@drawable/ic_settings" />
|
||||||
|
|
||||||
|
<com.futo.platformplayer.views.subscriptions.SubscribeButton
|
||||||
|
android:id="@+id/button_subscribe"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/cd_button_subscribe"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:minHeight="0dp"
|
||||||
|
app:contentInsetStart="0dp"
|
||||||
|
app:contentInsetEnd="0dp"
|
||||||
|
app:layout_collapseMode="pin"
|
||||||
|
android:layout_gravity="bottom">
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tabs"
|
||||||
|
app:tabMode="scrollable"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:tabSelectedTextColor="@color/white"
|
||||||
|
app:tabTextColor="@color/gray_8c"
|
||||||
|
android:background="@drawable/tab_border"
|
||||||
|
app:tabIndicatorColor="@color/white"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:textColor="@color/gray_8c"
|
||||||
|
android:fontFamily="@font/inter_medium"
|
||||||
|
app:tabTextAppearance="@style/Theme.FutoVideo.TextAppearance.TabLayout" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
android:id="@+id/view_pager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/channel_loading_overlay"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:background="#77000000"
|
||||||
|
android:gravity="center">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/channel_loader_frag"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
app:srcCompat="@drawable/ic_loader_animated"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:alpha="0.7"
|
||||||
|
android:contentDescription="@string/loading" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/overlay_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
android:id="@+id/feed_root"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout 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="47dp"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_back"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:contentDescription="@string/cd_button_back"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
app:srcCompat="@drawable/ic_back_nav" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_extra_light"
|
||||||
|
tools:text="FUTO"
|
||||||
|
android:maxLines="2" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/menu_buttons"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:fitsSystemWindows="false"
|
||||||
|
android:background="@drawable/bottom_menu_border"
|
||||||
|
android:id="@+id/root"
|
||||||
|
android:clickable="true">
|
||||||
|
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/container_tags"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="7dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<com.futo.platformplayer.views.PillV2
|
||||||
|
android:id="@+id/pill_songs"
|
||||||
|
app:pillV2Text="Songs"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<com.futo.platformplayer.views.PillV2
|
||||||
|
android:id="@+id/pill_artist"
|
||||||
|
app:pillV2Text="Artist"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<com.futo.platformplayer.views.PillV2
|
||||||
|
android:id="@+id/pill_albums"
|
||||||
|
app:pillV2Text="Albums"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_metadata"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="#D0D0D0"
|
||||||
|
android:text="0 artists"
|
||||||
|
app:layout_constraintLeft_toLeftOf="@id/container_tags"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/container_tags"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/text_metadata"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -4,41 +4,58 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="68dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
android:layout_marginBottom="5dp"
|
android:layout_marginBottom="5dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginRight="10dp"
|
||||||
|
android:background="@drawable/background_16_round_4dp"
|
||||||
android:id="@+id/root"
|
android:id="@+id/root"
|
||||||
android:clickable="true">
|
android:clickable="true">
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
<LinearLayout
|
||||||
android:id="@+id/image_thumbnail"
|
android:id="@+id/image_thumbnail_container"
|
||||||
android:layout_height="50dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="50dp"
|
android:layout_height="match_parent"
|
||||||
android:scaleType="centerCrop"
|
android:layout_marginTop="8dp"
|
||||||
app:shapeAppearanceOverlay="@style/roundedCorners_4dp"
|
android:layout_marginBottom="8dp"
|
||||||
app:srcCompat="@drawable/placeholder_video_thumbnail"
|
android:layout_marginLeft="8dp"
|
||||||
android:background="@drawable/video_thumbnail_outline"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent" />
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
android:background="@drawable/background_1b_round_6dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_thumbnail"
|
||||||
|
android:alpha="0.4"
|
||||||
|
android:layout_height="34dp"
|
||||||
|
android:layout_width="34dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_name"
|
android:id="@+id/text_name"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:textSize="13dp"
|
android:textSize="15dp"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:fontFamily="@font/inter_light"
|
android:fontFamily="@font/inter_regular"
|
||||||
tools:text="Legendary grant recipient: Marvin Wißfeld Very Long Title That is Long"
|
tools:text="Legendary grant recipient: Marvin Wißfeld Very Long Title That is Long"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
app:layout_constraintLeft_toRightOf="@id/image_thumbnail"
|
app:layout_constraintLeft_toRightOf="@id/image_thumbnail_container"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintRight_toLeftOf="@id/button_trash"
|
app:layout_constraintRight_toLeftOf="@id/button_delete"
|
||||||
app:layout_constraintBottom_toTopOf="@id/text_metadata"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:layout_marginStart="10dp" />
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_metadata"
|
android:id="@+id/text_metadata"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@@ -53,7 +70,7 @@
|
|||||||
app:layout_constraintLeft_toRightOf="@id/image_thumbnail"
|
app:layout_constraintLeft_toRightOf="@id/image_thumbnail"
|
||||||
app:layout_constraintRight_toLeftOf="@id/button_delete"
|
app:layout_constraintRight_toLeftOf="@id/button_delete"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:layout_marginStart="10dp" />
|
android:layout_marginStart="12dp" />-->
|
||||||
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:layout_marginLeft="10dp"
|
android:layout_marginLeft="10dp"
|
||||||
android:textSize="20sp"
|
android:textSize="17sp"
|
||||||
android:text="Albums" />
|
android:text="Albums" />
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/image_nav"
|
android:id="@+id/image_nav"
|
||||||
|
|||||||
Reference in New Issue
Block a user