mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-13 22:00:30 +05:30
feat: split downloads fragment into audio and video category
This commit is contained in:
parent
db0dc4c4fc
commit
f0fb359b5d
@ -7,7 +7,6 @@ import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.db.DatabaseHolder.Database
|
||||
import com.github.libretube.enums.FileType
|
||||
import com.github.libretube.extensions.toAndroidUri
|
||||
|
@ -16,9 +16,11 @@ import com.github.libretube.databinding.DownloadedMediaRowBinding
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.db.obj.DownloadWithItems
|
||||
import com.github.libretube.extensions.formatAsFileSize
|
||||
import com.github.libretube.helpers.BackgroundHelper
|
||||
import com.github.libretube.helpers.ImageHelper
|
||||
import com.github.libretube.ui.activities.OfflinePlayerActivity
|
||||
import com.github.libretube.ui.base.BaseActivity
|
||||
import com.github.libretube.ui.fragments.DownloadTab
|
||||
import com.github.libretube.ui.sheets.DownloadOptionsBottomSheet
|
||||
import com.github.libretube.ui.sheets.DownloadOptionsBottomSheet.Companion.DELETE_DOWNLOAD_REQUEST_KEY
|
||||
import com.github.libretube.ui.viewholders.DownloadsViewHolder
|
||||
@ -32,6 +34,7 @@ import kotlin.io.path.fileSize
|
||||
|
||||
class DownloadsAdapter(
|
||||
private val context: Context,
|
||||
private val downloadTab: DownloadTab,
|
||||
private val downloads: MutableList<DownloadWithItems>,
|
||||
private val toggleDownload: (DownloadWithItems) -> Boolean
|
||||
) : RecyclerView.Adapter<DownloadsViewHolder>() {
|
||||
@ -98,9 +101,13 @@ class DownloadsAdapter(
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
val intent = Intent(root.context, OfflinePlayerActivity::class.java)
|
||||
intent.putExtra(IntentData.videoId, download.videoId)
|
||||
root.context.startActivity(intent)
|
||||
if (downloadTab == DownloadTab.VIDEO) {
|
||||
val intent = Intent(root.context, OfflinePlayerActivity::class.java)
|
||||
intent.putExtra(IntentData.videoId, download.videoId)
|
||||
root.context.startActivity(intent)
|
||||
} else {
|
||||
BackgroundHelper.playOnBackgroundOffline(root.context, download.videoId)
|
||||
}
|
||||
}
|
||||
|
||||
root.setOnLongClickListener {
|
||||
|
@ -11,20 +11,26 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.databinding.FragmentDownloadContentBinding
|
||||
import com.github.libretube.databinding.FragmentDownloadsBinding
|
||||
import com.github.libretube.db.DatabaseHolder.Database
|
||||
import com.github.libretube.db.obj.DownloadWithItems
|
||||
import com.github.libretube.enums.FileType
|
||||
import com.github.libretube.extensions.ceilHalf
|
||||
import com.github.libretube.extensions.formatAsFileSize
|
||||
import com.github.libretube.helpers.BackgroundHelper
|
||||
import com.github.libretube.extensions.serializable
|
||||
import com.github.libretube.helpers.DownloadHelper
|
||||
import com.github.libretube.helpers.PreferenceHelper
|
||||
import com.github.libretube.obj.DownloadStatus
|
||||
@ -35,20 +41,77 @@ import com.github.libretube.ui.base.DynamicLayoutManagerFragment
|
||||
import com.github.libretube.ui.sheets.BaseBottomSheet
|
||||
import com.github.libretube.ui.viewholders.DownloadsViewHolder
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.io.path.fileSize
|
||||
|
||||
class DownloadsFragment : DynamicLayoutManagerFragment() {
|
||||
enum class DownloadTab {
|
||||
VIDEO,
|
||||
AUDIO
|
||||
}
|
||||
|
||||
class DownloadsFragment : Fragment() {
|
||||
private var _binding: FragmentDownloadsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentDownloadsBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.downloadsPager.adapter = DownloadsFragmentAdapter(this)
|
||||
|
||||
TabLayoutMediator(binding.tabLayout, binding.downloadsPager) { tab, position ->
|
||||
tab.text = when (position) {
|
||||
DownloadTab.VIDEO.ordinal -> getString(R.string.video)
|
||||
DownloadTab.AUDIO.ordinal -> getString(R.string.audio)
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}.attach()
|
||||
}
|
||||
|
||||
fun bindDownloadService() {
|
||||
childFragmentManager.fragments.filterIsInstance<DownloadsFragmentPage>().forEach {
|
||||
it.bindDownloadService()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadsFragmentAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
||||
override fun getItemCount() = DownloadTab.entries.size
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return DownloadsFragmentPage().apply {
|
||||
arguments = bundleOf(IntentData.currentPosition to DownloadTab.entries[position])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadsFragmentPage : DynamicLayoutManagerFragment() {
|
||||
private lateinit var adapter: DownloadsAdapter
|
||||
private var _binding: FragmentDownloadContentBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private var binder: DownloadService.LocalBinder? = null
|
||||
private val downloads = mutableListOf<DownloadWithItems>()
|
||||
private val downloadReceiver = DownloadReceiver()
|
||||
private lateinit var downloadTabSelf: DownloadTab
|
||||
|
||||
private val serviceConnection = object : ServiceConnection {
|
||||
var isBound = false
|
||||
@ -71,21 +134,28 @@ class DownloadsFragment : DynamicLayoutManagerFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
this.downloadTabSelf = requireArguments().serializable(IntentData.currentPosition)!!
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentDownloadsBinding.inflate(inflater)
|
||||
_binding = FragmentDownloadContentBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun setLayoutManagers(gridItems: Int) {
|
||||
_binding?.downloads?.layoutManager = GridLayoutManager(context, gridItems.ceilHalf())
|
||||
_binding?.downloadsRecView?.layoutManager = GridLayoutManager(context, gridItems.ceilHalf())
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
var selectedSortType =
|
||||
PreferenceHelper.getInt(PreferenceKeys.SELECTED_DOWNLOAD_SORT_TYPE, 0)
|
||||
val filterOptions = resources.getStringArray(R.array.downloadSortOptions)
|
||||
@ -95,6 +165,7 @@ class DownloadsFragment : DynamicLayoutManagerFragment() {
|
||||
binding.sortType.text = filterOptions[index]
|
||||
if (::adapter.isInitialized) {
|
||||
sortDownloadList(index, selectedSortType)
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
selectedSortType = index
|
||||
PreferenceHelper.putInt(
|
||||
@ -104,96 +175,111 @@ class DownloadsFragment : DynamicLayoutManagerFragment() {
|
||||
}.show(childFragmentManager)
|
||||
}
|
||||
|
||||
val dbDownloads = runBlocking(Dispatchers.IO) {
|
||||
Database.downloadDao().getAll()
|
||||
}.takeIf { it.isNotEmpty() } ?: return
|
||||
|
||||
downloads.clear()
|
||||
downloads.addAll(dbDownloads)
|
||||
binding.downloadsEmpty.isGone = true
|
||||
binding.downloads.isVisible = true
|
||||
adapter = DownloadsAdapter(requireContext(), downloads) {
|
||||
var isDownloading = false
|
||||
val ids = it.downloadItems
|
||||
.filter { item -> item.path.fileSize() < item.downloadSize }
|
||||
.map { item -> item.id }
|
||||
|
||||
if (!serviceConnection.isBound) {
|
||||
DownloadHelper.startDownloadService(requireContext())
|
||||
bindDownloadService(ids.toIntArray())
|
||||
return@DownloadsAdapter true
|
||||
lifecycleScope.launch {
|
||||
val dbDownloads = withContext(Dispatchers.IO) {
|
||||
Database.downloadDao().getAll()
|
||||
}
|
||||
|
||||
binder?.getService()?.let { service ->
|
||||
isDownloading = ids.any { id -> service.isDownloading(id) }
|
||||
downloads.clear()
|
||||
downloads.addAll(dbDownloads.filter { dl ->
|
||||
when (downloadTabSelf) {
|
||||
DownloadTab.AUDIO -> {
|
||||
dl.downloadItems.any { it.type == FileType.AUDIO } && dl.downloadItems.none { it.type == FileType.VIDEO }
|
||||
}
|
||||
|
||||
ids.forEach { id ->
|
||||
if (isDownloading) {
|
||||
service.pause(id)
|
||||
} else {
|
||||
service.resume(id)
|
||||
DownloadTab.VIDEO -> {
|
||||
dl.downloadItems.any { it.type == FileType.VIDEO }
|
||||
}
|
||||
}
|
||||
}
|
||||
return@DownloadsAdapter isDownloading.not()
|
||||
}
|
||||
sortDownloadList(selectedSortType)
|
||||
binding.downloads.adapter = adapter
|
||||
})
|
||||
|
||||
val itemTouchCallback = object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
|
||||
override fun getMovementFlags(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int = makeMovementFlags(0, ItemTouchHelper.LEFT)
|
||||
if (downloads.isEmpty()) return@launch
|
||||
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean = false
|
||||
sortDownloadList(selectedSortType)
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
adapter.showDeleteDialog(requireContext(), viewHolder.absoluteAdapterPosition)
|
||||
// put the item back to the center, as it's currently out of the screen
|
||||
adapter.restoreItem(viewHolder.absoluteAdapterPosition)
|
||||
}
|
||||
}
|
||||
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(binding.downloads)
|
||||
adapter = DownloadsAdapter(requireContext(), downloadTabSelf, downloads) {
|
||||
var isDownloading = false
|
||||
val ids = it.downloadItems
|
||||
.filter { item -> item.path.fileSize() < item.downloadSize }
|
||||
.map { item -> item.id }
|
||||
|
||||
binding.downloads.adapter?.registerAdapterDataObserver(
|
||||
object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
super.onItemRangeRemoved(positionStart, itemCount)
|
||||
toggleButtonsVisibility()
|
||||
if (!serviceConnection.isBound) {
|
||||
DownloadHelper.startDownloadService(requireContext())
|
||||
bindDownloadService(ids.toIntArray())
|
||||
return@DownloadsAdapter true
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
toggleButtonsVisibility()
|
||||
binder?.getService()?.let { service ->
|
||||
isDownloading = ids.any { id -> service.isDownloading(id) }
|
||||
|
||||
ids.forEach { id ->
|
||||
if (isDownloading) {
|
||||
service.pause(id)
|
||||
} else {
|
||||
service.resume(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return@DownloadsAdapter isDownloading.not()
|
||||
}
|
||||
binding.downloadsRecView.adapter = adapter
|
||||
|
||||
val itemTouchCallback =
|
||||
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
|
||||
override fun getMovementFlags(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int = makeMovementFlags(0, ItemTouchHelper.LEFT)
|
||||
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean = false
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
adapter.showDeleteDialog(
|
||||
requireContext(),
|
||||
viewHolder.absoluteAdapterPosition
|
||||
)
|
||||
// put the item back to the center, as it's currently out of the screen
|
||||
adapter.restoreItem(viewHolder.absoluteAdapterPosition)
|
||||
}
|
||||
}
|
||||
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(binding.downloadsRecView)
|
||||
|
||||
binding.downloadsRecView.adapter?.registerAdapterDataObserver(
|
||||
object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
super.onItemRangeRemoved(positionStart, itemCount)
|
||||
toggleVisibilities()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
toggleVisibilities()
|
||||
}
|
||||
|
||||
binding.deleteAll.setOnClickListener {
|
||||
showDeleteAllDialog(binding.root.context, adapter)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleButtonsVisibility() {
|
||||
private fun toggleVisibilities() {
|
||||
val binding = _binding ?: return
|
||||
|
||||
val isEmpty = binding.downloads.adapter?.itemCount == 0
|
||||
val isEmpty = downloads.isEmpty()
|
||||
binding.downloadsEmpty.isVisible = isEmpty
|
||||
binding.downloads.isGone = isEmpty
|
||||
binding.sortType.isGone = isEmpty
|
||||
binding.downloadsContainer.isGone = isEmpty
|
||||
binding.deleteAll.isGone = isEmpty
|
||||
}
|
||||
|
||||
private fun sortDownloadList(sortType: Int, previousSortType: Int? = null) {
|
||||
if (previousSortType == null && sortType == 1) {
|
||||
downloads.reverse()
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
if (previousSortType != null && sortType != previousSortType) {
|
||||
downloads.reverse()
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,7 +332,7 @@ class DownloadsFragment : DynamicLayoutManagerFragment() {
|
||||
it.downloadItems.any { item -> item.id == id }
|
||||
}
|
||||
val view =
|
||||
_binding?.downloads?.findViewHolderForAdapterPosition(index) as? DownloadsViewHolder
|
||||
_binding?.downloadsRecView?.findViewHolderForAdapterPosition(index) as? DownloadsViewHolder
|
||||
|
||||
view?.binding?.apply {
|
||||
when (status) {
|
||||
@ -293,4 +379,4 @@ class DownloadsFragment : DynamicLayoutManagerFragment() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
}
|
79
app/src/main/res/layout/fragment_download_content.xml
Normal file
79
app/src/main/res/layout/fragment_download_content.xml
Normal file
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/downloads_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sort_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="6dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginVertical="4dp"
|
||||
android:clickable="true"
|
||||
android:drawablePadding="10dp"
|
||||
android:focusable="true"
|
||||
android:layout_gravity="end"
|
||||
android:paddingVertical="5dp"
|
||||
android:text="@string/sort_by"
|
||||
android:textSize="15sp"
|
||||
app:drawableEndCompat="@drawable/ic_sort"
|
||||
android:background="@drawable/rounded_ripple" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/downloads_recView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/downloads_empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:src="@drawable/ic_download" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/emptyList"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/delete_all"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="18dp"
|
||||
android:layout_marginBottom="18dp"
|
||||
android:contentDescription="@string/delete_all"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:tooltipText="@string/delete"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:targetApi="o"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,79 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/colorBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sort_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:clickable="true"
|
||||
android:drawablePadding="10dp"
|
||||
android:focusable="true"
|
||||
android:fontFamily="@font/roboto"
|
||||
android:paddingVertical="5dp"
|
||||
android:text="@string/sort_by"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
app:drawableEndCompat="@drawable/ic_sort"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/downloads_empty"
|
||||
android:id="@+id/downloads_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:src="@drawable/ic_download" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/emptyList"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
app:tabMode="scrollable" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/downloads_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/downloads"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/sort_type" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/delete_all"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="18dp"
|
||||
android:layout_marginBottom="18dp"
|
||||
android:contentDescription="@string/shuffle"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:tooltipText="@string/delete"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:targetApi="o"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue
Block a user