From bae8cb7bc4e142cbb43bce2e5bc3af56d3a38017 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Tue, 11 Nov 2025 23:16:16 +0100 Subject: [PATCH] Library search support --- .../platformplayer/activities/MainActivity.kt | 5 ++ .../mainactivity/main/LibraryAlbumFragment.kt | 2 +- .../mainactivity/main/LibraryFragment.kt | 2 +- .../main/LibrarySearchFragment.kt | 55 +++++++++++++------ .../topbar/GeneralTopBarFragment.kt | 4 ++ .../topbar/SearchTopBarFragment.kt | 12 +++- .../platformplayer/states/StateLibrary.kt | 12 +++- .../res/layout/fragview_library_search.xml | 4 +- app/src/main/res/layout/list_album.xml | 6 +- app/src/main/res/layout/list_artist_tile.xml | 12 ++-- app/src/main/res/values/styles.xml | 4 ++ 11 files changed, 87 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt index aed666ea..6d0ce90b 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt @@ -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.LibraryFilesFragment 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.MainFragment import com.futo.platformplayer.fragment.mainactivity.main.PlaylistFragment @@ -194,6 +195,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { lateinit var _fragLibraryArtists: LibraryArtistsFragment; lateinit var _fragLibraryArtist: LibraryArtistFragment; lateinit var _fragLibraryVideos: LibraryVideosFragment; + lateinit var _fragLibrarySearch: LibrarySearchFragment; lateinit var _fragLibraryFiles: LibraryFilesFragment; lateinit var _fragBrowser: BrowserFragment; @@ -374,6 +376,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { _fragLibraryArtist = LibraryArtistFragment.newInstance(); _fragLibraryVideos = LibraryVideosFragment.newInstance(); _fragLibraryFiles = LibraryFilesFragment.newInstance(); + _fragLibrarySearch = LibrarySearchFragment.newInstance(); _fragBrowser = BrowserFragment.newInstance(); @@ -512,6 +515,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { _fragLibraryArtist.topBar = _fragTopBarNavigation; _fragLibraryVideos.topBar = _fragTopBarNavigation; _fragLibraryFiles.topBar = _fragTopBarFiles; + _fragLibrarySearch.topBar = _fragTopBarSearch; _fragBrowser.topBar = _fragTopBarNavigation; @@ -1323,6 +1327,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { LibraryArtistFragment::class -> _fragLibraryArtist as T; LibraryVideosFragment::class -> _fragLibraryVideos 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"); } } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryAlbumFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryAlbumFragment.kt index 830e3d08..34962c83 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryAlbumFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryAlbumFragment.kt @@ -120,8 +120,8 @@ class LibraryAlbumFragment : MainFragment() { holder.onClick.subscribe { c -> val playlist = _album?.toPlaylist(_tracks); + val index = playlist?.videos?.indexOfFirst { it.name == c.name } ?: -1; if (playlist != null) { - val index = playlist.videos.indexOf(c); if (index == -1) return@subscribe; diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryFragment.kt index 18723f96..a28f32c9 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibraryFragment.kt @@ -160,7 +160,7 @@ class LibraryFragment : MainFragment() { this.fragment = fragment; recycler = findViewById(R.id.recycler); sectionArtists = LibrarySection(context)//findViewById(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); } sectionAlbums = LibrarySection(context)//findViewById(R.id.section_albums); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibrarySearchFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibrarySearchFragment.kt index d50c6ce9..51fefc3d 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibrarySearchFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/LibrarySearchFragment.kt @@ -106,16 +106,18 @@ class LibrarySearchFragment : MainFragment() { val pillSongs: PillV2; val pills: List; + val textMetadata: TextView; + val recycler: RecyclerView; val adapterArtists: AnyAdapterView; val adapterSongs: AnyAdapterView; - val adapterAlbums: AnyAdapterView; + val adapterAlbums: AnyAdapterView; constructor(fragment: LibrarySearchFragment) : super(fragment.requireContext()) { - inflate(context, R.layout.fragview_library, this); + inflate(context, R.layout.fragview_library_search, this); this.fragment = fragment; pillArtist = findViewById(R.id.pill_artist); @@ -123,6 +125,8 @@ class LibrarySearchFragment : MainFragment() { pillSongs = findViewById(R.id.pill_songs); pills = listOf(pillArtist, pillAlbums, pillSongs); + textMetadata = findViewById(R.id.text_metadata); + pillArtist.onClick.subscribe { pills.forEach { it.setIsEnabled(false) }; pillArtist.setIsEnabled(true); @@ -146,7 +150,7 @@ class LibrarySearchFragment : MainFragment() { fragment.navigate(it); } }); - adapterAlbums = recycler.asAny(RecyclerView.VERTICAL, false, { + adapterAlbums = recycler.asAny(RecyclerView.VERTICAL, false, { it.onClick.subscribe { if(it != null) fragment.navigate(it); @@ -173,38 +177,57 @@ class LibrarySearchFragment : MainFragment() { fun loadArtists(){ recycler.adapter = adapterArtists.adapter.adapter; + fragment.topBar?.let { + if(it is SearchTopBarFragment) + search(it.getSearchText()); + } } fun loadAlbums() { recycler.adapter = adapterAlbums.adapter.adapter; + fragment.topBar?.let { + if(it is SearchTopBarFragment) + search(it.getSearchText()); + } } fun loadSongs() { recycler.adapter = adapterSongs.adapter.adapter; + fragment.topBar?.let { + if(it is SearchTopBarFragment) + search(it.getSearchText()); + } } fun search(str: String) { if(recycler.adapter == adapterArtists.adapter.adapter) { - if(str.isNullOrBlank()) - adapterArtists.adapter.setData(listOf()); - else - adapterArtists.setData(StateLibrary.instance.searchArtists(str)); + val data = if(!str.isNullOrBlank()) + StateLibrary.instance.searchArtists(str) + else listOf(); + adapterArtists.setData(data); + textMetadata.text = "${data.size} artists"; } else if(recycler.adapter == adapterAlbums.adapter.adapter) { - if(str.isNullOrBlank()) - adapterAlbums.adapter.setData(listOf()); - else - adapterAlbums.setData(StateLibrary.instance.searchAlbums(str)); + val data = if(!str.isNullOrBlank()) + StateLibrary.instance.searchAlbums(str) + else listOf(); + adapterAlbums.setData(data); + textMetadata.text = "${data.size} albums"; } else if(recycler.adapter == adapterSongs.adapter.adapter) { - if(str.isNullOrBlank()) - adapterSongs.adapter.setData(listOf()); - else - adapterSongs.setData(StateLibrary.instance.searchTracks(str)); + val data = if(!str.isNullOrBlank()) + StateLibrary.instance.searchTracks(str) + else listOf(); + + adapterSongs.setData(data); + textMetadata.text = "${data.size} songs"; } } fun onShown() { - + fragment.topBar?.let { + if(it is SearchTopBarFragment) + it.focus(); + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/topbar/GeneralTopBarFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/topbar/GeneralTopBarFragment.kt index 597fc9c7..43f9dc26 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/topbar/GeneralTopBarFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/topbar/GeneralTopBarFragment.kt @@ -9,6 +9,8 @@ import android.widget.ImageView import com.futo.platformplayer.R import com.futo.platformplayer.UIDialogs 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.PlaylistsFragment import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragment @@ -46,6 +48,8 @@ class GeneralTopBarFragment : TopFragment() { navigate(SuggestionsFragmentData("", SearchType.CREATOR)); } else if (currentMain is PlaylistsFragment || currentMain is PlaylistFragment) { navigate(SuggestionsFragmentData("", SearchType.PLAYLIST)); + } else if (currentMain is LibraryFragment) { + navigate(); } else { navigate(SuggestionsFragmentData("", SearchType.VIDEO)); } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/topbar/SearchTopBarFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/topbar/SearchTopBarFragment.kt index 15952d0a..6de5394c 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/topbar/SearchTopBarFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/topbar/SearchTopBarFragment.kt @@ -18,6 +18,7 @@ import com.futo.platformplayer.Settings import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.constructs.Event0 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.SuggestionsFragmentData import com.futo.platformplayer.logging.Logger @@ -112,7 +113,10 @@ class SearchTopBarFragment : TopFragment() { } fun clear() { _editSearch?.text?.clear(); - if (currentMain !is SuggestionsFragment) { + if(currentMain is LibrarySearchFragment) { + onSearch.emit(""); + } + else if (currentMain !is SuggestionsFragment) { navigate(SuggestionsFragmentData("", _searchType), false); } else { onSearch.emit(""); @@ -190,6 +194,12 @@ class SearchTopBarFragment : TopFragment() { _buttonFilter?.visibility = if (visible) View.VISIBLE else View.GONE; } + fun getSearchText(): String { + return _editSearch?.let { + it.text.toString(); + } ?: ""; + } + private fun onDone() { val editSearch = _editSearch if (editSearch != null) { diff --git a/app/src/main/java/com/futo/platformplayer/states/StateLibrary.kt b/app/src/main/java/com/futo/platformplayer/states/StateLibrary.kt index fb02f6ce..3a7b1e4a 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateLibrary.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateLibrary.kt @@ -90,6 +90,8 @@ class StateLibrary { fun searchTracks(str: String): List { + if(str.isNullOrBlank()) + return listOf(); val resolver = StateApp.instance.contextOrNull?.contentResolver; if(resolver == null) { Logger.w(TAG, "Album contentResolver not found"); @@ -97,7 +99,7 @@ class StateLibrary { } val cursor = resolver?.query( 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(); cursor.moveToFirst(); val list = mutableListOf() @@ -118,7 +120,9 @@ class StateLibrary { return null; } fun searchAlbums(str: String): List { - 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? { @@ -135,7 +139,9 @@ class StateLibrary { return null; } fun searchArtists(str: String): List { - 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? { diff --git a/app/src/main/res/layout/fragview_library_search.xml b/app/src/main/res/layout/fragview_library_search.xml index c3eedc02..a79e1c27 100644 --- a/app/src/main/res/layout/fragview_library_search.xml +++ b/app/src/main/res/layout/fragview_library_search.xml @@ -50,7 +50,9 @@ android:id="@+id/text_metadata" android:layout_width="wrap_content" 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:textSize="13sp" android:textColor="#D0D0D0" diff --git a/app/src/main/res/layout/list_album.xml b/app/src/main/res/layout/list_album.xml index cf45be48..ff0a0072 100644 --- a/app/src/main/res/layout/list_album.xml +++ b/app/src/main/res/layout/list_album.xml @@ -6,6 +6,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" + android:layout_marginBottom="10dp" android:id="@+id/root" android:clickable="true"> @@ -17,6 +18,7 @@ app:shapeAppearanceOverlay="@style/roundedCorners_4dp" app:srcCompat="@drawable/placeholder_video_thumbnail" android:background="@drawable/video_thumbnail_outline" + android:layout_marginLeft="10dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" /> @@ -36,7 +38,7 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintRight_toLeftOf="@id/button_trash" app:layout_constraintBottom_toTopOf="@id/text_metadata" - android:layout_marginStart="10dp" /> + android:layout_marginStart="15dp" /> + android:layout_marginStart="15dp" />