mirror of
https://github.com/clangen/musikcube.git
synced 2025-01-02 11:58:27 +00:00
Added "remove (track) from playlist" functionality to the Android client.
This commit is contained in:
parent
ede67d9726
commit
8bb70e7d98
@ -35,6 +35,8 @@ import javax.inject.Inject
|
||||
|
||||
class ItemContextMenuMixin(private val activity: AppCompatActivity,
|
||||
internal val listener: EventListener? = null): MixinBase() {
|
||||
private enum class TrackType { Normal, Playlist }
|
||||
|
||||
@Inject lateinit var provider: IDataProvider
|
||||
|
||||
open class EventListener {
|
||||
@ -56,6 +58,7 @@ class ItemContextMenuMixin(private val activity: AppCompatActivity,
|
||||
super.onCreate(bundle)
|
||||
ConfirmDeletePlaylistDialog.rebind(activity, this)
|
||||
EnterPlaylistNameDialog.rebind(activity, this)
|
||||
ConfirmRemoveFromPlaylistDialog.rebind(activity, this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -174,15 +177,32 @@ class ItemContextMenuMixin(private val activity: AppCompatActivity,
|
||||
}
|
||||
|
||||
fun showForTrack(track: ITrack, anchorView: View) {
|
||||
showForTrack(track, -1, -1, anchorView, TrackType.Normal)
|
||||
}
|
||||
|
||||
fun showForPlaylistTrack(track: ITrack, position: Int, playlistId: Long, anchorView: View) {
|
||||
showForTrack(track, position, playlistId, anchorView, TrackType.Playlist)
|
||||
}
|
||||
|
||||
private fun showForTrack(track: ITrack, position: Int, categoryId: Long, anchorView: View, type: TrackType) {
|
||||
val popup = PopupMenu(activity, anchorView)
|
||||
popup.inflate(R.menu.track_item_context_menu)
|
||||
|
||||
if (type != TrackType.Playlist) {
|
||||
popup.menu.removeItem(R.id.menu_remove_from_playlist)
|
||||
}
|
||||
|
||||
popup.setOnMenuItemClickListener { item ->
|
||||
val intent: Intent? = when (item.itemId) {
|
||||
R.id.menu_add_to_playlist -> {
|
||||
addToPlaylist(track)
|
||||
null
|
||||
}
|
||||
R.id.menu_remove_from_playlist -> {
|
||||
ConfirmRemoveFromPlaylistDialog.show(
|
||||
activity, this, categoryId, position, track)
|
||||
null
|
||||
}
|
||||
R.id.menu_show_artist_albums -> {
|
||||
AlbumBrowseActivity.getStartIntent(
|
||||
activity, Messages.Category.ARTIST, track.artistId, track.artist)
|
||||
@ -319,6 +339,17 @@ class ItemContextMenuMixin(private val activity: AppCompatActivity,
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeFromPlaylistConfirmed(playlistId: Long, externalId: String, position: Int) {
|
||||
provider.removeTracksFromPlaylist(playlistId, listOf(externalId), listOf(position)).subscribeBy(
|
||||
onNext = { success ->
|
||||
listener?.onPlaylistUpdated(playlistId)
|
||||
},
|
||||
onError = {
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showSuccess(stringId: Int) =
|
||||
showSuccess(activity.getString(stringId))
|
||||
|
||||
@ -331,6 +362,54 @@ class ItemContextMenuMixin(private val activity: AppCompatActivity,
|
||||
private fun showError(message: String) =
|
||||
showErrorSnackbar(activity.findViewById(android.R.id.content), message)
|
||||
|
||||
class ConfirmRemoveFromPlaylistDialog : BaseDialogFragment() {
|
||||
private lateinit var mixin: ItemContextMenuMixin
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val playlistId = arguments.getLong(EXTRA_PLAYLIST_ID, -1)
|
||||
val trackTitle = arguments.getString(EXTRA_TRACK_TITLE, "")
|
||||
val trackExternalId = arguments.getString(EXTRA_TRACK_EXTERNAL_ID, "")
|
||||
val trackPosition = arguments.getInt(EXTRA_TRACK_POSITION, -1)
|
||||
|
||||
val dlg = AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.playlist_confirm_delete_title)
|
||||
.setMessage(getString(R.string.playlist_confirm_delete_message, trackTitle))
|
||||
.setNegativeButton(R.string.button_no, null)
|
||||
.setPositiveButton(R.string.button_yes, { _: DialogInterface, _: Int ->
|
||||
mixin.removeFromPlaylistConfirmed(playlistId, trackExternalId, trackPosition)
|
||||
})
|
||||
.create()
|
||||
|
||||
dlg.setCancelable(false)
|
||||
return dlg
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = "confirm_delete_playlist_dialog"
|
||||
private val EXTRA_PLAYLIST_ID = "extra_playlist_id"
|
||||
private val EXTRA_TRACK_TITLE = "extra_track_title"
|
||||
private val EXTRA_TRACK_EXTERNAL_ID = "extra_track_external_id"
|
||||
private val EXTRA_TRACK_POSITION = "extra_track_position"
|
||||
|
||||
fun rebind(activity: AppCompatActivity, mixin: ItemContextMenuMixin) {
|
||||
find<ConfirmRemoveFromPlaylistDialog>(activity, TAG)?.mixin = mixin
|
||||
}
|
||||
|
||||
fun show(activity: AppCompatActivity, mixin: ItemContextMenuMixin, playlistId: Long, position: Int, track: ITrack) {
|
||||
dismiss(activity, TAG)
|
||||
val args = Bundle()
|
||||
args.putLong(EXTRA_PLAYLIST_ID, playlistId)
|
||||
args.putString(EXTRA_TRACK_TITLE, track.title)
|
||||
args.putString(EXTRA_TRACK_EXTERNAL_ID, track.externalId)
|
||||
args.putInt(EXTRA_TRACK_POSITION, position)
|
||||
val result = ConfirmRemoveFromPlaylistDialog()
|
||||
result.arguments = args
|
||||
result.mixin = mixin
|
||||
result.show(activity.supportFragmentManager, TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ConfirmDeletePlaylistDialog : BaseDialogFragment() {
|
||||
private lateinit var mixin: ItemContextMenuMixin
|
||||
|
||||
|
@ -27,6 +27,7 @@ import io.casey.musikcube.remote.util.Debouncer
|
||||
import io.casey.musikcube.remote.util.Strings
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.rxkotlin.subscribeBy
|
||||
import java.util.*
|
||||
|
||||
class TrackListActivity : BaseActivity(), Filterable {
|
||||
private lateinit var tracks: TrackListSlidingWindow
|
||||
@ -41,31 +42,10 @@ class TrackListActivity : BaseActivity(), Filterable {
|
||||
private var categoryId: Long = 0
|
||||
private var lastFilter = ""
|
||||
|
||||
private val onItemClickListener = { view: View ->
|
||||
val index = view.tag as Int
|
||||
|
||||
if (isValidCategory(categoryType, categoryId)) {
|
||||
playback.service.play(categoryType, categoryId, index, lastFilter)
|
||||
}
|
||||
else {
|
||||
playback.service.playAll(index, lastFilter)
|
||||
}
|
||||
|
||||
startActivity(MainActivity.getStartIntent(this))
|
||||
finish()
|
||||
}
|
||||
|
||||
private val onActionClickListener = { view: View ->
|
||||
val track = view.tag as ITrack
|
||||
mixin(ItemContextMenuMixin::class.java)?.showForTrack(track, view)
|
||||
Unit
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
component.inject(this)
|
||||
data = mixin(DataProviderMixin())
|
||||
playback = mixin(PlaybackMixin())
|
||||
mixin(ItemContextMenuMixin(this))
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@ -74,6 +54,8 @@ class TrackListActivity : BaseActivity(), Filterable {
|
||||
categoryId = intent.getLongExtra(EXTRA_SELECTED_ID, 0)
|
||||
val titleId = intent.getIntExtra(EXTRA_TITLE_ID, R.string.songs_title)
|
||||
|
||||
mixin(ItemContextMenuMixin(this, menuListener))
|
||||
|
||||
setContentView(R.layout.recycler_view_activity)
|
||||
|
||||
setTitleFromIntent(titleId)
|
||||
@ -83,7 +65,7 @@ class TrackListActivity : BaseActivity(), Filterable {
|
||||
val recyclerView = findViewById<FastScrollRecyclerView>(R.id.recycler_view)
|
||||
|
||||
tracks = TrackListSlidingWindow(recyclerView, data.provider, queryFactory)
|
||||
adapter = TrackListAdapter(tracks, onItemClickListener, onActionClickListener, playback)
|
||||
adapter = TrackListAdapter(tracks, eventListener, playback)
|
||||
|
||||
setupDefaultRecyclerView(recyclerView, adapter)
|
||||
|
||||
@ -127,6 +109,30 @@ class TrackListActivity : BaseActivity(), Filterable {
|
||||
filterDebouncer.call()
|
||||
}
|
||||
|
||||
private val eventListener = object: TrackListAdapter.EventListener {
|
||||
override fun onItemClick(view: View, track: ITrack, position: Int) {
|
||||
if (isValidCategory(categoryType, categoryId)) {
|
||||
playback.service.play(categoryType, categoryId, position, lastFilter)
|
||||
}
|
||||
else {
|
||||
playback.service.playAll(position, lastFilter)
|
||||
}
|
||||
|
||||
startActivity(MainActivity.getStartIntent(this@TrackListActivity))
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onActionItemClick(view: View, track: ITrack, position: Int) {
|
||||
val mixin = mixin(ItemContextMenuMixin::class.java)!!
|
||||
if (categoryType == Messages.Category.Companion.PLAYLISTS) {
|
||||
mixin.showForPlaylistTrack(track, position, categoryId, view)
|
||||
}
|
||||
else {
|
||||
mixin.showForTrack(track, view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initObservers() {
|
||||
disposables.add(data.provider.observeState().subscribeBy(
|
||||
onNext = { states ->
|
||||
@ -215,6 +221,19 @@ class TrackListActivity : BaseActivity(), Filterable {
|
||||
override fun onMetadataLoaded(offset: Int, count: Int) {}
|
||||
}
|
||||
|
||||
private val menuListener: ItemContextMenuMixin.EventListener?
|
||||
get() {
|
||||
if (categoryType == Messages.Category.PLAYLISTS) {
|
||||
return object: ItemContextMenuMixin.EventListener () {
|
||||
override fun onPlaylistUpdated(id: Long) {
|
||||
tracks.requery()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val EXTRA_CATEGORY_TYPE = "extra_category_type"
|
||||
private val EXTRA_SELECTED_ID = "extra_selected_id"
|
||||
|
@ -13,15 +13,31 @@ import io.casey.musikcube.remote.ui.shared.mixin.PlaybackMixin
|
||||
import io.casey.musikcube.remote.ui.shared.model.TrackListSlidingWindow
|
||||
|
||||
class TrackListAdapter(private val tracks: TrackListSlidingWindow,
|
||||
private val onItemClickListener: (View) -> Unit,
|
||||
private val onActionClickListener: (View) -> Unit,
|
||||
private val listener: EventListener?,
|
||||
private var playback: PlaybackMixin) : RecyclerView.Adapter<TrackListAdapter.ViewHolder>()
|
||||
{
|
||||
interface EventListener {
|
||||
fun onItemClick(view: View, track: ITrack, position: Int)
|
||||
fun onActionItemClick(view: View, track: ITrack, position: Int)
|
||||
}
|
||||
|
||||
private data class Tag(var position: Int?, var track: ITrack?)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackListAdapter.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val view = inflater.inflate(R.layout.simple_list_item, parent, false)
|
||||
view.setOnClickListener(onItemClickListener)
|
||||
view.findViewById<View>(R.id.action).setOnClickListener(onActionClickListener)
|
||||
view.tag = Tag(null, null)
|
||||
|
||||
view.setOnClickListener({ v ->
|
||||
val tag = v.tag as Tag
|
||||
listener?.onItemClick(v, tag.track!!, tag.position!!)
|
||||
})
|
||||
|
||||
view.findViewById<View>(R.id.action).setOnClickListener({ v ->
|
||||
val tag = v.tag as Tag
|
||||
listener?.onActionItemClick(v, tag.track!!, tag.position!!)
|
||||
})
|
||||
|
||||
return ViewHolder(view, playback)
|
||||
}
|
||||
|
||||
@ -39,8 +55,11 @@ class TrackListAdapter(private val tracks: TrackListSlidingWindow,
|
||||
private val action: View = view.findViewById(R.id.action)
|
||||
|
||||
internal fun bind(track: ITrack?, position: Int) {
|
||||
itemView.tag = position
|
||||
action.tag = track
|
||||
val tag = itemView.tag as Tag
|
||||
tag.position = position
|
||||
tag.track = track
|
||||
itemView.tag = tag
|
||||
action.tag = tag
|
||||
|
||||
var titleColor = R.color.theme_foreground
|
||||
var subtitleColor = R.color.theme_disabled_foreground
|
||||
|
@ -4,6 +4,10 @@
|
||||
android:id="@+id/menu_add_to_playlist"
|
||||
android:title="@string/menu_add_to_playlist"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_remove_from_playlist"
|
||||
android:title="@string/menu_remove_from_playlist"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_show_artist_tracks"
|
||||
android:title="@string/menu_show_artist_tracks"/>
|
||||
|
@ -68,6 +68,7 @@
|
||||
<string name="menu_remote_toggle">remote playback</string>
|
||||
<string name="menu_offline_tracks">offline songs</string>
|
||||
<string name="menu_add_to_playlist">add to playlist</string>
|
||||
<string name="menu_remove_from_playlist">remove from playlist</string>
|
||||
<string name="menu_show_tracks">songs</string>
|
||||
<string name="menu_show_albums">albums</string>
|
||||
<string name="menu_show_artists">artists</string>
|
||||
@ -143,6 +144,8 @@
|
||||
<string name="playlist_not_renamed">playlist \'%s\' not renamed</string>
|
||||
<string name="playlist_deleted">playlist \'%s\' deleted</string>
|
||||
<string name="playlist_not_deleted">could not delete playlist \'%s\'</string>
|
||||
<string name="remove_from_playlist_dialog_title">confirm</string>
|
||||
<string name="remove_from_playlist_dialog_message">are you sure you want to remove \`%s\` from this playlist?</string>
|
||||
<string name="spotlight_playback_mode_title">playback mode</string>
|
||||
<string name="spotlight_playback_mode_message">want to listen to music from your phone?\n\nclick here to switch between remote control and streaming playback modes.</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user