diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.kt b/app/src/main/java/code/name/monkey/retromusic/Constants.kt index 4cb587575..400b1c473 100644 --- a/app/src/main/java/code/name/monkey/retromusic/Constants.kt +++ b/app/src/main/java/code/name/monkey/retromusic/Constants.kt @@ -161,4 +161,5 @@ const val CIRCLE_PLAY_BUTTON = "circle_play_button" const val SWIPE_ANYWHERE_NOW_PLAYING = "swipe_anywhere_now_playing" const val PAUSE_HISTORY = "pause_history" const val MANAGE_AUDIO_FOCUS = "manage_audio_focus" -const val SWIPE_DOWN_DISMISS = "swipe_to_dismiss" \ No newline at end of file +const val SWIPE_DOWN_DISMISS = "swipe_to_dismiss" +const val ENABLE_SEARCH_PLAYLIST= "enable_search_playlist" \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt index bf85fe28b..78f2f53f6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt @@ -25,6 +25,7 @@ import code.name.monkey.retromusic.db.toSongEntity import code.name.monkey.retromusic.db.toSongsEntity import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog import code.name.monkey.retromusic.fragments.LibraryViewModel +import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.ViewUtil import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter @@ -43,9 +44,20 @@ class OrderablePlaylistSongAdapter( val libraryViewModel: LibraryViewModel by activity.viewModel() + private var filtered = false + private var filter: CharSequence? = null + private var fullDataSet: MutableList + init { this.setHasStableIds(true) this.setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection) + fullDataSet = dataSet.toMutableList() + } + + override fun swapDataSet(dataSet: List) { + super.swapDataSet(dataSet) + fullDataSet = dataSet.toMutableList() + onFilter(filter) } override fun getItemId(position: Int): Long { @@ -90,13 +102,22 @@ class OrderablePlaylistSongAdapter( return super.onSongMenuItemClick(item) } + override fun onClick(v: View?) { + if (isInQuickSelectMode || !filtered) { + super.onClick(v) + } else { + val position = fullDataSet.indexOf(dataSet.get(layoutPosition)) + MusicPlayerRemote.openQueueKeepShuffleMode(fullDataSet, position, true) + } + } + init { dragView?.isVisible = true } } override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean { - if (isInQuickSelectMode) { + if (isInQuickSelectMode || filtered) { return false } return ViewUtil.hitTest(holder.imageText!!, x, y) || ViewUtil.hitTest( @@ -127,8 +148,27 @@ class OrderablePlaylistSongAdapter( } fun saveSongs(playlistEntity: PlaylistEntity) { + onFilter(null) activity.lifecycleScope.launch(Dispatchers.IO) { libraryViewModel.insertSongs(dataSet.toSongsEntity(playlistEntity)) } } + + + fun onFilter(text: CharSequence?) { + filter = text + if (text.isNullOrEmpty()) { + filtered = false + dataSet = fullDataSet + } else { + filtered = true + dataSet = fullDataSet.filter { song -> song.title.contains(text, ignoreCase = true) } + .toMutableList() + } + notifyDataSetChanged() + } + + fun hasSongs(): Boolean { + return itemCount > 0 || (filtered && fullDataSet.size > 0) + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt index 352bf453b..a02cb3dde 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt @@ -8,6 +8,8 @@ import android.view.MenuItem import android.view.View import androidx.core.view.doOnPreDraw import androidx.core.view.isVisible +import androidx.core.widget.addTextChangedListener +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager @@ -21,12 +23,14 @@ import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.elevatedAccentColor import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment +import code.name.monkey.retromusic.fragments.search.clearText import code.name.monkey.retromusic.glide.RetroGlideExtension.playlistOptions import code.name.monkey.retromusic.glide.playlistPreview.PlaylistPreview import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.ThemedFastScroller import com.bumptech.glide.Glide import com.google.android.material.shape.MaterialShapeDrawable @@ -35,6 +39,9 @@ import com.google.android.material.transition.MaterialContainerTransform import com.google.android.material.transition.MaterialSharedAxis import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.launch import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf @@ -51,6 +58,8 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli private lateinit var playlist: PlaylistWithSongs private lateinit var playlistSongAdapter: OrderablePlaylistSongAdapter + private val _searchFlow = MutableSharedFlow() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = MaterialContainerTransform(requireContext(), true).apply { @@ -71,6 +80,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli // binding.container.transitionName = playlist.playlistEntity.playlistName setUpRecyclerView() + setUpSearch() setupButtons() viewModel.getPlaylist().observe(viewLifecycleOwner) { playlistWithSongs -> playlist = playlistWithSongs @@ -112,6 +122,33 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli } } + private fun setUpSearch() { + if (!PreferenceUtil.enableSearchPlaylist) { + binding.playlistSearchView.visibility = View.GONE + } else { + binding.playlistSearchView.visibility = View.VISIBLE + } + binding.playlistSearchView.addTextChangedListener { text -> + lifecycleScope.launch { + _searchFlow.emit(text) + binding.clearSearch.visibility = + if (text.isNullOrBlank()) View.GONE else View.VISIBLE + } + } + binding.clearSearch.setOnClickListener { + lifecycleScope.launch { + _searchFlow.emit(null) + binding.playlistSearchView.clearText() + binding.clearSearch.visibility = View.GONE + } + } + lifecycleScope.launch { + _searchFlow.debounce(300).collect { text -> + playlistSongAdapter.onFilter(text) + } + } + } + private fun setUpRecyclerView() { playlistSongAdapter = OrderablePlaylistSongAdapter( arguments.extraPlaylistId, @@ -150,8 +187,18 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli } private fun checkIsEmpty() { - binding.empty.isVisible = playlistSongAdapter.itemCount == 0 - binding.emptyText.isVisible = playlistSongAdapter.itemCount == 0 + if (_binding != null) { + if (playlistSongAdapter.itemCount != 0) { + binding.empty.isVisible = false + } else { + binding.empty.isVisible = true + if (playlistSongAdapter.hasSongs()) { + binding.emptyText.text = getString(R.string.no_search_results) + } else { + binding.emptyText.text = getString(R.string.no_songs) + } + } + } } override fun onDestroy() { @@ -160,6 +207,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli } override fun onPause() { + binding.playlistSearchView.clearText() playlistSongAdapter.saveSongs(playlist.playlistEntity) super.onPause() } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt index 6613c22ab..f01bb0346 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt @@ -680,6 +680,9 @@ object PreferenceUtil { val rememberLastTab: Boolean get() = sharedPreferences.getBoolean(REMEMBER_LAST_TAB, true) + val enableSearchPlaylist: Boolean + get() = sharedPreferences.getBoolean(ENABLE_SEARCH_PLAYLIST, true) + var lastTab: Int get() = sharedPreferences .getInt(LAST_USED_TAB, 0) diff --git a/app/src/main/res/layout/fragment_playlist_detail_new.xml b/app/src/main/res/layout/fragment_playlist_detail_new.xml index 1d2003ee4..a968a6989 100644 --- a/app/src/main/res/layout/fragment_playlist_detail_new.xml +++ b/app/src/main/res/layout/fragment_playlist_detail_new.xml @@ -30,6 +30,33 @@ android:paddingBottom="16dp" android:paddingHorizontal="16dp"> + + + + + No purchase found. No results You have no songs + No search results Normal Normal lyrics %s is not listed in the media store.]]> @@ -365,6 +366,7 @@ License details for open source software When enabled, newly played songs won\'t show in history Navigate to the last used tab on start + Show a search field in a playlist Display synced lyrics over album cover Show New Music Mix on homescreen Enables changing song by swiping anywhere on the now playing screen @@ -412,6 +414,7 @@ Open source licences Pause history Remember last tab + Enable search in playlist Show lyrics Show suggestions Swipe anywhere to change song diff --git a/app/src/main/res/xml/pref_ui.xml b/app/src/main/res/xml/pref_ui.xml index 3517e949b..58020fa24 100644 --- a/app/src/main/res/xml/pref_ui.xml +++ b/app/src/main/res/xml/pref_ui.xml @@ -67,6 +67,13 @@ android:summary="@string/pref_summary_remember_tab" android:title="@string/pref_title_remember_tab" /> + +