Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.content.Intent
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Rect
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.Editable
Expand All @@ -25,6 +26,7 @@ import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.discord.panels.OverlappingPanelsLayout
import com.discord.panels.PanelState
import com.discord.panels.PanelsChildGestureRegionObserver
Expand All @@ -46,6 +48,7 @@ import com.lagradost.cloudstream3.databinding.FragmentResultSwipeBinding
import com.lagradost.cloudstream3.databinding.ResultRecommendationsBinding
import com.lagradost.cloudstream3.databinding.ResultSyncBinding
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.launchSafe
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
Expand All @@ -66,6 +69,8 @@ import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent
import com.lagradost.cloudstream3.ui.search.SearchAdapter
import com.lagradost.cloudstream3.ui.search.SearchHelper
import com.lagradost.cloudstream3.ui.setRecycledViewPool
import com.lagradost.cloudstream3.ui.settings.SettingsGeneral.Companion.pickDownloadPath
import com.lagradost.cloudstream3.ui.settings.utils.getChooseFolderLauncher
import com.lagradost.cloudstream3.utils.AppContextUtils.getNameFull
import com.lagradost.cloudstream3.utils.AppContextUtils.isCastApiAvailable
import com.lagradost.cloudstream3.utils.AppContextUtils.loadCache
Expand All @@ -90,13 +95,15 @@ import com.lagradost.cloudstream3.utils.UIHelper.populateChips
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
import com.lagradost.cloudstream3.utils.UIHelper.setListViewHeightBasedOnItems
import com.lagradost.cloudstream3.utils.UIHelper.setNavigationBarColorCompat
import com.lagradost.cloudstream3.utils.downloader.DownloadFileManagement.getBasePath
import com.lagradost.cloudstream3.utils.downloader.DownloadObjects
import com.lagradost.cloudstream3.utils.downloader.VideoDownloadManager
import com.lagradost.cloudstream3.utils.getImageFromDrawable
import com.lagradost.cloudstream3.utils.setText
import com.lagradost.cloudstream3.utils.setTextHtml
import com.lagradost.cloudstream3.utils.txt
import java.net.URLEncoder
import java.util.concurrent.ConcurrentLinkedDeque
import kotlin.math.roundToInt

open class ResultFragmentPhone : FullScreenPlayer() {
Expand All @@ -107,6 +114,72 @@ open class ResultFragmentPhone : FullScreenPlayer() {
}
}

/** Queue of pending actions that is deferred to after a custom path is set */
private val pendingPathActions = ConcurrentLinkedDeque<Pair<Int, ResultEpisode>>()

/**
* Appends all actions to a queue, and asks for a user to enter the download folder if not already set up.
*
* Then processes the queue in the given order, only after the user has selected a folder.
* This is to defer the download to after a file path is set, due to perms.
* */
private fun requirePathForActions(list: Collection<Pair<Int, ResultEpisode>>) {
pendingPathActions.addAll(list)
val (_, path) = context?.getBasePath() ?: return
if (path == null) {
/** If we have not set any download path, then ask the user for it before we download it */
try {
/** Give the user some info of what we are doing and why, even if it may be missed */
showToast(R.string.download_path_pref)
pathPicker.launch(Uri.EMPTY)
} catch (t: Throwable) {
logError(t)
/** Something went wrong, TV Device?
* Use the fallback behavior of just downloading it even if no path is selected,
* and hope it works */
processPendingActions()
}
} else {
/**
* Otherwise dispatch everything, as we already have a valid download path
* Even if this is "wrong", we do not care as the user has entered something
* */
processPendingActions()
}
}

/** Clear all the items in the queue and dispatch them to the viewmodel in order */
private fun processPendingActions() = viewModel.viewModelScope.launchSafe {
while (!pendingPathActions.isEmpty()) {
try {
val (action, data) = pendingPathActions.pop()
viewModel.handleAction(
EpisodeClickEvent(
action,
data
)
)
} catch (_: NoSuchElementException) {
/** In case of a race */
}
}
}

private val pathPicker = getChooseFolderLauncher { uri, path ->
if (uri == null) {
/** No path selected, clear the list without acting on it, canceling */
if (!pendingPathActions.isEmpty()) {
/** Only show on non-empty, just in case */
showToast(R.string.download_canceled)
pendingPathActions.clear()
}
} else {
/** Select the folder, and dispatch everything */
pickDownloadPath(uri, path)
processPendingActions()
}
}

