diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 0ae057081..e03ac50ff 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -64,7 +64,7 @@ class AlbumDetailFragment : DetailFragment() { binding.lifecycleOwner = viewLifecycleOwner - setupToolbar(R.menu.menu_album_detail) { itemId -> + setupToolbar(detailModel.curAlbum.value!!, R.menu.menu_album_detail) { itemId -> if (itemId == R.id.action_queue_add) { playbackModel.addToUserQueue(detailModel.curAlbum.value!!) requireContext().showToast(R.string.lbl_queue_added) diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 6a3794798..775fd38a1 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -73,7 +73,7 @@ class ArtistDetailFragment : DetailFragment() { binding.lifecycleOwner = viewLifecycleOwner - setupToolbar() + setupToolbar(detailModel.curArtist.value!!) setupRecycler(detailAdapter) { pos -> // If the item is an ActionHeader we need to also make the item full-width val item = detailAdapter.currentList[pos] diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt new file mode 100644 index 000000000..267a96735 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt @@ -0,0 +1,133 @@ +package org.oxycblt.auxio.detail + +import android.animation.ValueAnimator +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import androidx.annotation.StyleRes +import androidx.appcompat.widget.AppCompatTextView +import androidx.appcompat.widget.Toolbar +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.appbar.AppBarLayout +import org.oxycblt.auxio.R +import org.oxycblt.auxio.ui.EdgeAppBarLayout + +/** + * An [EdgeAppBarLayout] variant that also shows the name of the toolbar whenever the detail + * recyclerview is scrolled beyond it's first item (a.k.a the header). This is used instead of + * CollapsingToolbarLayout since that thing is a mess with crippling bugs and state issues. + * This just works. + */ +class DetailAppBarLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + @StyleRes defStyleAttr: Int = -1 +) : EdgeAppBarLayout(context, attrs, defStyleAttr) { + private var mTitleView: AppCompatTextView? = null + private var mRecycler: RecyclerView? = null + + private var titleShown: Boolean? = null + private var mTitleAnimator: ValueAnimator? = null + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + + (layoutParams as CoordinatorLayout.LayoutParams).behavior = Behavior(context) + } + + private fun findTitleView(): AppCompatTextView { + val titleView = mTitleView + + if (titleView != null) { + return titleView + } + + val toolbar = findViewById(R.id.detail_toolbar) + + val newTitleView = Toolbar::class.java.getDeclaredField("mTitleTextView").run { + isAccessible = true + get(toolbar) as AppCompatTextView + } + + newTitleView.alpha = 0f + + mTitleView = newTitleView + return newTitleView + } + + private fun findRecyclerView(): RecyclerView { + val recycler = mRecycler + + if (recycler != null) { + return recycler + } + + val newRecycler = (parent as ViewGroup).findViewById(R.id.detail_recycler) + + mRecycler = newRecycler + return newRecycler + } + + private fun setTitleVisibility(visible: Boolean) { + if (titleShown == visible) return + + titleShown = visible + + if (mTitleAnimator != null) { + mTitleAnimator!!.cancel() + mTitleAnimator = null + } + + val titleView = findTitleView() + val from: Float + val to: Float + + if (visible) { + from = 0f + to = 1f + } else { + from = 1f + to = 0f + } + + if (titleView.alpha == to) return + + mTitleAnimator = ValueAnimator.ofFloat(from, to).apply { + addUpdateListener { + titleView.alpha = it.animatedValue as Float + } + + duration = resources.getInteger(R.integer.app_bar_elevation_anim_duration).toLong() + + start() + } + } + + class Behavior @JvmOverloads constructor( + context: Context? = null, + attrs: AttributeSet? = null + ) : AppBarLayout.Behavior(context, attrs) { + override fun onNestedPreScroll( + coordinatorLayout: CoordinatorLayout, + child: AppBarLayout, + target: View, + dx: Int, + dy: Int, + consumed: IntArray, + type: Int + ) { + super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type) + + val appBar = child as DetailAppBarLayout + val recycler = appBar.findRecyclerView() + + val showTitle = (recycler.layoutManager as LinearLayoutManager) + .findFirstVisibleItemPosition() > 0 + + appBar.setTitleVisibility(showTitle) + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt index cd9bd0365..070c95eae 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt @@ -31,6 +31,7 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentDetailBinding +import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.ui.SortMode import org.oxycblt.auxio.ui.memberBinding @@ -70,14 +71,18 @@ abstract class DetailFragment : Fragment() { /** * Shortcut method for doing setup of the detail toolbar. + * @param music Music data to use as the toolbar title * @param menu Menu resource to use * @param onMenuClick (Optional) a click listener for that menu */ protected fun setupToolbar( + data: Music, @MenuRes menu: Int = -1, onMenuClick: ((itemId: Int) -> Boolean)? = null ) { binding.detailToolbar.apply { + title = data.name + if (menu != -1) { inflateMenu(menu) } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index f900e55a3..2a1f471b7 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -64,7 +64,7 @@ class GenreDetailFragment : DetailFragment() { binding.lifecycleOwner = viewLifecycleOwner - setupToolbar() + setupToolbar(detailModel.curGenre.value!!) setupRecycler(detailAdapter) { pos -> val item = detailAdapter.currentList[pos] item is Header || item is ActionHeader || item is Genre diff --git a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollPopupDrawable.kt b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollPopupDrawable.kt index ba0fb1da6..aca7c3d41 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollPopupDrawable.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollPopupDrawable.kt @@ -47,6 +47,7 @@ import kotlin.math.sqrt * - Use modified Auxio resources instead of AFS resources * - Variable names are no longer prefixed with m * - Made path management compat-friendly + * - Converted to kotlin */ class FastScrollPopupDrawable(context: Context) : Drawable() { private val paint: Paint = Paint().apply { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarLayout.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarLayout.kt index 68d3c566e..599d721ed 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarLayout.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarLayout.kt @@ -38,6 +38,7 @@ import org.oxycblt.auxio.util.systemBarsCompat * this class was primarily written by me. * * TODO: Add a swipe-up behavior a la Phonograph. I think that would improve UX. + * - We need to use a separate drag helper to prevent issues * TODO: Leverage this layout to make more tablet-friendly UIs * * @author OxygenCobalt diff --git a/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt b/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt index 41b773a68..5dd3b8f8c 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt @@ -38,7 +38,7 @@ import org.oxycblt.auxio.util.systemBarsCompat * **Note:** This layout relies on [AppBarLayout.liftOnScrollTargetViewId] to figure out what * scrolling view to use. Failure to specify this will result in the layout not working. */ -class EdgeAppBarLayout @JvmOverloads constructor( +open class EdgeAppBarLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @StyleRes defStyleAttr: Int = -1 diff --git a/app/src/main/res/layout/fragment_detail.xml b/app/src/main/res/layout/fragment_detail.xml index 43942338c..db841e50b 100644 --- a/app/src/main/res/layout/fragment_detail.xml +++ b/app/src/main/res/layout/fragment_detail.xml @@ -13,7 +13,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - +