From 0dc8077296a16b38974d8929d6d6ab2f0d966b64 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Thu, 11 Aug 2022 19:39:34 +0200 Subject: [PATCH] extensions info + updated adapter --- .../cloudstream3/plugins/PluginManager.kt | 12 +- .../settings/extensions/ExtensionsFragment.kt | 47 +++++- .../extensions/ExtensionsViewModel.kt | 68 +++++++- .../ui/settings/extensions/RepoAdapter.kt | 31 +++- .../ui/setup/SetupFragmentExtensions.kt | 4 +- .../main/res/layout/fragment_extensions.xml | 146 ++++++++++++++++-- app/src/main/res/values/strings.xml | 3 + 7 files changed, 281 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt index fd918ec8..21933eb8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -107,7 +107,7 @@ object PluginManager { private val classLoaders: MutableMap = HashMap() - + private var loadedLocalPlugins = false private val gson = Gson() @@ -126,7 +126,7 @@ object PluginManager { // Helper class for updateAllOnlinePluginsAndLoadThem - private data class OnlinePluginData( + data class OnlinePluginData( val savedData: PluginData, val onlineData: Pair, ) { @@ -135,6 +135,8 @@ object PluginManager { val isDisabled = onlineData.second.status == PROVIDER_STATUS_DOWN } + var allCurrentOutDatedPlugins: Set = emptySet() + /** * Needs to be run before other plugin loading because plugin loading can not be overwritten * 1. Gets all online data about the downloaded plugins @@ -143,7 +145,8 @@ object PluginManager { * 4. Else load the plugin normally **/ fun updateAllOnlinePluginsAndLoadThem(activity: Activity) { - val urls = (getKey>(REPOSITORIES_KEY) ?: emptyArray()) + PREBUILT_REPOSITORIES + val urls = (getKey>(REPOSITORIES_KEY) + ?: emptyArray()) + PREBUILT_REPOSITORIES val onlinePlugins = urls.toList().apmap { getRepoPlugins(it.url)?.toList() ?: emptyList() @@ -156,6 +159,7 @@ object PluginManager { OnlinePluginData(savedData, onlineData) } }.flatten().distinctBy { it.onlineData.second.url } + allCurrentOutDatedPlugins = outdatedPlugins.toSet() Log.i(TAG, "Outdated plugins: ${outdatedPlugins.filter { it.isOutdated }}") @@ -308,7 +312,7 @@ object PluginManager { APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.__filename } extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.__filename } - classLoaders.values.removeIf { v -> v == plugin} + classLoaders.values.removeIf { v -> v == plugin } plugins.remove(absolutePath) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt index 6ff6ff7f..51c5696d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt @@ -7,16 +7,20 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout import android.widget.Toast import androidx.appcompat.app.AlertDialog +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.Some import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.plugins.PREBUILT_REPOSITORIES import com.lagradost.cloudstream3.plugins.RepositoryManager +import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.main @@ -35,6 +39,15 @@ class ExtensionsFragment : Fragment() { return inflater.inflate(R.layout.fragment_extensions, container, false) } + private fun View.setLayoutWidth(weight: Int) { + val param = LinearLayout.LayoutParams( + 0, + LinearLayout.LayoutParams.MATCH_PARENT, + weight.toFloat() + ) + this.layoutParams = param + } + private val extensionViewModel: ExtensionsViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -43,7 +56,7 @@ class ExtensionsFragment : Fragment() { setUpToolbar(R.string.extensions) - repo_recycler_view?.adapter = RepoAdapter(PREBUILT_REPOSITORIES, false, { + repo_recycler_view?.adapter = RepoAdapter(false, { findNavController().navigate( R.id.navigation_settings_extensions_to_navigation_settings_plugins, Bundle().apply { @@ -60,6 +73,7 @@ class ExtensionsFragment : Fragment() { DialogInterface.BUTTON_POSITIVE -> { ioSafe { RepositoryManager.removeRepository(view.context, repo) + extensionViewModel.loadStats() extensionViewModel.loadRepositories() } } @@ -78,8 +92,32 @@ class ExtensionsFragment : Fragment() { }) observe(extensionViewModel.repositories) { - (repo_recycler_view?.adapter as? RepoAdapter)?.repositories = it - (repo_recycler_view?.adapter as? RepoAdapter)?.notifyDataSetChanged() + (repo_recycler_view?.adapter as? RepoAdapter)?.updateList(it) + } + + observe(extensionViewModel.pluginStats) { + when (it) { + is Some.Success -> { + val value = it.value + + plugin_storage_appbar?.isVisible = true + if (value.total == 0) { + plugin_download?.setLayoutWidth(1) + plugin_disabled?.setLayoutWidth(0) + plugin_not_downloaded?.setLayoutWidth(0) + } else { + plugin_download?.setLayoutWidth(value.downloaded) + plugin_disabled?.setLayoutWidth(value.disabled) + plugin_not_downloaded?.setLayoutWidth(value.notDownloaded) + } + plugin_not_downloaded_txt.setText(value.notDownloadedText) + plugin_disabled_txt.setText(value.disabledText) + plugin_download_txt.setText(value.downloadedText) + } + is Some.None -> { + plugin_storage_appbar?.isVisible = false + } + } } add_repo_button?.setOnClickListener { @@ -118,6 +156,7 @@ class ExtensionsFragment : Fragment() { val newRepo = RepositoryData(fixedName, url) RepositoryManager.addRepository(newRepo) + extensionViewModel.loadStats() extensionViewModel.loadRepositories() } dialog.dismissSafe(activity) @@ -127,7 +166,7 @@ class ExtensionsFragment : Fragment() { } } - + extensionViewModel.loadStats() extensionViewModel.loadRepositories() } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt index a3379e13..1cbb312a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt @@ -3,9 +3,20 @@ package com.lagradost.cloudstream3.ui.settings.extensions import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.mvvm.Some +import com.lagradost.cloudstream3.mvvm.debugAssert import com.lagradost.cloudstream3.plugins.PREBUILT_REPOSITORIES +import com.lagradost.cloudstream3.plugins.PluginManager +import com.lagradost.cloudstream3.plugins.PluginManager.getPluginsOnline +import com.lagradost.cloudstream3.plugins.RepositoryManager +import com.lagradost.cloudstream3.ui.result.UiText +import com.lagradost.cloudstream3.ui.result.txt +import kotlinx.coroutines.launch data class RepositoryData( @JsonProperty("name") val name: String, @@ -15,12 +26,65 @@ data class RepositoryData( const val REPOSITORIES_KEY = "REPOSITORIES_KEY" class ExtensionsViewModel : ViewModel() { + data class PluginStats( + val total: Int, + + val downloaded: Int, + val disabled: Int, + val notDownloaded: Int, + + val downloadedText: UiText, + val disabledText: UiText, + val notDownloadedText: UiText, + ) + private val _repositories = MutableLiveData>() val repositories: LiveData> = _repositories + private val _pluginStats: MutableLiveData> = MutableLiveData(Some.None) + val pluginStats: LiveData> = _pluginStats + + //TODO CACHE GET REQUESTS + fun loadStats() = viewModelScope.launch { + val urls = (getKey>(REPOSITORIES_KEY) + ?: emptyArray()) + PREBUILT_REPOSITORIES + + val onlinePlugins = urls.toList().apmap { + RepositoryManager.getRepoPlugins(it.url)?.toList() ?: emptyList() + }.flatten().distinctBy { it.second.url } + + // Iterates over all offline plugins, compares to remote repo and returns the plugins which are outdated + val outdatedPlugins = getPluginsOnline().map { savedData -> + onlinePlugins.filter { onlineData -> savedData.internalName == onlineData.second.internalName } + .map { onlineData -> + PluginManager.OnlinePluginData(savedData, onlineData) + } + }.flatten().distinctBy { it.onlineData.second.url } + val total = onlinePlugins.count() + val disabled = outdatedPlugins.count { it.isDisabled } + val downloadedTotal = outdatedPlugins.count() + val downloaded = downloadedTotal - disabled + val notDownloaded = total - downloadedTotal + val stats = PluginStats( + total, + downloaded, + disabled, + notDownloaded, + txt(R.string.plugins_downloaded, downloaded), + txt(R.string.plugins_disabled, disabled), + txt(R.string.plugins_not_downloaded, notDownloaded) + ) + debugAssert({ stats.downloaded + stats.notDownloaded + stats.disabled != stats.total }) { + "downloaded(${stats.downloaded}) + notDownloaded(${stats.notDownloaded}) + disabled(${stats.disabled}) != total(${stats.total})" + } + _pluginStats.postValue(Some.Success(stats)) + } + + private fun repos() = (getKey>(REPOSITORIES_KEY) + ?: emptyArray()) + PREBUILT_REPOSITORIES + fun loadRepositories() { - // Crashes weirdly with List - val urls = (getKey>(REPOSITORIES_KEY) ?: emptyArray()) + PREBUILT_REPOSITORIES + val urls = repos() _repositories.postValue(urls) } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt index 1949894a..0b3620e5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt @@ -3,19 +3,21 @@ package com.lagradost.cloudstream3.ui.settings.extensions import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.plugins.PREBUILT_REPOSITORIES import kotlinx.android.synthetic.main.repository_item.view.* class RepoAdapter( - var repositories: Array, val isSetup: Boolean, val clickCallback: RepoAdapter.(RepositoryData) -> Unit, val imageClickCallback: RepoAdapter.(RepositoryData) -> Unit, /** In setup mode the trash icons will be replaced with download icons */ ) : RecyclerView.Adapter() { + private val repositories: MutableList = mutableListOf() + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return RepoViewHolder( LayoutInflater.from(parent.context).inflate(R.layout.repository_item, parent, false) @@ -42,6 +44,17 @@ class RepoAdapter( return repositories.size } + fun updateList(newList: Array) { + val diffResult = DiffUtil.calculateDiff( + RepoDiffCallback(this.repositories, newList) + ) + + repositories.clear() + repositories.addAll(newList) + + diffResult.dispatchUpdatesTo(this) + } + inner class RepoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind( @@ -68,4 +81,20 @@ class RepoAdapter( itemView.sub_text?.text = repositoryData.url } } +} + +class RepoDiffCallback( + private val oldList: List, + private val newList: Array +) : + DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition].url == newList[newItemPosition].url + + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition] == newList[newItemPosition] } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt index 324db565..2b9fa6b6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt @@ -41,9 +41,9 @@ class SetupFragmentExtensions : Fragment() { with(context) { if (this == null) return - repo_recycler_view?.adapter = RepoAdapter(PREBUILT_REPOSITORIES, true, {}, { + repo_recycler_view?.adapter = RepoAdapter(true, {}, { PluginsViewModel.downloadAll(activity, it.url, null) - }) + }).apply { updateList(PREBUILT_REPOSITORIES) } if (!isSetup) { next_btt.setText(R.string.setup_done) diff --git a/app/src/main/res/layout/fragment_extensions.xml b/app/src/main/res/layout/fragment_extensions.xml index f3cccf91..178520cf 100644 --- a/app/src/main/res/layout/fragment_extensions.xml +++ b/app/src/main/res/layout/fragment_extensions.xml @@ -1,29 +1,141 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/extensions_root" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + + + + + + + + + + + + android:layout_height="wrap_content" + android:orientation="horizontal"> + + + + + + + + + + + + + + + android:id="@+id/add_repo_button" + style="@style/ExtendedFloatingActionButton" + android:text="@string/add_repository" + android:textColor="?attr/textColor" + android:translationY="-80dp" + app:icon="@drawable/ic_baseline_add_24" + tools:ignore="ContentDescription" /> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ad6a078d..682f30c9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -588,4 +588,7 @@ This will also delete all repository plugins Delete repository Download the list of sites you want to use + Downloaded: %d + Disabled: %d + Not downloaded: %d