Skip to content

Commit

Permalink
initial now playing indicator + release date sort
Browse files Browse the repository at this point in the history
  • Loading branch information
nift4 committed May 17, 2024
1 parent 2e49307 commit 8190b6e
Show file tree
Hide file tree
Showing 13 changed files with 277 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class LifecycleCallbackListImpl<T>(lifecycle: Lifecycle? = null)
}

override fun addCallback(lifecycle: Lifecycle?, clear: Boolean, callback: T) {
if (list.containsKey(callback)) throw IllegalArgumentException("cannot add same callback twice")
list[callback] = Pair(clear, lifecycle?.let { CallbackLifecycleObserver(it, callback) })
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.media3.common.Player
import androidx.media3.session.MediaController
import androidx.media3.session.SessionCommand
import androidx.media3.session.SessionResult
Expand Down Expand Up @@ -57,12 +58,13 @@ class MediaControllerViewModel(application: Application) : AndroidViewModel(appl
}
}

fun addOneOffControllerCallback(lifecycle: Lifecycle?, callback: (MediaController) -> Unit) {
fun addOneOffControllerCallback(lifecycle: Lifecycle?, clear: Boolean = true, callback: (MediaController) -> Unit) {
val instance = get()
if (instance == null || !clear) {
connectionListeners.addCallback(lifecycle, clear, callback)
}
if (instance != null) {
callback(instance)
} else {
connectionListeners.addCallback(lifecycle, true, callback)
}
}

Expand Down Expand Up @@ -113,4 +115,13 @@ class MediaControllerViewModel(application: Application) : AndroidViewModel(appl
}
return future
}
}

fun Player.registerLifecycleCallback(lifecycle: Lifecycle, callback: Player.Listener) {
addListener(callback)
lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
removeListener(callback)
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.akanework.gramophone.ui.adapters

import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.net.Uri
import android.os.Handler
Expand Down Expand Up @@ -63,7 +62,7 @@ import org.akanework.gramophone.ui.fragments.AdapterFragment
import java.util.Collections

abstract class BaseAdapter<T>(
val context: Context,
protected val fragment: Fragment,
protected var liveData: MutableLiveData<List<T>>?,
sortHelper: Sorter.Helper<T>,
naturalOrderHelper: Sorter.NaturalOrderHelper<T>?,
Expand All @@ -78,45 +77,17 @@ abstract class BaseAdapter<T>(
private val fallbackSpans: Int = 1
) : AdapterFragment.BaseInterface<BaseAdapter<T>.ViewHolder>(), Observer<List<T>>,
PopupTextProvider, ItemHeightHelper {
constructor(
fragment: Fragment,
liveData: MutableLiveData<List<T>>?,
sortHelper: Sorter.Helper<T>,
naturalOrderHelper: Sorter.NaturalOrderHelper<T>?,
initialSortType: Sorter.Type,
pluralStr: Int,
ownsView: Boolean,
defaultLayoutType: LayoutType,
isSubFragment: Boolean = false,
rawOrderExposed: Boolean = false,
allowDiffUtils: Boolean = false,
canSort: Boolean = true,
fallbackSpans: Int = 1
) : this(
fragment.requireContext(),
liveData,
sortHelper,
naturalOrderHelper,
initialSortType,
pluralStr,
ownsView,
defaultLayoutType,
isSubFragment,
rawOrderExposed,
allowDiffUtils,
canSort,
fallbackSpans
) { this.fragment = fragment }

companion object {
// this relies on the assumption that all RecyclerViews always have same width
// (though it does get invalidated if that is not the case, for eg rotation)
private var gridHeightCache = 0
}
protected var fragment: Fragment? = null
protected val mainActivity = context as MainActivity
internal val layoutInflater: LayoutInflater
get() = fragment?.layoutInflater ?: LayoutInflater.from(context)
val context = fragment.requireContext()
protected inline val mainActivity
get() = context as MainActivity
internal inline val layoutInflater: LayoutInflater
get() = fragment.layoutInflater
private val listHeight = context.resources.getDimensionPixelSize(R.dimen.list_height)
private val largerListHeight = context.resources.getDimensionPixelSize(R.dimen.larger_list_height)
private var gridHeight: Int? = null
Expand Down Expand Up @@ -217,6 +188,7 @@ abstract class BaseAdapter<T>(
view: View,
) : RecyclerView.ViewHolder(view) {
val songCover: ImageView = view.findViewById(R.id.cover)
val nowPlaying: MaterialButton = view.findViewById(R.id.now_playing)
val title: TextView = view.findViewById(R.id.title)
val subTitle: TextView = view.findViewById(R.id.artist)
val moreButton: MaterialButton = view.findViewById(R.id.more)
Expand Down Expand Up @@ -340,6 +312,7 @@ abstract class BaseAdapter<T>(
notifyDataSetChanged()
}
if (oldCount != newCount) decorAdapter.updateSongCounter()
onListUpdated()
} finally {
listLock.release()
}
Expand All @@ -364,6 +337,8 @@ abstract class BaseAdapter<T>(
}
}

protected open fun onListUpdated() {}

protected open fun createDecorAdapter(): BaseDecorAdapter<out BaseAdapter<T>> {
return BaseDecorAdapter(this, pluralStr, isSubFragment)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ open class BaseDecorAdapter<T : BaseAdapter<*>>(
Pair(R.id.album, Sorter.Type.ByAlbumTitleAscending),
Pair(R.id.size, Sorter.Type.BySizeDescending),
Pair(R.id.add_date, Sorter.Type.ByAddDateDescending),
Pair(R.id.release_date, Sorter.Type.ByReleaseDateDescending),
Pair(R.id.mod_date, Sorter.Type.ByModifiedDateDescending)
)
val layoutMap = mapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,26 @@
package org.akanework.gramophone.ui.adapters

import android.net.Uri
import androidx.activity.viewModels
import android.view.View
import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.akanework.gramophone.R
import org.akanework.gramophone.ui.LibraryViewModel
import org.akanework.gramophone.ui.MediaControllerViewModel
import org.akanework.gramophone.ui.fragments.ArtistSubFragment
import org.akanework.gramophone.ui.fragments.DetailDialogFragment
import org.akanework.gramophone.ui.fragments.GeneralSubFragment
import org.akanework.gramophone.ui.registerLifecycleCallback
import java.util.GregorianCalendar


/**
Expand Down Expand Up @@ -96,7 +101,40 @@ class SongAdapter(

fun getActivity() = mainActivity

private val viewModel: LibraryViewModel by mainActivity.viewModels()
private val viewModel: LibraryViewModel by fragment.activityViewModels()
private val mediaControllerViewModel: MediaControllerViewModel by fragment.activityViewModels()
private val idToPosMap = hashMapOf<String, Int>()
private var currentMediaItem = mediaControllerViewModel.get()?.currentMediaItem?.mediaId
set(value) {
if (field != value) {
val oldValue = field
field = value
val oldPos = idToPosMap[oldValue]
val newPos = idToPosMap[value]
if (oldPos != null) {
notifyItemChanged(oldPos)
}
if (newPos != null) {
notifyItemChanged(newPos)
}
}
}

init {
mediaControllerViewModel.addOneOffControllerCallback(fragment.viewLifecycleOwner.lifecycle, false) {
it.registerLifecycleCallback(fragment.viewLifecycleOwner.lifecycle, object : Player.Listener {
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
currentMediaItem = mediaItem?.mediaId
}
})
}
}

override fun onListUpdated() {
// TODO run this method on a different thread / in advance
idToPosMap.clear()
list.forEachIndexed { i, item -> idToPosMap[item.mediaId] = i }
}

override fun virtualTitleOf(item: MediaItem): String {
return "null"
Expand Down Expand Up @@ -262,13 +300,22 @@ class SongAdapter(
}
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
super.onBindViewHolder(holder, position)
val item = list[position]
holder.nowPlaying.visibility =
if (currentMediaItem != null && item.mediaId == currentMediaItem)
View.VISIBLE else View.GONE
}

class MediaItemHelper(
types: Set<Sorter.Type> = setOf(
Sorter.Type.ByTitleDescending, Sorter.Type.ByTitleAscending,
Sorter.Type.ByArtistDescending, Sorter.Type.ByArtistAscending,
Sorter.Type.ByAlbumTitleDescending, Sorter.Type.ByAlbumTitleAscending,
Sorter.Type.ByAlbumArtistDescending, Sorter.Type.ByAlbumArtistAscending,
Sorter.Type.ByAddDateDescending, Sorter.Type.ByAddDateAscending,
Sorter.Type.ByReleaseDateDescending, Sorter.Type.ByReleaseDateAscending,
Sorter.Type.ByModifiedDateDescending, Sorter.Type.ByModifiedDateAscending
)
) : Sorter.Helper<MediaItem>(types) {
Expand Down Expand Up @@ -300,6 +347,13 @@ class SongAdapter(
return item.mediaMetadata.extras!!.getLong("AddDate")
}

override fun getReleaseDate(item: MediaItem): Long {
return GregorianCalendar((item.mediaMetadata.releaseYear ?: 0) + 1900,
(item.mediaMetadata.releaseMonth ?: 1) - 1,
item.mediaMetadata.releaseDay ?: 0, 0, 0, 0)
.timeInMillis
}

override fun getModifiedDate(item: MediaItem): Long {
return item.mediaMetadata.extras!!.getLong("ModifiedDate")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Sorter<T>(
open fun getAlbumArtist(item: T): String? = throw UnsupportedOperationException()
open fun getSize(item: T): Int = throw UnsupportedOperationException()
open fun getAddDate(item: T): Long = throw UnsupportedOperationException()
open fun getReleaseDate(item: T): Long = throw UnsupportedOperationException()
open fun getModifiedDate(item: T): Long = throw UnsupportedOperationException()
fun canGetTitle(): Boolean = typesSupported.contains(Type.ByTitleAscending)
|| typesSupported.contains(Type.ByTitleDescending)
Expand All @@ -62,6 +63,9 @@ class Sorter<T>(
fun canGetAddDate(): Boolean = typesSupported.contains(Type.ByAddDateAscending)
|| typesSupported.contains(Type.ByAddDateDescending)

fun canGetReleaseDate(): Boolean = typesSupported.contains(Type.ByReleaseDateAscending)
|| typesSupported.contains(Type.ByReleaseDateDescending)

fun canGetModifiedDate(): Boolean = typesSupported.contains(Type.ByModifiedDateAscending)
|| typesSupported.contains(Type.ByModifiedDateDescending)
}
Expand All @@ -77,8 +81,9 @@ class Sorter<T>(
ByAlbumArtistDescending, ByAlbumArtistAscending,
BySizeDescending, BySizeAscending,
NaturalOrder, ByAddDateDescending, ByAddDateAscending,
ByReleaseDateDescending, ByReleaseDateAscending,
ByModifiedDateDescending, ByModifiedDateAscending,
/* do not use nativeorder for smth other than title */
/* do not use NativeOrder for something other than title -> TODO why was that lol */
None, NativeOrder, NativeOrderDescending
}

Expand Down Expand Up @@ -172,6 +177,18 @@ class Sorter<T>(
)
}

Type.ByReleaseDateDescending -> {
SupportComparator.createInversionComparator(
compareBy { sortingHelper.getReleaseDate(it) }, true
)
}

Type.ByReleaseDateAscending -> {
SupportComparator.createInversionComparator(
compareBy { sortingHelper.getReleaseDate(it) }, false
)
}

Type.ByModifiedDateDescending -> {
SupportComparator.createInversionComparator(
compareBy { sortingHelper.getModifiedDate(it) }, true
Expand Down Expand Up @@ -223,6 +240,10 @@ class Sorter<T>(
CalculationUtils.convertUnixTimestampToMonthDay(sortingHelper.getAddDate(item))
}

Type.ByReleaseDateDescending, Type.ByReleaseDateAscending -> {
CalculationUtils.convertUnixTimestampToMonthDay(sortingHelper.getReleaseDate(item))
}

Type.ByModifiedDateDescending, Type.ByModifiedDateAscending -> {
CalculationUtils.convertUnixTimestampToMonthDay(sortingHelper.getAddDate(item))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ class FullBottomSheet(context: Context, attrs: AttributeSet?, defStyleAttr: Int,
}

fun onStop() {
instance?.removeListener(this)
runnableRunning = false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,6 @@ class PlayerBottomSheet private constructor(
.toWindowInsets()!!
}

fun getPlayer(): MediaController? = instance

@OptIn(ExperimentalCoilApi::class)
override fun onMediaItemTransition(
mediaItem: MediaItem?,
Expand Down Expand Up @@ -431,6 +429,7 @@ class PlayerBottomSheet private constructor(

override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
instance?.removeListener(this)
fullPlayer.onStop()
}

Expand Down
25 changes: 21 additions & 4 deletions app/src/main/res/layout/adapter_grid_card.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
app:layout_constraintTop_toBottomOf="@id/cover_frame"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/cover_frame"
app:layout_constraintEnd_toStartOf="@id/more"
app:layout_constraintEnd_toStartOf="@id/now_playing"
android:orientation="vertical"
tools:ignore="RtlSymmetry">

Expand Down Expand Up @@ -74,14 +74,31 @@

</LinearLayout>

<com.google.android.material.button.MaterialButton
android:id="@+id/now_playing"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintTop_toTopOf="@id/linearLayout"
app:layout_constraintBottom_toBottomOf="@id/linearLayout"
app:layout_constraintEnd_toStartOf="@id/more"
android:background="@drawable/rp_buttons"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
app:icon="@drawable/media3_icon_play"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />

<com.google.android.material.button.MaterialButton
android:id="@+id/more"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintTop_toTopOf="@id/linearLayout"
app:layout_constraintBottom_toBottomOf="@id/linearLayout"
app:layout_constraintStart_toEndOf="@id/linearLayout"
app:layout_constraintEnd_toEndOf="@id/cover_frame"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:visibility="gone" />

</androidx.constraintlayout.widget.ConstraintLayout>
Loading

0 comments on commit 8190b6e

Please sign in to comment.