mirror of
https://github.com/recloudstream/cloudstream.git
synced 2026-06-19 20:05:41 +00:00
Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eef078d07c |
5 changed files with 105 additions and 155 deletions
|
|
@ -1,16 +1,16 @@
|
||||||
package com.lagradost.cloudstream3.ui.download
|
package com.lagradost.cloudstream3.ui.download
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.text.format.Formatter.formatShortFileSize
|
import android.text.format.Formatter.formatShortFileSize
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.FragmentChildDownloadsBinding
|
import com.lagradost.cloudstream3.databinding.FragmentChildDownloadsBinding
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||||
import com.lagradost.cloudstream3.ui.BaseFragment
|
import com.lagradost.cloudstream3.ui.BaseFragment
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
||||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||||
|
|
@ -55,22 +55,6 @@ class DownloadChildFragment : BaseFragment<FragmentChildDownloadsBinding>(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindingCreated(binding: FragmentChildDownloadsBinding) {
|
override fun onBindingCreated(binding: FragmentChildDownloadsBinding) {
|
||||||
/**
|
|
||||||
* We never want to retain multi-delete state
|
|
||||||
* when navigating to downloads. Setting this state
|
|
||||||
* immediately can sometimes result in the observer
|
|
||||||
* not being notified in time to update the UI.
|
|
||||||
*
|
|
||||||
* By posting to the main looper, we ensure that this
|
|
||||||
* operation is executed after the view has been fully created
|
|
||||||
* and all initializations are completed, allowing the
|
|
||||||
* observer to properly receive and handle the state change.
|
|
||||||
*/
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
|
||||||
downloadViewModel.setIsMultiDeleteState(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val folder = arguments?.getString("folder")
|
val folder = arguments?.getString("folder")
|
||||||
val name = arguments?.getString("name")
|
val name = arguments?.getString("name")
|
||||||
if (folder == null) {
|
if (folder == null) {
|
||||||
|
|
@ -101,30 +85,56 @@ class DownloadChildFragment : BaseFragment<FragmentChildDownloadsBinding>(
|
||||||
}
|
}
|
||||||
(binding.downloadChildList.adapter as? DownloadAdapter)?.submitList(cards.value)
|
(binding.downloadChildList.adapter as? DownloadAdapter)?.submitList(cards.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
(binding.downloadChildList.adapter as? DownloadAdapter)?.submitList(null)
|
(binding.downloadChildList.adapter as? DownloadAdapter)?.submitList(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
observe(downloadViewModel.isMultiDeleteState) { isMultiDeleteState ->
|
|
||||||
val adapter = binding.downloadChildList.adapter as? DownloadAdapter
|
|
||||||
adapter?.setIsMultiDeleteState(isMultiDeleteState)
|
|
||||||
binding.downloadDeleteAppbar.isVisible = isMultiDeleteState
|
|
||||||
if (!isMultiDeleteState) {
|
|
||||||
activity?.detachBackPressedCallback("Downloads")
|
|
||||||
downloadViewModel.clearSelectedItems()
|
|
||||||
binding.downloadChildToolbar.isVisible = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
observe(downloadViewModel.selectedBytes) {
|
observe(downloadViewModel.selectedBytes) {
|
||||||
updateDeleteButton(downloadViewModel.selectedItemIds.value?.count() ?: 0, it)
|
updateDeleteButton(downloadViewModel.selectedItemIds.value?.count() ?: 0, it)
|
||||||
}
|
}
|
||||||
observe(downloadViewModel.selectedItemIds) {
|
|
||||||
handleSelectedChange(it)
|
|
||||||
updateDeleteButton(it.count(), downloadViewModel.selectedBytes.value ?: 0L)
|
|
||||||
|
|
||||||
binding.btnDelete.isVisible = it.isNotEmpty()
|
|
||||||
binding.selectItemsText.isVisible = it.isEmpty()
|
binding.apply {
|
||||||
|
btnDelete.setOnClickListener { view ->
|
||||||
|
downloadViewModel.handleMultiDelete(view.context ?: return@setOnClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
btnCancel.setOnClickListener {
|
||||||
|
downloadViewModel.cancelSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
btnToggleAll.setOnClickListener {
|
||||||
|
val allSelected = downloadViewModel.isAllChildrenSelected()
|
||||||
|
if (allSelected) {
|
||||||
|
downloadViewModel.clearSelectedItems()
|
||||||
|
} else {
|
||||||
|
downloadViewModel.selectAllChildren()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observeNullable(downloadViewModel.selectedItemIds) { selection ->
|
||||||
|
val isMultiDeleteState = selection != null
|
||||||
|
val adapter = binding.downloadChildList.adapter as? DownloadAdapter
|
||||||
|
adapter?.setIsMultiDeleteState(isMultiDeleteState)
|
||||||
|
binding.downloadDeleteAppbar.isVisible = isMultiDeleteState
|
||||||
|
binding.downloadChildToolbar.isGone = isMultiDeleteState
|
||||||
|
|
||||||
|
if (selection == null) {
|
||||||
|
activity?.detachBackPressedCallback("Downloads")
|
||||||
|
return@observeNullable
|
||||||
|
}
|
||||||
|
activity?.attachBackPressedCallback("Downloads") {
|
||||||
|
downloadViewModel.cancelSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDeleteButton(selection.count(), downloadViewModel.selectedBytes.value ?: 0L)
|
||||||
|
|
||||||
|
binding.btnDelete.isVisible = selection.isNotEmpty()
|
||||||
|
binding.selectItemsText.isVisible = selection.isEmpty()
|
||||||
|
|
||||||
val allSelected = downloadViewModel.isAllChildrenSelected()
|
val allSelected = downloadViewModel.isAllChildrenSelected()
|
||||||
if (allSelected) {
|
if (allSelected) {
|
||||||
|
|
@ -160,37 +170,6 @@ class DownloadChildFragment : BaseFragment<FragmentChildDownloadsBinding>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSelectedChange(selected: Set<Int>) {
|
|
||||||
if (selected.isNotEmpty()) {
|
|
||||||
binding?.downloadDeleteAppbar?.isVisible = true
|
|
||||||
binding?.downloadChildToolbar?.isVisible = false
|
|
||||||
activity?.attachBackPressedCallback("Downloads") {
|
|
||||||
downloadViewModel.setIsMultiDeleteState(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding?.btnDelete?.setOnClickListener {
|
|
||||||
context?.let { ctx ->
|
|
||||||
downloadViewModel.handleMultiDelete(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding?.btnCancel?.setOnClickListener {
|
|
||||||
downloadViewModel.setIsMultiDeleteState(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding?.btnToggleAll?.setOnClickListener {
|
|
||||||
val allSelected = downloadViewModel.isAllChildrenSelected()
|
|
||||||
if (allSelected) {
|
|
||||||
downloadViewModel.clearSelectedItems()
|
|
||||||
} else {
|
|
||||||
downloadViewModel.selectAllChildren()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadViewModel.setIsMultiDeleteState(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateDeleteButton(count: Int, selectedBytes: Long) {
|
private fun updateDeleteButton(count: Int, selectedBytes: Long) {
|
||||||
val formattedSize = formatShortFileSize(context, selectedBytes)
|
val formattedSize = formatShortFileSize(context, selectedBytes)
|
||||||
binding?.btnDelete?.text =
|
binding?.btnDelete?.text =
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
|
import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.text.format.Formatter.formatShortFileSize
|
import android.text.format.Formatter.formatShortFileSize
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
|
@ -28,6 +26,7 @@ import com.lagradost.cloudstream3.isEpisodeBased
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.safe
|
import com.lagradost.cloudstream3.mvvm.safe
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||||
import com.lagradost.cloudstream3.ui.BaseFragment
|
import com.lagradost.cloudstream3.ui.BaseFragment
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
||||||
import com.lagradost.cloudstream3.ui.player.BasicLink
|
import com.lagradost.cloudstream3.ui.player.BasicLink
|
||||||
|
|
@ -87,21 +86,6 @@ class DownloadFragment : BaseFragment<FragmentDownloadsBinding>(
|
||||||
binding.downloadAppbar.setAppBarNoScrollFlagsOnTV()
|
binding.downloadAppbar.setAppBarNoScrollFlagsOnTV()
|
||||||
binding.downloadDeleteAppbar.setAppBarNoScrollFlagsOnTV()
|
binding.downloadDeleteAppbar.setAppBarNoScrollFlagsOnTV()
|
||||||
|
|
||||||
/**
|
|
||||||
* We never want to retain multi-delete state
|
|
||||||
* when navigating to downloads. Setting this state
|
|
||||||
* immediately can sometimes result in the observer
|
|
||||||
* not being notified in time to update the UI.
|
|
||||||
*
|
|
||||||
* By posting to the main looper, we ensure that this
|
|
||||||
* operation is executed after the view has been fully created
|
|
||||||
* and all initializations are completed, allowing the
|
|
||||||
* observer to properly receive and handle the state change.
|
|
||||||
*/
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
|
||||||
downloadViewModel.setIsMultiDeleteState(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
observe(downloadViewModel.headerCards) { cards ->
|
observe(downloadViewModel.headerCards) { cards ->
|
||||||
when (cards) {
|
when (cards) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
|
|
@ -161,26 +145,44 @@ class DownloadFragment : BaseFragment<FragmentDownloadsBinding>(
|
||||||
observe(downloadViewModel.selectedBytes) {
|
observe(downloadViewModel.selectedBytes) {
|
||||||
updateDeleteButton(downloadViewModel.selectedItemIds.value?.count() ?: 0, it)
|
updateDeleteButton(downloadViewModel.selectedItemIds.value?.count() ?: 0, it)
|
||||||
}
|
}
|
||||||
observe(downloadViewModel.isMultiDeleteState) { isMultiDeleteState ->
|
|
||||||
|
binding.apply {
|
||||||
|
btnDelete.setOnClickListener { view ->
|
||||||
|
downloadViewModel.handleMultiDelete(view.context ?: return@setOnClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
btnCancel.setOnClickListener {
|
||||||
|
downloadViewModel.cancelSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
btnToggleAll.setOnClickListener {
|
||||||
|
val allSelected = downloadViewModel.isAllHeadersSelected()
|
||||||
|
if (allSelected) {
|
||||||
|
downloadViewModel.clearSelectedItems()
|
||||||
|
} else {
|
||||||
|
downloadViewModel.selectAllHeaders()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observeNullable(downloadViewModel.selectedItemIds) { selection ->
|
||||||
|
val isMultiDeleteState = selection != null
|
||||||
val adapter = binding.downloadList.adapter as? DownloadAdapter
|
val adapter = binding.downloadList.adapter as? DownloadAdapter
|
||||||
adapter?.setIsMultiDeleteState(isMultiDeleteState)
|
adapter?.setIsMultiDeleteState(isMultiDeleteState)
|
||||||
binding.downloadDeleteAppbar.isVisible = isMultiDeleteState
|
binding.downloadDeleteAppbar.isVisible = isMultiDeleteState
|
||||||
if (!isMultiDeleteState) {
|
binding.downloadAppbar.isGone = isMultiDeleteState
|
||||||
activity?.detachBackPressedCallback("Downloads")
|
|
||||||
downloadViewModel.clearSelectedItems()
|
|
||||||
// Prevent race condition and make sure
|
|
||||||
// we don't display it early
|
|
||||||
if (downloadViewModel.usedBytes.value?.let { it > 0 } == true) {
|
|
||||||
binding.downloadAppbar.isVisible = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
observe(downloadViewModel.selectedItemIds) {
|
|
||||||
handleSelectedChange(it)
|
|
||||||
updateDeleteButton(it.count(), downloadViewModel.selectedBytes.value ?: 0L)
|
|
||||||
|
|
||||||
binding.btnDelete.isVisible = it.isNotEmpty()
|
if (selection == null) {
|
||||||
binding.selectItemsText.isVisible = it.isEmpty()
|
activity?.detachBackPressedCallback("Downloads")
|
||||||
|
return@observeNullable
|
||||||
|
}
|
||||||
|
activity?.attachBackPressedCallback("Downloads") {
|
||||||
|
downloadViewModel.cancelSelection()
|
||||||
|
}
|
||||||
|
updateDeleteButton(selection.count(), downloadViewModel.selectedBytes.value ?: 0L)
|
||||||
|
|
||||||
|
binding.btnDelete.isVisible = selection.isNotEmpty()
|
||||||
|
binding.selectItemsText.isVisible = selection.isEmpty()
|
||||||
|
|
||||||
val allSelected = downloadViewModel.isAllHeadersSelected()
|
val allSelected = downloadViewModel.isAllHeadersSelected()
|
||||||
if (allSelected) {
|
if (allSelected) {
|
||||||
|
|
@ -260,37 +262,6 @@ class DownloadFragment : BaseFragment<FragmentDownloadsBinding>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSelectedChange(selected: Set<Int>) {
|
|
||||||
if (selected.isNotEmpty()) {
|
|
||||||
binding?.downloadDeleteAppbar?.isVisible = true
|
|
||||||
binding?.downloadAppbar?.isVisible = false
|
|
||||||
activity?.attachBackPressedCallback("Downloads") {
|
|
||||||
downloadViewModel.setIsMultiDeleteState(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding?.btnDelete?.setOnClickListener {
|
|
||||||
context?.let { ctx ->
|
|
||||||
downloadViewModel.handleMultiDelete(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding?.btnCancel?.setOnClickListener {
|
|
||||||
downloadViewModel.setIsMultiDeleteState(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding?.btnToggleAll?.setOnClickListener {
|
|
||||||
val allSelected = downloadViewModel.isAllHeadersSelected()
|
|
||||||
if (allSelected) {
|
|
||||||
downloadViewModel.clearSelectedItems()
|
|
||||||
} else {
|
|
||||||
downloadViewModel.selectAllHeaders()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadViewModel.setIsMultiDeleteState(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateDeleteButton(count: Int, selectedBytes: Long) {
|
private fun updateDeleteButton(count: Int, selectedBytes: Long) {
|
||||||
val formattedSize = formatShortFileSize(context, selectedBytes)
|
val formattedSize = formatShortFileSize(context, selectedBytes)
|
||||||
binding?.btnDelete?.text =
|
binding?.btnDelete?.text =
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,8 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class DownloadViewModel : ViewModel() {
|
class DownloadViewModel : ViewModel() {
|
||||||
private val _headerCards = ResourceLiveData<List<VisualDownloadCached.Header>>(Resource.Loading())
|
private val _headerCards =
|
||||||
|
ResourceLiveData<List<VisualDownloadCached.Header>>(Resource.Loading())
|
||||||
val headerCards: LiveData<Resource<List<VisualDownloadCached.Header>>> = _headerCards
|
val headerCards: LiveData<Resource<List<VisualDownloadCached.Header>>> = _headerCards
|
||||||
|
|
||||||
private val _childCards = ResourceLiveData<List<VisualDownloadCached.Child>>(Resource.Loading())
|
private val _childCards = ResourceLiveData<List<VisualDownloadCached.Child>>(Resource.Loading())
|
||||||
|
|
@ -47,22 +48,20 @@ class DownloadViewModel : ViewModel() {
|
||||||
private val _selectedBytes = ConsistentLiveData<Long>(0)
|
private val _selectedBytes = ConsistentLiveData<Long>(0)
|
||||||
val selectedBytes: LiveData<Long> = _selectedBytes
|
val selectedBytes: LiveData<Long> = _selectedBytes
|
||||||
|
|
||||||
private val _isMultiDeleteState = ConsistentLiveData(false)
|
private val _selectedItemIds = ConsistentLiveData<Set<Int>?>(null)
|
||||||
val isMultiDeleteState: LiveData<Boolean> = _isMultiDeleteState
|
val selectedItemIds: LiveData<Set<Int>?> = _selectedItemIds
|
||||||
|
|
||||||
private val _selectedItemIds = ConsistentLiveData<Set<Int>>(mutableSetOf())
|
|
||||||
val selectedItemIds: LiveData<Set<Int>> = _selectedItemIds
|
|
||||||
|
|
||||||
fun setIsMultiDeleteState(value: Boolean) {
|
fun cancelSelection() {
|
||||||
_isMultiDeleteState.postValue(value)
|
updateSelectedItems { null }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addSelected(itemId: Int) {
|
fun addSelected(itemId: Int) {
|
||||||
updateSelectedItems { it + itemId }
|
updateSelectedItems { it?.plus(itemId) ?: setOf(itemId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeSelected(itemId: Int) {
|
fun removeSelected(itemId: Int) {
|
||||||
updateSelectedItems { it - itemId }
|
updateSelectedItems { it?.minus(itemId) ?: emptySet() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectAllHeaders() {
|
fun selectAllHeaders() {
|
||||||
|
|
@ -97,8 +96,8 @@ class DownloadViewModel : ViewModel() {
|
||||||
return currentSelected.size == headers.size && headers.all { it.data.id in currentSelected }
|
return currentSelected.size == headers.size && headers.all { it.data.id in currentSelected }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSelectedItems(action: (Set<Int>) -> Set<Int>) {
|
private fun updateSelectedItems(action: (Set<Int>?) -> Set<Int>?) {
|
||||||
val currentSelected = action(selectedItemIds.value ?: mutableSetOf())
|
val currentSelected = action(selectedItemIds.value)
|
||||||
_selectedItemIds.postValue(currentSelected)
|
_selectedItemIds.postValue(currentSelected)
|
||||||
postHeaders()
|
postHeaders()
|
||||||
postChildren()
|
postChildren()
|
||||||
|
|
@ -115,7 +114,6 @@ class DownloadViewModel : ViewModel() {
|
||||||
fun updateHeaderList(context: Context) = viewModelScope.launchSafe {
|
fun updateHeaderList(context: Context) = viewModelScope.launchSafe {
|
||||||
// Do not push loading as it interrupts the UI
|
// Do not push loading as it interrupts the UI
|
||||||
//_headerCards.postValue(Resource.Loading())
|
//_headerCards.postValue(Resource.Loading())
|
||||||
clearSelectedItems()
|
|
||||||
|
|
||||||
val visual = withContext(Dispatchers.IO) {
|
val visual = withContext(Dispatchers.IO) {
|
||||||
val children = context.getKeys(DOWNLOAD_EPISODE_CACHE)
|
val children = context.getKeys(DOWNLOAD_EPISODE_CACHE)
|
||||||
|
|
@ -232,7 +230,6 @@ class DownloadViewModel : ViewModel() {
|
||||||
|
|
||||||
fun updateChildList(context: Context, folder: String) = viewModelScope.launchSafe {
|
fun updateChildList(context: Context, folder: String) = viewModelScope.launchSafe {
|
||||||
_childCards.postValue(Resource.Loading()) // always push loading
|
_childCards.postValue(Resource.Loading()) // always push loading
|
||||||
clearSelectedItems()
|
|
||||||
|
|
||||||
val visual = withContext(Dispatchers.IO) {
|
val visual = withContext(Dispatchers.IO) {
|
||||||
context.getKeys(folder).mapNotNull { key ->
|
context.getKeys(folder).mapNotNull { key ->
|
||||||
|
|
@ -260,6 +257,7 @@ class DownloadViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeItems(idsToRemove: Set<Int>) = viewModelScope.launchSafe {
|
private fun removeItems(idsToRemove: Set<Int>) = viewModelScope.launchSafe {
|
||||||
|
_selectedItemIds.postValue(null)
|
||||||
postHeaders(_headerCards.success?.filter { it.data.id !in idsToRemove })
|
postHeaders(_headerCards.success?.filter { it.data.id !in idsToRemove })
|
||||||
postChildren(_childCards.success?.filter { it.data.id !in idsToRemove })
|
postChildren(_childCards.success?.filter { it.data.id !in idsToRemove })
|
||||||
}
|
}
|
||||||
|
|
@ -368,16 +366,16 @@ class DownloadViewModel : ViewModel() {
|
||||||
.joinToString(separator = "\n") { "• $it" }
|
.joinToString(separator = "\n") { "• $it" }
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
|
data.seriesNames.isNotEmpty() && data.names.isEmpty() -> {
|
||||||
|
context.getString(R.string.delete_message_series_only).format(formattedSeriesNames)
|
||||||
|
}
|
||||||
|
|
||||||
data.ids.count() == 1 -> {
|
data.ids.count() == 1 -> {
|
||||||
context.getString(R.string.delete_message).format(
|
context.getString(R.string.delete_message).format(
|
||||||
data.names.firstOrNull()
|
data.names.firstOrNull()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data.seriesNames.isNotEmpty() && data.names.isEmpty() -> {
|
|
||||||
context.getString(R.string.delete_message_series_only).format(formattedSeriesNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
data.parentName != null && data.names.isNotEmpty() -> {
|
data.parentName != null && data.names.isNotEmpty() -> {
|
||||||
context.getString(R.string.delete_message_series_episodes)
|
context.getString(R.string.delete_message_series_episodes)
|
||||||
.format(data.parentName, formattedNames)
|
.format(data.parentName, formattedNames)
|
||||||
|
|
@ -406,7 +404,6 @@ class DownloadViewModel : ViewModel() {
|
||||||
when (which) {
|
when (which) {
|
||||||
DialogInterface.BUTTON_POSITIVE -> {
|
DialogInterface.BUTTON_POSITIVE -> {
|
||||||
viewModelScope.launchSafe {
|
viewModelScope.launchSafe {
|
||||||
setIsMultiDeleteState(false)
|
|
||||||
deleteFilesAndUpdateSettings(context, ids, this) { successfulIds ->
|
deleteFilesAndUpdateSettings(context, ids, this) { successfulIds ->
|
||||||
// We always remove parent because if we are deleting from here
|
// We always remove parent because if we are deleting from here
|
||||||
// and we have it as non-empty, it was triggered on
|
// and we have it as non-empty, it was triggered on
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.lagradost.api.Log
|
import com.lagradost.api.Log
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getFolder
|
import com.lagradost.cloudstream3.utils.VideoDownloadManager.basePathToFile
|
||||||
import com.lagradost.safefile.SafeFile
|
|
||||||
|
|
||||||
object SubtitleUtils {
|
object SubtitleUtils {
|
||||||
|
|
||||||
|
|
@ -14,16 +13,20 @@ object SubtitleUtils {
|
||||||
)
|
)
|
||||||
|
|
||||||
fun deleteMatchingSubtitles(context: Context, info: VideoDownloadManager.DownloadedFileInfo) {
|
fun deleteMatchingSubtitles(context: Context, info: VideoDownloadManager.DownloadedFileInfo) {
|
||||||
val relative = info.relativePath
|
val cleanDisplay = cleanDisplayName(info.displayName)
|
||||||
val display = info.displayName
|
|
||||||
val cleanDisplay = cleanDisplayName(display)
|
|
||||||
|
|
||||||
getFolder(context, relative, info.basePath)?.forEach { (name, uri) ->
|
val base = basePathToFile(context, info.basePath)
|
||||||
if (isMatchingSubtitle(name, display, cleanDisplay)) {
|
val folder =
|
||||||
val subtitleFile = SafeFile.fromUri(context, uri)
|
base?.gotoDirectory(info.relativePath, createMissingDirectories = false) ?: return
|
||||||
if (subtitleFile == null || subtitleFile.delete() != true) {
|
val folderFiles = folder.listFiles() ?: return
|
||||||
Log.e("SubtitleDeletion", "Failed to delete subtitle file: ${subtitleFile?.name()}")
|
|
||||||
|
for (file in folderFiles) {
|
||||||
|
val name = file.name() ?: continue
|
||||||
|
if (!isMatchingSubtitle(name, info.displayName, cleanDisplay)) {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
if (file.delete() != true) {
|
||||||
|
Log.e("SubtitleDeletion", "Failed to delete subtitle file: $name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1628,7 +1628,7 @@ object VideoDownloadManager {
|
||||||
* Turns a string to an UniFile. Used for stored string paths such as settings.
|
* Turns a string to an UniFile. Used for stored string paths such as settings.
|
||||||
* Should only be used to get a download path.
|
* Should only be used to get a download path.
|
||||||
* */
|
* */
|
||||||
private fun basePathToFile(context: Context, path: String?): SafeFile? {
|
fun basePathToFile(context: Context, path: String?): SafeFile? {
|
||||||
return when {
|
return when {
|
||||||
path.isNullOrBlank() -> getDefaultDir(context)
|
path.isNullOrBlank() -> getDefaultDir(context)
|
||||||
path.startsWith("content://") -> SafeFile.fromUri(context, path.toUri())
|
path.startsWith("content://") -> SafeFile.fromUri(context, path.toUri())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue