diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 0ed802ba..7e4c47d4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -6,6 +6,7 @@ import android.content.Context import android.content.pm.PackageManager import android.content.res.Resources import android.os.Build +import android.os.Looper import android.util.Log import android.view.* import android.widget.TextView @@ -17,11 +18,13 @@ import androidx.preference.PreferenceManager import com.google.android.gms.cast.framework.CastSession import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.player.PlayerEventType +import com.lagradost.cloudstream3.ui.result.UiText import com.lagradost.cloudstream3.utils.Event import com.lagradost.cloudstream3.utils.UIHelper import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission import com.lagradost.cloudstream3.utils.UIHelper.shouldShowPIPMode import com.lagradost.cloudstream3.utils.UIHelper.toPx +import kotlinx.coroutines.currentCoroutineContext import org.schabi.newpipe.extractor.NewPipe import java.util.* @@ -43,6 +46,13 @@ object CommonActivity { var currentToast: Toast? = null + fun showToast(act: Activity?, text: UiText, duration: Int) { + if (act == null) return + text.asStringNull(act)?.let { + showToast(act, it, duration) + } + } + fun showToast(act: Activity?, @StringRes message: Int, duration: Int) { if (act == null) return showToast(act, act.getString(message), duration) diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt index 5e20469c..5ed1931b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt @@ -10,7 +10,7 @@ abstract class Plugin { * @param context Context */ @Throws(Throwable::class) - open fun load(context: Context?) { + open fun load(context: Context) { } class Manifest { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index d0b03774..e38b5e1e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -126,10 +126,10 @@ class GeneratorPlayer : FullScreenPlayer() { private fun loadExtractorJob(extractorLink: ExtractorLink?) { currentVerifyLink?.cancel() - extractorLink?.let { + extractorLink?.let { link -> currentVerifyLink = ioSafe { - if (it.extractorData != null) { - getApiFromNameNull(it.source)?.extractorVerifierJob(it.extractorData) + if (link.extractorData != null) { + getApiFromNameNull(link.source)?.extractorVerifierJob(link.extractorData) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index 10f45ecb..de926c76 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -41,6 +41,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast import com.lagradost.cloudstream3.utils.CastHelper.startCast import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioWork +import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultEpisode @@ -689,7 +690,7 @@ class ResultViewModel2 : ViewModel() { }) if (currentLinks.isEmpty()) { - Coroutines.main { + main { showToast( activity, R.string.no_links_found_toast, @@ -698,7 +699,7 @@ class ResultViewModel2 : ViewModel() { } return@ioSafe } else { - Coroutines.main { + main { showToast( activity, R.string.download_started, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index d57e7275..5f3499f9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -1,21 +1,14 @@ package com.lagradost.cloudstream3.ui.settings.extensions import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast +import android.view.* +import androidx.appcompat.view.menu.MenuBuilder import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.observe -import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar -import com.lagradost.cloudstream3.utils.Coroutines.ioSafe -import com.lagradost.cloudstream3.utils.Coroutines.main -import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar -import kotlinx.android.synthetic.main.fragment_extensions.* +import kotlinx.android.synthetic.main.fragment_plugins.* const val PLUGINS_BUNDLE_NAME = "name" const val PLUGINS_BUNDLE_URL = "url" @@ -26,7 +19,7 @@ class PluginsFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle?, ): View? { - return inflater.inflate(R.layout.fragment_extensions, container, false) + return inflater.inflate(R.layout.fragment_plugins, container, false) } private val pluginViewModel: PluginsViewModel by activityViewModels() @@ -37,20 +30,30 @@ class PluginsFragment : Fragment() { val name = arguments?.getString(PLUGINS_BUNDLE_NAME) val url = arguments?.getString(PLUGINS_BUNDLE_URL) - if (url == null) { + if (url == null || name == null) { activity?.onBackPressed() return } - setUpToolbar(name ?: "Unknown") + setUpToolbar(name) - repo_recycler_view?.adapter = + settings_toolbar?.setOnMenuItemClickListener { menuItem -> + when (menuItem?.itemId) { + R.id.download_all -> { + pluginViewModel.downloadAll(activity, url) + } + else -> {} + } + return@setOnMenuItemClickListener true + } + + plugin_recycler_view?.adapter = PluginAdapter { pluginViewModel.handlePluginAction(activity, url, it) } observe(pluginViewModel.plugins) { - (repo_recycler_view?.adapter as? PluginAdapter?)?.updateList(it) + (plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(it) } pluginViewModel.updatePluginList(url) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt index 0912c3ba..19f2d04a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt @@ -9,11 +9,14 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.plugins.PluginData import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.RepositoryManager import com.lagradost.cloudstream3.plugins.SitePlugin +import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread import kotlinx.coroutines.launch @@ -23,9 +26,8 @@ class PluginsViewModel : ViewModel() { private val _plugins = MutableLiveData>() val plugins: LiveData> = _plugins - private val repositoryCache: MutableMap> = mutableMapOf() - companion object { + private val repositoryCache: MutableMap> = mutableMapOf() const val TAG = "PLG" } @@ -55,6 +57,51 @@ class PluginsViewModel : ViewModel() { return (data ?: getDownloads()).contains(plugin.second.internalName) } + fun downloadAll(activity: Activity?, repositoryUrl: String) = ioSafe { + if (activity == null) return@ioSafe + val stored = getDownloads() + val plugins = getPlugins(repositoryUrl) + + plugins.filter { plugin -> !isDownloaded(plugin, stored) }.also { list -> + main { + showToast( + activity, + if (list.isEmpty()) { + txt( + R.string.batch_download_nothing_to_download_format, + txt(R.string.plugin) + ) + } else { + txt(R.string.batch_download_start_format, list.size, txt(if(list.size == 1) R.string.plugin_singular else R.string.plugin)) + }, + Toast.LENGTH_SHORT + ) + } + }.apmap { (repo, metadata) -> + PluginManager.downloadAndLoadPlugin( + activity, + metadata.url, + metadata.name, + repo + ) + }.main { list -> + if (list.any { it }) { + showToast( + activity, + txt( + R.string.batch_download_finish_format, + list.count { it }, + txt(if(list.size == 1) R.string.plugin_singular else R.string.plugin) + ), + Toast.LENGTH_SHORT + ) + updatePluginListPrivate(repositoryUrl) + } else if (list.isNotEmpty()) { + showToast(activity, R.string.download_failed, Toast.LENGTH_SHORT) + } + } + } + fun handlePluginAction(activity: Activity?, repositoryUrl: String, plugin: Plugin) = ioSafe { Log.i(TAG, "handlePluginAction = $repositoryUrl, $plugin") diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt index 978b2720..c2d15614 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt @@ -3,28 +3,34 @@ package com.lagradost.cloudstream3.utils import android.os.Handler import android.os.Looper import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.Coroutines.main import kotlinx.coroutines.* object Coroutines { - fun main(work: suspend (() -> Unit)): Job { + fun T.main(work: suspend ((T) -> Unit)): Job { + val value = this return CoroutineScope(Dispatchers.Main).launch { - work() + work(value) } } - fun ioSafe(work: suspend (CoroutineScope.() -> Unit)): Job { + fun T.ioSafe(work: suspend (CoroutineScope.(T) -> Unit)): Job { + val value = this + return CoroutineScope(Dispatchers.IO).launch { try { - work() + work(value) } catch (e: Exception) { logError(e) } } } - suspend fun ioWork(work: suspend (CoroutineScope.() -> T)): T { + suspend fun V.ioWork(work: suspend (CoroutineScope.(V) -> T)): T { + val value = this return withContext(Dispatchers.IO) { - work() + work(value) } } diff --git a/app/src/main/res/layout/fragment_plugins.xml b/app/src/main/res/layout/fragment_plugins.xml new file mode 100644 index 00000000..69702c57 --- /dev/null +++ b/app/src/main/res/layout/fragment_plugins.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/repository.xml b/app/src/main/res/menu/repository.xml new file mode 100644 index 00000000..e108c005 --- /dev/null +++ b/app/src/main/res/menu/repository.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b6b8da5a..2c437cf5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -577,4 +577,10 @@ Plugin Deleted Failed to load %s 18+ + Started downloading %d %s + Downloaded %d %s successfully + All %s already downloaded + Batch download + plugin + plugins