protected lateinit var viewModel: ResultViewModel2
protected lateinit var syncModel: SyncViewModel

Expand Down Expand Up @@ -433,7 +506,13 @@ open class ResultFragmentPhone : FullScreenPlayer() {
EpisodeAdapter(
api?.hasDownloadSupport == true,
{ episodeClick ->
viewModel.handleAction(episodeClick)
when (episodeClick.action) {
ACTION_DOWNLOAD_EPISODE, ACTION_DOWNLOAD_MIRROR -> {
requirePathForActions(listOf(episodeClick.action to episodeClick.data))
}

else -> viewModel.handleAction(episodeClick)
}
},
{ downloadClickEvent ->
DownloadButtonSetup.handleDownloadClick(downloadClickEvent)
Expand Down Expand Up @@ -753,30 +832,12 @@ open class ResultFragmentPhone : FullScreenPlayer() {
.setTitle(R.string.download_all)
.setMessage(rangeMessage)
.setPositiveButton(R.string.yes) { _, _ ->
ioSafe {
episodes.value.forEach { episode ->
viewModel.handleAction(
EpisodeClickEvent(
ACTION_DOWNLOAD_EPISODE,
episode
)
)
// Join to make the episodes ordered
.join()
}
}
requirePathForActions(episodes.value.map { ACTION_DOWNLOAD_EPISODE to it })
}
.setNegativeButton(R.string.cancel) { _, _ ->

}.show()

.setNegativeButton(R.string.cancel) { _, _ -> }.show()
}

}


}

}

observeNullable(viewModel.movie) { data ->
Expand Down Expand Up @@ -825,18 +886,11 @@ open class ResultFragmentPhone : FullScreenPlayer() {

when (click.action) {
DOWNLOAD_ACTION_DOWNLOAD -> {
viewModel.handleAction(
EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep)
)
requirePathForActions(listOf(ACTION_DOWNLOAD_EPISODE to ep))
}

DOWNLOAD_ACTION_LONG_CLICK -> {
viewModel.handleAction(
EpisodeClickEvent(
ACTION_DOWNLOAD_MIRROR,
ep
)
)
requirePathForActions(listOf(ACTION_DOWNLOAD_MIRROR to ep))
}

else -> DownloadButtonSetup.handleDownloadClick(click)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.edit
import androidx.core.os.ConfigurationCompat
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder.allProviders
Expand Down Expand Up @@ -155,16 +156,23 @@ class SettingsGeneral : BasePreferenceFragmentCompat() {
val lang: String,
)

private val pathPicker = getChooseFolderLauncher { uri, path ->
val context = context ?: CloudStreamApp.context ?: return@getChooseFolderLauncher
(path ?: uri.toString()).let {
companion object {
fun Fragment.pickDownloadPath(uri: Uri?, path: String?) {
if (uri == null) return

val context = context ?: CloudStreamApp.context ?: return
val visual = path ?: uri.toString()
PreferenceManager.getDefaultSharedPreferences(context).edit {
putString(getString(R.string.download_path_key), uri.toString())
putString(getString(R.string.download_path_key_visual), it)
putString(context.getString(R.string.download_path_key_visual), visual)
}
}
}

private val pathPicker = getChooseFolderLauncher { uri, path ->
pickDownloadPath(uri, path)
}

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard()
setPreferencesFromResource(R.xml.settings_general, rootKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class SettingsUpdates : BasePreferenceFragmentCompat() {
}

private val pathPicker = getChooseFolderLauncher { uri, path ->
if(uri == null) return@getChooseFolderLauncher

val context = context ?: CloudStreamApp.context ?: return@getChooseFolderLauncher
(path ?: uri.toString()).let {
PreferenceManager.getDefaultSharedPreferences(context).edit {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import com.lagradost.safefile.SafeFile
fun Fragment.getChooseFolderLauncher(dirSelected: (uri: Uri?, path: String?) -> Unit) =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
// It lies, it can be null if file manager quits.
if (uri == null) return@registerForActivityResult
if(uri == null) {
dirSelected(null, null)
return@registerForActivityResult
}
val context = context ?: CloudStreamApp.context ?: return@registerForActivityResult
// RW perms for the path
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Expand Down