Commit dbacc708 authored by Geoffrey Métais's avatar Geoffrey Métais

Extract animation delegate

parent b2414040
......@@ -14,7 +14,7 @@
<variable
name="holder"
type="org.videolan.vlc.gui.tv.SongHeaderAdapter.ViewHolder" />
type="org.videolan.vlc.gui.tv.MediaHeaderAdapter.ViewHolder" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
......
/*
* ************************************************************************
* MediaBrowserTvFragment.kt
* *************************************************************************
* Copyright © 2019 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*
* *************************************************************************
*/
package org.videolan.vlc.gui.tv
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.ChangeBounds
import androidx.transition.Transition
import androidx.transition.TransitionManager
import kotlinx.android.synthetic.main.song_browser.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import org.videolan.vlc.R
import org.videolan.vlc.gui.tv.browser.MediaBrowserTvFragment
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
internal class MediaBrowserAnimatorDelegate(val browser: MediaBrowserTvFragment, private val cl: ConstraintLayout) : RecyclerView.OnScrollListener(), View.OnFocusChangeListener {
private val scrolledUpConstraintSet = ConstraintSet()
private val scrolledNoFABConstraintSet = ConstraintSet()
private val scrolledFABConstraintSet = ConstraintSet()
private val transition = ChangeBounds().apply {
interpolator = AccelerateDecelerateInterpolator()
duration = 300
}
private val fakeToolbar = browser.toolbar
private val fabSettings = browser.imageButtonSettings
private val fabHeader = browser.imageButtonHeader
private val fabSort = browser.imageButtonSort
var menuHidden = false
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (recyclerView.computeVerticalScrollOffset() > 0) {
if (!menuHidden) {
TransitionManager.beginDelayedTransition(cl, transition)
scrolledNoFABConstraintSet.applyTo(cl)
menuHidden = true
}
} else if (menuHidden) {
TransitionManager.beginDelayedTransition(cl, transition)
scrolledUpConstraintSet.applyTo(cl)
menuHidden = false
}
}
override fun onFocusChange(v: View, hasFocus: Boolean) {
when(v) {
browser.headerButton -> browser.headerDescription
browser.sortButton -> browser.sortDescription
else -> null
}?.animate()?.alpha(if (hasFocus) 1f else 0f)
}
internal fun expandExtendedFAB() {
browser.sortDescription.animate().alpha(1f)
browser.headerDescription.animate().alpha(1f)
TransitionManager.beginDelayedTransition(cl, transition)
scrolledFABConstraintSet.applyTo(cl)
}
internal fun collapseExtendedFAB() {
browser.sortDescription.animate().alpha(0f)
browser.headerDescription.animate().alpha(0f)
TransitionManager.beginDelayedTransition(cl, transition)
scrolledNoFABConstraintSet.applyTo(cl)
}
internal fun hideFAB() {
val marginBottom = (fabSettings.layoutParams as ConstraintLayout.LayoutParams).bottomMargin.toFloat()
fabSettings.animate().translationY(fabSettings.height + marginBottom)
fabHeader.animate().translationY(fabSettings.height + marginBottom)
fabSort.animate().translationY(fabSettings.height + marginBottom)
fabSettings.isFocusable = false
fabHeader.isFocusable = false
fabSort.isFocusable = false
fakeToolbar.visibility = View.GONE
}
internal fun showFAB() {
fabSettings.animate().translationY(0f).setListener(null)
fabHeader.animate().translationY(0f)
fabSort.animate().translationY(0f)
fabSettings.isFocusable = true
fabHeader.isFocusable = true
fabSort.isFocusable = true
fakeToolbar.visibility = View.VISIBLE
}
init {
scrolledUpConstraintSet.clone(cl)
scrolledNoFABConstraintSet.clone(cl)
//Buttons
scrolledNoFABConstraintSet.setMargin(R.id.sortButton, ConstraintSet.START, 0)
scrolledNoFABConstraintSet.setMargin(R.id.sortButton, ConstraintSet.END, 0)
scrolledNoFABConstraintSet.setMargin(R.id.sortButton, ConstraintSet.TOP, 0)
scrolledNoFABConstraintSet.setMargin(R.id.sortButton, ConstraintSet.BOTTOM, 0)
scrolledNoFABConstraintSet.setMargin(R.id.headerButton, ConstraintSet.START, 0)
scrolledNoFABConstraintSet.setMargin(R.id.headerButton, ConstraintSet.END, 0)
scrolledNoFABConstraintSet.setMargin(R.id.headerButton, ConstraintSet.TOP, 0)
scrolledNoFABConstraintSet.setMargin(R.id.headerButton, ConstraintSet.BOTTOM, 0)
scrolledNoFABConstraintSet.connect(R.id.sortButton, ConstraintSet.START, R.id.imageButtonSettings, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.sortButton, ConstraintSet.END, R.id.imageButtonSettings, ConstraintSet.END)
scrolledNoFABConstraintSet.connect(R.id.sortButton, ConstraintSet.TOP, R.id.imageButtonSettings, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.sortButton, ConstraintSet.BOTTOM, R.id.imageButtonSettings, ConstraintSet.BOTTOM)
scrolledNoFABConstraintSet.connect(R.id.headerButton, ConstraintSet.START, R.id.imageButtonSettings, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.headerButton, ConstraintSet.END, R.id.imageButtonSettings, ConstraintSet.END)
scrolledNoFABConstraintSet.connect(R.id.headerButton, ConstraintSet.TOP, R.id.imageButtonSettings, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.headerButton, ConstraintSet.BOTTOM, R.id.imageButtonSettings, ConstraintSet.BOTTOM)
//descriptions
scrolledNoFABConstraintSet.clear(R.id.sortDescription, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.sortDescription, ConstraintSet.END, R.id.imageButtonSort, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.sortDescription, ConstraintSet.TOP, R.id.imageButtonSort, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.sortDescription, ConstraintSet.BOTTOM, R.id.imageButtonSort, ConstraintSet.BOTTOM)
scrolledNoFABConstraintSet.clear(R.id.headerDescription, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.headerDescription, ConstraintSet.END, R.id.imageButtonHeader, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.headerDescription, ConstraintSet.TOP, R.id.imageButtonHeader, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.headerDescription, ConstraintSet.BOTTOM, R.id.imageButtonHeader, ConstraintSet.BOTTOM)
scrolledNoFABConstraintSet.constrainMaxHeight(R.id.title, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.title, ConstraintSet.BOTTOM, 0, ConstraintSet.TOP)
//FAB hiding
scrolledNoFABConstraintSet.connect(R.id.imageButtonSettings, ConstraintSet.BOTTOM, 0, ConstraintSet.BOTTOM)
scrolledNoFABConstraintSet.clear(R.id.imageButtonSettings, ConstraintSet.TOP)
scrolledFABConstraintSet.clone(scrolledNoFABConstraintSet)
scrolledFABConstraintSet.clear(R.id.imageButtonHeader, ConstraintSet.TOP)
scrolledFABConstraintSet.clear(R.id.imageButtonSort, ConstraintSet.TOP)
scrolledFABConstraintSet.connect(R.id.imageButtonHeader, ConstraintSet.BOTTOM, R.id.imageButtonSettings, ConstraintSet.TOP)
scrolledFABConstraintSet.connect(R.id.imageButtonSort, ConstraintSet.BOTTOM, R.id.imageButtonHeader, ConstraintSet.TOP)
transition.addListener(object : Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition) {
if (menuHidden) {
fakeToolbar.visibility = View.GONE
} else {
fakeToolbar.visibility = View.VISIBLE
}
}
override fun onTransitionResume(transition: Transition) {}
override fun onTransitionPause(transition: Transition) {}
override fun onTransitionCancel(transition: Transition) {}
override fun onTransitionStart(transition: Transition) {}
})
fabSettings.setOnClickListener { expandExtendedFAB() }
val fabOnFocusChangedListener = View.OnFocusChangeListener { v, hasFocus ->
if (!fabSettings.hasFocus() && !fabSort.hasFocus() && !fabHeader.hasFocus()) {
collapseExtendedFAB()
}
if (v == fabSettings && hasFocus) {
expandExtendedFAB()
}
}
fabSettings.onFocusChangeListener = fabOnFocusChangedListener
fabSort.onFocusChangeListener = fabOnFocusChangedListener
fabHeader.onFocusChangeListener = fabOnFocusChangedListener
}
}
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
fun MediaBrowserTvFragment.setAnimator(cl: ConstraintLayout) {
animationDelegate = MediaBrowserAnimatorDelegate(this, cl)
headerButton.onFocusChangeListener = animationDelegate
sortButton.onFocusChangeListener = animationDelegate
list.addOnScrollListener(animationDelegate)
}
......@@ -9,7 +9,7 @@ import org.videolan.medialibrary.Medialibrary
import org.videolan.vlc.R
import org.videolan.vlc.databinding.SongHeaderItemBinding
class SongHeaderAdapter(private val onHeaderSelected: OnHeaderSelected) : RecyclerView.Adapter<SongHeaderAdapter.ViewHolder>() {
class MediaHeaderAdapter(private val onHeaderSelected: OnHeaderSelected) : RecyclerView.Adapter<MediaHeaderAdapter.ViewHolder>() {
val alphaItems = listOf("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "#")
......@@ -17,9 +17,8 @@ class SongHeaderAdapter(private val onHeaderSelected: OnHeaderSelected) : Recycl
var items = ArrayList<String>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongHeaderAdapter.ViewHolder {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaHeaderAdapter.ViewHolder {
return ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.song_header_item, parent, false) as SongHeaderItemBinding)
}
override fun getItemCount(): Int {
......@@ -27,11 +26,9 @@ class SongHeaderAdapter(private val onHeaderSelected: OnHeaderSelected) : Recycl
Medialibrary.SORT_ALPHA -> alphaItems.size
else -> items.size
}
}
override fun onBindViewHolder(holder: SongHeaderAdapter.ViewHolder, position: Int) {
override fun onBindViewHolder(holder: MediaHeaderAdapter.ViewHolder, position: Int) {
if (sortType == Medialibrary.SORT_ALPHA) {
holder.binding.headerText = alphaItems[position]
......@@ -48,7 +45,6 @@ class SongHeaderAdapter(private val onHeaderSelected: OnHeaderSelected) : Recycl
} else {
items[position]
}
}
......@@ -61,11 +57,7 @@ class SongHeaderAdapter(private val onHeaderSelected: OnHeaderSelected) : Recycl
fun onClick(v: View) {
val item = getItem(layoutPosition)
onHeaderSelected.onHeaderSelected(item)
}
}
interface OnHeaderSelected {
......
......@@ -54,6 +54,7 @@ import org.videolan.vlc.media.MediaUtils
import org.videolan.vlc.providers.medialibrary.MedialibraryProvider
import org.videolan.vlc.util.*
import org.videolan.vlc.viewmodels.BaseModel
import org.videolan.vlc.viewmodels.paged.MLPagedModel
@ExperimentalCoroutinesApi
@ObsoleteCoroutinesApi
......@@ -172,7 +173,7 @@ object TvUtil {
is MediaWrapper -> when {
item.type == MediaWrapper.TYPE_AUDIO -> {
val list = withContext(Dispatchers.IO) {
(provider.getAll() as Array<MediaWrapper>).filter { it.itemType != MediaWrapper.TYPE_DIR }
(provider.getAll().toList()).filter { it.itemType != MediaWrapper.TYPE_DIR } as ArrayList<MediaWrapper>
}
val position = list.getposition(item)
playAudioList(activity, list, position)
......@@ -192,7 +193,7 @@ object TvUtil {
}
else -> {
val list = withContext(Dispatchers.IO) {
(provider.getAll() as Array<MediaWrapper>).filter { it.type != MediaWrapper.TYPE_DIR }
(provider.getAll().toList() as List<MediaWrapper>).filter { it.type != MediaWrapper.TYPE_DIR }
}
val position = list.getposition(item)
MediaUtils.openList(activity, list, position)
......
/*
* ************************************************************************
* AudioBrowserTvFragment.kt
* MediaBrowserTvFragment.kt
* *************************************************************************
* Copyright © 2016 VLC authors and VideoLAN
* Author: Geoffrey Métais
* Copyright © 2016-2019 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -36,21 +35,16 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.ImageButton
import android.widget.PopupMenu
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.Fragment
import androidx.leanback.app.BackgroundManager
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.ChangeBounds
import androidx.transition.Transition
import androidx.transition.TransitionManager
import kotlinx.android.synthetic.main.song_browser.*
import kotlinx.coroutines.*
import org.videolan.medialibrary.Medialibrary
import org.videolan.medialibrary.media.Folder
......@@ -58,53 +52,40 @@ import org.videolan.medialibrary.media.MediaLibraryItem
import org.videolan.vlc.BuildConfig
import org.videolan.vlc.R
import org.videolan.vlc.gui.audio.AudioBrowserAdapter
import org.videolan.vlc.gui.tv.SongHeaderAdapter
import org.videolan.vlc.gui.tv.MediaBrowserAnimatorDelegate
import org.videolan.vlc.gui.tv.MediaHeaderAdapter
import org.videolan.vlc.gui.tv.TvUtil
import org.videolan.vlc.gui.tv.browser.interfaces.BrowserFragmentInterface
import org.videolan.vlc.gui.tv.setAnimator
import org.videolan.vlc.gui.view.RecyclerSectionItemGridDecoration
import org.videolan.vlc.interfaces.IEventsHandler
import org.videolan.vlc.util.*
import org.videolan.vlc.viewmodels.paged.*
import java.util.*
private const val TAG = "MediaBrowserTvFragment"
@ExperimentalCoroutinesApi
@ObsoleteCoroutinesApi
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
class AudioBrowserTvFragment : Fragment(), BrowserFragmentInterface, IEventsHandler,
PopupMenu.OnMenuItemClickListener, SongHeaderAdapter.OnHeaderSelected,
class MediaBrowserTvFragment : Fragment(), BrowserFragmentInterface, IEventsHandler,
PopupMenu.OnMenuItemClickListener, MediaHeaderAdapter.OnHeaderSelected,
VerticalGridActivity.OnKeyPressedListener, CoroutineScope by MainScope() {
private lateinit var viewModel: MLPagedModel<MediaLibraryItem>
private lateinit var list: RecyclerView
private lateinit var adapter: AudioBrowserAdapter
private lateinit var headerList: RecyclerView
private lateinit var headerAdapter: SongHeaderAdapter
private lateinit var headerListContainer: View
private lateinit var fabSettings: ImageButton
private lateinit var fabHeader: ImageButton
private lateinit var fabSort: ImageButton
private lateinit var sortDescription: TextView
private lateinit var headerDescription: TextView
// private lateinit var headerList: RecyclerView
private lateinit var headerAdapter: MediaHeaderAdapter
private var nbColumns: Int = 0
private lateinit var gridLayoutManager: GridLayoutManager
private var currentItem: MediaLibraryItem? = null
private var currentArt: String? = null
private lateinit var backgroundManager: BackgroundManager
private lateinit var fakeToolbar: View
var menuHidden = false
private lateinit var cl: ConstraintLayout
private val scrolledUpConstraintSet = ConstraintSet()
private val scrolledNoFABConstraintSet = ConstraintSet()
private val scrolledFABConstraintSet = ConstraintSet()
val transition = ChangeBounds().apply {
interpolator = AccelerateDecelerateInterpolator()
duration = 500
}
internal lateinit var animationDelegate : MediaBrowserAnimatorDelegate
companion object {
fun newInstance(type: Long, item: MediaLibraryItem?) =
AudioBrowserTvFragment().apply {
MediaBrowserTvFragment().apply {
arguments = Bundle().apply {
this.putLong(AUDIO_CATEGORY, type)
this.putParcelable(AUDIO_ITEM, item)
......@@ -125,7 +106,6 @@ class AudioBrowserTvFragment : Fragment(), BrowserFragmentInterface, IEventsHand
currentItem = if (savedInstanceState != null) savedInstanceState.getParcelable<Parcelable>(AUDIO_ITEM) as? MediaLibraryItem
else requireActivity().intent.getParcelableExtra<Parcelable>(AUDIO_ITEM) as? MediaLibraryItem
when (arguments?.getLong(AUDIO_CATEGORY, CATEGORY_SONGS)) {
CATEGORY_SONGS ->
viewModel = ViewModelProviders.of(this, PagedTracksModel.Factory(requireContext(), currentItem)).get(PagedTracksModel::class.java) as MLPagedModel<MediaLibraryItem>
......@@ -144,7 +124,6 @@ class AudioBrowserTvFragment : Fragment(), BrowserFragmentInterface, IEventsHand
if (items != null) adapter.submitList(items)
//headers
var nbColumns = 1
when (viewModel.sort) {
......@@ -153,10 +132,11 @@ class AudioBrowserTvFragment : Fragment(), BrowserFragmentInterface, IEventsHand
headerList.layoutManager = GridLayoutManager(requireActivity(), nbColumns)
headerAdapter.sortType = viewModel.sort
val headers = viewModel.liveHeaders.value
val headerItems = ArrayList<String>()
for (i in 0 until headers!!.size()) {
headerItems.add(headers.valueAt(i))
viewModel.liveHeaders.value?.run {
for (i in 0 until size()) {
headerItems.add(valueAt(i))
}
}
headerAdapter.items = headerItems
headerAdapter.notifyDataSetChanged()
......@@ -166,28 +146,14 @@ class AudioBrowserTvFragment : Fragment(), BrowserFragmentInterface, IEventsHand
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
list = view.findViewById(R.id.list)
headerList = view.findViewById(R.id.headerList)
headerListContainer = view.findViewById(R.id.headerListContainer)
val title = view.findViewById<TextView>(R.id.title)
val sortButton = view.findViewById<ImageButton>(R.id.sortButton)
val headerButton = view.findViewById<ImageButton>(R.id.headerButton)
fabSettings = view.findViewById(R.id.imageButtonSettings)
fabHeader = view.findViewById(R.id.imageButtonHeader)
fabSort = view.findViewById(R.id.imageButtonSort)
fakeToolbar = view.findViewById(R.id.toolbar)
sortDescription = view.findViewById(R.id.sortDescription)
headerDescription = view.findViewById(R.id.headerDescription)
//overscan
val hp = TvUtil.getOverscanHorizontal(requireContext())
val vp = TvUtil.getOverscanVertical(requireContext())
list.setPadding(list.paddingLeft + hp, list.paddingTop + vp, list.paddingRight + hp, list.paddingBottom + vp)
headerList.setPadding(list.paddingLeft + hp, list.paddingTop + vp, list.paddingRight + hp, list.paddingBottom + vp)
fabSettings.setOnClickListener { expandExtendedFAB() }
val lp = (fabSettings.layoutParams as ConstraintLayout.LayoutParams)
val lp = (imageButtonSettings.layoutParams as ConstraintLayout.LayoutParams)
lp.leftMargin += hp
lp.rightMargin += hp
lp.topMargin += vp
......@@ -207,47 +173,23 @@ class AudioBrowserTvFragment : Fragment(), BrowserFragmentInterface, IEventsHand
headerListContainer.visibility = View.VISIBLE
headerList.requestFocus()
list.visibility = View.GONE
hideFAB()
animationDelegate.hideFAB()
}
val sortClick: (View) -> Unit = { v -> sort(v) }
headerButton.setOnClickListener(searchHeaderClick)
fabHeader.setOnClickListener(searchHeaderClick)
headerButton.setOnFocusChangeListener { view: View, focused: Boolean ->
headerDescription.animate().alpha(if (focused) 1f else 0f)
}
sortButton.setOnFocusChangeListener { view: View, focused: Boolean ->
sortDescription.animate().alpha(if (focused) 1f else 0f)
}
imageButtonHeader.setOnClickListener(searchHeaderClick)
sortButton.setOnClickListener(sortClick)
fabSort.setOnClickListener(sortClick)
val fabOnFocusChangedListener = View.OnFocusChangeListener { v, hasFocus ->
if (!fabSettings.hasFocus() && !fabSort.hasFocus() && !fabHeader.hasFocus()) {
collapseExtendedFAB()
}
if (v == fabSettings && hasFocus) {
expandExtendedFAB()
}
}
fabSettings.onFocusChangeListener = fabOnFocusChangedListener
fabSort.onFocusChangeListener = fabOnFocusChangedListener
fabHeader.onFocusChangeListener = fabOnFocusChangedListener
imageButtonSort.setOnClickListener(sortClick)
nbColumns = resources.getInteger(R.integer.tv_songs_col_count)
gridLayoutManager = object : GridLayoutManager(requireActivity(), nbColumns) {
override fun requestChildRectangleOnScreen(parent: RecyclerView, child: View, rect: Rect, immediate: Boolean): Boolean {
return false
}
override fun requestChildRectangleOnScreen(parent: RecyclerView, child: View, rect: Rect, immediate: Boolean) = false
override fun requestChildRectangleOnScreen(parent: RecyclerView, child: View, rect: Rect, immediate: Boolean, focusedChildVisible: Boolean): Boolean {
return false
}
override fun requestChildRectangleOnScreen(parent: RecyclerView, child: View, rect: Rect, immediate: Boolean, focusedChildVisible: Boolean) = false
}
val spacing = resources.getDimensionPixelSize(R.dimen.recycler_section_header_spacing)
......@@ -271,119 +213,19 @@ class AudioBrowserTvFragment : Fragment(), BrowserFragmentInterface, IEventsHand
return nbColumns - nbItems % nbColumns
}
return 1
}
}
list.layoutManager = gridLayoutManager
adapter = AudioBrowserAdapter(MediaLibraryItem.TYPE_MEDIA, this, itemSize)
adapter.setTV(true)
adapter = AudioBrowserAdapter(MediaLibraryItem.TYPE_MEDIA, this, itemSize).apply { setTV(true) }
list.addItemDecoration(RecyclerSectionItemGridDecoration(resources.getDimensionPixelSize(R.dimen.recycler_section_header_tv_height), spacing, true, nbColumns, viewModel))
list.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (list.computeVerticalScrollOffset() > 0) {
if (!menuHidden) {
if (!this@AudioBrowserTvFragment::cl.isInitialized) {
cl = view as ConstraintLayout
scrolledUpConstraintSet.clone(cl)
scrolledNoFABConstraintSet.clone(cl)
//Buttons
scrolledNoFABConstraintSet.setMargin(R.id.sortButton, ConstraintSet.START, 0)
scrolledNoFABConstraintSet.setMargin(R.id.sortButton, ConstraintSet.END, 0)
scrolledNoFABConstraintSet.setMargin(R.id.sortButton, ConstraintSet.TOP, 0)
scrolledNoFABConstraintSet.setMargin(R.id.sortButton, ConstraintSet.BOTTOM, 0)
scrolledNoFABConstraintSet.setMargin(R.id.headerButton, ConstraintSet.START, 0)
scrolledNoFABConstraintSet.setMargin(R.id.headerButton, ConstraintSet.END, 0)
scrolledNoFABConstraintSet.setMargin(R.id.headerButton, ConstraintSet.TOP, 0)
scrolledNoFABConstraintSet.setMargin(R.id.headerButton, ConstraintSet.BOTTOM, 0)
scrolledNoFABConstraintSet.connect(R.id.sortButton, ConstraintSet.START, fabSettings.id, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.sortButton, ConstraintSet.END, fabSettings.id, ConstraintSet.END)
scrolledNoFABConstraintSet.connect(R.id.sortButton, ConstraintSet.TOP, fabSettings.id, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.sortButton, ConstraintSet.BOTTOM, fabSettings.id, ConstraintSet.BOTTOM)
scrolledNoFABConstraintSet.connect(R.id.headerButton, ConstraintSet.START, fabSettings.id, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.headerButton, ConstraintSet.END, fabSettings.id, ConstraintSet.END)
scrolledNoFABConstraintSet.connect(R.id.headerButton, ConstraintSet.TOP, fabSettings.id, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.headerButton, ConstraintSet.BOTTOM, fabSettings.id, ConstraintSet.BOTTOM)
//descriptions
scrolledNoFABConstraintSet.clear(R.id.sortDescription, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.sortDescription, ConstraintSet.END, fabSort.id, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.sortDescription, ConstraintSet.TOP, fabSort.id, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.sortDescription, ConstraintSet.BOTTOM, fabSort.id, ConstraintSet.BOTTOM)
scrolledNoFABConstraintSet.clear(R.id.headerDescription, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.headerDescription, ConstraintSet.END, fabHeader.id, ConstraintSet.START)
scrolledNoFABConstraintSet.connect(R.id.headerDescription, ConstraintSet.TOP, fabHeader.id, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.headerDescription, ConstraintSet.BOTTOM, fabHeader.id, ConstraintSet.BOTTOM)
scrolledNoFABConstraintSet.constrainMaxHeight(R.id.title, ConstraintSet.TOP)
scrolledNoFABConstraintSet.connect(R.id.title, ConstraintSet.BOTTOM, 0, ConstraintSet.TOP)
//FAB hiding
scrolledNoFABConstraintSet.connect(R.id.imageButtonSettings, ConstraintSet.BOTTOM, 0, ConstraintSet.BOTTOM)
scrolledNoFABConstraintSet.clear(R.id.imageButtonSettings, ConstraintSet.TOP)
scrolledFABConstraintSet.clone(scrolledNoFABConstraintSet)
scrolledFABConstraintSet.clear(fabHeader.id, ConstraintSet.TOP)
scrolledFABConstraintSet.clear(fabSort.id, ConstraintSet.TOP)
scrolledFABConstraintSet.connect(fabHeader.id, ConstraintSet.BOTTOM, fabSettings.id, ConstraintSet.TOP)
scrolledFABConstraintSet.connect(fabSort.id, ConstraintSet.BOTTOM, fabHeader.id, ConstraintSet.TOP)
transition.addListener(object : Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition) {
if (menuHidden) {
fakeToolbar.visibility = View.GONE
} else {
fakeToolbar.visibility = View.VISIBLE
}
}
override fun onTransitionResume(transition: Transition) {
}
override fun onTransitionPause(transition: Transition) {
}
override fun onTransitionCancel(transition: Transition) {
}
override fun onTransitionStart(transition: Transition) {
}
})
}
TransitionManager.beginDelayedTransition(cl, transition)
scrolledNoFABConstraintSet.applyTo(cl)
menuHidden = true
}
} else if (menuHidden) {
TransitionManager.beginDelayedTransition(cl, transition)
scrolledUpConstraintSet.applyTo(cl)
menuHidden = false
}
}
})
//header list
headerListContainer.visibility = View.GONE
headerAdapter = SongHeaderAdapter(this)
headerAdapter = MediaHeaderAdapter(this)
headerList.adapter = headerAdapter
headerList.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
......@@ -394,52 +236,10 @@ class AudioBrowserTvFragment : Fragment(), BrowserFragmentInterface, IEventsHand
outRect.right = 2
}
})
setAnimator(view as ConstraintLayout)
super.onViewCreated(view, savedInstanceState)
}
private fun expandExtendedFAB() {
// fabHeader.animate().translationY(-(resources.getDimension(R.dimen.kl_normal) + fabHeader.height))
// fabSort.animate().translationY(-2 * (resources.getDimension(R.dimen.kl_normal) + fabHeader.height))
sortDescription.animate().alpha(1f)
headerDescription.animate().alpha(1f)
TransitionManager.beginDelayedTransition(cl, transition)
scrolledFABConstraintSet.applyTo(cl)
}
private fun collapseExtendedFAB() {
// fabHeader.animate().translationY(0f)
// fabSort.animate().translationY(0f)
sortDescription.animate().alpha(0f)
headerDescription.animate().alpha(0f)
TransitionManager.beginDelayedTransition(cl, transition)
scrolledNoFABConstraintSet.applyTo(cl)
}
private fun hideFAB() {
val marginBottom = (fabSettings.layoutParams as ConstraintLayout.LayoutParams).bottomMargin.toFloat()
fabSettings.animate().translationY(fabSettings.height + mar