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.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");
}
}
@@ -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;
@@ -160,7 +160,7 @@ class LibraryFragment : MainFragment() {
this.fragment = fragment;
recycler = findViewById(R.id.recycler);
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);
}
sectionAlbums = LibrarySection(context)//findViewById(R.id.section_albums);
@@ -106,16 +106,18 @@ class LibrarySearchFragment : MainFragment() {
val pillSongs: PillV2;
val pills: List<PillV2>;
val textMetadata: TextView;
val recycler: RecyclerView;
val adapterArtists: AnyAdapterView<Artist, LibraryArtistsFragment.ArtistViewHolder>;
val adapterSongs: AnyAdapterView<IPlatformContent, TrackViewHolder>;
val adapterAlbums: AnyAdapterView<Album, AlbumTileViewHolder>;
val adapterAlbums: AnyAdapterView<Album, LibraryAlbumsFragment.AlbumViewHolder>;
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<LibraryArtistFragment>(it);
}
});
adapterAlbums = recycler.asAny<Album, AlbumTileViewHolder>(RecyclerView.VERTICAL, false, {
adapterAlbums = recycler.asAny<Album, LibraryAlbumsFragment.AlbumViewHolder>(RecyclerView.VERTICAL, false, {
it.onClick.subscribe {
if(it != null)
fragment.navigate<LibraryAlbumFragment>(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();
}
}
}
}
@@ -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<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.CREATOR));
} else if (currentMain is PlaylistsFragment || currentMain is PlaylistFragment) {
navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.PLAYLIST));
} else if (currentMain is LibraryFragment) {
navigate<LibrarySearchFragment>();
} else {
navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.VIDEO));
}
@@ -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<SuggestionsFragment>(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) {
@@ -90,6 +90,8 @@ class StateLibrary {
fun searchTracks(str: String): List<IPlatformVideo> {
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<IPlatformVideo>()
@@ -118,7 +120,9 @@ class StateLibrary {
return null;
}
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? {
@@ -135,7 +139,9 @@ class StateLibrary {
return null;
}
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? {
@@ -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"
+4 -2
View File
@@ -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" />
<TextView
android:id="@+id/text_metadata"
@@ -53,7 +55,7 @@
app:layout_constraintLeft_toRightOf="@id/image_thumbnail"
app:layout_constraintRight_toLeftOf="@id/button_trash"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="10dp" />
android:layout_marginStart="15dp" />
<!--
<ImageButton
+6 -6
View File
@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="86dp"
android:layout_width="71dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
@@ -14,12 +14,12 @@
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_thumbnail"
android:layout_height="66dp"
android:layout_width="66dp"
android:layout_height="50dp"
android:layout_width="50dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/roundedCorners_33dp"
app:shapeAppearanceOverlay="@style/roundedCorners_25dp"
app:srcCompat="@drawable/unknown_music"
android:background="@drawable/video_thumbnail_outline"
app:layout_constraintTop_toTopOf="parent"
@@ -29,7 +29,7 @@
android:id="@+id/text_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textSize="12sp"
android:textColor="@color/white"
android:fontFamily="@font/inter_medium"
android:textAlignment="center"
@@ -45,7 +45,7 @@
android:id="@+id/text_metadata"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textSize="10sp"
android:textColor="#75FFFFFF"
android:fontFamily="@font/inter_regular"
android:textAlignment="center"
+4
View File
@@ -31,6 +31,10 @@
<item name="cornerFamily">rounded</item>
<item name="cornerSize">33dp</item>
</style>
<style name="roundedCorners_25dp" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">25dp</item>
</style>
<style name="roundedCorners_40dp" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">40dp</item>