mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Push initial UI
This commit is contained in:
parent
3016a5176f
commit
068b218f7a
8 changed files with 276 additions and 157 deletions
|
@ -27,7 +27,6 @@ const val DOWNLOAD_ACTION_RESUME_DOWNLOAD = 2
|
|||
const val DOWNLOAD_ACTION_PAUSE_DOWNLOAD = 3
|
||||
const val DOWNLOAD_ACTION_DOWNLOAD = 4
|
||||
const val DOWNLOAD_ACTION_LONG_CLICK = 5
|
||||
const val DOWNLOAD_ACTION_DELETE_MULTIPLE_FILES = 6
|
||||
|
||||
const val DOWNLOAD_ACTION_GO_TO_CHILD = 0
|
||||
const val DOWNLOAD_ACTION_LOAD_RESULT = 1
|
||||
|
@ -58,15 +57,11 @@ abstract class VisualDownloadCached(
|
|||
}
|
||||
}
|
||||
|
||||
abstract class DownloadActionEventBase(
|
||||
open val action: Int,
|
||||
open val data: VideoDownloadHelper.DownloadEpisodeCached?
|
||||
)
|
||||
|
||||
data class VisualDownloadChildCached(
|
||||
override val currentBytes: Long,
|
||||
override val totalBytes: Long,
|
||||
override val data: VideoDownloadHelper.DownloadEpisodeCached,
|
||||
val selected: Boolean = false,
|
||||
): VisualDownloadCached(currentBytes, totalBytes, data)
|
||||
|
||||
data class VisualDownloadHeaderCached(
|
||||
|
@ -76,17 +71,13 @@ data class VisualDownloadHeaderCached(
|
|||
val child: VideoDownloadHelper.DownloadEpisodeCached?,
|
||||
val currentOngoingDownloads: Int,
|
||||
val totalDownloads: Int,
|
||||
val selected: Boolean = false,
|
||||
): VisualDownloadCached(currentBytes, totalBytes, data)
|
||||
|
||||
data class DownloadClickEvent(
|
||||
override val action: Int,
|
||||
override val data: VideoDownloadHelper.DownloadEpisodeCached
|
||||
): DownloadActionEventBase(action, data)
|
||||
|
||||
data class DownloadDeleteEvent(
|
||||
override val action: Int,
|
||||
val items: List<VideoDownloadHelper.DownloadEpisodeCached?>
|
||||
): DownloadActionEventBase(action, null)
|
||||
val action: Int,
|
||||
val data: VideoDownloadHelper.DownloadEpisodeCached
|
||||
)
|
||||
|
||||
data class DownloadHeaderClickEvent(
|
||||
val action: Int,
|
||||
|
@ -94,19 +85,20 @@ data class DownloadHeaderClickEvent(
|
|||
)
|
||||
|
||||
class DownloadAdapter(
|
||||
private val actionCallback: (DownloadActionEventBase) -> Unit,
|
||||
private val clickCallback: (DownloadHeaderClickEvent) -> Unit,
|
||||
private val headerClickCallback: (DownloadHeaderClickEvent) -> Unit,
|
||||
private val mediaClickCallback: (DownloadClickEvent) -> Unit,
|
||||
private val selectedChangedCallback: (Int, String, Boolean) -> Unit,
|
||||
) : ListAdapter<VisualDownloadCached, DownloadAdapter.DownloadViewHolder>(DiffCallback()) {
|
||||
|
||||
private var showDeleteCheckbox: Boolean = false
|
||||
|
||||
companion object {
|
||||
private const val VIEW_TYPE_HEADER = 0
|
||||
private const val VIEW_TYPE_CHILD = 1
|
||||
}
|
||||
|
||||
inner class DownloadViewHolder(
|
||||
private val binding: ViewBinding,
|
||||
private val actionCallback: (DownloadActionEventBase) -> Unit,
|
||||
private val clickCallback: (DownloadHeaderClickEvent) -> Unit,
|
||||
private val binding: ViewBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(card: VisualDownloadCached?) {
|
||||
|
@ -116,86 +108,105 @@ class DownloadAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindHeader(card: VisualDownloadHeaderCached?) {
|
||||
if (binding !is DownloadHeaderEpisodeBinding) return
|
||||
card ?: return
|
||||
val d = card.data
|
||||
if (binding !is DownloadHeaderEpisodeBinding || card == null) return
|
||||
|
||||
val data = card.data
|
||||
binding.apply {
|
||||
downloadHeaderPoster.apply {
|
||||
setImage(d.poster)
|
||||
setImage(data.poster)
|
||||
setOnClickListener {
|
||||
clickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_LOAD_RESULT, d))
|
||||
headerClickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_LOAD_RESULT, data))
|
||||
}
|
||||
}
|
||||
|
||||
downloadHeaderTitle.text = d.name
|
||||
val formattedSizeString = formatShortFileSize(itemView.context, card.totalBytes)
|
||||
downloadHeaderTitle.text = data.name
|
||||
val formattedSize = formatShortFileSize(itemView.context, card.totalBytes)
|
||||
|
||||
if (card.child != null) {
|
||||
downloadHeaderGotoChild.isVisible = false
|
||||
handleChildDownload(card, formattedSize)
|
||||
} else handleParentDownload(card, formattedSize)
|
||||
}
|
||||
}
|
||||
|
||||
val status = downloadButton.getStatus(card.child.id, card.currentBytes, card.totalBytes)
|
||||
if (status == DownloadStatusTell.IsDone) {
|
||||
// We do this here instead if we are finished downloading
|
||||
// so that we can use the value from the view model
|
||||
// rather than extra unneeded disk operations and to prevent a
|
||||
// delay in updating download icon state.
|
||||
downloadButton.setProgress(card.currentBytes, card.totalBytes)
|
||||
downloadButton.applyMetaData(card.child.id, card.currentBytes, card.totalBytes)
|
||||
// We will let the view model handle this
|
||||
downloadButton.doSetProgress = false
|
||||
downloadButton.progressBar.progressDrawable =
|
||||
downloadButton.getDrawableFromStatus(status)
|
||||
?.let { ContextCompat.getDrawable(downloadButton.context, it) }
|
||||
downloadHeaderInfo.text = formattedSizeString
|
||||
} else {
|
||||
downloadButton.doSetProgress = true
|
||||
downloadButton.progressBar.progressDrawable =
|
||||
ContextCompat.getDrawable(downloadButton.context, downloadButton.progressDrawable)
|
||||
}
|
||||
private fun DownloadHeaderEpisodeBinding.handleChildDownload(
|
||||
card: VisualDownloadHeaderCached,
|
||||
formattedSize: String
|
||||
) {
|
||||
card.child ?: return
|
||||
downloadHeaderGotoChild.isVisible = false
|
||||
|
||||
downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, actionCallback)
|
||||
downloadButton.isVisible = true
|
||||
val status = downloadButton.getStatus(card.child.id, card.currentBytes, card.totalBytes)
|
||||
if (status == DownloadStatusTell.IsDone) {
|
||||
// We do this here instead if we are finished downloading
|
||||
// so that we can use the value from the view model
|
||||
// rather than extra unneeded disk operations and to prevent a
|
||||
// delay in updating download icon state.
|
||||
downloadButton.setProgress(card.currentBytes, card.totalBytes)
|
||||
downloadButton.applyMetaData(card.child.id, card.currentBytes, card.totalBytes)
|
||||
// We will let the view model handle this
|
||||
downloadButton.doSetProgress = false
|
||||
downloadButton.progressBar.progressDrawable =
|
||||
downloadButton.getDrawableFromStatus(status)
|
||||
?.let { ContextCompat.getDrawable(downloadButton.context, it) }
|
||||
downloadHeaderInfo.text = formattedSize
|
||||
} else {
|
||||
downloadButton.doSetProgress = true
|
||||
downloadButton.progressBar.progressDrawable =
|
||||
ContextCompat.getDrawable(downloadButton.context, downloadButton.progressDrawable)
|
||||
}
|
||||
|
||||
episodeHolder.setOnClickListener {
|
||||
actionCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, card.child))
|
||||
}
|
||||
} else {
|
||||
downloadButton.isVisible = false
|
||||
downloadHeaderGotoChild.isVisible = true
|
||||
downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, mediaClickCallback)
|
||||
downloadButton.isVisible = !showDeleteCheckbox
|
||||
|
||||
try {
|
||||
downloadHeaderInfo.text = downloadHeaderInfo.context.getString(R.string.extra_info_format)
|
||||
.format(
|
||||
card.totalDownloads,
|
||||
downloadHeaderInfo.context.resources.getQuantityString(
|
||||
R.plurals.episodes,
|
||||
card.totalDownloads
|
||||
),
|
||||
formattedSizeString
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
// You probably formatted incorrectly
|
||||
downloadHeaderInfo.text = "Error"
|
||||
logError(e)
|
||||
}
|
||||
episodeHolder.apply {
|
||||
setOnClickListener {
|
||||
mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, card.child))
|
||||
}
|
||||
setOnLongClickListener {
|
||||
mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_LONG_CLICK, card.child))
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
episodeHolder.setOnClickListener {
|
||||
clickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_GO_TO_CHILD, d))
|
||||
}
|
||||
deleteCheckbox.apply {
|
||||
isVisible = showDeleteCheckbox
|
||||
isChecked = card.selected
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
selectedChangedCallback.invoke(card.data.id, card.data.name, isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindChild(card: VisualDownloadChildCached?) {
|
||||
if (binding !is DownloadChildEpisodeBinding) return
|
||||
card ?: return
|
||||
val d = card.data
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun DownloadHeaderEpisodeBinding.handleParentDownload(
|
||||
card: VisualDownloadHeaderCached,
|
||||
formattedSize: String
|
||||
) {
|
||||
downloadButton.isVisible = false
|
||||
downloadHeaderGotoChild.isVisible = true
|
||||
|
||||
try {
|
||||
downloadHeaderInfo.text = downloadHeaderInfo.context.getString(R.string.extra_info_format).format(
|
||||
card.totalDownloads,
|
||||
downloadHeaderInfo.context.resources.getQuantityString(R.plurals.episodes, card.totalDownloads),
|
||||
formattedSize
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
downloadHeaderInfo.text = "Error"
|
||||
logError(e)
|
||||
}
|
||||
|
||||
episodeHolder.setOnClickListener {
|
||||
headerClickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_GO_TO_CHILD, card.data))
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindChild(card: VisualDownloadChildCached?) {
|
||||
if (binding !is DownloadChildEpisodeBinding || card == null) return
|
||||
|
||||
val data = card.data
|
||||
binding.apply {
|
||||
val posDur = getViewPos(d.id)
|
||||
val posDur = getViewPos(data.id)
|
||||
downloadChildEpisodeProgress.apply {
|
||||
isVisible = posDur != null
|
||||
posDur?.let {
|
||||
|
@ -205,14 +216,14 @@ class DownloadAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
val status = downloadButton.getStatus(d.id, card.currentBytes, card.totalBytes)
|
||||
val status = downloadButton.getStatus(data.id, card.currentBytes, card.totalBytes)
|
||||
if (status == DownloadStatusTell.IsDone) {
|
||||
// We do this here instead if we are finished downloading
|
||||
// so that we can use the value from the view model
|
||||
// rather than extra unneeded disk operations and to prevent a
|
||||
// delay in updating download icon state.
|
||||
downloadButton.setProgress(card.currentBytes, card.totalBytes)
|
||||
downloadButton.applyMetaData(d.id, card.currentBytes, card.totalBytes)
|
||||
downloadButton.applyMetaData(data.id, card.currentBytes, card.totalBytes)
|
||||
// We will let the view model handle this
|
||||
downloadButton.doSetProgress = false
|
||||
downloadButton.progressBar.progressDrawable =
|
||||
|
@ -225,16 +236,16 @@ class DownloadAdapter(
|
|||
ContextCompat.getDrawable(downloadButton.context, downloadButton.progressDrawable)
|
||||
}
|
||||
|
||||
downloadButton.setDefaultClickListener(d, downloadChildEpisodeTextExtra, actionCallback)
|
||||
downloadButton.setDefaultClickListener(data, downloadChildEpisodeTextExtra, mediaClickCallback)
|
||||
downloadButton.isVisible = true
|
||||
|
||||
downloadChildEpisodeText.apply {
|
||||
text = context.getNameFull(d.name, d.episode, d.season)
|
||||
text = context.getNameFull(data.name, data.episode, data.season)
|
||||
isSelected = true // Needed for text repeating
|
||||
}
|
||||
|
||||
downloadChildEpisodeHolder.setOnClickListener {
|
||||
actionCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, d))
|
||||
mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +258,7 @@ class DownloadAdapter(
|
|||
VIEW_TYPE_CHILD -> DownloadChildEpisodeBinding.inflate(inflater, parent, false)
|
||||
else -> throw IllegalArgumentException("Invalid view type")
|
||||
}
|
||||
return DownloadViewHolder(binding, actionCallback, clickCallback)
|
||||
return DownloadViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: DownloadViewHolder, position: Int) {
|
||||
|
@ -262,6 +273,12 @@ class DownloadAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
fun setDeleteCheckboxVisibility(visible: Boolean) {
|
||||
if (showDeleteCheckbox == visible) return
|
||||
showDeleteCheckbox = visible
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
}
|
||||
|
||||
class DiffCallback : DiffUtil.ItemCallback<VisualDownloadCached>() {
|
||||
override fun areItemsTheSame(oldItem: VisualDownloadCached, newItem: VisualDownloadCached): Boolean {
|
||||
return oldItem.data.id == newItem.data.id
|
||||
|
|
|
@ -20,11 +20,10 @@ import com.lagradost.cloudstream3.utils.VideoDownloadManager
|
|||
import kotlinx.coroutines.MainScope
|
||||
|
||||
object DownloadButtonSetup {
|
||||
fun handleDownloadClick(click: DownloadActionEventBase) {
|
||||
fun handleDownloadClick(click: DownloadClickEvent) {
|
||||
val id = click.data.id
|
||||
when (click.action) {
|
||||
DOWNLOAD_ACTION_DELETE_FILE -> {
|
||||
if (click !is DownloadDeleteEvent) return
|
||||
val id = click.items.firstOrNull()?.id ?: return
|
||||
activity?.let { ctx ->
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(ctx)
|
||||
val dialogClickListener =
|
||||
|
@ -43,9 +42,9 @@ object DownloadButtonSetup {
|
|||
.setMessage(
|
||||
ctx.getString(R.string.delete_message).format(
|
||||
ctx.getNameFull(
|
||||
click.items.firstOrNull()?.name,
|
||||
click.items.firstOrNull()?.episode,
|
||||
click.items.firstOrNull()?.season
|
||||
click.data.name,
|
||||
click.data.episode,
|
||||
click.data.season
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -59,17 +58,15 @@ object DownloadButtonSetup {
|
|||
}
|
||||
}
|
||||
DOWNLOAD_ACTION_PAUSE_DOWNLOAD -> {
|
||||
val id = click.data?.id ?: return
|
||||
VideoDownloadManager.downloadEvent.invoke(
|
||||
Pair(id, VideoDownloadManager.DownloadActionType.Pause)
|
||||
Pair(click.data.id, VideoDownloadManager.DownloadActionType.Pause)
|
||||
)
|
||||
}
|
||||
DOWNLOAD_ACTION_RESUME_DOWNLOAD -> {
|
||||
val id = click.data?.id ?: return
|
||||
activity?.let { ctx ->
|
||||
if (VideoDownloadManager.downloadStatus.containsKey(id) && VideoDownloadManager.downloadStatus[id] == VideoDownloadManager.DownloadType.IsPaused) {
|
||||
VideoDownloadManager.downloadEvent.invoke(
|
||||
Pair(id, VideoDownloadManager.DownloadActionType.Resume)
|
||||
Pair(click.data.id, VideoDownloadManager.DownloadActionType.Resume)
|
||||
)
|
||||
} else {
|
||||
val pkg = VideoDownloadManager.getDownloadResumePackage(ctx, id)
|
||||
|
@ -77,19 +74,18 @@ object DownloadButtonSetup {
|
|||
VideoDownloadManager.downloadFromResumeUsingWorker(ctx, pkg)
|
||||
} else {
|
||||
VideoDownloadManager.downloadEvent.invoke(
|
||||
Pair(id, VideoDownloadManager.DownloadActionType.Resume)
|
||||
Pair(click.data.id, VideoDownloadManager.DownloadActionType.Resume)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DOWNLOAD_ACTION_LONG_CLICK -> {
|
||||
val id = click.data?.id ?: return
|
||||
activity?.let { act ->
|
||||
val length =
|
||||
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
|
||||
act,
|
||||
id
|
||||
click.data.id
|
||||
)?.fileLength
|
||||
?: 0
|
||||
if (length > 0) {
|
||||
|
@ -100,20 +96,19 @@ object DownloadButtonSetup {
|
|||
}
|
||||
}
|
||||
DOWNLOAD_ACTION_PLAY_FILE -> {
|
||||
val id = click.data?.id ?: return
|
||||
activity?.let { act ->
|
||||
val info =
|
||||
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
|
||||
act,
|
||||
id
|
||||
click.data.id
|
||||
) ?: return
|
||||
val keyInfo = getKey<VideoDownloadManager.DownloadedFileInfo>(
|
||||
VideoDownloadManager.KEY_DOWNLOAD_INFO,
|
||||
id.toString()
|
||||
click.data.id.toString()
|
||||
) ?: return
|
||||
val parent = getKey<VideoDownloadHelper.DownloadHeaderCached>(
|
||||
DOWNLOAD_HEADER_CACHE,
|
||||
click.data?.parentId.toString()
|
||||
click.data.parentId.toString()
|
||||
) ?: return
|
||||
|
||||
act.navigate(
|
||||
|
@ -123,11 +118,11 @@ object DownloadButtonSetup {
|
|||
ExtractorUri(
|
||||
uri = info.path,
|
||||
|
||||
id = id,
|
||||
parentId = click.data?.parentId,
|
||||
id = click.data.id,
|
||||
parentId = click.data.parentId,
|
||||
name = act.getString(R.string.downloaded_file), //click.data.name ?: keyInfo.displayName
|
||||
season = click.data?.season,
|
||||
episode = click.data?.episode,
|
||||
season = click.data.season,
|
||||
episode = click.data.episode,
|
||||
headerName = parent.name,
|
||||
tvType = parent.type,
|
||||
|
||||
|
@ -144,13 +139,13 @@ object DownloadButtonSetup {
|
|||
// keyInfo.basePath,
|
||||
// keyInfo.relativePath,
|
||||
// keyInfo.displayName,
|
||||
// click.data?.parentId,
|
||||
// click.data?.id,
|
||||
// click.data.parentId,
|
||||
// click.data.id,
|
||||
// headerName ?: "null",
|
||||
// if (click.data.episode <= 0) null else click.data.episode,
|
||||
// click.data.season
|
||||
// ),
|
||||
// getViewPos(click.data?.id)?.position ?: 0
|
||||
// getViewPos(click.data.id)?.position ?: 0
|
||||
//)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -107,8 +107,13 @@ class DownloadChildFragment : Fragment() {
|
|||
}
|
||||
|
||||
val adapter = DownloadAdapter(
|
||||
{ actionEvent -> handleActionEvent(folder, actionEvent, context) },
|
||||
{}
|
||||
{},
|
||||
{ downloadClickEvent ->
|
||||
handleDownloadClick(downloadClickEvent)
|
||||
if (downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) {
|
||||
setUpDownloadDeleteListener(folder)
|
||||
}
|
||||
}, { _, _, _ -> }
|
||||
)
|
||||
|
||||
binding?.downloadChildList?.apply {
|
||||
|
@ -125,25 +130,6 @@ class DownloadChildFragment : Fragment() {
|
|||
updateList(folder)
|
||||
}
|
||||
|
||||
private fun handleActionEvent(folder: String, actionEvent: DownloadActionEventBase, context: Context?) {
|
||||
when (actionEvent.action) {
|
||||
DOWNLOAD_ACTION_DELETE_MULTIPLE_FILES -> {
|
||||
if (actionEvent is DownloadDeleteEvent) {
|
||||
context?.let { downloadsViewModel.handleMultiDelete(it, actionEvent) }
|
||||
}
|
||||
}
|
||||
DOWNLOAD_ACTION_DELETE_FILE -> {
|
||||
val downloadDeleteEvent = DownloadDeleteEvent(
|
||||
action = DOWNLOAD_ACTION_DELETE_FILE,
|
||||
items = listOf(actionEvent.data)
|
||||
)
|
||||
handleDownloadClick(downloadDeleteEvent)
|
||||
setUpDownloadDeleteListener(folder)
|
||||
}
|
||||
else -> handleDownloadClick(actionEvent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpDownloadDeleteListener(folder: String) {
|
||||
downloadDeleteEventListener = { id: Int ->
|
||||
val list = (binding?.downloadChildList?.adapter as? DownloadAdapter)?.currentList
|
||||
|
|
|
@ -91,6 +91,10 @@ class DownloadFragment : Fragment() {
|
|||
hideKeyboard()
|
||||
binding?.downloadStorageAppbar?.setAppBarNoScrollFlagsOnTV()
|
||||
|
||||
// We always want fresh selections
|
||||
// when navigating to downloads
|
||||
downloadsViewModel.resetSelected()
|
||||
|
||||
observe(downloadsViewModel.headerCards) {
|
||||
(binding?.downloadList?.adapter as? DownloadAdapter)?.submitList(it)
|
||||
binding?.downloadLoading?.isVisible = false
|
||||
|
@ -106,10 +110,32 @@ class DownloadFragment : Fragment() {
|
|||
observe(downloadsViewModel.downloadBytes) {
|
||||
updateStorageInfo(view.context, it, R.string.app_storage, binding?.downloadAppTxt, binding?.downloadApp)
|
||||
}
|
||||
observe(downloadsViewModel.selectedIds) {
|
||||
handleSelectedChange(it)
|
||||
updateSelectedState(it)
|
||||
binding?.downloadDeleteToolbar?.btnDelete?.text =
|
||||
getString(R.string.delete_count).format(it.count())
|
||||
}
|
||||
|
||||
val adapter = DownloadAdapter(
|
||||
{ actionEvent -> handleActionEvent(actionEvent, context) },
|
||||
{ click -> handleItemClick(click) }
|
||||
{ click -> handleItemClick(click) },
|
||||
{ downloadClickEvent ->
|
||||
handleDownloadClick(downloadClickEvent)
|
||||
when (downloadClickEvent.action) {
|
||||
DOWNLOAD_ACTION_DELETE_FILE -> setUpDownloadDeleteListener()
|
||||
DOWNLOAD_ACTION_LONG_CLICK -> downloadClickEvent.data.name?.let {
|
||||
downloadsViewModel.addSelected(
|
||||
downloadClickEvent.data.id,
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{ id, name, isChecked ->
|
||||
if (isChecked) {
|
||||
downloadsViewModel.addSelected(id, name)
|
||||
} else downloadsViewModel.removeSelected(id)
|
||||
}
|
||||
)
|
||||
|
||||
binding?.downloadList?.apply {
|
||||
|
@ -144,25 +170,6 @@ class DownloadFragment : Fragment() {
|
|||
fixPaddingStatusbar(binding?.downloadRoot)
|
||||
}
|
||||
|
||||
private fun handleActionEvent(actionEvent: DownloadActionEventBase, context: Context?) {
|
||||
when (actionEvent.action) {
|
||||
DOWNLOAD_ACTION_DELETE_MULTIPLE_FILES -> {
|
||||
if (actionEvent is DownloadDeleteEvent) {
|
||||
context?.let { downloadsViewModel.handleMultiDelete(it, actionEvent) }
|
||||
}
|
||||
}
|
||||
DOWNLOAD_ACTION_DELETE_FILE -> {
|
||||
val downloadDeleteEvent = DownloadDeleteEvent(
|
||||
action = DOWNLOAD_ACTION_DELETE_FILE,
|
||||
items = listOf(actionEvent.data)
|
||||
)
|
||||
handleDownloadClick(downloadDeleteEvent)
|
||||
setUpDownloadDeleteListener()
|
||||
}
|
||||
else -> handleDownloadClick(actionEvent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleItemClick(click: DownloadHeaderClickEvent) {
|
||||
when (click.action) {
|
||||
DOWNLOAD_ACTION_GO_TO_CHILD -> {
|
||||
|
@ -180,6 +187,37 @@ class DownloadFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleSelectedChange(selected: HashMap<Int, String>) {
|
||||
val adapter = (binding?.downloadList?.adapter as? DownloadAdapter)
|
||||
if (selected.isNotEmpty()) {
|
||||
binding?.downloadDeleteToolbar?.downloadDeleteToolbar?.isVisible = true
|
||||
binding?.downloadStorageAppbar?.isVisible = false
|
||||
binding?.downloadDeleteToolbar?.btnDelete?.setOnClickListener {
|
||||
context?.let { ctx -> downloadsViewModel.handleMultiDelete(ctx) }
|
||||
}
|
||||
binding?.downloadDeleteToolbar?.btnCancel?.setOnClickListener {
|
||||
downloadsViewModel.resetSelected()
|
||||
}
|
||||
|
||||
adapter?.setDeleteCheckboxVisibility(true)
|
||||
} else {
|
||||
binding?.downloadDeleteToolbar?.downloadDeleteToolbar?.isVisible = false
|
||||
binding?.downloadStorageAppbar?.isVisible =
|
||||
// Make sure we don't display it early
|
||||
!downloadsViewModel.headerCards.value.isNullOrEmpty() &&
|
||||
downloadsViewModel.usedBytes.value?.let { it > 0 } == true
|
||||
adapter?.setDeleteCheckboxVisibility(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSelectedState(selected: HashMap<Int, String>) {
|
||||
val currentList = downloadsViewModel.headerCards.value ?: return
|
||||
val updatedList = currentList.map { header ->
|
||||
header.copy(selected = selected.keys.contains(header.data.id))
|
||||
}
|
||||
(binding?.downloadList?.adapter as? DownloadAdapter)?.submitList(updatedList)
|
||||
}
|
||||
|
||||
private fun setUpDownloadDeleteListener() {
|
||||
downloadDeleteEventListener = { id ->
|
||||
val list = (binding?.downloadList?.adapter as? DownloadAdapter)?.currentList
|
||||
|
|
|
@ -34,12 +34,42 @@ class DownloadViewModel : ViewModel() {
|
|||
private val _availableBytes = MutableLiveData<Long>()
|
||||
private val _downloadBytes = MutableLiveData<Long>()
|
||||
|
||||
private val _selectedIds = MutableLiveData<HashMap<Int, String>>(HashMap())
|
||||
|
||||
val usedBytes: LiveData<Long> = _usedBytes
|
||||
val availableBytes: LiveData<Long> = _availableBytes
|
||||
val downloadBytes: LiveData<Long> = _downloadBytes
|
||||
|
||||
val selectedIds: LiveData<HashMap<Int, String>> = _selectedIds
|
||||
|
||||
private var previousVisual: List<VisualDownloadHeaderCached>? = null
|
||||
|
||||
fun addSelected(id: Int, name: String) {
|
||||
_selectedIds.value?.let { selectedIds ->
|
||||
selectedIds[id] = name
|
||||
_selectedIds.postValue(selectedIds)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeSelected(id: Int) {
|
||||
_selectedIds.value?.let { selectedIds ->
|
||||
selectedIds.remove(id)
|
||||
_selectedIds.postValue(selectedIds)
|
||||
}
|
||||
}
|
||||
|
||||
fun resetSelected() {
|
||||
_selectedIds.postValue(HashMap())
|
||||
}
|
||||
|
||||
private fun getSelectedIds(): List<Int> {
|
||||
return _selectedIds.value?.keys?.toList() ?: emptyList()
|
||||
}
|
||||
|
||||
private fun getSelectedNames(): List<String> {
|
||||
return _selectedIds.value?.values?.toList() ?: emptyList()
|
||||
}
|
||||
|
||||
fun updateList(context: Context) = viewModelScope.launchSafe {
|
||||
val children = withContext(Dispatchers.IO) {
|
||||
context.getKeys(DOWNLOAD_EPISODE_CACHE)
|
||||
|
@ -127,12 +157,9 @@ class DownloadViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
fun handleMultiDelete(context: Context, event: DownloadDeleteEvent) = viewModelScope.launchSafe {
|
||||
val ids: List<Int> = event.items.mapNotNull { it?.id }
|
||||
.takeIf { it.isNotEmpty() } ?: return@launchSafe
|
||||
|
||||
val names: List<String> = event.items.mapNotNull { it?.name }
|
||||
.takeIf { it.isNotEmpty() } ?: return@launchSafe
|
||||
fun handleMultiDelete(context: Context) = viewModelScope.launchSafe {
|
||||
val ids: List<Int> = getSelectedIds()
|
||||
val names: List<String> = getSelectedNames()
|
||||
|
||||
showDeleteConfirmationDialog(context, ids, names)
|
||||
}
|
||||
|
|
41
app/src/main/res/layout/download_delete_toolbar.xml
Normal file
41
app/src/main/res/layout/download_delete_toolbar.xml
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
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="wrap_content"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
tools:layout_height="100dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:padding="8dp"
|
||||
android:id="@+id/download_delete_toolbar"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btnCancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_baseline_close_24"
|
||||
android:contentDescription="@string/cancel"
|
||||
android:padding="8dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnDelete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:text="@string/delete"
|
||||
android:textColor="@android:color/white"
|
||||
android:layout_gravity="center_vertical" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
|
@ -77,5 +77,16 @@
|
|||
android:focusable="true"
|
||||
android:nextFocusLeft="@id/episode_holder"
|
||||
android:padding="10dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/delete_checkbox"
|
||||
android:layout_width="@dimen/download_size"
|
||||
android:layout_height="@dimen/download_size"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginStart="-50dp"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@id/episode_holder"
|
||||
android:padding="10dp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
|
@ -8,6 +8,10 @@
|
|||
android:orientation="vertical"
|
||||
tools:context=".ui.download.DownloadFragment">
|
||||
|
||||
<include
|
||||
layout="@layout/download_delete_toolbar"
|
||||
android:id="@+id/download_delete_toolbar" />
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue