let extensions patch all resources

This commit is contained in:
Cloudburst 2022-09-16 17:01:16 +02:00
parent 9402a28041
commit 6baeadf55e
12 changed files with 277 additions and 5 deletions

View File

@ -18,6 +18,7 @@ import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStore.removeKeys import com.lagradost.cloudstream3.utils.DataStore.removeKeys
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.resources.ResourcePackManager
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.acra.ACRA import org.acra.ACRA
import org.acra.ReportField import org.acra.ReportField
@ -79,6 +80,7 @@ class ExceptionHandler(val errorFile: File, val onError: (() -> Unit)): Thread.U
ACRA.errorReporter.handleException(error) ACRA.errorReporter.handleException(error)
try { try {
PrintStream(errorFile).use { ps -> PrintStream(errorFile).use { ps ->
ps.println(String.format("Enabled resource pack: ${ResourcePackManager.activePackId ?: "none"}"))
ps.println(String.format("Currently loading extension: ${PluginManager.currentlyLoading ?: "none"}")) ps.println(String.format("Currently loading extension: ${PluginManager.currentlyLoading ?: "none"}"))
ps.println(String.format("Fatal exception on thread %s (%d)", thread.name, thread.id)) ps.println(String.format("Fatal exception on thread %s (%d)", thread.name, thread.id))
error.printStackTrace(ps) error.printStackTrace(ps)

View File

@ -4,13 +4,13 @@ import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.WindowManager import android.view.WindowManager
import android.widget.Toast
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -59,6 +59,9 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSet
import com.lagradost.cloudstream3.ui.settings.SettingsGeneral import com.lagradost.cloudstream3.ui.settings.SettingsGeneral
import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY
import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.IOnBackPressed
import com.lagradost.cloudstream3.utils.resources.ResourcePatch
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
@ -70,8 +73,6 @@ import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.IOnBackPressed
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
@ -82,6 +83,8 @@ import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.UIHelper.requestRW
import com.lagradost.cloudstream3.utils.USER_PROVIDER_API import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.utils.resources.ResourcePackManager
import com.lagradost.cloudstream3.utils.resources.ResourcePatchActivity
import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.ResponseParser import com.lagradost.nicehttp.ResponseParser
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
@ -133,7 +136,18 @@ var app = Requests(responseParser = object : ResponseParser {
defaultHeaders = mapOf("user-agent" to USER_AGENT) defaultHeaders = mapOf("user-agent" to USER_AGENT)
} }
class MainActivity : AppCompatActivity(), ColorPickerDialogListener { class MainActivity : AppCompatActivity(), ColorPickerDialogListener, ResourcePatchActivity {
private var resourcePatch: ResourcePatch? = null
override fun getResources(): Resources = resourcePatch ?: super.getResources()
override fun reloadResourcePatch() {
resourcePatch = try {
ResourcePackManager.activePack?.invoke(super.getResources())
} catch (e: Throwable) {
null
}
}
companion object { companion object {
const val TAG = "MAINACT" const val TAG = "MAINACT"

View File

@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.extractorApis import com.lagradost.cloudstream3.utils.extractorApis
import android.util.Log import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.utils.resources.ResourcePackManager
import com.lagradost.cloudstream3.utils.resources.ResourcePatch
const val PLUGIN_TAG = "PluginInstance" const val PLUGIN_TAG = "PluginInstance"
@ -50,6 +52,16 @@ abstract class Plugin {
extractorApis.add(element) extractorApis.add(element)
} }
/**
* Used to register a new resource pack
* @param factory The function that provided the original resources will generate a ResourcePatch instance
* You should probably use MapResourcePatch
* @see com.lagradost.cloudstream3.utils.resources.MapResourcePatch
*/
fun registerResourcePack(name: String, factory: (Resources) -> ResourcePatch) {
ResourcePackManager.registerPack(name, factory, this.__filename)
}
class Manifest { class Manifest {
@JsonProperty("name") var name: String? = null @JsonProperty("name") var name: String? = null
@JsonProperty("pluginClassName") var pluginClassName: String? = null @JsonProperty("pluginClassName") var pluginClassName: String? = null

View File

@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIE
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.extractorApis import com.lagradost.cloudstream3.utils.extractorApis
import com.lagradost.cloudstream3.utils.resources.ResourcePackManager
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import org.acra.log.debug import org.acra.log.debug
@ -376,6 +377,7 @@ object PluginManager {
} }
APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.__filename } APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.__filename }
extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.__filename } extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.__filename }
ResourcePackManager.unregisterPlugin(plugin.__filename)
classLoaders.values.removeIf { v -> v == plugin } classLoaders.values.removeIf { v -> v == plugin }

View File

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.ui.player package com.lagradost.cloudstream3.ui.player
import android.content.Intent import android.content.Intent
import android.content.res.Resources
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@ -11,10 +12,13 @@ import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.ExtractorUri
import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.resources.ResourcePackManager
import com.lagradost.cloudstream3.utils.resources.ResourcePatch
import com.lagradost.cloudstream3.utils.resources.ResourcePatchActivity
const val DTAG = "PlayerActivity" const val DTAG = "PlayerActivity"
class DownloadedPlayerActivity : AppCompatActivity() { class DownloadedPlayerActivity : AppCompatActivity(), ResourcePatchActivity {
override fun dispatchKeyEvent(event: KeyEvent?): Boolean { override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
CommonActivity.dispatchKeyEvent(this, event)?.let { CommonActivity.dispatchKeyEvent(this, event)?.let {
return it return it
@ -106,4 +110,14 @@ class DownloadedPlayerActivity : AppCompatActivity() {
return return
} }
} }
private var resourcePatch: ResourcePatch? = null
override fun getResources(): Resources = resourcePatch ?: super.getResources()
override fun reloadResourcePatch() {
resourcePatch = try {
ResourcePackManager.activePack?.invoke(super.getResources())
} catch (e: Throwable) {
null
}
}
} }

View File

@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.View import android.view.View
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.MainActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.SearchQuality
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
@ -16,6 +17,8 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.resources.ResourcePackManager
import kotlin.streams.toList
class SettingsUI : PreferenceFragmentCompat() { class SettingsUI : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -130,6 +133,27 @@ class SettingsUI : PreferenceFragmentCompat() {
} }
return@setOnPreferenceClickListener true return@setOnPreferenceClickListener true
} }
getPref(R.string.active_resource_pack_key)?.setOnPreferenceClickListener {
val prefNames = ResourcePackManager.packs.keys.toMutableList()
prefNames.add(0, getString(R.string.none))
val prefValues: MutableList<String?> = ResourcePackManager.packs.keys.toMutableList()
prefValues.add(0, null)
activity?.showDialog(
prefNames.toList(),
prefValues.indexOf(ResourcePackManager.activePackId),
getString(R.string.resource_pack),
true,
{}) {
try {
ResourcePackManager.selectPack(prefValues[it], activity as MainActivity)
activity?.recreate()
} catch (e: Exception) {
logError(e)
}
}
return@setOnPreferenceClickListener true
}
getPref(R.string.pref_filter_search_quality_key)?.setOnPreferenceClickListener { getPref(R.string.pref_filter_search_quality_key)?.setOnPreferenceClickListener {
val names = enumValues<SearchQuality>().sorted().map { it.name } val names = enumValues<SearchQuality>().sorted().map { it.name }

View File

@ -0,0 +1,47 @@
package com.lagradost.cloudstream3.utils.resources
import android.content.res.ColorStateList
import android.content.res.Resources
import android.content.res.XmlResourceParser
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
typealias Generator<T> = () -> T?
typealias GeneratorWithTheme<T> = (Resources.Theme?) -> T?
open class MapResourcePatch(private val original: Resources): ResourcePatch(original) {
val mapping: HashMap<Int, Any> = hashMapOf()
val idMapping: HashMap<Int, Int> = hashMapOf()
override fun mapId(id: Int): Int =
idMapping[id] ?: super.mapId(id)
override fun getLayout(id: Int): XmlResourceParser =
mapping.getMappingKA(id, null) ?: super.getLayout(id)
override fun getDrawable(id: Int, theme: Theme?): Drawable =
mapping.getMappingKA(id, theme) ?: super.getDrawable(id, theme)
@RequiresApi(Build.VERSION_CODES.M)
override fun getColorStateList(id: Int, theme: Theme?): ColorStateList =
mapping.getMappingKA(id, theme) ?: super.getColorStateList(id, theme)
override fun getXml(id: Int): XmlResourceParser =
mapping.getMappingKA(id, null) ?: super.getXml(id)
@RequiresApi(Build.VERSION_CODES.M)
override fun getColor(id: Int, theme: Theme?): Int =
mapping.getMappingKA(id, theme) ?: super.getColor(id, theme)
}
private inline fun <K, reified V> HashMap<K, Any>.getMappingKA(id: K?, theme: Resources.Theme?): V? {
return when (val res = this[id]) {
equals(null) -> null
is V -> res
is Function0<*> -> (res as? Generator<V>)?.invoke()
is Function1<*, *> -> (res as? GeneratorWithTheme<V>)?.invoke(theme)
else -> null
}
}

View File

@ -0,0 +1,66 @@
package com.lagradost.cloudstream3.utils.resources
import android.content.res.Resources
import android.util.Log
import com.lagradost.cloudstream3.AcraApplication
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.R
typealias PatchFactory = (Resources) -> ResourcePatch
object ResourcePackManager {
private const val KEY = "ResourcePackManager"
const val SETTINGS_KEY = "active_resource_pack_key" // TODO
val packs: HashMap<String, PatchFactory> = hashMapOf()
private val pluginMapping: HashMap<String, ArrayList<String>> = hashMapOf()
var activePackId: String? = null
internal set
val activePack: PatchFactory?
get() = if (activePackId == null) null else packs[activePackId]
init {
afterPluginsLoadedEvent += ::onExtensionsLoaded
}
fun registerPack(name: String, factory: PatchFactory, pluginId: String?) {
packs[name] = factory
if (pluginId != null) {
if (!pluginMapping.containsKey(pluginId))
pluginMapping[pluginId] = arrayListOf()
pluginMapping[pluginId]?.add(name)
}
}
fun unregisterPlugin(pluginId: String?) {
pluginMapping[pluginId]?.forEach {
packs.remove(it)
if (activePackId == it) // if pack is being removed make sure its not set
selectPack(null, AcraApplication.context?.getActivity() as? ResourcePatchActivity)
}
pluginMapping.remove(pluginId)
}
fun selectPack(packId: String?, activity: ResourcePatchActivity?) {
if (packId == null) activePackId = null
else if (packs.containsKey(packId)) activePackId = packId
else activePackId = null
Log.d(KEY, "Selecting: ${activePackId ?: "none"}")
setKey(SETTINGS_KEY, activePackId)
activity?.reloadResourcePatch()
}
private fun onExtensionsLoaded(successful: Boolean = false) {
if (!successful) return
activePackId = getKey(SETTINGS_KEY)
Log.d(KEY, "Selecting saved: ${activePackId ?: "none"}")
val activity = AcraApplication.context?.getActivity() as? ResourcePatchActivity
activity?.reloadResourcePatch()
}
}

View File

@ -0,0 +1,80 @@
package com.lagradost.cloudstream3.utils.resources
import android.annotation.SuppressLint
import android.content.res.*
import android.content.res.loader.ResourcesLoader
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.util.TypedValue
import androidx.annotation.RequiresApi
import java.io.InputStream
open class ResourcePatch(private val original: Resources) :
Resources(original.assets, original.displayMetrics, original.configuration) {
open fun mapId(id: Int): Int = id
@RequiresApi(Build.VERSION_CODES.R)
override fun addLoaders(vararg loaders: ResourcesLoader?) = original.addLoaders(*loaders)
override fun getAnimation(id: Int): XmlResourceParser = original.getAnimation(mapId(id))
override fun getBoolean(id: Int): Boolean = original.getBoolean(mapId(id))
@RequiresApi(Build.VERSION_CODES.M)
override fun getColor(id: Int, theme: Theme?): Int = original.getColor(mapId(id), theme)
override fun getConfiguration(): Configuration = original.configuration
override fun getDisplayMetrics(): DisplayMetrics = original.displayMetrics
@RequiresApi(Build.VERSION_CODES.M)
override fun getColorStateList(id: Int, theme: Theme?): ColorStateList = original.getColorStateList(mapId(id), theme)
override fun getLayout(id: Int): XmlResourceParser = original.getLayout(mapId(id))
override fun getDimension(id: Int): Float = original.getDimension(mapId(id))
override fun getIdentifier(name: String?, defType: String?, defPackage: String?): Int = original.getIdentifier(name, defType, defPackage)
override fun getDimensionPixelOffset(id: Int): Int = original.getDimensionPixelOffset(mapId(id))
override fun getDimensionPixelSize(id: Int): Int = original.getDimensionPixelSize(mapId(id))
@SuppressLint("UseCompatLoadingForDrawables")
override fun getDrawable(id: Int, theme: Theme?): Drawable = original.getDrawable(mapId(id), theme)
override fun getDrawableForDensity(id: Int, density: Int, theme: Theme?): Drawable? = original.getDrawableForDensity(mapId(id), density, theme)
@RequiresApi(Build.VERSION_CODES.Q)
override fun getFloat(id: Int): Float = original.getFloat(mapId(id))
@RequiresApi(Build.VERSION_CODES.O)
override fun getFont(id: Int): Typeface = original.getFont(mapId(id))
override fun getIntArray(id: Int): IntArray = original.getIntArray(mapId(id))
override fun getFraction(id: Int, base: Int, pbase: Int): Float = original.getFraction(mapId(id), base, pbase)
override fun getInteger(id: Int): Int = original.getInteger(mapId(id))
override fun getQuantityString(id: Int, quantity: Int): String = original.getQuantityString(mapId(id), quantity)
override fun getQuantityString(id: Int, quantity: Int, vararg formatArgs: Any?): String = original.getQuantityString(mapId(id), quantity, *formatArgs)
override fun getQuantityText(id: Int, quantity: Int): CharSequence = original.getQuantityText(mapId(id), quantity)
override fun getResourceEntryName(id: Int): String = original.getResourceEntryName(mapId(id))
override fun getResourceName(id: Int): String = original.getResourceName(mapId(id))
override fun getResourcePackageName(id: Int): String = original.getResourcePackageName(mapId(id))
override fun getResourceTypeName(id: Int): String = original.getResourceTypeName(mapId(id))
override fun getString(id: Int): String = original.getString(mapId(id))
override fun getString(id: Int, vararg formatArgs: Any?): String = original.getString(mapId(id), *formatArgs)
override fun getStringArray(id: Int): Array<String> = original.getStringArray(mapId(id))
override fun getText(id: Int): CharSequence = original.getText(mapId(id))
override fun getText(id: Int, def: CharSequence?): CharSequence = original.getText(mapId(id), def)
override fun getTextArray(id: Int): Array<CharSequence> = original.getTextArray(mapId(id))
override fun getValue(id: Int, outValue: TypedValue?, resolveRefs: Boolean) = original.getValue(mapId(id), outValue, resolveRefs)
override fun getValue(name: String?, outValue: TypedValue?, resolveRefs: Boolean) = original.getValue(name, outValue, resolveRefs)
override fun getValueForDensity(id: Int, density: Int, outValue: TypedValue?, resolveRefs: Boolean) = original.getValueForDensity(mapId(id), density, outValue, resolveRefs)
override fun getXml(id: Int): XmlResourceParser = original.getXml(mapId(id))
override fun obtainTypedArray(id: Int): TypedArray = original.obtainTypedArray(mapId(id))
override fun openRawResource(id: Int): InputStream = original.openRawResource(mapId(id))
override fun openRawResource(id: Int, value: TypedValue?): InputStream = original.openRawResource(mapId(id), value)
override fun openRawResourceFd(id: Int): AssetFileDescriptor = original.openRawResourceFd(mapId(id))
override fun obtainAttributes(set: AttributeSet?, attrs: IntArray?): TypedArray = original.obtainAttributes(set, attrs)
override fun parseBundleExtra(tagName: String?, attrs: AttributeSet?, outBundle: Bundle?) = original.parseBundleExtra(tagName, attrs, outBundle)
override fun parseBundleExtras(parser: XmlResourceParser?, outBundle: Bundle?) = original.parseBundleExtras(parser, outBundle)
@RequiresApi(Build.VERSION_CODES.R)
override fun removeLoaders(vararg loaders: ResourcesLoader?) = original.removeLoaders(*loaders)
override fun equals(other: Any?): Boolean = original == other
override fun hashCode(): Int = original.hashCode()
override fun getColor(id: Int): Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) getColor(mapId(id), null) else original.getColor(mapId(id))
@SuppressLint("UseCompatLoadingForColorStateLists")
override fun getColorStateList(id: Int): ColorStateList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) getColorStateList(mapId(id), null) else original.getColorStateList(mapId(id))
@SuppressLint("UseCompatLoadingForDrawables")
override fun getDrawable(id: Int): Drawable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) getDrawable(mapId(id), null) else original.getDrawable(mapId(id))
}

View File

@ -0,0 +1,5 @@
package com.lagradost.cloudstream3.utils.resources
interface ResourcePatchActivity {
fun reloadResourcePatch()
}

View File

@ -41,6 +41,7 @@
<string name="app_name_download_path" translatable="false">Cloudstream</string> <string name="app_name_download_path" translatable="false">Cloudstream</string>
<string name="app_layout_key" translatable="false">app_layout_key</string> <string name="app_layout_key" translatable="false">app_layout_key</string>
<string name="primary_color_key" translatable="false">primary_color_key</string> <string name="primary_color_key" translatable="false">primary_color_key</string>
<string name="active_resource_pack_key" translatable="false">active_resource_pack_key</string>
<string name="restore_key" translatable="false">restore_key</string> <string name="restore_key" translatable="false">restore_key</string>
<string name="backup_key" translatable="false">backup_key</string> <string name="backup_key" translatable="false">backup_key</string>
<string name="prefer_media_type_key" translatable="false">prefer_media_type_key_2</string> <string name="prefer_media_type_key" translatable="false">prefer_media_type_key_2</string>
@ -457,6 +458,7 @@
<string name="primary_color_settings">Primary color</string> <string name="primary_color_settings">Primary color</string>
<string name="app_theme_settings">App theme</string> <string name="app_theme_settings">App theme</string>
<string name="resource_pack">Resource pack</string>
<string name="bottom_title_settings">Poster title location</string> <string name="bottom_title_settings">Poster title location</string>
<string name="bottom_title_settings_des">Put the title under the poster</string> <string name="bottom_title_settings_des">Put the title under the poster</string>

View File

@ -9,6 +9,10 @@
android:icon="@drawable/ic_baseline_color_lens_24" android:icon="@drawable/ic_baseline_color_lens_24"
android:key="@string/app_theme_key" android:key="@string/app_theme_key"
android:title="@string/app_theme_settings" /> android:title="@string/app_theme_settings" />
<Preference
android:icon="@drawable/ic_baseline_extension_24"
android:key="@string/active_resource_pack_key"
android:title="@string/resource_pack" />
<Preference <Preference
android:icon="@drawable/ic_baseline_tv_24" android:icon="@drawable/ic_baseline_tv_24"
android:key="@string/app_layout_key" android:key="@string/app_layout_key"