Skip to content

Commit 1bc1534

Browse files
committed
first steps towards playlist editing
1 parent de8ca2a commit 1bc1534

13 files changed

Lines changed: 233 additions & 15 deletions

File tree

app/src/main/java/org/akanework/gramophone/ui/adapters/ArtistAdapter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class ArtistAdapter(
102102

103103
private class ArtistDecorAdapter(
104104
artistAdapter: ArtistAdapter
105-
) : BaseDecorAdapter<ArtistAdapter>(artistAdapter, R.plurals.artists) {
105+
) : BaseDecorAdapter<ArtistAdapter>(artistAdapter, R.plurals.artists, false) {
106106

107107
override fun onSortButtonPressed(popupMenu: PopupMenu) {
108108
popupMenu.menu.findItem(R.id.album_artist_checkbox).isVisible = true

app/src/main/java/org/akanework/gramophone/ui/adapters/BaseDecorAdapter.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import org.akanework.gramophone.ui.getAdapterType
3838
open class BaseDecorAdapter<T : BaseAdapter<*>>(
3939
protected val adapter: T,
4040
private val pluralStr: Int,
41-
private val isSubFragment: Boolean = false
41+
private val isSubFragment: Boolean
4242
) : MyRecyclerView.Adapter<BaseDecorAdapter<T>.ViewHolder>(), ItemHeightHelper {
4343

4444
protected val context: Context = adapter.context
@@ -56,7 +56,7 @@ open class BaseDecorAdapter<T : BaseAdapter<*>>(
5656
return ViewHolder(view)
5757
}
5858

59-
final override fun onBindViewHolder(holder: ViewHolder, position: Int) {
59+
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
6060
val count = adapter.itemCount
6161
holder.playAll.visibility =
6262
if (adapter is SongAdapter) View.VISIBLE else View.GONE
@@ -245,6 +245,7 @@ open class BaseDecorAdapter<T : BaseAdapter<*>>(
245245
view: View,
246246
) : RecyclerView.ViewHolder(view) {
247247
val sortButton: MaterialButton = view.findViewById(R.id.sort)
248+
val createPlaylist: MaterialButton = view.findViewById(R.id.create_playlist)
248249
val playAll: MaterialButton = view.findViewById(R.id.play_all)
249250
val shuffleAll: MaterialButton = view.findViewById(R.id.shuffle_all)
250251
val jumpUp: MaterialButton = view.findViewById(R.id.jumpUp)

app/src/main/java/org/akanework/gramophone/ui/adapters/PlaylistAdapter.kt

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,23 @@
1717

1818
package org.akanework.gramophone.ui.adapters
1919

20+
import android.util.Log
21+
import android.view.View
22+
import android.widget.Toast
23+
import androidx.appcompat.app.AlertDialog
2024
import androidx.appcompat.widget.PopupMenu
25+
import androidx.core.view.iterator
2126
import androidx.fragment.app.Fragment
27+
import kotlinx.coroutines.CoroutineScope
28+
import kotlinx.coroutines.Dispatchers
29+
import kotlinx.coroutines.launch
30+
import kotlinx.coroutines.withContext
2231
import org.akanework.gramophone.R
2332
import org.akanework.gramophone.ui.MainActivity
2433
import org.akanework.gramophone.ui.fragments.GeneralSubFragment
2534
import uk.akane.libphonograph.dynamicitem.RecentlyAdded
2635
import uk.akane.libphonograph.items.Playlist
36+
import uk.akane.libphonograph.manipulator.ItemManipulator
2737

2838
/**
2939
* [PlaylistAdapter] is an adapter for displaying artists.
@@ -41,8 +51,8 @@ class PlaylistAdapter(
4151
ownsView = true,
4252
defaultLayoutType = LayoutType.LIST
4353
) {
44-
4554
override val defaultCover = R.drawable.ic_default_cover_playlist
55+
private val ioScope = CoroutineScope(Dispatchers.IO)
4656

4757
override fun virtualTitleOf(item: Playlist): String {
4858
return context.getString(
@@ -59,8 +69,10 @@ class PlaylistAdapter(
5969
}
6070

6171
override fun onMenu(item: Playlist, popupMenu: PopupMenu) {
62-
popupMenu.inflate(R.menu.more_menu_less)
63-
72+
popupMenu.inflate(R.menu.more_menu)
73+
popupMenu.menu.iterator().forEach {
74+
it.isVisible = it.itemId == R.id.play_next || it.itemId == R.id.rename || it.itemId == R.id.delete
75+
}
6476
popupMenu.setOnMenuItemClickListener { it1 ->
6577
when (it1.itemId) {
6678
R.id.play_next -> {
@@ -72,9 +84,55 @@ class PlaylistAdapter(
7284
true
7385
}
7486

87+
R.id.delete -> {
88+
AlertDialog.Builder(context)
89+
.setTitle(R.string.delete)
90+
.setMessage(context.getString(R.string.delete_really, item.title))
91+
.setPositiveButton(R.string.yes) { _, _ ->
92+
ioScope.launch {
93+
try {
94+
ItemManipulator.deletePlaylist(context, item.path!!)
95+
} catch (e: Exception) {
96+
Log.e("PlaylistAdapter", Log.getStackTraceString(e))
97+
withContext(Dispatchers.Main) {
98+
Toast.makeText(context, R.string.delete_failed_playlist, Toast.LENGTH_LONG).show()
99+
}
100+
}
101+
}
102+
}
103+
.setNegativeButton(R.string.no) { _, _ -> }
104+
.show()
105+
true
106+
}
107+
108+
R.id.rename -> TODO()
109+
75110
else -> false
76111
}
77112
}
78113
}
79114

115+
116+
override fun createDecorAdapter(): BaseDecorAdapter<out BaseAdapter<Playlist>> {
117+
return PlaylistDecorAdapter(this)
118+
}
119+
120+
private inner class PlaylistDecorAdapter(
121+
playlistAdapter: PlaylistAdapter
122+
) : BaseDecorAdapter<PlaylistAdapter>(playlistAdapter, R.plurals.items, false) {
123+
124+
override fun onBindViewHolder(
125+
holder: BaseDecorAdapter<PlaylistAdapter>.ViewHolder,
126+
position: Int,
127+
payloads: List<Any?>
128+
) {
129+
super.onBindViewHolder(holder, position, payloads)
130+
holder.createPlaylist.visibility = View.VISIBLE
131+
holder.createPlaylist.setOnClickListener { _ ->
132+
ioScope.launch {
133+
ItemManipulator.createPlaylist(context, "test123")
134+
}
135+
}
136+
}
137+
}
80138
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?colorControlNormal" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
2+
3+
<path android:fillColor="@android:color/white" android:pathData="M440,680L520,680L520,520L680,520L680,440L520,440L520,280L440,280L440,440L280,440L280,520L440,520L440,680ZM200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM200,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200Z"/>
4+
5+
</vector>

app/src/main/res/layout/general_decor.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@
2424
android:gravity="center_vertical"
2525
android:layout_marginEnd="9dp">
2626

27+
<com.google.android.material.button.MaterialButton
28+
android:id="@+id/create_playlist"
29+
android:layout_width="48dp"
30+
android:layout_height="48dp"
31+
android:background="@drawable/rp_buttons"
32+
android:insetLeft="0dp"
33+
android:insetTop="0dp"
34+
android:insetRight="0dp"
35+
android:insetBottom="0dp"
36+
android:visibility="gone"
37+
app:tooltipText="@string/create_playlist"
38+
android:contentDescription="@string/create_playlist"
39+
app:icon="@drawable/outline_add_box_24"
40+
app:iconGravity="textStart"
41+
app:iconPadding="0dp"
42+
app:iconSize="26dp"
43+
app:iconTint="?attr/colorPrimary"
44+
tools:visibility="visible" />
45+
2746
<com.google.android.material.button.MaterialButton
2847
android:id="@+id/play_all"
2948
android:layout_width="48dp"

app/src/main/res/menu/more_menu.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
<item
1010
android:id="@+id/artist"
1111
android:title="@string/go_to_artist" />
12+
<item
13+
android:id="@+id/rename"
14+
android:title="@string/rename"
15+
android:visible="false" />
1216
<item
1317
android:id="@+id/details"
1418
android:title="@string/details" />

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
<string name="dialog_edit_metadata">Edit</string>
160160
<string name="dialog_lyrics">Lyrics</string>
161161
<string name="delete_failed">Deleting song failed</string>
162+
<string name="delete_failed_playlist">Deleting playlist failed</string>
162163
<string name="album_covers_enhance">Enhanced album cover loading</string>
163164
<string name="album_covers_enhance_desc">Support high quality song covers and allow album and song cover to differ (requires photos access)</string>
164165
<string name="deny_images"><![CDATA[Please deny \"Photos and videos\" permission]]></string>
@@ -421,4 +422,6 @@
421422
<string name="do_not_enter_email_enter_msg">Enter message, not an e-mail address</string>
422423
<string name="log_will_auto_send">Logs will be sent automatically, explain the issue instead</string>
423424
<string name="log_attachment">Logs will be sent as attachment automatically</string>
425+
<string name="create_playlist">Create playlist</string>
426+
<string name="rename">Rename</string>
424427
</resources>

libPhonograph/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ android {
1414
testFixtures {
1515
enable = true
1616
}
17-
minSdk = 16
17+
minSdk = 21
1818
consumerProguardFiles("consumer-rules.pro")
1919
}
2020

libPhonograph/src/main/java/uk/akane/libphonograph/dynamicitem/RecentlyAdded.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import androidx.media3.common.MediaItem
44
import uk.akane.libphonograph.items.Playlist
55
import uk.akane.libphonograph.items.addDate
66

7-
class RecentlyAdded(minAddDate: Long, songList: List<MediaItem>) : Playlist(-1, null) {
7+
class RecentlyAdded(minAddDate: Long, songList: List<MediaItem>) : Playlist(-1, null, null, null, null) {
88
private val rawList: List<MediaItem> = songList.sortedByDescending {
99
it.mediaMetadata.addDate ?: -1 }
1010
private var filteredList = filterList(minAddDate)

libPhonograph/src/main/java/uk/akane/libphonograph/items/Playlist.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package uk.akane.libphonograph.items
22

33
import androidx.media3.common.MediaItem
4+
import java.io.File
45

56
open class Playlist protected constructor(
67
override val id: Long?,
7-
override val title: String?
8+
override val title: String?,
9+
val path: File?,
10+
val dateAdded: Long?,
11+
val dateModified: Long?,
812
) : Item {
913
private var _songList: List<MediaItem>? = null
10-
constructor(id: Long?, title: String?, songList: List<MediaItem>) : this(id, title) {
14+
constructor(id: Long?, title: String?, path: File?, dateAdded: Long?, dateModified: Long?,
15+
songList: List<MediaItem>) : this(id, title, path, dateAdded, dateModified) {
1116
_songList = songList
1217
}
1318
override val songList: List<MediaItem>
@@ -38,11 +43,14 @@ open class Playlist protected constructor(
3843
internal data class RawPlaylist(
3944
val id: Long?,
4045
val title: String?,
46+
val path: File?,
47+
val dateAdded: Long?,
48+
val dateModified: Long?,
4149
val songList: List<Long>
4250
) {
4351
// idMap may be null if and only if all playlists are empty
4452
fun toPlaylist(idMap: Map<Long, MediaItem>?): Playlist {
45-
return Playlist(id, title, songList.mapNotNull { value ->
53+
return Playlist(id, title, path, dateAdded, dateModified, songList.mapNotNull { value ->
4654
idMap!![value]
4755
// if song is null it's 100% of time a library (or MediaStore?) bug
4856
// and because I found the MediaStore bug in the wild, don't be so stingy

0 commit comments

Comments
 (0)