Plugin setup screen and hopefully fix critical error

This commit is contained in:
Blatzar 2022-08-09 04:53:24 +02:00
parent 2c399e6916
commit 6e819e3b96
12 changed files with 333 additions and 128 deletions

View file

@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.lagradost.cloudstream3.mvvm.debugWarning
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
@ -69,7 +70,11 @@ object APIHolder {
fun getApiFromNameNull(apiName: String?): MainAPI? { fun getApiFromNameNull(apiName: String?): MainAPI? {
if (apiName == null) return null if (apiName == null) return null
initMap() initMap()
return apiMap?.get(apiName)?.let { apis.getOrNull(it) } // Fuck it load from allProviders since they're dynamically loaded
// This is required right now because apiMap might be outdated
// TODO FIX when we switch to LoadPlugin()
debugWarning { "FIX LoadPlugin! getApiFromNameNull sucks right now 💀" }
return apiMap?.get(apiName)?.let { apis.getOrNull(it) } ?: allProviders.firstOrNull { it.name == apiName }
} }
fun getApiFromUrlNull(url: String?): MainAPI? { fun getApiFromUrlNull(url: String?): MainAPI? {

View file

@ -670,6 +670,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
try { try {
if (getKey(HAS_DONE_SETUP_KEY, false) != true) { if (getKey(HAS_DONE_SETUP_KEY, false) != true) {
navController.navigate(R.id.navigation_setup_language) navController.navigate(R.id.navigation_setup_language)
// If no plugins bring up extensions screen
} else if (PluginManager.getPluginsOnline().isEmpty()
&& PluginManager.getPluginsLocal().isEmpty()
) {
navController.navigate(R.id.navigation_setup_extensions)
} }
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)

View file

@ -18,6 +18,16 @@ import java.io.File
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
/**
* Comes with the app, always available in the app, non removable.
* */
val PREBUILT_REPOSITORIES = arrayOf(
// TODO FIX
RepositoryData(
"Testing repository",
"https://raw.githubusercontent.com/recloudstream/cs-repos/master/test.json"
)
)
data class Repository( data class Repository(
@JsonProperty("name") val name: String, @JsonProperty("name") val name: String,

View file

@ -42,7 +42,7 @@ class ExtensionsFragment : Fragment() {
setUpToolbar(R.string.extensions) setUpToolbar(R.string.extensions)
repo_recycler_view?.adapter = RepoAdapter(emptyArray(), { repo_recycler_view?.adapter = RepoAdapter(emptyArray(), false, {
findNavController().navigate( findNavController().navigate(
R.id.navigation_settings_extensions_to_navigation_settings_plugins, R.id.navigation_settings_extensions_to_navigation_settings_plugins,
Bundle().apply { Bundle().apply {

View file

@ -40,7 +40,7 @@ class PluginsFragment : Fragment() {
settings_toolbar?.setOnMenuItemClickListener { menuItem -> settings_toolbar?.setOnMenuItemClickListener { menuItem ->
when (menuItem?.itemId) { when (menuItem?.itemId) {
R.id.download_all -> { R.id.download_all -> {
pluginViewModel.downloadAll(activity, url) PluginsViewModel.downloadAll(activity, url, pluginViewModel)
} }
else -> {} else -> {}
} }

View file

@ -29,75 +29,82 @@ class PluginsViewModel : ViewModel() {
companion object { companion object {
private val repositoryCache: MutableMap<String, List<Plugin>> = mutableMapOf() private val repositoryCache: MutableMap<String, List<Plugin>> = mutableMapOf()
const val TAG = "PLG" const val TAG = "PLG"
}
private suspend fun getPlugins( private fun isDownloaded(plugin: Plugin, data: Set<String>? = null): Boolean {
repositoryUrl: String, return (data ?: getDownloads()).contains(plugin.second.internalName)
canUseCache: Boolean = true
): List<Plugin> {
Log.i(TAG, "getPlugins = $repositoryUrl")
if (canUseCache && repositoryCache.containsKey(repositoryUrl)) {
repositoryCache[repositoryUrl]?.let {
return it
}
} }
return RepositoryManager.getRepoPlugins(repositoryUrl)
?.also { repositoryCache[repositoryUrl] = it } ?: emptyList()
}
private fun getStoredPlugins(): Array<PluginData> { private suspend fun getPlugins(
return PluginManager.getPluginsOnline() repositoryUrl: String,
} canUseCache: Boolean = true
): List<Plugin> {
private fun getDownloads(): Set<String> { Log.i(TAG, "getPlugins = $repositoryUrl")
return getStoredPlugins().map { it.internalName }.toSet() if (canUseCache && repositoryCache.containsKey(repositoryUrl)) {
} repositoryCache[repositoryUrl]?.let {
return it
private fun isDownloaded(plugin: Plugin, data: Set<String>? = null): Boolean { }
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) -> return RepositoryManager.getRepoPlugins(repositoryUrl)
PluginManager.downloadAndLoadPlugin( ?.also { repositoryCache[repositoryUrl] = it } ?: emptyList()
activity, }
metadata.url,
metadata.name, private fun getStoredPlugins(): Array<PluginData> {
repo return PluginManager.getPluginsOnline()
) }
}.main { list ->
if (list.any { it }) { private fun getDownloads(): Set<String> {
showToast( return getStoredPlugins().map { it.internalName }.toSet()
}
/**
* @param viewModel optional, updates the plugins livedata for that viewModel if included
* */
fun downloadAll(activity: Activity?, repositoryUrl: String, viewModel: PluginsViewModel?) = 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, activity,
txt( metadata.url,
R.string.batch_download_finish_format, metadata.name,
list.count { it }, repo
txt(if(list.size == 1) R.string.plugin_singular else R.string.plugin)
),
Toast.LENGTH_SHORT
) )
updatePluginListPrivate(repositoryUrl) }.main { list ->
} else if (list.isNotEmpty()) { if (list.any { it }) {
showToast(activity, R.string.download_failed, Toast.LENGTH_SHORT) 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
)
viewModel?.updatePluginListPrivate(repositoryUrl)
} else if (list.isNotEmpty()) {
showToast(activity, R.string.download_failed, Toast.LENGTH_SHORT)
}
} }
} }
} }

View file

@ -5,13 +5,16 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.plugins.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.ui.settings.AccountClickCallback import com.lagradost.cloudstream3.ui.settings.AccountClickCallback
import kotlinx.android.synthetic.main.repository_item.view.* import kotlinx.android.synthetic.main.repository_item.view.*
class RepoAdapter( class RepoAdapter(
var repositories: Array<RepositoryData>, var repositories: Array<RepositoryData>,
val isSetup: Boolean,
val clickCallback: RepoAdapter.(RepositoryData) -> Unit, val clickCallback: RepoAdapter.(RepositoryData) -> Unit,
val imageClickCallback: RepoAdapter.(RepositoryData) -> Unit val imageClickCallback: RepoAdapter.(RepositoryData) -> Unit,
/** In setup mode the trash icons will be replaced with download icons */
) : ) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
@ -37,7 +40,16 @@ class RepoAdapter(
fun bind( fun bind(
repositoryData: RepositoryData repositoryData: RepositoryData
) { ) {
itemView.action_button?.setImageResource(R.drawable.ic_baseline_delete_outline_24) val isPrebuilt = PREBUILT_REPOSITORIES.contains(repositoryData)
val drawable =
if (isSetup) R.drawable.netflix_download else R.drawable.ic_baseline_delete_outline_24
// Only shows icon if on setup or if it isn't a prebuilt repo.
// No delete buttons on prebuilt repos.
if (!isPrebuilt || isSetup) {
itemView.action_button?.setImageResource(drawable)
}
itemView.action_button?.setOnClickListener { itemView.action_button?.setOnClickListener {
imageClickCallback(repositoryData) imageClickCallback(repositoryData)
} }

View file

@ -0,0 +1,68 @@
package com.lagradost.cloudstream3.ui.setup
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.plugins.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel
import com.lagradost.cloudstream3.ui.settings.extensions.RepoAdapter
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_extensions.*
import kotlinx.android.synthetic.main.fragment_setup_media.*
class SetupFragmentExtensions : Fragment() {
companion object {
const val SETUP_EXTENSION_BUNDLE_IS_SETUP = "isSetup"
fun newInstance(isSetup: Boolean): Bundle {
return Bundle().apply {
putBoolean(SETUP_EXTENSION_BUNDLE_IS_SETUP, isSetup)
}
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_setup_extensions, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(setup_root)
val isSetup = arguments?.getBoolean(SETUP_EXTENSION_BUNDLE_IS_SETUP) ?: false
with(context) {
if (this == null) return
repo_recycler_view?.adapter = RepoAdapter(PREBUILT_REPOSITORIES, true, {}, {
PluginsViewModel.downloadAll(activity, it.url, null)
})
if (!isSetup) {
next_btt.setText(R.string.setup_done)
}
prev_btt?.isVisible = isSetup
next_btt?.setOnClickListener {
// Continue setup
if (isSetup)
findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_provider_languages)
else
findNavController().popBackStack()
}
prev_btt?.setOnClickListener {
findNavController().popBackStack()
}
}
}
}

View file

@ -14,6 +14,7 @@ import com.lagradost.cloudstream3.BuildConfig
import com.lagradost.cloudstream3.CommonActivity import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.settings.appLanguages import com.lagradost.cloudstream3.ui.settings.appLanguages
import com.lagradost.cloudstream3.ui.settings.getCurrentLocale import com.lagradost.cloudstream3.ui.settings.getCurrentLocale
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
@ -23,6 +24,7 @@ import kotlinx.android.synthetic.main.fragment_setup_media.listview1
import kotlinx.android.synthetic.main.fragment_setup_media.next_btt import kotlinx.android.synthetic.main.fragment_setup_media.next_btt
const val HAS_DONE_SETUP_KEY = "HAS_DONE_SETUP" const val HAS_DONE_SETUP_KEY = "HAS_DONE_SETUP"
class SetupFragmentLanguage : Fragment() { class SetupFragmentLanguage : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@ -76,7 +78,13 @@ class SetupFragmentLanguage : Fragment() {
} }
next_btt?.setOnClickListener { next_btt?.setOnClickListener {
findNavController().navigate(R.id.action_navigation_setup_language_to_navigation_setup_provider_languages) // If no plugins go to plugins page
val nextDestination = if (PluginManager.getPluginsOnline()
.isEmpty()
) R.id.action_navigation_global_to_navigation_setup_extensions
else R.id.action_navigation_setup_language_to_navigation_setup_provider_languages
findNavController().navigate(nextDestination, SetupFragmentExtensions.newInstance(true))
} }
skip_btt?.setOnClickListener { skip_btt?.setOnClickListener {

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/setup_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/extensions"
android:textSize="18sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/setup_extensions_subtext" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/repo_recycler_view"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/repository_item" />
<LinearLayout
android:id="@+id/apply_btt_holder"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_gravity="bottom"
android:layout_marginTop="-60dp"
android:gravity="bottom|end"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/next_btt"
style="@style/WhiteButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:text="@string/next" />
<com.google.android.material.button.MaterialButton
android:id="@+id/prev_btt"
style="@style/BlackButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:text="@string/previous" />
</LinearLayout>
</LinearLayout>

View file

@ -5,30 +5,30 @@
android:id="@+id/mobile_navigation" android:id="@+id/mobile_navigation"
app:startDestination="@+id/navigation_home"> app:startDestination="@+id/navigation_home">
<action <action
android:id="@+id/global_to_navigation_results_tv" android:id="@+id/global_to_navigation_results_tv"
app:destination="@id/navigation_results_tv" app:destination="@id/navigation_results_tv"
app:enterAnim="@anim/enter_anim" app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim"> app:popExitAnim="@anim/exit_anim">
<argument <argument
android:name="url" android:name="url"
app:argType="string" /> app:argType="string" />
<argument <argument
android:name="apiName" android:name="apiName"
app:argType="string" /> app:argType="string" />
<argument <argument
android:name="startAction" android:name="startAction"
android:defaultValue="0" android:defaultValue="0"
app:argType="integer" /> app:argType="integer" />
<argument <argument
android:name="startValue" android:name="startValue"
android:defaultValue="0" android:defaultValue="0"
app:argType="integer" /> app:argType="integer" />
<argument <argument
android:name="restart" android:name="restart"
android:defaultValue="false" android:defaultValue="false"
app:argType="boolean" /> app:argType="boolean" />
</action> </action>
<action <action
android:id="@+id/global_to_navigation_results_phone" android:id="@+id/global_to_navigation_results_phone"
@ -160,7 +160,8 @@
app:enterAnim="@anim/enter_anim" app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim"> app:popExitAnim="@anim/exit_anim"
tools:layout="@layout/fragment_extensions">
<action <action
android:id="@+id/navigation_settings_extensions_to_navigation_settings_plugins" android:id="@+id/navigation_settings_extensions_to_navigation_settings_plugins"
app:destination="@id/navigation_settings_plugins" app:destination="@id/navigation_settings_plugins"
@ -186,7 +187,8 @@
app:enterAnim="@anim/enter_anim" app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" /> app:popExitAnim="@anim/exit_anim"
tools:layout="@layout/fragment_plugins" />
<fragment <fragment
android:id="@+id/navigation_settings_lang" android:id="@+id/navigation_settings_lang"
@ -258,8 +260,7 @@
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" app:popExitAnim="@anim/exit_anim"
tools:layout="@layout/fragment_search"> tools:layout="@layout/fragment_search" />
</fragment>
<fragment <fragment
android:id="@+id/navigation_downloads" android:id="@+id/navigation_downloads"
@ -406,53 +407,53 @@
</fragment> </fragment>
<fragment <fragment
android:id="@+id/navigation_results_phone" android:id="@+id/navigation_results_phone"
android:name="com.lagradost.cloudstream3.ui.result.ResultFragmentPhone" android:name="com.lagradost.cloudstream3.ui.result.ResultFragmentPhone"
android:layout_height="match_parent" android:layout_height="match_parent"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim"
tools:layout="@layout/fragment_result_swipe">
<action
android:id="@+id/action_navigation_results_phone_to_navigation_quick_search"
app:destination="@id/navigation_quick_search"
app:enterAnim="@anim/enter_anim" app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" app:popExitAnim="@anim/exit_anim" />
tools:layout="@layout/fragment_result_swipe">
<action <action
android:id="@+id/action_navigation_results_phone_to_navigation_quick_search" android:id="@+id/action_navigation_results_phone_to_navigation_player"
app:destination="@id/navigation_quick_search" app:destination="@id/navigation_player"
app:enterAnim="@anim/enter_anim" app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" /> app:popExitAnim="@anim/exit_anim" />
<action
android:id="@+id/action_navigation_results_phone_to_navigation_player"
app:destination="@id/navigation_player"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/navigation_results_tv" android:id="@+id/navigation_results_tv"
android:name="com.lagradost.cloudstream3.ui.result.ResultFragmentTv" android:name="com.lagradost.cloudstream3.ui.result.ResultFragmentTv"
android:layout_height="match_parent" android:layout_height="match_parent"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim"
tools:layout="@layout/fragment_result_swipe">
<action
android:id="@+id/action_navigation_results_tv_to_navigation_quick_search"
app:destination="@id/navigation_quick_search"
app:enterAnim="@anim/enter_anim" app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" app:popExitAnim="@anim/exit_anim" />
tools:layout="@layout/fragment_result_swipe">
<action <action
android:id="@+id/action_navigation_results_tv_to_navigation_quick_search" android:id="@+id/action_navigation_results_tv_to_navigation_player"
app:destination="@id/navigation_quick_search" app:destination="@id/navigation_player"
app:enterAnim="@anim/enter_anim" app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" /> app:popExitAnim="@anim/exit_anim" />
<action
android:id="@+id/action_navigation_results_tv_to_navigation_player"
app:destination="@id/navigation_player"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" />
</fragment> </fragment>
<!--<fragment <!--<fragment
@ -508,6 +509,38 @@
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" /> app:popExitAnim="@anim/exit_anim" />
</fragment> </fragment>
<action
android:id="@+id/action_navigation_global_to_navigation_setup_extensions"
app:destination="@id/navigation_setup_extensions"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim">
<argument
android:name="isSetup"
android:defaultValue="false"
app:argType="boolean" />
</action>
<fragment
android:id="@+id/navigation_setup_extensions"
android:name="com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions"
android:layout_height="match_parent"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim"
tools:layout="@layout/fragment_setup_extensions">
<action
android:id="@+id/action_navigation_setup_extensions_to_navigation_setup_provider_languages"
app:destination="@id/navigation_setup_provider_languages"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" />
</fragment>
<fragment <fragment
android:id="@+id/navigation_setup_provider_languages" android:id="@+id/navigation_setup_provider_languages"
android:name="com.lagradost.cloudstream3.ui.setup.SetupFragmentProviderLanguage" android:name="com.lagradost.cloudstream3.ui.setup.SetupFragmentProviderLanguage"

View file

@ -585,4 +585,5 @@
<string name="plugin">plugins</string> <string name="plugin">plugins</string>
<string name="delete_repository_plugins">This will also delete all repository plugins</string> <string name="delete_repository_plugins">This will also delete all repository plugins</string>
<string name="delete_repository">Delete repository</string> <string name="delete_repository">Delete repository</string>
<string name="setup_extensions_subtext">Download the list of sites you want to use</string>
</resources> </resources>