Library search support

This commit is contained in:
Kelvin
2025-11-11 23:16:16 +01:00
parent d5a696289b
commit bae8cb7bc4
11 changed files with 87 additions and 31 deletions
@@ -63,6 +63,7 @@ import com.futo.platformplayer.fragment.mainactivity.main.LibraryArtistFragment
import com.futo.platformplayer.fragment.mainactivity.main.LibraryArtistsFragment import com.futo.platformplayer.fragment.mainactivity.main.LibraryArtistsFragment
import com.futo.platformplayer.fragment.mainactivity.main.LibraryFilesFragment import com.futo.platformplayer.fragment.mainactivity.main.LibraryFilesFragment
import com.futo.platformplayer.fragment.mainactivity.main.LibraryFragment import com.futo.platformplayer.fragment.mainactivity.main.LibraryFragment
import com.futo.platformplayer.fragment.mainactivity.main.LibrarySearchFragment
import com.futo.platformplayer.fragment.mainactivity.main.LibraryVideosFragment import com.futo.platformplayer.fragment.mainactivity.main.LibraryVideosFragment
import com.futo.platformplayer.fragment.mainactivity.main.MainFragment import com.futo.platformplayer.fragment.mainactivity.main.MainFragment
import com.futo.platformplayer.fragment.mainactivity.main.PlaylistFragment import com.futo.platformplayer.fragment.mainactivity.main.PlaylistFragment
@@ -194,6 +195,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
lateinit var _fragLibraryArtists: LibraryArtistsFragment; lateinit var _fragLibraryArtists: LibraryArtistsFragment;
lateinit var _fragLibraryArtist: LibraryArtistFragment; lateinit var _fragLibraryArtist: LibraryArtistFragment;
lateinit var _fragLibraryVideos: LibraryVideosFragment; lateinit var _fragLibraryVideos: LibraryVideosFragment;
lateinit var _fragLibrarySearch: LibrarySearchFragment;
lateinit var _fragLibraryFiles: LibraryFilesFragment; lateinit var _fragLibraryFiles: LibraryFilesFragment;
lateinit var _fragBrowser: BrowserFragment; lateinit var _fragBrowser: BrowserFragment;
@@ -374,6 +376,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
_fragLibraryArtist = LibraryArtistFragment.newInstance(); _fragLibraryArtist = LibraryArtistFragment.newInstance();
_fragLibraryVideos = LibraryVideosFragment.newInstance(); _fragLibraryVideos = LibraryVideosFragment.newInstance();
_fragLibraryFiles = LibraryFilesFragment.newInstance(); _fragLibraryFiles = LibraryFilesFragment.newInstance();
_fragLibrarySearch = LibrarySearchFragment.newInstance();
_fragBrowser = BrowserFragment.newInstance(); _fragBrowser = BrowserFragment.newInstance();
@@ -512,6 +515,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
_fragLibraryArtist.topBar = _fragTopBarNavigation; _fragLibraryArtist.topBar = _fragTopBarNavigation;
_fragLibraryVideos.topBar = _fragTopBarNavigation; _fragLibraryVideos.topBar = _fragTopBarNavigation;
_fragLibraryFiles.topBar = _fragTopBarFiles; _fragLibraryFiles.topBar = _fragTopBarFiles;
_fragLibrarySearch.topBar = _fragTopBarSearch;
_fragBrowser.topBar = _fragTopBarNavigation; _fragBrowser.topBar = _fragTopBarNavigation;
@@ -1323,6 +1327,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
LibraryArtistFragment::class -> _fragLibraryArtist as T; LibraryArtistFragment::class -> _fragLibraryArtist as T;
LibraryVideosFragment::class -> _fragLibraryVideos as T; LibraryVideosFragment::class -> _fragLibraryVideos as T;
LibraryFilesFragment::class -> _fragLibraryFiles as T; LibraryFilesFragment::class -> _fragLibraryFiles as T;
LibrarySearchFragment::class -> _fragLibrarySearch as T;
else -> throw IllegalArgumentException("Fragment type ${T::class.java.name} is not available in MainActivity"); else -> throw IllegalArgumentException("Fragment type ${T::class.java.name} is not available in MainActivity");
} }
} }
@@ -120,8 +120,8 @@ class LibraryAlbumFragment : MainFragment() {
holder.onClick.subscribe { c -> holder.onClick.subscribe { c ->
val playlist = _album?.toPlaylist(_tracks); val playlist = _album?.toPlaylist(_tracks);
val index = playlist?.videos?.indexOfFirst { it.name == c.name } ?: -1;
if (playlist != null) { if (playlist != null) {
val index = playlist.videos.indexOf(c);
if (index == -1) if (index == -1)
return@subscribe; return@subscribe;
@@ -160,7 +160,7 @@ class LibraryFragment : MainFragment() {
this.fragment = fragment; this.fragment = fragment;
recycler = findViewById(R.id.recycler); recycler = findViewById(R.id.recycler);
sectionArtists = LibrarySection(context)//findViewById<LibrarySection>(R.id.section_artists); sectionArtists = LibrarySection(context)//findViewById<LibrarySection>(R.id.section_artists);
sectionArtists.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 155.dp(resources)).apply { sectionArtists.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 140.dp(resources)).apply {
this.setMargins(0,10.dp(resources), 0, 0); this.setMargins(0,10.dp(resources), 0, 0);
} }
sectionAlbums = LibrarySection(context)//findViewById(R.id.section_albums); sectionAlbums = LibrarySection(context)//findViewById(R.id.section_albums);
@@ -106,16 +106,18 @@ class LibrarySearchFragment : MainFragment() {
val pillSongs: PillV2; val pillSongs: PillV2;
val pills: List<PillV2>; val pills: List<PillV2>;
val textMetadata: TextView;
val recycler: RecyclerView; val recycler: RecyclerView;
val adapterArtists: AnyAdapterView<Artist, LibraryArtistsFragment.ArtistViewHolder>; val adapterArtists: AnyAdapterView<Artist, LibraryArtistsFragment.ArtistViewHolder>;
val adapterSongs: AnyAdapterView<IPlatformContent, TrackViewHolder>; val adapterSongs: AnyAdapterView<IPlatformContent, TrackViewHolder>;
val adapterAlbums: AnyAdapterView<Album, AlbumTileViewHolder>; val adapterAlbums: AnyAdapterView<Album, LibraryAlbumsFragment.AlbumViewHolder>;
constructor(fragment: LibrarySearchFragment) : super(fragment.requireContext()) { constructor(fragment: LibrarySearchFragment) : super(fragment.requireContext()) {
inflate(context, R.layout.fragview_library, this); inflate(context, R.layout.fragview_library_search, this);
this.fragment = fragment; this.fragment = fragment;
pillArtist = findViewById(R.id.pill_artist); pillArtist = findViewById(R.id.pill_artist);
@@ -123,6 +125,8 @@ class LibrarySearchFragment : MainFragment() {
pillSongs = findViewById(R.id.pill_songs); pillSongs = findViewById(R.id.pill_songs);
pills = listOf(pillArtist, pillAlbums, pillSongs); pills = listOf(pillArtist, pillAlbums, pillSongs);
textMetadata = findViewById(R.id.text_metadata);
pillArtist.onClick.subscribe { pillArtist.onClick.subscribe {
pills.forEach { it.setIsEnabled(false) }; pills.forEach { it.setIsEnabled(false) };
pillArtist.setIsEnabled(true); pillArtist.setIsEnabled(true);
@@ -146,7 +150,7 @@ class LibrarySearchFragment : MainFragment() {
fragment.navigate<LibraryArtistFragment>(it); fragment.navigate<LibraryArtistFragment>(it);
} }
}); });
adapterAlbums = recycler.asAny<Album, AlbumTileViewHolder>(RecyclerView.VERTICAL, false, { adapterAlbums = recycler.asAny<Album, LibraryAlbumsFragment.AlbumViewHolder>(RecyclerView.VERTICAL, false, {
it.onClick.subscribe { it.onClick.subscribe {
if(it != null) if(it != null)
fragment.navigate<LibraryAlbumFragment>(it); fragment.navigate<LibraryAlbumFragment>(it);
@@ -173,38 +177,57 @@ class LibrarySearchFragment : MainFragment() {
fun loadArtists(){ fun loadArtists(){
recycler.adapter = adapterArtists.adapter.adapter; recycler.adapter = adapterArtists.adapter.adapter;
fragment.topBar?.let {
if(it is SearchTopBarFragment)
search(it.getSearchText());
}
} }
fun loadAlbums() { fun loadAlbums() {
recycler.adapter = adapterAlbums.adapter.adapter; recycler.adapter = adapterAlbums.adapter.adapter;
fragment.topBar?.let {
if(it is SearchTopBarFragment)
search(it.getSearchText());
}
} }
fun loadSongs() { fun loadSongs() {
recycler.adapter = adapterSongs.adapter.adapter; recycler.adapter = adapterSongs.adapter.adapter;
fragment.topBar?.let {
if(it is SearchTopBarFragment)
search(it.getSearchText());
}
} }
fun search(str: String) { fun search(str: String) {
if(recycler.adapter == adapterArtists.adapter.adapter) { if(recycler.adapter == adapterArtists.adapter.adapter) {
if(str.isNullOrBlank()) val data = if(!str.isNullOrBlank())
adapterArtists.adapter.setData(listOf()); StateLibrary.instance.searchArtists(str)
else else listOf();
adapterArtists.setData(StateLibrary.instance.searchArtists(str)); adapterArtists.setData(data);
textMetadata.text = "${data.size} artists";
} }
else if(recycler.adapter == adapterAlbums.adapter.adapter) { else if(recycler.adapter == adapterAlbums.adapter.adapter) {
if(str.isNullOrBlank()) val data = if(!str.isNullOrBlank())
adapterAlbums.adapter.setData(listOf()); StateLibrary.instance.searchAlbums(str)
else else listOf();
adapterAlbums.setData(StateLibrary.instance.searchAlbums(str)); adapterAlbums.setData(data);
textMetadata.text = "${data.size} albums";
} }
else if(recycler.adapter == adapterSongs.adapter.adapter) { else if(recycler.adapter == adapterSongs.adapter.adapter) {
if(str.isNullOrBlank()) val data = if(!str.isNullOrBlank())
adapterSongs.adapter.setData(listOf()); StateLibrary.instance.searchTracks(str)
else else listOf();
adapterSongs.setData(StateLibrary.instance.searchTracks(str));
adapterSongs.setData(data);
textMetadata.text = "${data.size} songs";
} }
} }
fun onShown() { fun onShown() {
fragment.topBar?.let {
if(it is SearchTopBarFragment)
it.focus();
}
} }
} }
} }
@@ -9,6 +9,8 @@ import android.widget.ImageView
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.fragment.mainactivity.main.CreatorsFragment import com.futo.platformplayer.fragment.mainactivity.main.CreatorsFragment
import com.futo.platformplayer.fragment.mainactivity.main.LibraryFragment
import com.futo.platformplayer.fragment.mainactivity.main.LibrarySearchFragment
import com.futo.platformplayer.fragment.mainactivity.main.PlaylistFragment import com.futo.platformplayer.fragment.mainactivity.main.PlaylistFragment
import com.futo.platformplayer.fragment.mainactivity.main.PlaylistsFragment import com.futo.platformplayer.fragment.mainactivity.main.PlaylistsFragment
import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragment import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragment
@@ -46,6 +48,8 @@ class GeneralTopBarFragment : TopFragment() {
navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.CREATOR)); navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.CREATOR));
} else if (currentMain is PlaylistsFragment || currentMain is PlaylistFragment) { } else if (currentMain is PlaylistsFragment || currentMain is PlaylistFragment) {
navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.PLAYLIST)); navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.PLAYLIST));
} else if (currentMain is LibraryFragment) {
navigate<LibrarySearchFragment>();
} else { } else {
navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.VIDEO)); navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.VIDEO));
} }
@@ -18,6 +18,7 @@ import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.fragment.mainactivity.main.LibrarySearchFragment
import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragment import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragment
import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragmentData import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragmentData
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
@@ -112,7 +113,10 @@ class SearchTopBarFragment : TopFragment() {
} }
fun clear() { fun clear() {
_editSearch?.text?.clear(); _editSearch?.text?.clear();
if (currentMain !is SuggestionsFragment) { if(currentMain is LibrarySearchFragment) {
onSearch.emit("");
}
else if (currentMain !is SuggestionsFragment) {
navigate<SuggestionsFragment>(SuggestionsFragmentData("", _searchType), false); navigate<SuggestionsFragment>(SuggestionsFragmentData("", _searchType), false);
} else { } else {
onSearch.emit(""); onSearch.emit("");
@@ -190,6 +194,12 @@ class SearchTopBarFragment : TopFragment() {
_buttonFilter?.visibility = if (visible) View.VISIBLE else View.GONE; _buttonFilter?.visibility = if (visible) View.VISIBLE else View.GONE;
} }
fun getSearchText(): String {
return _editSearch?.let {
it.text.toString();
} ?: "";
}
private fun onDone() { private fun onDone() {
val editSearch = _editSearch val editSearch = _editSearch
if (editSearch != null) { if (editSearch != null) {
@@ -90,6 +90,8 @@ class StateLibrary {
fun searchTracks(str: String): List<IPlatformVideo> { fun searchTracks(str: String): List<IPlatformVideo> {
if(str.isNullOrBlank())
return listOf();
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");
@@ -97,7 +99,7 @@ class StateLibrary {
} }
val cursor = resolver?.query( val cursor = resolver?.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, StateLibrary.PROJECTION_MEDIA, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, StateLibrary.PROJECTION_MEDIA,
"LOWER(" + MediaStore.Audio.Media.DISPLAY_NAME + ") LIKE ? ", arrayOf(str.trim().lowercase()), "LOWER(" + MediaStore.Audio.Media.DISPLAY_NAME + ") LIKE ? ", arrayOf("%" + str.trim().lowercase() + "%"),
null) ?: return listOf(); null) ?: return listOf();
cursor.moveToFirst(); cursor.moveToFirst();
val list = mutableListOf<IPlatformVideo>() val list = mutableListOf<IPlatformVideo>()
@@ -118,7 +120,9 @@ class StateLibrary {
return null; return null;
} }
fun searchAlbums(str: String): List<Album> { fun searchAlbums(str: String): List<Album> {
return Album.getAlbums("LOWER(" + MediaStore.Audio.Albums.ALBUM + ") LIKE ? ", arrayOf(str.trim().lowercase())); if(str.isNullOrBlank())
return listOf();
return Album.getAlbums("LOWER(" + MediaStore.Audio.Albums.ALBUM + ") LIKE ? ", arrayOf("%" + str.trim().lowercase() + "%"));
} }
fun getAlbum(id: Long): Album? { fun getAlbum(id: Long): Album? {
@@ -135,7 +139,9 @@ class StateLibrary {
return null; return null;
} }
fun searchArtists(str: String): List<Artist> { fun searchArtists(str: String): List<Artist> {
return Artist.getArtists(ArtistOrdering.TrackCount, "LOWER(" + MediaStore.Audio.Artists.ARTIST + ") LIKE ? ", arrayOf(str.trim().lowercase())); if(str.isNullOrBlank())
return listOf();
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? {
@@ -50,7 +50,9 @@
android:id="@+id/text_metadata" android:id="@+id/text_metadata"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_margin="3dp" android:layout_marginLeft="15dp"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:textSize="13sp" android:textSize="13sp"
android:textColor="#D0D0D0" android:textColor="#D0D0D0"
+4 -2
View File
@@ -6,6 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:layout_marginBottom="10dp"
android:id="@+id/root" android:id="@+id/root"
android:clickable="true"> android:clickable="true">
@@ -17,6 +18,7 @@
app:shapeAppearanceOverlay="@style/roundedCorners_4dp" app:shapeAppearanceOverlay="@style/roundedCorners_4dp"
app:srcCompat="@drawable/placeholder_video_thumbnail" app:srcCompat="@drawable/placeholder_video_thumbnail"
android:background="@drawable/video_thumbnail_outline" android:background="@drawable/video_thumbnail_outline"
android:layout_marginLeft="10dp"
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" />
@@ -36,7 +38,7 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_trash" app:layout_constraintRight_toLeftOf="@id/button_trash"
app:layout_constraintBottom_toTopOf="@id/text_metadata" app:layout_constraintBottom_toTopOf="@id/text_metadata"
android:layout_marginStart="10dp" /> android:layout_marginStart="15dp" />
<TextView <TextView
android:id="@+id/text_metadata" android:id="@+id/text_metadata"
@@ -53,7 +55,7 @@
app:layout_constraintLeft_toRightOf="@id/image_thumbnail" app:layout_constraintLeft_toRightOf="@id/image_thumbnail"
app:layout_constraintRight_toLeftOf="@id/button_trash" app:layout_constraintRight_toLeftOf="@id/button_trash"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="10dp" /> android:layout_marginStart="15dp" />
<!-- <!--
<ImageButton <ImageButton
+6 -6
View File
@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
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="86dp" android:layout_width="71dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
@@ -14,12 +14,12 @@
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_thumbnail" android:id="@+id/image_thumbnail"
android:layout_height="66dp" android:layout_height="50dp"
android:layout_width="66dp" android:layout_width="50dp"
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/roundedCorners_33dp" app:shapeAppearanceOverlay="@style/roundedCorners_25dp"
app:srcCompat="@drawable/unknown_music" app:srcCompat="@drawable/unknown_music"
android:background="@drawable/video_thumbnail_outline" android:background="@drawable/video_thumbnail_outline"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@@ -29,7 +29,7 @@
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:textSize="14sp" android:textSize="12sp"
android:textColor="@color/white" android:textColor="@color/white"
android:fontFamily="@font/inter_medium" android:fontFamily="@font/inter_medium"
android:textAlignment="center" android:textAlignment="center"
@@ -45,7 +45,7 @@
android:id="@+id/text_metadata" android:id="@+id/text_metadata"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="12sp" android:textSize="10sp"
android:textColor="#75FFFFFF" android:textColor="#75FFFFFF"
android:fontFamily="@font/inter_regular" android:fontFamily="@font/inter_regular"
android:textAlignment="center" android:textAlignment="center"
+4
View File
@@ -31,6 +31,10 @@
<item name="cornerFamily">rounded</item> <item name="cornerFamily">rounded</item>
<item name="cornerSize">33dp</item> <item name="cornerSize">33dp</item>
</style> </style>
<style name="roundedCorners_25dp" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">25dp</item>
</style>
<style name="roundedCorners_40dp" parent=""> <style name="roundedCorners_40dp" parent="">
<item name="cornerFamily">rounded</item> <item name="cornerFamily">rounded</item>
<item name="cornerSize">40dp</item> <item name="cornerSize">40dp</item>