From 9964f6fbf0214528e9a877b68ece5c247159ec88 Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Fri, 31 Jul 2026 01:53:01 +0000 Subject: [PATCH 01/23] fix subtitle clipping --- .../lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt | 3 +++ app/src/main/res/layout/player_custom_layout.xml | 2 ++ app/src/main/res/layout/player_custom_layout_tv.xml | 2 ++ app/src/main/res/layout/trailer_custom_layout.xml | 2 ++ 4 files changed, 9 insertions(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt index cf892424f..bf77ef247 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt @@ -115,6 +115,9 @@ class SubtitlesFragment : BaseDialogFragment( ) } + view.clipToPadding = false + view.clipChildren = false + // we default to 25sp, this is needed as RoundedBackgroundColorSpan breaks on override sizes val size = data.fixedTextSize ?: 25.0f view.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, size) diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index 04a2a1f1f..c4aa4c473 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -1076,6 +1076,8 @@ diff --git a/app/src/main/res/layout/player_custom_layout_tv.xml b/app/src/main/res/layout/player_custom_layout_tv.xml index 077929d87..88e4cd984 100644 --- a/app/src/main/res/layout/player_custom_layout_tv.xml +++ b/app/src/main/res/layout/player_custom_layout_tv.xml @@ -1116,6 +1116,8 @@ diff --git a/app/src/main/res/layout/trailer_custom_layout.xml b/app/src/main/res/layout/trailer_custom_layout.xml index 88a318874..ddaa53464 100644 --- a/app/src/main/res/layout/trailer_custom_layout.xml +++ b/app/src/main/res/layout/trailer_custom_layout.xml @@ -102,6 +102,8 @@ From 1e33e4227700c90c972b1a3d9071002ad6e8e8ac Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:59:31 +0000 Subject: [PATCH 02/23] fix subtitle clipping (#3099) --- .../lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt | 3 +++ app/src/main/res/layout/player_custom_layout.xml | 2 ++ app/src/main/res/layout/player_custom_layout_tv.xml | 2 ++ app/src/main/res/layout/trailer_custom_layout.xml | 2 ++ 4 files changed, 9 insertions(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt index cf892424f..bf77ef247 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt @@ -115,6 +115,9 @@ class SubtitlesFragment : BaseDialogFragment( ) } + view.clipToPadding = false + view.clipChildren = false + // we default to 25sp, this is needed as RoundedBackgroundColorSpan breaks on override sizes val size = data.fixedTextSize ?: 25.0f view.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, size) diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index 04a2a1f1f..c4aa4c473 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -1076,6 +1076,8 @@ diff --git a/app/src/main/res/layout/player_custom_layout_tv.xml b/app/src/main/res/layout/player_custom_layout_tv.xml index 077929d87..88e4cd984 100644 --- a/app/src/main/res/layout/player_custom_layout_tv.xml +++ b/app/src/main/res/layout/player_custom_layout_tv.xml @@ -1116,6 +1116,8 @@ diff --git a/app/src/main/res/layout/trailer_custom_layout.xml b/app/src/main/res/layout/trailer_custom_layout.xml index 88a318874..ddaa53464 100644 --- a/app/src/main/res/layout/trailer_custom_layout.xml +++ b/app/src/main/res/layout/trailer_custom_layout.xml @@ -102,6 +102,8 @@ From 2eb05e8f354ffdd4f0833dbc365250ac7a99eeb2 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 31 Jul 2026 09:01:45 -0600 Subject: [PATCH 03/23] DataStore: deprecate toKotlinObject (#2964) 1. It can sometimes cause type erasure 2. It is less clear what it is doing 3. It is easy to replace 4. It is very rarely used, almost all app usage has already been replaced, and I haven't found any extensions actually using it but still following normal deprecation cycle as some still might be using it. --- .../lagradost/cloudstream3/utils/DataStore.kt | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt index 02ee69791..68f262d8d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt @@ -181,11 +181,11 @@ object DataStore { } fun Context.getKey(path: String, valueType: Class): T? { - try { + return try { val json: String = getSharedPrefs().getString(path, null) ?: return null - return parseJson(json, valueType.kotlin) - } catch (e: Exception) { - return null + parseJson(json, valueType.kotlin) + } catch (_: Exception) { + null } } @@ -193,21 +193,37 @@ object DataStore { setKey(getFolderName(folder, path), value) } + @Deprecated( + message = "Use parseJson(this) directly instead.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith( + expression = "parseJson(this)", + imports = ["com.lagradost.cloudstream3.utils.AppUtils.parseJson"], + ), + ) inline fun String.toKotlinObject(): T { return parseJson(this) } + @Deprecated( + message = "Use parseJson(this) directly instead.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith( + expression = "parseJson(this)", + imports = ["com.lagradost.cloudstream3.utils.AppUtils.parseJson"], + ), + ) fun String.toKotlinObject(valueType: Class): T { return parseJson(this, valueType.kotlin) } // GET KEY GIVEN PATH AND DEFAULT VALUE, NULL IF ERROR inline fun Context.getKey(path: String, defVal: T?): T? { - try { + return try { val json: String = getSharedPrefs().getString(path, null) ?: return defVal - return json.toKotlinObject() - } catch (e: Exception) { - return null + parseJson(json) + } catch (_: Exception) { + null } } From f09435dc183ba3e6c37579f10d8d8ba6aa5ba307 Mon Sep 17 00:00:00 2001 From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com> Date: Fri, 31 Jul 2026 15:25:00 +0000 Subject: [PATCH 04/23] Add source hiding (#2570) --- .../cloudstream3/ui/player/GeneratorPlayer.kt | 110 ++++++++++++---- .../ui/player/PlayerGeneratorViewModel.kt | 62 ++++++++- .../source_priority/QualityDataHelper.kt | 48 ++++++- .../source_priority/SourcePriorityDialog.kt | 121 +++++++++--------- .../SourceProfileSettingsDialog.kt | 48 +++++++ .../player_select_source_priority.xml | 34 +++-- .../layout/player_select_source_priority.xml | 7 + .../layout/source_profile_settings_dialog.xml | 99 ++++++++++++++ app/src/main/res/values/strings.xml | 8 ++ 9 files changed, 426 insertions(+), 111 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourceProfileSettingsDialog.kt create mode 100644 app/src/main/res/layout/source_profile_settings_dialog.xml 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 91e3ff970..683a322c4 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 @@ -8,6 +8,7 @@ import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.graphics.Bitmap +import android.graphics.Typeface import android.os.Build import android.os.Bundle import android.text.Spanned @@ -79,6 +80,7 @@ import com.lagradost.cloudstream3.ui.player.CS3IPlayer.Companion.preferredAudioT import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType import com.lagradost.cloudstream3.ui.player.source_priority.LinkSource +import com.lagradost.cloudstream3.ui.player.source_priority.ProfileSettings import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getLinkPriority import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog @@ -132,6 +134,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import java.io.Serializable +import java.lang.ref.WeakReference import java.util.Calendar import java.util.UUID import java.util.concurrent.ConcurrentHashMap @@ -510,7 +513,7 @@ class GeneratorPlayer : FullScreenPlayer() { showDownloadProgress(DownloadEvent(0, 0, 0, null)) // uiReset() // Removed due to UX - + currentSelectedLink = link // setEpisodes(viewModel.getAllMeta() ?: emptyList()) setPlayerDimen(null) @@ -1112,21 +1115,29 @@ class GeneratorPlayer : FullScreenPlayer() { var sourceIndex = 0 var startSource = 0 - var sortedUrls = emptyList>() + // Filtered and sorted links + var currentHiddenFooter: View? = null + var filteredLinks: List = emptyList() fun refreshLinks(qualityProfile: Int) { - sortedUrls = viewModel.state.sortLinks(qualityProfile) - if (sortedUrls.isEmpty()) { + val currentLinkUsed = currentSelectedLink + // Always display current linkFooter + val sortedLinks = viewModel.state.sortLinks(qualityProfile) + + filteredLinks = sortedLinks.filter { it.shouldUseLink || it.link == currentLinkUsed } + + if (sortedLinks.isEmpty()) { sourceDialog.findViewById(R.id.sort_sources_holder)?.isGone = true } else { - startSource = sortedUrls.indexOf(currentSelectedLink) + startSource = filteredLinks.indexOfFirst { it.link == currentLinkUsed } sourceIndex = startSource val sourcesArrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) - sourcesArrayAdapter.addAll(sortedUrls.map { (link, uri) -> + sourcesArrayAdapter.addAll(filteredLinks.map { displayLink -> + val (link, uri) = displayLink.link val name = link?.name ?: uri?.name ?: "NULL" "$name ${Qualities.getStringByInt(link?.quality)}" }) @@ -1142,7 +1153,7 @@ class GeneratorPlayer : FullScreenPlayer() { } providerList.setOnItemLongClickListener { _, _, position, _ -> - sortedUrls.getOrNull(position)?.first?.url?.let { + sortedLinks.getOrNull(position)?.link?.first?.url?.let { clipboardHelper( txt(R.string.video_source), it @@ -1150,6 +1161,25 @@ class GeneratorPlayer : FullScreenPlayer() { } true } + + val hiddenLinks = sortedLinks.size - filteredLinks.size + providerList.removeFooterView(currentHiddenFooter) + + if (hiddenLinks > 0) { + val hiddenLinksFooter: TextView = layoutInflater.inflate( + R.layout.sort_bottom_footer_add_choice, null + ) as TextView + + providerList.addFooterView(hiddenLinksFooter, null, false) + currentHiddenFooter = hiddenLinksFooter + + val hiddenLinksText = + ctx.resources.getQuantityString(R.plurals.links_hidden, hiddenLinks) + .format(hiddenLinks) + hiddenLinksFooter.text = hiddenLinksText + hiddenLinksFooter.setCompoundDrawables(null, null, null, null) + hiddenLinksFooter.setTypeface(null, Typeface.ITALIC) + } } } @@ -1363,8 +1393,8 @@ class GeneratorPlayer : FullScreenPlayer() { } } if (init) { - sortedUrls.getOrNull(sourceIndex)?.let { - loadLink(it, true) + filteredLinks.getOrNull(sourceIndex)?.let { + loadLink(it.link, true) } } sourceDialog.dismissSafe(activity) @@ -1532,6 +1562,10 @@ class GeneratorPlayer : FullScreenPlayer() { } override fun playerError(exception: Throwable) { + currentSelectedLink?.let { link -> + viewModel.modifyState { this.addError(link) } + } + val currentUrl = currentSelectedLink?.let { it.first?.url ?: it.second?.uri?.toString() } ?: "unknown" val headers = currentSelectedLink?.first?.headers?.toString() ?: "none" @@ -1554,8 +1588,22 @@ class GeneratorPlayer : FullScreenPlayer() { private fun noLinksFound() { viewModel.forceClearCache = true + val hiddenLinks = viewModel.state.sortLinks(currentQualityProfile).count { !it.shouldUseLink } + + context?.let { ctx -> + // Display that there are hidden links to the user. + if (hiddenLinks > 0) { + val noLinksString = ctx.getString(R.string.no_links_found_toast) + val hiddenString = + ctx.resources.getQuantityString(R.plurals.links_hidden, hiddenLinks) + .format(hiddenLinks) + val toastText = "$noLinksString\n($hiddenString)" + showToast(toastText, Toast.LENGTH_SHORT) + } else { + showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT) + } + } - showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT) activity?.popCurrentPage() } @@ -1566,7 +1614,9 @@ class GeneratorPlayer : FullScreenPlayer() { } val links = viewModel.state.sortLinks(currentQualityProfile) - if (links.isEmpty()) { + + val firstAvailableLink = links.firstOrNull { it.shouldUseLink }?.link + if (firstAvailableLink == null) { noLinksFound() return } @@ -1574,7 +1624,7 @@ class GeneratorPlayer : FullScreenPlayer() { if (!isPlayerActive.compareAndSet(false, true)) { return } - loadLink(links.first(), false) + loadLink(firstAvailableLink, false) showPlayerMetadata() } @@ -1641,25 +1691,26 @@ class GeneratorPlayer : FullScreenPlayer() { } } - override fun hasNextMirror(): Boolean { + private fun getNextLink(): DisplayLink? { val links = viewModel.state.sortLinks(currentQualityProfile) - return links.isNotEmpty() && links.indexOf(currentSelectedLink) + 1 < links.size + val currentIndex = links.indexOfFirst { it.link == currentSelectedLink } + val nextPotentialLink = + links.withIndex().firstOrNull { it.index > currentIndex && it.value.shouldUseLink } + return nextPotentialLink?.value + } + + override fun hasNextMirror(): Boolean { + return getNextLink() != null } override fun nextMirror() { - val links = viewModel.state.sortLinks(currentQualityProfile) - if (links.isEmpty()) { + val nextLink = getNextLink() + if (nextLink == null) { noLinksFound() return } - val newIndex = links.indexOf(currentSelectedLink) + 1 - if (newIndex >= links.size) { - noLinksFound() - return - } - - loadLink(links[newIndex], true) + loadLink(nextLink.link, true) } override fun onDestroy() { @@ -2166,6 +2217,7 @@ class GeneratorPlayer : FullScreenPlayer() { isPlayerActive.set(false) binding?.overlayLoadingSkipButton?.isVisible = false binding?.playerLoadingOverlay?.isVisible = true + viewModel.modifyState { setError(emptyList()) } uiReset() } @@ -2297,19 +2349,23 @@ class GeneratorPlayer : FullScreenPlayer() { } } - observe(viewModel.currentLinks) { (links, instance) -> + observe(viewModel.currentLinks) { (_, instance) -> if (instance != viewModel.state.instance) return@observe // Outdated observe - val turnVisible = links.isNotEmpty() && viewModel.generator?.canSkipLoading == true + val sortedLinks = viewModel.state.sortLinks(currentQualityProfile) + val usableLinks = sortedLinks.count { link -> link.shouldUseLink } + + val turnVisible = usableLinks > 0 && viewModel.generator?.canSkipLoading == true val wasGone = binding.overlayLoadingSkipButton.isGone binding.overlayLoadingSkipButton.apply { isVisible = turnVisible - if (links.isEmpty()) { + + if (usableLinks == 0) { setText(R.string.skip_loading) } else { @SuppressLint("SetTextI18n") - text = "${context.getString(R.string.skip_loading)} (${links.size})" + text = "${context.getString(R.string.skip_loading)} (${usableLinks})" } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt index e3c390d50..cb8cf8bff 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt @@ -10,6 +10,8 @@ import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.ui.player.source_priority.ProfileSettings +import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getLinkPriority import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -40,12 +42,20 @@ data class GeneratorState( val id: Int?, ) +data class DisplayLink( + val link: VideoLink, + // If the link should be displayed and used by the player + val shouldUseLink: Boolean, + val priority: Int +) + /** Immutable state of all current links relevant to displaying the video */ // @MustUseReturnValues // @Immutable data class VideoState( val subtitles: PersistentSet = persistentSetOf(), val links: PersistentSet = persistentSetOf(), + val erroredLinks: PersistentSet = persistentSetOf(), val stamps: PersistentList = persistentListOf(), val loading: Resource = Resource.Loading(), val generatorState: GeneratorState? = null, @@ -56,19 +66,52 @@ data class VideoState( * * sortedBy is not exactly expensive, but each hasNextMirror does it again, so this alleviates unnecessary recomputation * */ - private val sortedLinks: ConcurrentHashMap> = ConcurrentHashMap() + private val sortedLinks: ConcurrentHashMap> = ConcurrentHashMap() + /** + * The cache is guaranteed to be up to date link-wise due to the immutable links. + * However, hideNegativeSources and hideErrorSources could be updated, which requires clearing the cache. + */ fun clearSortedLinksCache() = sortedLinks.clear() + private fun hasLinkErrored(link: VideoLink): Boolean { + return erroredLinks.any { it == link } + } + + private fun VideoLink.toDisplayLink( + qualityProfile: Int, + hideNegativeSources: Boolean, + hideErrorSources: Boolean + ): DisplayLink { + val priority = getLinkPriority(qualityProfile, this.first) + val shouldHideLink = + (hideNegativeSources && priority < 0) || (hideErrorSources && hasLinkErrored(this)) + val displayLink = DisplayLink(this, !shouldHideLink, priority) + + return displayLink + } + // Modifying sortedLinks is not considered a "visible" side effect, and rerunning it does not change the result // It is by all standards, idempotent and by extension also pure as it has no "visible" side effect /** Returns .links in the sorted order according to the qualityProfile. * Use .links if order is not needed */ @Contract(pure = true) - fun sortLinks(qualityProfile: Int): List { - return sortedLinks[qualityProfile] ?: links.sortedBy { link -> + fun sortLinks(qualityProfile: Int): List { + sortedLinks[qualityProfile]?.let { + return it + } + + val hideNegativeSources = + QualityDataHelper.getProfileSetting(qualityProfile, ProfileSettings.HideNegativeSources) + val hideErrorSources = + QualityDataHelper.getProfileSetting(qualityProfile, ProfileSettings.HideErrorSources) + + return links.map { link -> // negative because we want to sort highest quality first - -getLinkPriority(qualityProfile, link.first) + link.toDisplayLink(qualityProfile, hideNegativeSources, hideErrorSources) + }.sortedBy { + // negative because we want to sort highest quality first + -it.priority }.also { value -> sortedLinks[qualityProfile] = value } } @@ -113,6 +156,12 @@ data class VideoState( @JvmName("setVideoSkipStamp") @Contract(pure = true) fun set(items: Collection): VideoState = copy(stamps = items.toPersistentList()) + + @Contract(pure = true) + fun addError(item: VideoLink): VideoState = copy(erroredLinks = erroredLinks.add(item)) + + @Contract(pure = true) + fun setError(items: Collection): VideoState = copy(erroredLinks = items.toPersistentSet()) } data class VideoLive( @@ -141,9 +190,8 @@ class PlayerGeneratorViewModel : ViewModel() { var state = VideoState(instance = 0) private set - private val _currentLinks = - MutableLiveData>>>(null) - val currentLinks: LiveData>>> = _currentLinks + private val _currentLinks = MutableLiveData>>(null) + val currentLinks: LiveData>> = _currentLinks private val _currentSubtitles = MutableLiveData>>(null) val currentSubtitles: LiveData>> = _currentSubtitles diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt index 15d25148c..d8d47bd10 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt @@ -12,12 +12,16 @@ import com.lagradost.cloudstream3.utils.txt import com.lagradost.cloudstream3.utils.DataStoreHelper.currentAccount import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities +import java.util.EnumMap +import java.util.concurrent.ConcurrentHashMap +import kotlin.also import kotlin.math.abs object QualityDataHelper { private const val VIDEO_SOURCE_PRIORITY = "video_source_priority" private const val VIDEO_PROFILE_NAME = "video_profile_name" private const val VIDEO_QUALITY_PRIORITY = "video_quality_priority" + const val VIDEO_PROFILE_SETTINGS = "video_profile_settings" // Old key only supporting one type per profile @Deprecated("Changed to support multiple types per profile") @@ -53,13 +57,21 @@ object QualityDataHelper { val types: Set ) + + // Map profile and name to priority + val sourcePriorityCache: ConcurrentHashMap> = ConcurrentHashMap() + fun getSourcePriority(profile: Int, name: String?): Int { if (name == null) return DEFAULT_SOURCE_PRIORITY - return getKey( + + return sourcePriorityCache[profile]?.get(name) ?: (getKey( "$currentAccount/$VIDEO_SOURCE_PRIORITY/$profile", name, DEFAULT_SOURCE_PRIORITY - ) ?: DEFAULT_SOURCE_PRIORITY + ) ?: DEFAULT_SOURCE_PRIORITY).also { + sourcePriorityCache.getOrPut(profile) { hashMapOf() } + sourcePriorityCache[profile]?.set(name, it) + } } fun getAllSourcePriorityNames(profile: Int): List { @@ -77,6 +89,8 @@ object QualityDataHelper { } else { setKey(folder, name, priority) } + + sourcePriorityCache[profile]?.set(name, priority) } fun setProfileName(profile: Int, name: String?) { @@ -93,12 +107,17 @@ object QualityDataHelper { ?: txt(R.string.profile_number, profile) } + // Map profile and quality to priority + val qualityPriorityCache: ConcurrentHashMap> = ConcurrentHashMap() fun getQualityPriority(profile: Int, quality: Qualities): Int { - return getKey( + return qualityPriorityCache[profile]?.get(quality) ?: (getKey( "$currentAccount/$VIDEO_QUALITY_PRIORITY/$profile", quality.value.toString(), quality.defaultPriority - ) ?: quality.defaultPriority + )?.also { + qualityPriorityCache.getOrPut(profile) { EnumMap(Qualities::class.java) } + qualityPriorityCache[profile]?.set(quality, it) + }) ?: quality.defaultPriority } fun setQualityPriority(profile: Int, quality: Qualities, priority: Int) { @@ -107,8 +126,24 @@ object QualityDataHelper { quality.value.toString(), priority ) + qualityPriorityCache[profile]?.set(quality, priority) } + fun setProfileSetting(profile: Int, setting: ProfileSettings, value: T) { + val folder = "$currentAccount/$VIDEO_PROFILE_SETTINGS/$profile" + // Prevent unnecessary keys + if (value == setting.defaultValue) { + removeKey(folder, setting.key) + } else { + setKey(folder, setting.key, value) + } + } + + inline fun getProfileSetting(profile: Int, setting: ProfileSettings): T { + val folder = "$currentAccount/$VIDEO_PROFILE_SETTINGS/$profile" + val value = getKey(folder, setting.key) + return value ?: setting.defaultValue + } @Suppress("DEPRECATION") fun getQualityProfileTypes(profile: Int): Set { @@ -224,3 +259,8 @@ object QualityDataHelper { return Qualities.entries.minBy { abs(it.value - target) } } } + +sealed class ProfileSettings(val key: String, val defaultValue: T) { + object HideErrorSources : ProfileSettings("hide_error_sources", false) + object HideNegativeSources : ProfileSettings("hide_negative_sources", false) +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt index c8ac96ebb..604ec3149 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt @@ -14,7 +14,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding class SourcePriorityDialog( val ctx: Context, - @StyleRes themeRes: Int, + @StyleRes val themeRes: Int, val links: List, private val profile: QualityDataHelper.QualityProfile, /** @@ -28,76 +28,79 @@ class SourcePriorityDialog( PlayerSelectSourcePriorityBinding.inflate(LayoutInflater.from(ctx), null, false) setContentView(binding.root) fixSystemBarsPadding(binding.root) - val sourcesRecyclerView = binding.sortSources - val qualitiesRecyclerView = binding.sortQualities - val profileText = binding.profileTextEditable - val saveBtt = binding.saveBtt - val exitBtt = binding.closeBtt - val helpBtt = binding.helpBtt - profileText.setText(QualityDataHelper.getProfileName(profile.id).asString(context)) - profileText.hint = txt(R.string.profile_number, profile.id).asString(context) + binding.apply { + profileTextEditable.setText( + QualityDataHelper.getProfileName(profile.id).asString(context) + ) + profileTextEditable.hint = txt(R.string.profile_number, profile.id).asString(context) - sourcesRecyclerView.adapter = PriorityAdapter( - ).apply { - submitList(links.map { link -> - SourcePriority( - null, - link.source, - QualityDataHelper.getSourcePriority(profile.id, link.source) - ) - }.distinctBy { it.name }.sortedBy { -it.priority }) - } + sortSources.adapter = PriorityAdapter( + ).apply { + val sortedLinks = links.map { link -> + SourcePriority( + null, + link.source, + QualityDataHelper.getSourcePriority(profile.id, link.source) + ) + }.distinctBy { it.name }.sortedBy { -it.priority } - qualitiesRecyclerView.adapter = PriorityAdapter( - ).apply { - submitList(Qualities.entries.mapNotNull { - SourcePriority( - it, - Qualities.getStringByIntFull(it.value).ifBlank { return@mapNotNull null }, - QualityDataHelper.getQualityPriority(profile.id, it) - ) - }.sortedBy { -it.priority }) - } - - @Suppress("UNCHECKED_CAST") // We know the types - saveBtt.setOnClickListener { - val qualityAdapter = qualitiesRecyclerView.adapter as? PriorityAdapter - val sourcesAdapter = sourcesRecyclerView.adapter as? PriorityAdapter - - val qualities = qualityAdapter?.immutableCurrentList ?: emptyList() - val sources = sourcesAdapter?.immutableCurrentList ?: emptyList() - - qualities.forEach { - QualityDataHelper.setQualityPriority(profile.id, it.data, it.priority) + submitList(sortedLinks) } - sources.forEach { - QualityDataHelper.setSourcePriority(profile.id, it.name, it.priority) + sortQualities.adapter = PriorityAdapter( + ).apply { + submitList(Qualities.entries.mapNotNull { + SourcePriority( + it, + Qualities.getStringByIntFull(it.value).ifBlank { return@mapNotNull null }, + QualityDataHelper.getQualityPriority(profile.id, it) + ) + }.sortedBy { -it.priority }) } - qualityAdapter?.submitList(qualities.sortedBy { -it.priority }) - sourcesAdapter?.submitList(sources.sortedBy { -it.priority }) + @Suppress("UNCHECKED_CAST") // We know the types + saveBtt.setOnClickListener { + val qualityAdapter = sortQualities.adapter as? PriorityAdapter + val sourcesAdapter = sortSources.adapter as? PriorityAdapter - val savedProfileName = profileText.text.toString() - if (savedProfileName.isBlank()) { - QualityDataHelper.setProfileName(profile.id, null) - } else { - QualityDataHelper.setProfileName(profile.id, savedProfileName) + val qualities = qualityAdapter?.immutableCurrentList ?: emptyList() + val sources = sourcesAdapter?.immutableCurrentList ?: emptyList() + + qualities.forEach { + QualityDataHelper.setQualityPriority(profile.id, it.data, it.priority) + } + + sources.forEach { + QualityDataHelper.setSourcePriority(profile.id, it.name, it.priority) + } + + qualityAdapter?.submitList(qualities.sortedBy { -it.priority }) + sourcesAdapter?.submitList(sources.sortedBy { -it.priority }) + + val savedProfileName = profileTextEditable.text.toString() + if (savedProfileName.isBlank()) { + QualityDataHelper.setProfileName(profile.id, null) + } else { + QualityDataHelper.setProfileName(profile.id, savedProfileName) + } + updatedCallback.invoke() } - updatedCallback.invoke() - } - exitBtt.setOnClickListener { - this.dismissSafe() - } + closeBtt.setOnClickListener { + dismissSafe() + } - helpBtt.setOnClickListener { - AlertDialog.Builder(context, R.style.AlertDialogCustom).apply { - setMessage(R.string.quality_profile_help) - }.show() - } + helpBtt.setOnClickListener { + AlertDialog.Builder(context, R.style.AlertDialogCustom).apply { + setMessage(R.string.quality_profile_help) + }.show() + } + settingsBtt.setOnClickListener { + SourceProfileSettingsDialog(ctx, themeRes, profile.id).show() + } + } super.show() } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourceProfileSettingsDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourceProfileSettingsDialog.kt new file mode 100644 index 000000000..c26955b5e --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourceProfileSettingsDialog.kt @@ -0,0 +1,48 @@ +package com.lagradost.cloudstream3.ui.player.source_priority + +import android.app.Dialog +import android.content.Context +import android.view.LayoutInflater +import androidx.annotation.StyleRes +import com.lagradost.cloudstream3.databinding.SourceProfileSettingsDialogBinding +import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe +import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding + +class SourceProfileSettingsDialog( + val ctx: Context, + @StyleRes themeRes: Int, + val profile: Int +) : Dialog(ctx, themeRes) { + override fun show() { + val binding = + SourceProfileSettingsDialogBinding.inflate(LayoutInflater.from(ctx), null, false) + setContentView(binding.root) + fixSystemBarsPadding(binding.root) + + binding.apply { + var hideErrorSources = QualityDataHelper.getProfileSetting(profile, ProfileSettings.HideErrorSources) + var hideNegativeSources = QualityDataHelper.getProfileSetting(profile, ProfileSettings.HideNegativeSources) + + profileHideErrorSources.isChecked = hideErrorSources + profileHideErrorSources.setOnCheckedChangeListener { _, bool -> + hideErrorSources = bool + } + + profileHideNegativeSources.isChecked = hideNegativeSources + profileHideNegativeSources.setOnCheckedChangeListener { _, bool -> + hideNegativeSources = bool + } + + applyBtt.setOnClickListener { + QualityDataHelper.setProfileSetting(profile, ProfileSettings.HideErrorSources, hideErrorSources) + QualityDataHelper.setProfileSetting(profile, ProfileSettings.HideNegativeSources, hideNegativeSources) + dismissSafe() + } + + cancelBtt.setOnClickListener { + dismissSafe() + } + } + super.show() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout-port/player_select_source_priority.xml b/app/src/main/res/layout-port/player_select_source_priority.xml index 2cba9c869..926227c58 100644 --- a/app/src/main/res/layout-port/player_select_source_priority.xml +++ b/app/src/main/res/layout-port/player_select_source_priority.xml @@ -1,6 +1,5 @@ - - + @@ -33,14 +32,21 @@ android:textSize="20sp" android:textStyle="bold" /> + + + android:src="@drawable/baseline_help_outline_24" /> - + + tools:ignore="LabelFor" + tools:text="@string/profile_number" /> + android:text="@string/sort_save" /> + android:text="@string/sort_close" /> diff --git a/app/src/main/res/layout/player_select_source_priority.xml b/app/src/main/res/layout/player_select_source_priority.xml index 182cd1861..0d825de77 100644 --- a/app/src/main/res/layout/player_select_source_priority.xml +++ b/app/src/main/res/layout/player_select_source_priority.xml @@ -160,6 +160,13 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 31cf951cf..14780da6b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -622,6 +622,14 @@ Subscribe Unsubscribe Profile %d + Profile settings + Hide sources with a negative priority + Hide sources with errors + + %d hidden link + %d hidden links + + Wi-Fi Mobile data Set default From a2adb6caaad38465b7b1e75a2d0de35c7e95ccf2 Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Fri, 31 Jul 2026 15:35:02 +0000 Subject: [PATCH 05/23] [skip ci] Swallow exception when device does not support channels --- .../com/lagradost/cloudstream3/utils/TvChannelUtils.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/TvChannelUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/TvChannelUtils.kt index 4c0717b3f..b5b367262 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/TvChannelUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/TvChannelUtils.kt @@ -149,10 +149,12 @@ object TvChannelUtils { .setInputId(inputId) .build() - val channelUri = context.contentResolver.insert( - TvContractCompat.Channels.CONTENT_URI, - channel.toContentValues() - ) + val channelUri = runCatching { + context.contentResolver.insert( + TvContractCompat.Channels.CONTENT_URI, + channel.toContentValues() + ) + }.getOrNull() channelUri?.let { val channelId = ContentUris.parseId(it) From ff701cb220897af32b02623db333adf263b443aa Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:59:46 +0000 Subject: [PATCH 06/23] Add Belarusian, Catalan and Albanian --- .../com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 3 +++ app/src/main/res/{values-be => values-b+be}/strings.xml | 0 app/src/main/res/{values-ca => values-b+ca}/strings.xml | 0 app/src/main/res/{values-sq => values-b+sq}/strings.xml | 0 4 files changed, 3 insertions(+) rename app/src/main/res/{values-be => values-b+be}/strings.xml (100%) rename app/src/main/res/{values-ca => values-b+ca}/strings.xml (100%) rename app/src/main/res/{values-sq => values-b+sq}/strings.xml (100%) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt index 354424978..631b12990 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt @@ -132,6 +132,9 @@ val appLanguages = arrayListOf( Pair("日本語 (にほんご)", "ja"), Pair("正體中文(臺灣)", "zh-TW"), Pair("한국어", "ko"), + Pair("беларуская мова", "be"), + Pair("català", "ca"), + Pair("Shqip мова", "sq"), /* end language list */ ).sortedBy { it.first.lowercase(Locale.ROOT) } // ye, we go alphabetical, so ppl don't put their lang on top diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-b+be/strings.xml similarity index 100% rename from app/src/main/res/values-be/strings.xml rename to app/src/main/res/values-b+be/strings.xml diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-b+ca/strings.xml similarity index 100% rename from app/src/main/res/values-ca/strings.xml rename to app/src/main/res/values-b+ca/strings.xml diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-b+sq/strings.xml similarity index 100% rename from app/src/main/res/values-sq/strings.xml rename to app/src/main/res/values-b+sq/strings.xml From 7f750964f3929679a6674f1d13d62d082110af56 Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:00:05 +0000 Subject: [PATCH 07/23] chore(locales): fix locale issues --- .../lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 6 +++--- app/src/main/res/values-b+be/strings.xml | 2 +- app/src/main/res/values-b+ca/strings.xml | 2 +- app/src/main/res/values-b+sq/strings.xml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt index 631b12990..b04f9a4a2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt @@ -77,6 +77,7 @@ val appLanguages = arrayListOf( Pair("Azərbaycan dili", "az"), Pair("Bahasa Indonesia", "in"), Pair("Bahasa Melayu", "ms"), + Pair("català", "ca"), Pair("Deutsch", "de"), Pair("English", "en"), Pair("Español", "es"), @@ -97,6 +98,7 @@ val appLanguages = arrayListOf( Pair("Português", "pt"), Pair("Português (Brasil)", "pt-BR"), Pair("Română", "ro"), + Pair("Shqip мова", "sq"), Pair("Slovenčina", "sk"), Pair("Soomaaliga", "so"), Pair("Svenska", "sv"), @@ -106,6 +108,7 @@ val appLanguages = arrayListOf( Pair("Wikang Filipino", "fil"), Pair("Čeština", "cs"), Pair("Ελληνικά", "el"), + Pair("беларуская мова", "be"), Pair("български", "bg"), Pair("македонски", "mk"), Pair("русский", "ru"), @@ -132,9 +135,6 @@ val appLanguages = arrayListOf( Pair("日本語 (にほんご)", "ja"), Pair("正體中文(臺灣)", "zh-TW"), Pair("한국어", "ko"), - Pair("беларуская мова", "be"), - Pair("català", "ca"), - Pair("Shqip мова", "sq"), /* end language list */ ).sortedBy { it.first.lowercase(Locale.ROOT) } // ye, we go alphabetical, so ppl don't put their lang on top diff --git a/app/src/main/res/values-b+be/strings.xml b/app/src/main/res/values-b+be/strings.xml index 3d9200faf..9fbf8a8d5 100644 --- a/app/src/main/res/values-b+be/strings.xml +++ b/app/src/main/res/values-b+be/strings.xml @@ -125,7 +125,7 @@ Працягнуць прагляд Выдаліць Больш інфармацыі - \@string/home_play + @string/home_play Для карэктнай працы гэтага пастаўшчыка можа спатрэбіцца VPN Гэты пастаўшчык — Torrent, рэкамендуецца VPN Вэб-сайт не пастаўляе метададзеных, загрузіць відэа не ўдасца, калі на сайце яго няма. diff --git a/app/src/main/res/values-b+ca/strings.xml b/app/src/main/res/values-b+ca/strings.xml index 495eb5e3c..621243c78 100644 --- a/app/src/main/res/values-b+ca/strings.xml +++ b/app/src/main/res/values-b+ca/strings.xml @@ -123,7 +123,7 @@ Continua mirant Eliminar Més info - \@string/home_play + @string/home_play Potser necessiteu una VPN per que aquest proveïdor funcioni correctament Aquest proveïdor és un Torrent, es recomana fer servir una VPN El lloc no proveeix metadades, la càrrega del video fallarà si no existeix al lloc. diff --git a/app/src/main/res/values-b+sq/strings.xml b/app/src/main/res/values-b+sq/strings.xml index a13f87bfa..f45c8d87a 100644 --- a/app/src/main/res/values-b+sq/strings.xml +++ b/app/src/main/res/values-b+sq/strings.xml @@ -127,7 +127,7 @@ Vazhdo shikimin Hiq Më shumë informacion - \@string/home_play + @string/home_play Një VPN mund të nevojitet që ky ofrues të funksionojë në rregull Ky ofrues është Torrent, rekomandohet një VPN Metadata nuk ofrohet nga kjo faqe, ngarkimi i videos do të dështojë nëse nuk ekziston në këtë faqe. From e80fa1b30dc56b6cfb39ace68c8c284b255df785 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Fri, 31 Jul 2026 19:00:10 +0200 Subject: [PATCH 08/23] Translated using Weblate (Albanian) Currently translated at 100.0% (733 of 733 strings) Translated using Weblate (Polish) Currently translated at 100.0% (733 of 733 strings) Translated using Weblate (Bengali) Currently translated at 52.6% (384 of 729 strings) Translated using Weblate (English) Currently translated at 100.0% (729 of 729 strings) Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Update translation files Updated by "Remove blank strings" hook in Weblate. Translated using Weblate (Bengali) Currently translated at 51.0% (372 of 729 strings) Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Translated using Weblate (Dutch) Currently translated at 92.4% (674 of 729 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (729 of 729 strings) Translated using Weblate (Bengali) Currently translated at 75.0% (3 of 4 strings) Translated using Weblate (Bengali) Currently translated at 49.9% (364 of 729 strings) Co-authored-by: 007 Co-authored-by: Aishik Mukherjee Co-authored-by: Hosted Weblate Co-authored-by: Man Co-authored-by: Matthaiks Co-authored-by: caviaz Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sq/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/bn/ Translation: Cloudstream/App Translation: Cloudstream/Fastlane --- app/src/main/res/values-b+bn/strings.xml | 49 ++++++++++++++++--- app/src/main/res/values-b+nl/strings.xml | 13 +++++ app/src/main/res/values-b+pl/strings.xml | 9 ++++ app/src/main/res/values-b+sq/strings.xml | 7 +++ app/src/main/res/values-b+vi/strings.xml | 10 ++-- app/src/main/res/values/strings.xml | 1 - fastlane/metadata/android/bn/changelogs/2.txt | 1 + .../metadata/android/bn/full_description.txt | 7 +++ .../metadata/android/bn/short_description.txt | 1 + fastlane/metadata/android/bn/title.txt | 1 + 10 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 fastlane/metadata/android/bn/changelogs/2.txt create mode 100644 fastlane/metadata/android/bn/full_description.txt create mode 100644 fastlane/metadata/android/bn/short_description.txt create mode 100644 fastlane/metadata/android/bn/title.txt diff --git a/app/src/main/res/values-b+bn/strings.xml b/app/src/main/res/values-b+bn/strings.xml index e90608793..b6edbd1cd 100644 --- a/app/src/main/res/values-b+bn/strings.xml +++ b/app/src/main/res/values-b+bn/strings.xml @@ -3,7 +3,7 @@ পোস্টার ক্লাউডস্ট্রিম দিয়ে চালান হোম - অভিনয়েঃ %s + অভিনয়ে: %s %1$dদিন %2$dঘন্টা %3$dমিনিট %1$dঘন্টা %2$dমিনিট %d মিনিট @@ -22,7 +22,7 @@ খুঁজুন ডাউনলোডসমূহ সেটিংস - %1$s এপি %2$d + %1$s পর্ব %2$d পর্ব %d মুক্তির তারিখ শেয়ার ব্রাউজারে খুলুন @@ -131,8 +131,8 @@ স্টোরেজ এর অনুমতি অনুপস্থিত। দয়া করে আবার চেষ্টা করুন। তথ্য ট্রেইলার প্রদর্শন করুন - কিটসু হতে পোস্টারসমূহ প্রদর্শন করুন - স্বয়ংক্রিয়ভাবে প্লাগিন এর হালনাগাদ + Kitsu থেকে পোস্টারসমূহ প্রদর্শন করুন + স্বয়ংক্রিয় প্লাগইন আপডেট স্বয়ংক্রিয়ভাবে প্লাগিনসমুহের ডাউনলোড হালনাগাদ ও ব্যাকআপ অ্যাপ এর হালনাগাদ দেখান @@ -148,9 +148,9 @@ সামনে বা পিছনের দিকে যেতে ডান বা বাম দিকে দুবার আলতো চাপুন ফাইল ডিলিট মান ডিফল্ট এ রিসেট করুন - স্টার্টআপে নতুন আপডেটের জন্য স্বয়ংক্রিয়ভাবে অনুসন্ধান করুন + স্টার্টআপে নতুন আপডেটের জন্য স্বয়ংক্রিয়ভাবে অনুসন্ধান করুন যোগ। সিনেমা - এনিমে এর ফিলার পর্ব দেখায় + অ্যানিমে ফিলার পর্ব প্রদর্শন টিভি সিরিজ লিংক পাওয়া যায়নি চা খাওয়ানো হয়েছে @@ -174,7 +174,7 @@ ডিলিট শুরু কার্টুন - কিছু ফোন নতুন প্যাকেজ ইনস্টলার সাপোর্ট করে না। যদি আপডেটগুলি ইনস্টল না হয় তবে পুরোনো পদ্ধতি ব্যবহার করে দেখুন। + Some devices do not support the new package installer. Try the legacy option if updates do not install। সাবটাইটেল নেই এই প্রোভাইডার ক্রোমকাস্ট সাপোর্ট করে না উন্নত অনুসন্ধান @@ -204,7 +204,7 @@ আমাদের তৈরি Anime দেখার অ্যাপ্লিকেশন 18+ এনিমে - অনুসন্ধান ফলাফলে নির্বাচিত ভিডিও কুয়ালিটি লুকান + অনুসন্ধানের ফলে নির্বাচিত ভিডিওর মান লুকান অ্যাপ লাইভস্ট্রিম APK ইনস্টলার @@ -354,4 +354,37 @@ স্পিচ রিকগনিশন উপলব্ধ নেই কথা বলা শুরু করুন… ডাউনলোড তালিকা + সম্পূর্ণ সিরিজ প্লে করুন + এই ভিডিওটি একটি টরেন্ট, অর্থাৎ আপনার ভিডিও অ্যাক্টিভিটি ট্র্যাক করা সম্ভব।\nএগোনোর আগে টরেন্টিং সম্পর্কে ভালোভাবে বুঝে নিন। + ডিলিট করার জন্য আইটেম সিলেক্ট করুন + বর্তমানে কোনো ডাউনলোড নেই। + বর্তমানে কোনো ডাউনলোড কিউতে নেই। + অফলাইনে দেখার জন্য উপলব্ধ + সব সিলেক্ট করুন + সব ডিসিলেক্ট করুন + লোকাল ভিডিও খুলুন + অতিরিক্ত ব্রাইটনেস + ডিসপ্লে ব্রাইটনেস ১০০% ছাড়িয়ে গেলে ব্রাইটনেস ফিল্টার চালু করুন + সার্চ সাজেশন + টাইপ করার সময় সার্চ সাজেশন দেখান + সাজেশন মুছে ফেলুন + প্লেয়ার মেটাডেটা ওভারলে প্রদর্শন + কাস্ট প্যানেল প্রদর্শন + প্রাক-মুক্তি সংস্করণ ইনস্টলেশন + প্রাক-মুক্তি সংস্করণ ইতোমধ্যে ইনস্টল রয়েছে। + প্রাক-মুক্তি সংস্করণ ইনস্টল করতে সমস্যা হয়েছে। + (%1$d | %2$s) মুছুন + সাবধান + আপনি কি নিম্নলিখিত আইটেমগুলো স্থায়ীভাবে মুছতে চান?\n\n%s + ফাইল মুছুন + আপনি কি নিশ্চিত যে আপনি %1$s-এ নিচের এপিসোডগুলো স্থায়ীভাবে মুছে ফেলতে চান?\n\n%2$s + আপনি নিচের সিরিজগুলোর সব এপিসোডও স্থায়ীভাবে মুছে ফেলবেন:\n\n%s + আপনি কি নিশ্চিত যে আপনি নিচের সিরিজটির সব এপিসোড স্থায়ীভাবে মুছে ফেলতে চান?\n\n%s + সঙ্গীত + অডিও বই + মিডিয়া + অডিও + পডকাস্ট + ভিডিও + এনকোডিং ত্রুটি diff --git a/app/src/main/res/values-b+nl/strings.xml b/app/src/main/res/values-b+nl/strings.xml index 8a2424326..feec84263 100644 --- a/app/src/main/res/values-b+nl/strings.xml +++ b/app/src/main/res/values-b+nl/strings.xml @@ -682,4 +682,17 @@ Zet torrent aan in Instellingen/Providers/Media voorkeur Herstart de app en accepteer de Stream Torrent pop-up om verder te gaan. Handmatige Update Knop + Open de app met Vingerafdruk, Face ID, PIN, Pattern en Wachtwoord. + Seekbar preview + Zet preview thumbnail aan op seekbar + Software decodering + Software decodering staat de mogelijkheid toe om de speler videobestanden af te laten spelen op jouw apparaat, maar het kan een haperige of onstabiele kijkervaring zorgen op hoge resolutie. + Volume heeft de 100% overschreden + Update Plugins + Geen plugins zijn geupdate. + Speler meldingen + De speler melding om de kijkervaring van de achtergrond de besturen + Online + Hoeveel verschillende items kunnen in parallel gedownload worden + Parallel downloads diff --git a/app/src/main/res/values-b+pl/strings.xml b/app/src/main/res/values-b+pl/strings.xml index 35367a1b2..b69146c92 100644 --- a/app/src/main/res/values-b+pl/strings.xml +++ b/app/src/main/res/values-b+pl/strings.xml @@ -742,4 +742,13 @@ Wideo Zapowiedź Na żywo + Ustawienia profilu + Ukryj źródła z negatywnym priorytetem + Ukryj źródła z błędami + + %d ukryty odnośnik + %d ukryte odnośniki + %d ukrytych odnośników + %d ukrytych odnośników + diff --git a/app/src/main/res/values-b+sq/strings.xml b/app/src/main/res/values-b+sq/strings.xml index a13f87bfa..4716d757f 100644 --- a/app/src/main/res/values-b+sq/strings.xml +++ b/app/src/main/res/values-b+sq/strings.xml @@ -735,4 +735,11 @@ %d shkarkime në radhë Live + Cilësimet e profil-it + Fshih burimet me prioritet negativ + Fshih burimet me error-e + + %d link i fshehur + %d linqe të fshehura + diff --git a/app/src/main/res/values-b+vi/strings.xml b/app/src/main/res/values-b+vi/strings.xml index 3b2b3876f..53679c05c 100644 --- a/app/src/main/res/values-b+vi/strings.xml +++ b/app/src/main/res/values-b+vi/strings.xml @@ -36,7 +36,7 @@ Tập tiếp theo Thể loại Chia sẻ - Mở bằng trình duyệt + Phát bằng Trình duyệt Bỏ tải Đang tải… Đang xem @@ -255,13 +255,13 @@ Chất lượng xem ưu tiên (WiFi) Số ký tự tối đa tiêu đề trình phát Hiển thị thông tin trình phát - Kích thước bộ nhớ đệm video - Thời lượng bộ nhớ đệm + Kích thước bộ đệm video + Thời lượng bộ đệm video Bộ nhớ đệm video trên thiết bị Xoá bộ nhớ đệm hình ảnh và video Sẽ gây lỗi nếu đặt quá cao trên thiết bị có bộ nhớ thấp như Android TV. - Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng lưu trữ thấp như Android TV. - DNS over HTTPS + Sẽ gây lỗi nếu đặt quá cao trên thiết bị có dung lượng lưu trữ thấp như Android TV. + DNS qua HTTPS Rất hữu ích để bỏ chặn ISP Bản sao trang web Xoá trang web diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 14780da6b..4a918231f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -791,5 +791,4 @@ %d downloads queued Live - diff --git a/fastlane/metadata/android/bn/changelogs/2.txt b/fastlane/metadata/android/bn/changelogs/2.txt new file mode 100644 index 000000000..91032cb8a --- /dev/null +++ b/fastlane/metadata/android/bn/changelogs/2.txt @@ -0,0 +1 @@ +- চেঞ্জলগ যোগ করা হয়েছে! diff --git a/fastlane/metadata/android/bn/full_description.txt b/fastlane/metadata/android/bn/full_description.txt new file mode 100644 index 000000000..e1866f54f --- /dev/null +++ b/fastlane/metadata/android/bn/full_description.txt @@ -0,0 +1,7 @@ +CloudStream-3 দিয়ে সিনেমা, টিভি-সিরিজ ও অ্যানিমে স্ট্রিম ও ডাউনলোড করা যায়। + +অ্যাপে কোনো বিজ্ঞাপন ও অ্যানালিটিক্স নেই এবং +এটি একাধিক ট্রেলার ও মুভি সাইট সাপোর্ট করে, আরও আছে যেমন— +বুকমার্ক +সাবটাইটেল ডাউনলোড +ক্রোমকাস্ট সাপোর্ট diff --git a/fastlane/metadata/android/bn/short_description.txt b/fastlane/metadata/android/bn/short_description.txt new file mode 100644 index 000000000..3d62da485 --- /dev/null +++ b/fastlane/metadata/android/bn/short_description.txt @@ -0,0 +1 @@ +সিনেমা, টিভি সিরিজ ও অ্যানিমে স্ট্রিম ও ডাউনলোড করুন। diff --git a/fastlane/metadata/android/bn/title.txt b/fastlane/metadata/android/bn/title.txt new file mode 100644 index 000000000..dde89d58f --- /dev/null +++ b/fastlane/metadata/android/bn/title.txt @@ -0,0 +1 @@ +CloudStream From caf0db3dc13bd75a496d4a94152bb8f22f8fdb1e Mon Sep 17 00:00:00 2001 From: Jelle Thijs Date: Sat, 1 Aug 2026 03:14:02 +0200 Subject: [PATCH 09/23] Feat: Add optional clock to TV player --- .../cloudstream3/ui/home/HomeFragment.kt | 28 +++++++++++++++++++ .../ui/player/FullScreenPlayer.kt | 3 +- .../cloudstream3/ui/player/GeneratorPlayer.kt | 8 ++++++ .../cloudstream3/ui/settings/SettingsUI.kt | 2 ++ .../res/drawable/ic_baseline_clock_24.xml | 9 ++++++ app/src/main/res/layout/fragment_home.xml | 6 ++++ app/src/main/res/layout/fragment_home_tv.xml | 17 +++++++++++ .../main/res/layout/player_custom_layout.xml | 9 ++++++ .../res/layout/player_custom_layout_tv.xml | 16 +++++++++++ .../main/res/layout/trailer_custom_layout.xml | 6 ++++ .../res/values/donottranslate-strings.xml | 1 + app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/settings_ui.xml | 6 ++++ 13 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/ic_baseline_clock_24.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 604695859..0e52baff6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -701,6 +701,23 @@ class HomeFragment : BaseFragment( homeViewModel.queryTextSubmit("") } + // Load value for toggling Tv layout real time clock. Hide by default at startup + // set visibility first, to apply a scroll effect later + context?.let { + if (isLayout(TV)) { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(it) + val toggleClock = + settingsManager.getBoolean( + getString(R.string.tv_layout_clock_key), + false + ) + binding.homeClock.isVisible = toggleClock + } else { + binding.homeClock.isVisible = false + } + } + + homeMasterRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { if (isLayout(PHONE)) { @@ -740,6 +757,17 @@ class HomeFragment : BaseFragment( view.getLocationInWindow(rect) scrollParent.isVisible = true scrollParent.translationY = rect[1].toFloat() - 60.toPx + + // Move the TV layout real time clock out of the way too + // We check if we have the correct layout and if the clock is enabled + if(isLayout(TV) && binding.homeClock.isVisible) { + val scrollParent = binding.homeClock + + val rect = IntArray(2) + view.getLocationInWindow(rect) + scrollParent.isVisible = true + scrollParent.translationY = rect[1].toFloat() - 60.toPx + } } } super.onScrolled(recyclerView, dx, dy) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index 20c37dcf4..d90b6043f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -307,6 +307,7 @@ open class FullScreenPlayer : AbstractPlayerFragment( playerVideoTitleRez, playerVideoInfo, playerGoBackHolder, + playerVideoClock, ).forEach { it.animateY(titleMove) } @@ -772,7 +773,7 @@ open class FullScreenPlayer : AbstractPlayerFragment( val showPlayerEpisodes = !isGone && isThereEpisodes() playerEpisodesButtonRoot.isVisible = showPlayerEpisodes playerEpisodesButton.isVisible = showPlayerEpisodes - playerVideoTitleHolder.isGone = togglePlayerTitleGone + playerVideoTitleHolder.isGone = togglePlayerTitleGone || playerVideoTitle.text.isBlank() playerVideoTitleRez.isGone = isGone || playerVideoTitleRez.text.isBlank() playerEpisodeFiller.isGone = isGone playerCenterMenu.isGone = isGone 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 683a322c4..4495a5602 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 @@ -2271,6 +2271,14 @@ class GeneratorPlayer : FullScreenPlayer() { fromTagToEnglishLanguageName(it)?.lowercase() ?: return@mapNotNull null } ?: listOf() } + + // Set up TV clock visibility + if (isLayout(TV)) { + val showTvClock = settingsManager.getBoolean(ctx.getString(R.string.tv_layout_clock_key), false) + playerBinding?.playerVideoClock?.isVisible = showTvClock + } else { + playerBinding?.playerVideoClock?.isVisible = false + } } unwrapBundle(savedInstanceState) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt index f4c522bf9..5f86a3a5a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt @@ -228,6 +228,8 @@ class SettingsUI : BasePreferenceFragmentCompat() { return@setOnPreferenceClickListener true } + getPref(R.string.tv_layout_clock_key)?.hideOn(PHONE or EMULATOR) + getPref(R.string.confirm_exit_key)?.setOnPreferenceClickListener { val prefNames = resources.getStringArray(R.array.confirm_exit) val prefValues = resources.getIntArray(R.array.confirm_exit_values) diff --git a/app/src/main/res/drawable/ic_baseline_clock_24.xml b/app/src/main/res/drawable/ic_baseline_clock_24.xml new file mode 100644 index 000000000..3436c6208 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_clock_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 99a764dee..8d78c01b3 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -227,6 +227,12 @@ tools:listitem="@layout/homepage_parent" tools:visibility="gone" /> + + + + + + + diff --git a/app/src/main/res/layout/player_custom_layout_tv.xml b/app/src/main/res/layout/player_custom_layout_tv.xml index 88e4cd984..c6445d80f 100644 --- a/app/src/main/res/layout/player_custom_layout_tv.xml +++ b/app/src/main/res/layout/player_custom_layout_tv.xml @@ -352,6 +352,22 @@ android:layout_marginEnd="32dp" android:orientation="vertical"> + + + + opensubtitles_key subdl_key animeskip_key + tv_layout_clock_key pref_category_security_key pref_category_gestures_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a918231f..eed41ce81 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -374,6 +374,8 @@ Looks Features General + Show real time clock + Show a real time clock at the top of the screen. Applies to the homepage and player Random Button Show random button on Homepage and Library Extension languages diff --git a/app/src/main/res/xml/settings_ui.xml b/app/src/main/res/xml/settings_ui.xml index 1b516ffa3..774724f99 100644 --- a/app/src/main/res/xml/settings_ui.xml +++ b/app/src/main/res/xml/settings_ui.xml @@ -89,6 +89,12 @@ android:icon="@drawable/metadata_overlay_icon" android:key="@string/show_player_metadata_key" android:title="@string/show_player_metadata_overlay" /> + Date: Thu, 6 Aug 2026 00:52:36 +0200 Subject: [PATCH 10/23] feat(extractors): add vidara mirrors and fix links with filecodes (#3104) [skip ci] --- .../cloudstream3/extractors/Vidara.kt | 45 ++++++++++++++++++- .../cloudstream3/utils/ExtractorApi.kt | 16 +++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidara.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidara.kt index 04c60675e..e3a33b0cb 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidara.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidara.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.extractors import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.Prerelease import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.newSubtitleFile @@ -43,6 +44,46 @@ class VidaraSo : Vidara() { override val mainUrl = "https://vidara.so" } +@Prerelease +class Odysseusa : Vidara() { + override val mainUrl = "https://odysseusa.cc" +} + +@Prerelease +class Handfacesnap : Vidara() { + override val mainUrl = "https://handfacesnap.cc" +} + +@Prerelease +class Namefacesnap : Vidara() { + override val mainUrl = "https://namefacesnap.cc" +} + +@Prerelease +class Thebesthostertv : Vidara() { + override val mainUrl = "https://thebesthosterv.com" +} + +@Prerelease +class Vidmatrixa : Vidara() { + override val mainUrl = "https://vidmatrixa.com" +} + +@Prerelease +class VidChampions : Vidara() { + override val mainUrl = "https://vidchampions.com/" +} + +@Prerelease +class Antarcticadocs : Vidara() { + override val mainUrl = "https://antarcticadocs.com" +} + +@Prerelease +class Nameitweb : Vidara() { + override val mainUrl = "https://nameitweb.com" +} + open class Vidara : ExtractorApi() { override val name = "Vidara" override val mainUrl = "https://vidara.to" @@ -54,7 +95,9 @@ open class Vidara : ExtractorApi() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit, ) { - val fileCode = url.substringAfterLast("/") + // URLs sometimes contain a mirror selector query parameter, e.g. "https://vidara.to/xkNOuHSrCNMj?24" + // that is not part of the file code and hence we must remove it + val fileCode = url.substringAfterLast("/").substringBefore("?") val fileInfo = app.post( "$mainUrl/api/stream", json = mapOf( "filecode" to fileCode, diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 163059a97..2c3fff571 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.extractors.Acefile import com.lagradost.cloudstream3.extractors.Ahvsh import com.lagradost.cloudstream3.extractors.Aico +import com.lagradost.cloudstream3.extractors.Antarcticadocs import com.lagradost.cloudstream3.extractors.Asnwish import com.lagradost.cloudstream3.extractors.Auvexiug import com.lagradost.cloudstream3.extractors.Awish @@ -109,6 +110,7 @@ import com.lagradost.cloudstream3.extractors.HDMomPlayer import com.lagradost.cloudstream3.extractors.HDPlayerSystem import com.lagradost.cloudstream3.extractors.HDStreamAble import com.lagradost.cloudstream3.extractors.Habetar +import com.lagradost.cloudstream3.extractors.Handfacesnap import com.lagradost.cloudstream3.extractors.Haxloppd import com.lagradost.cloudstream3.extractors.Hgcloudto import com.lagradost.cloudstream3.extractors.HglinkTo @@ -158,6 +160,8 @@ import com.lagradost.cloudstream3.extractors.Multimovies import com.lagradost.cloudstream3.extractors.Mvidoo import com.lagradost.cloudstream3.extractors.MyVidPlay import com.lagradost.cloudstream3.extractors.Mwish +import com.lagradost.cloudstream3.extractors.Namefacesnap +import com.lagradost.cloudstream3.extractors.Nameitweb import com.lagradost.cloudstream3.extractors.NathanFromSubject import com.lagradost.cloudstream3.extractors.Nekostream import com.lagradost.cloudstream3.extractors.Nekowish @@ -165,6 +169,7 @@ import com.lagradost.cloudstream3.extractors.Neonime7n import com.lagradost.cloudstream3.extractors.Neonime8n import com.lagradost.cloudstream3.extractors.Obeywish import com.lagradost.cloudstream3.extractors.Odnoklassniki +import com.lagradost.cloudstream3.extractors.Odysseusa import com.lagradost.cloudstream3.extractors.OkRuHTTP import com.lagradost.cloudstream3.extractors.OkRuHTTPMobile import com.lagradost.cloudstream3.extractors.OkRuSSL @@ -269,6 +274,8 @@ import com.lagradost.cloudstream3.extractors.VidHidePro6 import com.lagradost.cloudstream3.extractors.VidHideHub import com.lagradost.cloudstream3.extractors.Ryderjet import com.lagradost.cloudstream3.extractors.Streamcash +import com.lagradost.cloudstream3.extractors.Thebesthostertv +import com.lagradost.cloudstream3.extractors.VidChampions import com.lagradost.cloudstream3.extractors.VidMoxy import com.lagradost.cloudstream3.extractors.VidStack import com.lagradost.cloudstream3.extractors.VideoSeyred @@ -295,6 +302,7 @@ import com.lagradost.cloudstream3.extractors.Vidaraw import com.lagradost.cloudstream3.extractors.Vidarax import com.lagradost.cloudstream3.extractors.Vidavaca import com.lagradost.cloudstream3.extractors.Vide0Net +import com.lagradost.cloudstream3.extractors.Vidmatrixa import com.lagradost.cloudstream3.extractors.Vids import com.lagradost.cloudstream3.extractors.Vidsonic import com.lagradost.cloudstream3.extractors.VkExtractor @@ -1181,6 +1189,14 @@ val extractorApis: AtomicMutableList = atomicListOf( Vidaratem(), VidaaraxCom(), VidaaraxNet(), + Odysseusa(), + Handfacesnap(), + Namefacesnap(), + Thebesthostertv(), + Vidmatrixa(), + VidChampions(), + Antarcticadocs(), + Nameitweb(), GamoVideo(), Gdriveplayerapi(), From 9d070417ed3d807d0482a3c63dd173bfad4a6d9a Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 6 Aug 2026 00:53:20 +0200 Subject: [PATCH 11/23] Translated using Weblate (Albanian) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (4 of 4 strings) Translated using Weblate (Albanian) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Tamil) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Italian) Currently translated at 100.0% (735 of 735 strings) Update translation files Updated by "Remove blank strings" hook in Weblate. Translated using Weblate (Spanish) Currently translated at 99.7% (733 of 735 strings) Translated using Weblate (Vietnamese) Currently translated at 99.8% (734 of 735 strings) Translated using Weblate (Swedish) Currently translated at 99.8% (734 of 735 strings) Translated using Weblate (Polish) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Arabic) Currently translated at 99.7% (733 of 735 strings) Translated using Weblate (English) Currently translated at 99.8% (734 of 735 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Polish) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Czech) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Italian) Currently translated at 100.0% (733 of 733 strings) Translated using Weblate (Czech) Currently translated at 100.0% (733 of 733 strings) Co-authored-by: 007 Co-authored-by: Aziz Hadidi Co-authored-by: Dan Co-authored-by: Fjuro Co-authored-by: Hosted Weblate Co-authored-by: Hosted Weblate user 54392 Co-authored-by: Man Co-authored-by: Massimo Pissarello Co-authored-by: Matthaiks Co-authored-by: Oğuz Ersen Co-authored-by: win7guru Co-authored-by: தமிழ்நேரம் Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sq/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ta/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/sq/ Translation: Cloudstream/App Translation: Cloudstream/Fastlane --- app/src/main/res/values-b+ar/strings.xml | 5 ++++ app/src/main/res/values-b+cs/strings.xml | 11 +++++++++ app/src/main/res/values-b+es/strings.xml | 5 ++++ app/src/main/res/values-b+it/strings.xml | 24 +++++++++++++------ app/src/main/res/values-b+pl/strings.xml | 4 +++- app/src/main/res/values-b+sq/strings.xml | 8 ++++--- app/src/main/res/values-b+sv/strings.xml | 9 +++++++ app/src/main/res/values-b+ta/strings.xml | 17 +++++++++++-- app/src/main/res/values-b+tr/strings.xml | 11 ++++++++- app/src/main/res/values-b+uk/strings.xml | 13 +++++++++- app/src/main/res/values-b+vi/strings.xml | 8 +++++++ app/src/main/res/values-b+zh/strings.xml | 10 +++++++- app/src/main/res/values/strings.xml | 3 +-- .../metadata/android/sq/full_description.txt | 2 +- 14 files changed, 111 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-b+ar/strings.xml b/app/src/main/res/values-b+ar/strings.xml index f543136dc..65fcc149d 100644 --- a/app/src/main/res/values-b+ar/strings.xml +++ b/app/src/main/res/values-b+ar/strings.xml @@ -756,4 +756,9 @@ مقطع استعراض البث قائم + أظهر ساعة الوقت الحقيقي + أظهر شاشة الوقت الحقيقي في قمة الشاشة. ينطبق على الصفحة الرئيسية و المشغل + اعدادات الملف الشخصي + أخفي المصادر صاحبة الأولوية السلبية + أخفي المصادر التي تحتوي أخطاء diff --git a/app/src/main/res/values-b+cs/strings.xml b/app/src/main/res/values-b+cs/strings.xml index 983a03db7..e03da1d6f 100644 --- a/app/src/main/res/values-b+cs/strings.xml +++ b/app/src/main/res/values-b+cs/strings.xml @@ -754,4 +754,15 @@ Video Náhled Živě + Nastavení profilu + Skrýt zdroje s negativní prioritou + Skrýt zdroje s chybami + + %d skrytý odkaz + %d skryté odkazy + %d skrytých odkazů + %d skrytých odkazů + + Zobrazit hodiny s reálným časem + Zobrazit hodiny s reálným časem v horní části obrazovky. Platí pro domovskou obrazovku a přehrávač diff --git a/app/src/main/res/values-b+es/strings.xml b/app/src/main/res/values-b+es/strings.xml index 167de546d..1287c7c09 100644 --- a/app/src/main/res/values-b+es/strings.xml +++ b/app/src/main/res/values-b+es/strings.xml @@ -737,4 +737,9 @@ Vídeo Vista previa En Vivo + Mostrar reloj en tiempo real + Muestra un reloj en tiempo real en la parte superior de la pantalla. Se aplica a la página de inicio y al reproductor + Configuración del perfil + Ocultar fuentes con prioridad negativa + Ocultar fuentes con errores diff --git a/app/src/main/res/values-b+it/strings.xml b/app/src/main/res/values-b+it/strings.xml index c47a84842..2672f39b1 100644 --- a/app/src/main/res/values-b+it/strings.xml +++ b/app/src/main/res/values-b+it/strings.xml @@ -225,7 +225,7 @@ Livestream NSFW Altro - Errore sorgente + Errore fonte Errore remoto Errore del renderer Errore imprevisto del lettore @@ -247,7 +247,7 @@ Controlla aggiornamenti Blocca Ridimensiona - Sorgente + Fonte Salta OP Non mostrare di nuovo Salta questo aggiornamento @@ -346,7 +346,7 @@ Protagonista Supporto Secondario - Sorgente + Fonte Casuale Prossimamente… Cam @@ -670,7 +670,7 @@ Data di messa in onda (più vecchia) Valutazione %s Avvio del processo di aggiornamento plugin! - %d plugin aggiornati con successo! + %d plugin aggiornati correttamente Nessun plugin è stato aggiornato. Notifiche lettore La notifica del lettore per controllare la riproduzione in background @@ -733,7 +733,7 @@ Cancella suggerimenti Mostra pannello cast Info sui media - Nome sorgente + Nome fonte Luminosità extra Attiva il filtro di luminosità quando viene superato il 100% della luminosità dello schermo extra_brightness_enabled @@ -753,10 +753,20 @@ %d download in coda %d download in coda - Priorità sorgente - Decidi come le sorgenti video devono essere ordinate nel lettore + Priorità fonti + Decidi come le fonti video devono essere ordinate nel lettore Mostra sovrapposizione metadati lettore Video Anteprima Live + Impostazioni profilo + Nascondi fonti con priorità negativa + Nascondi fonti con errori + + %d collegamento nascosto + %d collegamenti nascosti + %d collegamenti nascosti + + Mostra orologio in tempo reale + Mostra un orologio in tempo reale nella parte superiore dello schermo. Si applica alla homepage e al lettore diff --git a/app/src/main/res/values-b+pl/strings.xml b/app/src/main/res/values-b+pl/strings.xml index b69146c92..418a4a2ca 100644 --- a/app/src/main/res/values-b+pl/strings.xml +++ b/app/src/main/res/values-b+pl/strings.xml @@ -649,7 +649,7 @@ Odcinek (malejąco) Ocena (najwyższa) Zaktualizuj wtyczki - Pomyślnie zaktualizowano wtyczki: %d! + Pomyślnie zaktualizowano wtyczki: %d Nie zaktualizowano żadnych wtyczek. Zaktualizuj wtyczki ręcznie Rozpoczęcie procesu aktualizacji wtyczek! @@ -751,4 +751,6 @@ %d ukrytych odnośników %d ukrytych odnośników + Pokaż zegar czasu rzeczywistego + Wyświetl zegar czasu rzeczywistego u góry ekranu. Dotyczy strony głównej i odtwarzacza diff --git a/app/src/main/res/values-b+sq/strings.xml b/app/src/main/res/values-b+sq/strings.xml index c33ea0447..828066ead 100644 --- a/app/src/main/res/values-b+sq/strings.xml +++ b/app/src/main/res/values-b+sq/strings.xml @@ -117,7 +117,7 @@ Madhësia e shkrimit Kërko duke përdorur ofruesit Kërko duke përdorur tipet - %d Banane të dhëna per developer-at + %d Banane të dhëna për developer-at Asnjë Banane e dhënë Zgjedhje automatike e gjuhës Shkarko gjuhët @@ -216,7 +216,7 @@ %1$s %2$d%3$s Asnje sezon Episodi - Episodet + Episode %1$d-%2$d %1$d %2$s Vazhdon në %s @@ -679,7 +679,7 @@ Përditëso shtesat Përditëso shtesat manualisht Filloi procesi i përditësimit të shtesave! - %d Shtes(at) u përditësuan me sukses! + %d Shtes(a) u përditësuan me sukses Nuk u përditësua asnjë shtesë. Njoftimet e luajtësit Njoftimi për kontrollin e luajtjes nga sfondi @@ -742,4 +742,6 @@ %d link i fshehur %d linqe të fshehura + Shfaq orën në kohë reale + Shfaq një orë në krye të ekranit. Aplikohet në faqen kryesore dhe në luajtës diff --git a/app/src/main/res/values-b+sv/strings.xml b/app/src/main/res/values-b+sv/strings.xml index 52d4e1d05..0740e0b8d 100644 --- a/app/src/main/res/values-b+sv/strings.xml +++ b/app/src/main/res/values-b+sv/strings.xml @@ -735,4 +735,13 @@ Video Förhandsvisning Live + Visa realtidsklocka + Visa en realtidsklocka högst upp på skärmen. Gäller startsidan och spelaren + Profilinställningar + Dölj källor med negativ prioritet + Dölj källor med fel + + %d dold länk + %d dolda länkar + diff --git a/app/src/main/res/values-b+ta/strings.xml b/app/src/main/res/values-b+ta/strings.xml index 4cde39d8f..2537eba0c 100644 --- a/app/src/main/res/values-b+ta/strings.xml +++ b/app/src/main/res/values-b+ta/strings.xml @@ -609,7 +609,7 @@ பயன்பாட்டிலிருந்து வெளியேறுவதற்கு முன் உரையாடலைக் காட்டு உள்நாட்டில் அங்கீகரிக்கவும் தள்ளுபடி - உங்கள் ச்மார்ட்போன் அல்லது கணினியில் %s ஐப் பார்வையிட்டு மேலே உள்ள குறியீட்டை உள்ளிடவும் + உங்கள் ச்மார்ட்போன் அல்லது கணினியில் %s ஐப் பார்வையிட்டு மேலே உள்ள குறியீட்டை உள்ளிடவும் சாதன முள் குறியீட்டைப் பெற முடியாது, உள்ளக அங்கீகாரத்தை முயற்சிக்கவும் முள் குறியீடு இப்போது காலாவதியானது! விளிம்பு அளவு @@ -652,7 +652,7 @@ செருகுநிரல்களைப் புதுப்பிக்கவும் செருகுநிரல்களை கைமுறையாக புதுப்பிக்கவும் சொருகி புதுப்பிப்பு செயல்முறையைத் தொடங்குகிறது! - வெற்றிகரமாக புதுப்பிக்கப்பட்டது %d சொருகி (கள்)! + %d செருகுநிரல்(கள்) வெற்றிகரமாக புதுப்பிக்கப்பட்டது செருகுநிரல்கள் எதுவும் புதுப்பிக்கப்படவில்லை. பிளேயர் அறிவிப்புகள் பின்னணியில் இருந்து பின்னணியைக் கட்டுப்படுத்துவதற்கான பிளேயர் அறிவிப்பு @@ -731,4 +731,17 @@ %d பதிவிறக்கம் வரிசையில் உள்ளது %d பதிவிறக்கங்கள் வரிசையில் உள்ளன + பிளேயர் மேனிலை தரவு மேலடுக்கைக் காட்டு + ஒளிதோற்றம் + உண்மையான நேர கடிகாரத்தைக் காட்டு + திரையின் மேற்புறத்தில் நிகழ் நேர கடிகாரத்தைக் காட்டு. முகப்புப்பக்கம் மற்றும் பிளேயருக்குப் பொருந்தும் + முன்னோட்டம் + சுயவிவர அமைப்புகள் + எதிர்மறையான முன்னுரிமையுடன் ஆதாரங்களை மறை + பிழைகளுடன் ஆதாரங்களை மறைக்கவும் + + %d மறைக்கப்பட்ட இணைப்பு + %d மறைக்கப்பட்ட இணைப்புகள் + + வாழ்க diff --git a/app/src/main/res/values-b+tr/strings.xml b/app/src/main/res/values-b+tr/strings.xml index d711505a8..e4a2dbc37 100644 --- a/app/src/main/res/values-b+tr/strings.xml +++ b/app/src/main/res/values-b+tr/strings.xml @@ -661,7 +661,7 @@ Tarih %s Eklentileri Güncelle Eklenti güncellemesi başlıyor! - %d eklenti başarıyla güncellendi! + %d eklenti başarıyla güncellendi Hiçbir eklenti güncellenmedi. Eklentileri el ile güncelle Oynatıcı bildirimi arka planda oynatmasını kontrol etmek içindir @@ -749,4 +749,13 @@ Canlı Ön Gösterim Video + Gerçek zamanlı saati göster + Ekranın üst kısmında gerçek zamanlı bir saat göster. Ana sayfa ve oynatıcı için uygulanır + Profil ayarları + Eksi önceliğe sahip kaynakları gizle + Hatalı kaynakları gizle + + %d gizli bağlantı + %d gizli bağlantı + diff --git a/app/src/main/res/values-b+uk/strings.xml b/app/src/main/res/values-b+uk/strings.xml index ba46736ac..56e5a916e 100644 --- a/app/src/main/res/values-b+uk/strings.xml +++ b/app/src/main/res/values-b+uk/strings.xml @@ -646,7 +646,7 @@ Еп. %s Дата %s Оновити розширення - Успішно оновлено %d розширення(-ь)! + Успішно оновлено %d розширення(-ь) Оновити розширення вручну Починається оновлення розширень! Не оновлено жодного розширення. @@ -739,4 +739,15 @@ Відео Передперегляд Наживо + Показувати поточний час + Показувати поточний час у верхній частині екрана (на головній та у програвачі) + Налаштування профілю + Приховати джерела з від’ємним пріоритетом + Приховати джерела з помилками + + %d приховане посилання + %d приховані посилання + %d прихованих посилань + %d прихованих посилань + diff --git a/app/src/main/res/values-b+vi/strings.xml b/app/src/main/res/values-b+vi/strings.xml index 53679c05c..3e2a93fb4 100644 --- a/app/src/main/res/values-b+vi/strings.xml +++ b/app/src/main/res/values-b+vi/strings.xml @@ -748,4 +748,12 @@ Trực tiếp Video Xem trước + Hiển thị đồng hồ thời gian thực + Hiển thị đồng hồ thời gian thực ở phía trên màn hình. Áp dụng cho trang chủ và trình phát + Cài đặt hồ sơ + Ẩn nguồn có độ ưu tiên âm + Ẩn nguồn có lỗi + + %d liên kết bị ẩn + diff --git a/app/src/main/res/values-b+zh/strings.xml b/app/src/main/res/values-b+zh/strings.xml index 56ec8f43e..dc7eab451 100644 --- a/app/src/main/res/values-b+zh/strings.xml +++ b/app/src/main/res/values-b+zh/strings.xml @@ -655,7 +655,7 @@ 得分 %s 日期 %s 更新插件 - 成功更新了 %d 个插件! + 成功更新了 %d 个插件 没有插件被更新。 手动更新插件 集数(升序) @@ -747,4 +747,12 @@ 视频 预览 播放中 + 显示实时时钟 + 在屏幕顶部显示实时时钟。应用于主页和播放器 + 配置文件设置 + 隐藏负优先级的来源 + 隐藏有错误的来源 + + %d 条隐藏链接 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eed41ce81..7d530b59f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -631,7 +631,6 @@ %d hidden link %d hidden links - Wi-Fi Mobile data Set default @@ -734,7 +733,7 @@ Update plugins manually Starting plugin update process! - Successfully updated %d plugin(s)! + Successfully updated %d plugin(s) No plugins were updated. Player notifications The player notification for controlling the playback from the background diff --git a/fastlane/metadata/android/sq/full_description.txt b/fastlane/metadata/android/sq/full_description.txt index f9d6e6f9e..a7881e62e 100644 --- a/fastlane/metadata/android/sq/full_description.txt +++ b/fastlane/metadata/android/sq/full_description.txt @@ -1,7 +1,7 @@ CloudStream-3 ju lejon të shikoni dhe shkarkoni filma, seriale televizive dhe anime. Aplikacioni vjen pa asnjë reklamë dhe analitikë -dhe suporton faqe të shumta trailerash dhe filmash dhe më shumë, p.sh. +dhe suporton faqe të shumta trailer-ash dhe filmash, dhe më shumë, p.sh. Të ruajturat From 2207215a829030afff96268b840cb38dadac2e8e Mon Sep 17 00:00:00 2001 From: Bnyro Date: Thu, 6 Aug 2026 00:53:51 +0200 Subject: [PATCH 12/23] feat(extractors): add playmate extractor (#3103) --- .../cloudstream3/extractors/Playmate.kt | 43 +++++++++++++++++++ .../cloudstream3/utils/ExtractorApi.kt | 2 + 2 files changed, 45 insertions(+) create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Playmate.kt diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Playmate.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Playmate.kt new file mode 100644 index 000000000..e319d4c17 --- /dev/null +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Playmate.kt @@ -0,0 +1,43 @@ +package com.lagradost.cloudstream3.extractors + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.Prerelease +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.ExtractorLinkType +import com.lagradost.cloudstream3.utils.newExtractorLink +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Prerelease +class Playmate : ExtractorApi() { + override val name: String = "Playmate" + override val mainUrl: String = "https://playmate.to" + override val requiresReferer: Boolean = false + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val id = url.substringAfterLast("/") + + val resp = app.post( + "$mainUrl/api/s", + json = mapOf("c" to id, "d" to "web"), + headers = mapOf("User-Agent" to "Mozilla/5.0 (X11; Linux x86_64; rv:153.0) Gecko/20100101 Firefox/153.0") + ).parsed() + + callback.invoke(newExtractorLink(source = name, name = name, url = resp.sx, type = ExtractorLinkType.M3U8)) + } + + @Serializable + private data class StreamInfo( + @SerialName("sx") + @JsonProperty("sx") + val sx: String, + ) +} \ No newline at end of file diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 2c3fff571..e09ebbc8b 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -183,6 +183,7 @@ import com.lagradost.cloudstream3.extractors.PlayLtXyz import com.lagradost.cloudstream3.extractors.PlayRu import com.lagradost.cloudstream3.extractors.PlayerVoxzer import com.lagradost.cloudstream3.extractors.Playerwish +import com.lagradost.cloudstream3.extractors.Playmate import com.lagradost.cloudstream3.extractors.Playmogo import com.lagradost.cloudstream3.extractors.Rabbitstream import com.lagradost.cloudstream3.extractors.RapidVid @@ -1336,6 +1337,7 @@ val extractorApis: AtomicMutableList = atomicListOf( Flyfile(), Firestream(), Vids(), + Playmate() ) From 0361a6f1dd4587d0b71969295daf58f2d7c61fa7 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 23 Aug 2026 15:21:29 +0200 Subject: [PATCH 13/23] feat(extractors): add hubu.cloud extractor (#3131) --- .../cloudstream3/extractors/HubuCloud.kt | 27 +++++++++++++++++++ .../cloudstream3/utils/ExtractorApi.kt | 2 ++ 2 files changed, 29 insertions(+) create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HubuCloud.kt diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HubuCloud.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HubuCloud.kt new file mode 100644 index 000000000..85151b1d7 --- /dev/null +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/HubuCloud.kt @@ -0,0 +1,27 @@ +package com.lagradost.cloudstream3.extractors + +import com.lagradost.cloudstream3.Prerelease +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.newExtractorLink + +@Prerelease +open class HubuCloud: ExtractorApi() { + override val name: String = "Hubu" + override val mainUrl: String = "https://hubu.cloud" + override val requiresReferer: Boolean = false + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val doc = app.get(url).document + + val streamUrl = doc.select("source").attr("src") + callback.invoke(newExtractorLink(source = name, name = name, url = streamUrl)) + } +} \ No newline at end of file diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt index e09ebbc8b..245339f00 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -119,6 +119,7 @@ import com.lagradost.cloudstream3.extractors.Hotlinger import com.lagradost.cloudstream3.extractors.HubCloud import com.lagradost.cloudstream3.extractors.Hxfile import com.lagradost.cloudstream3.extractors.HlsWish +import com.lagradost.cloudstream3.extractors.HubuCloud import com.lagradost.cloudstream3.extractors.InternetArchive import com.lagradost.cloudstream3.extractors.JWPlayer import com.lagradost.cloudstream3.extractors.Jeniusplay @@ -1148,6 +1149,7 @@ val extractorApis: AtomicMutableList = atomicListOf( Vicloud(), Uservideo(), Userscloud(), + HubuCloud(), Movhide(), StreamhideCom(), From a4824a2950f56e4c33105c677d29f7a9a767d59f Mon Sep 17 00:00:00 2001 From: RjBiermann <198417406+RjBiermann@users.noreply.github.com> Date: Wed, 26 Aug 2026 15:23:59 -0400 Subject: [PATCH 14/23] feat(tv): add plugin settings button to home top bar (#3122) --- .../cloudstream3/ui/home/HomeFragment.kt | 23 +++++++++++++++++++ app/src/main/res/layout/fragment_home.xml | 7 ++++++ app/src/main/res/layout/fragment_home_tv.xml | 21 +++++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 0e52baff6..f382e01d3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -30,6 +30,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.chip.Chip import com.lagradost.api.Log +import com.lagradost.cloudstream3.APIHolder import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.CommonActivity.showToast @@ -701,6 +702,21 @@ class HomeFragment : BaseFragment( homeViewModel.queryTextSubmit("") } + homePreviewSettingsButton.setOnClickListener { view -> + val apiName = homeViewModel.apiName.value + val plugin = APIHolder.getApiFromNameNull(apiName) + ?.sourcePlugin?.let { PluginManager.plugins[it] } as? Plugin + val openSettings = plugin?.openSettings + if (openSettings != null) { + try { + val activityContext = view.context.getActivity() ?: view.context + openSettings.invoke(activityContext) + } catch (e: Throwable) { + logError(e) + } + } + } + // Load value for toggling Tv layout real time clock. Hide by default at startup // set visibility first, to apply a scroll effect later context?.let { @@ -800,6 +816,12 @@ class HomeFragment : BaseFragment( observe(homeViewModel.page) { data -> binding.apply { + if (isLayout(TV or EMULATOR)) { + val plugin = APIHolder.getApiFromNameNull(homeViewModel.apiName.value) + ?.sourcePlugin?.let { PluginManager.plugins[it] } as? Plugin + homePreviewSettingsButton.isGone = plugin?.openSettings == null + } + when (data) { is Resource.Success -> { val d = data.value @@ -815,6 +837,7 @@ class HomeFragment : BaseFragment( homeLoadingError.isVisible = false homeMasterRecycler.isVisible = true homeLoadingShimmer.stopShimmer() + //home_loaded?.isVisible = true if (toggleRandomButton) { val distinct = d.values diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 8d78c01b3..dc1635392 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -260,4 +260,11 @@ android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone" /> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home_tv.xml b/app/src/main/res/layout/fragment_home_tv.xml index d50c4c2e4..f8559ab14 100644 --- a/app/src/main/res/layout/fragment_home_tv.xml +++ b/app/src/main/res/layout/fragment_home_tv.xml @@ -200,11 +200,28 @@ android:gravity="center" android:nextFocusLeft="@id/navigation_home" tools:text="Hello!" - android:nextFocusRight="@id/home_preview_reload_provider" + android:nextFocusRight="@id/home_preview_settings_button" android:nextFocusDown="@id/home_preview_info_btt" > + + Date: Wed, 26 Aug 2026 22:28:55 +0300 Subject: [PATCH 15/23] Fix: Missing translations in extension menu (#3138) --- .../cloudstream3/ui/home/HomeFragment.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index f382e01d3..d0b4b0f20 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -110,6 +110,14 @@ class HomeFragment : BaseFragment( val errorProfilePic = errorProfilePics.random() + fun Context.getDisplayName(apiName: String?): String? { + return when (apiName) { + noneApi.name -> getString(R.string.none) + randomApi.name -> getString(R.string.home_random) + else -> apiName + } + } + //fun Activity.loadHomepageList( // item: HomePageList, // deleteCallback: (() -> Unit)? = null, @@ -503,8 +511,10 @@ class HomeFragment : BaseFragment( addAll(remainingApis) } - val names = - currentValidApis.map { if (isMultiLang) "${getFlagFromIso(it.lang)?.plus(" ") ?: ""}${it.name}" else it.name } + val names = currentValidApis.map { + val displayName = getDisplayName(it.name) + if (isMultiLang) "${getFlagFromIso(it.lang)?.plus(" ") ?: ""}$displayName" else displayName + } val index = currentValidApis.map { it.name }.indexOf(currentApiName) listView?.setItemChecked(index, true) arrayAdapter.addAll(names) @@ -806,9 +816,10 @@ class HomeFragment : BaseFragment( observe(homeViewModel.apiName) { apiName -> currentApiName = apiName + val displayApiName = context?.getDisplayName(apiName) ?: apiName binding.apply { - homeApiFab.text = apiName - homeChangeApi.text = apiName + homeApiFab.text = displayApiName + homeChangeApi.text = displayApiName homePreviewReloadProvider.isGone = (apiName == noneApi.name) homePreviewSearchButton.isGone = (apiName == noneApi.name) } From adcbae055e9e14d8538526dbd68c1e0cd0926485 Mon Sep 17 00:00:00 2001 From: ByAyzen <189399597+ByAyzen@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:40:24 +0300 Subject: [PATCH 16/23] Fix: Upcoming post alignment issue (#3136) --- app/src/main/res/layout/fragment_result.xml | 3 ++- app/src/main/res/layout/fragment_result_tv.xml | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index a5c933d6a..9db120ad0 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -553,7 +553,8 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" - android:paddingTop="50dp" + android:paddingTop="20dp" + android:paddingBottom="20dp" android:text="@string/coming_soon" android:textColor="?attr/textColor" android:textSize="20sp" diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml index 3dff8f0d5..db07250c3 100644 --- a/app/src/main/res/layout/fragment_result_tv.xml +++ b/app/src/main/res/layout/fragment_result_tv.xml @@ -654,7 +654,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" - android:paddingTop="50dp" + android:paddingTop="20dp" + android:paddingBottom="20dp" android:text="@string/coming_soon" android:textColor="?attr/textColor" android:textSize="20sp" @@ -1036,7 +1037,8 @@ https://developer.android.com/design/ui/tv/samples/jet-fit android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" - android:paddingTop="50dp" + android:paddingTop="20dp" + android:paddingBottom="20dp" android:text="@string/coming_soon" android:textColor="?attr/textColor" android:textSize="20sp" From e3ddcd741dbf178d15a9e6a6ddf24d715daf7469 Mon Sep 17 00:00:00 2001 From: ByAyzen <189399597+ByAyzen@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:49:40 +0300 Subject: [PATCH 17/23] Fix: Cast text in emulator layout (#3134) --- .../ui/result/ResultFragmentTv.kt | 2 +- .../main/res/layout/fragment_result_tv.xml | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt index ce67763d0..c316fae17 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt @@ -874,7 +874,6 @@ class ResultFragmentTv : BaseFragment( resultMetaRating.setText(d.ratingText) resultMetaStatus.setText(d.onGoingText) resultMetaContentRating.setText(d.contentRatingText) - resultCastText.setText(d.actorsText) resultNextAiring.setText(d.nextAiringEpisode) resultNextAiringTime.setText(d.nextAiringDate) resultPoster.loadImage(d.posterImage, headers = d.posterHeaders) @@ -932,6 +931,7 @@ class ResultFragmentTv : BaseFragment( true ) + resultCastText.setText(if (showCast) d.actorsText else null) resultCastItems.isGone = !showCast || d.actors.isNullOrEmpty() (resultCastItems.adapter as? ActorAdaptor)?.submitList(if (showCast) d.actors else emptyList()) diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml index db07250c3..b06886a97 100644 --- a/app/src/main/res/layout/fragment_result_tv.xml +++ b/app/src/main/res/layout/fragment_result_tv.xml @@ -631,6 +631,18 @@ https://developer.android.com/design/ui/tv/samples/jet-fit + + - - Date: Wed, 26 Aug 2026 22:41:22 +0200 Subject: [PATCH 18/23] Translated using Weblate (Albanian) Currently translated at 100.0% (735 of 735 strings) Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Merge remote-tracking branch 'origin/master' Translated using Weblate (Japanese) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Somali) Currently translated at 66.8% (491 of 735 strings) Translated using Weblate (German) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Russian) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Swedish) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Somali) Currently translated at 62.8% (462 of 735 strings) Translated using Weblate (Bengali) Currently translated at 54.1% (398 of 735 strings) Translated using Weblate (French) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (French) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Indonesian) Currently translated at 99.8% (734 of 735 strings) Translated using Weblate (Russian) Currently translated at 99.8% (734 of 735 strings) Translated using Weblate (Czech) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (French) Currently translated at 100.0% (735 of 735 strings) Translated using Weblate (French) Currently translated at 100.0% (735 of 735 strings) Co-authored-by: 007 Co-authored-by: Aesthetic Fighter Co-authored-by: Aishik Mukherjee Co-authored-by: Dmitry Warkentin Co-authored-by: Fjuro Co-authored-by: Gabriel Co-authored-by: Hosted Weblate Co-authored-by: Man Co-authored-by: Noordin Mohamed Co-authored-by: Posemartonis Co-authored-by: Takeru Mikenu Co-authored-by: ardev Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ja/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt_BR/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sq/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translation: Cloudstream/App --- app/src/main/res/values-b+bn/strings.xml | 14 +++++ app/src/main/res/values-b+cs/strings.xml | 2 +- app/src/main/res/values-b+de/strings.xml | 11 +++- app/src/main/res/values-b+es/strings.xml | 7 ++- app/src/main/res/values-b+fr/strings.xml | 14 ++++- app/src/main/res/values-b+in/strings.xml | 8 +++ app/src/main/res/values-b+ja/strings.xml | 12 +++- app/src/main/res/values-b+pt+BR/strings.xml | 12 +++- app/src/main/res/values-b+ru/strings.xml | 11 ++++ app/src/main/res/values-b+so/strings.xml | 65 +++++++++++++++++---- app/src/main/res/values-b+sq/strings.xml | 2 +- app/src/main/res/values-b+sv/strings.xml | 2 +- app/src/main/res/values-b+vi/strings.xml | 2 +- 13 files changed, 139 insertions(+), 23 deletions(-) diff --git a/app/src/main/res/values-b+bn/strings.xml b/app/src/main/res/values-b+bn/strings.xml index b6edbd1cd..da3f819b6 100644 --- a/app/src/main/res/values-b+bn/strings.xml +++ b/app/src/main/res/values-b+bn/strings.xml @@ -387,4 +387,18 @@ পডকাস্ট ভিডিও এনকোডিং ত্রুটি + অসমর্থিত ত্রুটি + কাস্ট মিরর + প্লে মিরর" + রেটিং লেবেল + পর্বের বিবরণ + প্লেয়ারে প্রদর্শিত - স্কিপের পরিমাণ + প্লেয়ার দৃশ্যমান থাকলে যে সময় পরিমাণ স্কিপ করা হয় + GitHub-এর সাথে সংযোগ করা যায়নি। jsDelivr proxy চালু করা হচ্ছে। + jsDelivr ব্যবহার করে GitHub-এর সরাসরি লিংক ব্লক করা এড়িয়ে যান। এতে আপডেট কয়েকদিন বিলম্বিত হতে পারে। + নিরাপত্তা + অ্যাকাউন্টসমূহ + লাইভ ঘড়ি দেখান + পর্দার উপরের অংশে লাইভ ঘড়ি দেখায়। হোমপেজ ও প্লেয়ারে প্রযোজ্য + %1$s %2$s diff --git a/app/src/main/res/values-b+cs/strings.xml b/app/src/main/res/values-b+cs/strings.xml index e03da1d6f..c713b17fd 100644 --- a/app/src/main/res/values-b+cs/strings.xml +++ b/app/src/main/res/values-b+cs/strings.xml @@ -660,7 +660,7 @@ Spouštím proces aktualizace doplňků! Nebyly aktualizovány žádné doplňky. Ep %s - Úspěšně aktualizováno %d doplňků! + Úspěšně aktualizováno %d doplňků Epizoda (sestupně) Hodnocení (nejnižší) Ručně aktualizovat doplňky diff --git a/app/src/main/res/values-b+de/strings.xml b/app/src/main/res/values-b+de/strings.xml index e674fafd6..f00db9ab4 100644 --- a/app/src/main/res/values-b+de/strings.xml +++ b/app/src/main/res/values-b+de/strings.xml @@ -639,7 +639,7 @@ Softwaredekodierung ermöglicht es dem Player, Videodateien abzuspielen, die von deinem Gerät nicht unterstützt werden, könnte jedoch bei hoher Auflösung zu ruckelnder oder instabiler Wiedergabe führen. App neu starten und Stream Torrent Pop-Up akzeptieren, um fortzufahren. Plugins aktualisieren - %d Plugin(s) erfolgreich aktualisiert! + %d Plugin(s) erfolgreich aktualisiert Plugins manuell aktualisieren Podcast Nicht unterstützter Fehler @@ -735,4 +735,13 @@ Video Vorschau Live + Echtzeituhr anzeigen + Echtzeituhr oben auf dem Bildschirm anzeigen. Gilt für Startseite und Player + Profil-Einstellungen + Quellen mit negativer Priorität verbergen + Quellen mit Fehlern ausblenden + + %d ausgeblendeter Link + %d ausgeblendete Links + diff --git a/app/src/main/res/values-b+es/strings.xml b/app/src/main/res/values-b+es/strings.xml index 1287c7c09..df6b181f1 100644 --- a/app/src/main/res/values-b+es/strings.xml +++ b/app/src/main/res/values-b+es/strings.xml @@ -647,7 +647,7 @@ Actualizar complementos Fecha de emisión (más antigua) Puntuar %s - ¡%d complemento(s) actualizados correctamente! + %d complemento(s) actualizados correctamente No se actualizó ningún complemento. Episodio (Ascendente) Reconocimiento de habla no disponible @@ -742,4 +742,9 @@ Configuración del perfil Ocultar fuentes con prioridad negativa Ocultar fuentes con errores + + %d enlace oculto + %d enlaces ocultos + %d enlaces ocultos + diff --git a/app/src/main/res/values-b+fr/strings.xml b/app/src/main/res/values-b+fr/strings.xml index bf401b20a..76f7240cf 100644 --- a/app/src/main/res/values-b+fr/strings.xml +++ b/app/src/main/res/values-b+fr/strings.xml @@ -641,7 +641,7 @@ Note %s Date %s Mettre à jour les plugins - %d plugin(s) mis à jour avec succès ! + %d plugin(s) mis à jour avec succès Aucun plugin n\'a été mis à jour. Note (Plus Haute) Mettre à jour les plugins manuellement @@ -737,5 +737,15 @@ Afficher les métadata de l\'overlay du lecteur vidéo Vidéo Prévisualisation - Direct + Live + Cacher les sources avec une priorité négative + Afficher l\'heure + Affiche l\'heure en haut de l\'écran. S\'applique à la page d\'Accueil et au lecteur + Paramètres du profil + Cacher les sources avec des erreurs + + %d lien caché + %d liens cachés + %d liens cachés + diff --git a/app/src/main/res/values-b+in/strings.xml b/app/src/main/res/values-b+in/strings.xml index 7d6475f31..4597ec5ac 100644 --- a/app/src/main/res/values-b+in/strings.xml +++ b/app/src/main/res/values-b+in/strings.xml @@ -747,4 +747,12 @@ Live Video Pratinjau + Tampilkan jam waktu nyata + Tampilkan jam waktu nyata di bagian atas layar. Berlaku untuk halaman rumah dan pemain + Pengaturan profil + Sembunyikan sumber dengan prioritas negatif + Sembunyikan sumber dengan kesalahan + + %d link tersembunyi + diff --git a/app/src/main/res/values-b+ja/strings.xml b/app/src/main/res/values-b+ja/strings.xml index c5e6272b2..c7bdb4ef0 100644 --- a/app/src/main/res/values-b+ja/strings.xml +++ b/app/src/main/res/values-b+ja/strings.xml @@ -383,7 +383,7 @@ お気に入り 生体認証は、このデバイスではサポートされていません 指紋、顔ID、PIN、パターン、パスワードでアプリをロック解除します. - スマホやPCで%sにアクセスし、上記のコードを入力してください + スマホやパソコンで %s にアクセスし、上記のコードを入力してください PINコードが期限切れになりました! ライブラリが空です :(\nライブラリアカウントにログインするか、ローカルライブラリに表示を追加してください。 このリストは空です。 別のものに切り替えてみてください。 @@ -645,7 +645,7 @@ アプリを再起動し、ポップアップに同意して続行します。 ソフトウェアデコーディング ソフトウェアデコーディングにより、機器でサポートされていないビデオファイルを再生できますが、高解像度で遅延や不安定な再生を引き起こす可能性があります。 - %d 個のプラグインを正常に更新しました! + %d 個のプラグインを更新しました 更新されたプラグインはありません。 プレーヤー通知 バックグラウンドから再生を制御するためのプレーヤー通知 @@ -733,4 +733,12 @@ 動画 プレビュー ライブ + 現地時刻を表示 + 画面上部に現地時刻を表示します。ホーム画面とプレイヤーに適用されます + プロフィール設定 + 優先度がマイナスのソースを非表示 + エラーが発生したソースを非表示 + + %d件の非表示リンク + diff --git a/app/src/main/res/values-b+pt+BR/strings.xml b/app/src/main/res/values-b+pt+BR/strings.xml index 455478b5a..680307ae9 100644 --- a/app/src/main/res/values-b+pt+BR/strings.xml +++ b/app/src/main/res/values-b+pt+BR/strings.xml @@ -662,7 +662,7 @@ Data %s Atualizar plugins Iniciando o processo de atualização do plugin! - Atualização bem-sucedida para %d plugin(s)! + Atualização bem-sucedida para %d plugin(s) Nenhum plugin foi atualizado. Atualizar plugins manualmente Notificações de reprodução @@ -752,4 +752,14 @@ Vídeo Visualização Ao vivo + Mostrar relógio em tempo real + Exiba um relógio em tempo real na parte superior da tela. Aplica-se à página inicial e ao player + Configurações de perfil + Ocultar fontes com prioridade negativa + Ocultar fontes com erros + + %d link oculto + %d link ocultos + %d link ocultos + diff --git a/app/src/main/res/values-b+ru/strings.xml b/app/src/main/res/values-b+ru/strings.xml index 38e576baa..7a174483f 100644 --- a/app/src/main/res/values-b+ru/strings.xml +++ b/app/src/main/res/values-b+ru/strings.xml @@ -739,4 +739,15 @@ Видео Предпросмотр Прямой эфир + Отображать текущее время + Отображать в верхней части экрана часы с текущим временем. Применяется к главной странице и плееру + Настройки профиля + Скрывать источники с отрицательным приоритетом + Скрывать источники с ошибками + + %d скрытая ссылка + %d скрытые ссылки + %d скрытых ссылок + %d скрытых ссылок + diff --git a/app/src/main/res/values-b+so/strings.xml b/app/src/main/res/values-b+so/strings.xml index 0eb787311..8a8f29095 100644 --- a/app/src/main/res/values-b+so/strings.xml +++ b/app/src/main/res/values-b+so/strings.xml @@ -65,7 +65,7 @@ Kaydka gudaha Trj Way bilaabmatay soo dejintu - Daalaco + Khadka ka daaro Waa la joojiyey dejintan Way bilaabantay cuaboonaysiintu Soo raridda lifaaqyadu way fashilantay @@ -91,7 +91,7 @@ Tirtir Nooca cidhifyada Isticmaal xaddiga iftiinka ee moobilkaaga halkii aad adigu habayn lahayd ugu horreynba - Gurmadka iyo cusbooneysiinta + Cusboonaysiinta iyo Kaydinta Waa la kaydiyet xogta gurmadka Raadinta waxay kuugu kala qaybinaysaa mid kasta iyo qaybiyihiisa Way fashilantay in xogta laga soo celiyo faylka gurmadka%s @@ -100,7 +100,7 @@ Xogta waa la kaydiyey Heerka Ogolaanshaha lagu kaydinayo ma hayno. Fadlan isku day mar kale. - Koontooyinka + Akoonada iyo Amniga Xog Si iskii ah usoo deji sidkanaha Qari tayada muqaalka aad dooratay natiijada waxa aad raadiso @@ -153,7 +153,7 @@ Tusi Boodhka Kitsu Si iskii ah u cusbooneysii sidkanaha Si iskii ah usoo deji dhamaan sidkanayaasha aan weli lasoo dejin ee ku jira kaydka aad keentay. - Marka ugu horreysa ee aad appka furto ayaad arkaysaa haddii wax cusub yihiin + Si toos ah u raadi update cusub markii aad furto appka. Light novel app, isla dhiseyaasha appkan baa leh Tus wixii appka ku cusub Dib uso bilow fadhiisinta appka @@ -238,13 +238,13 @@ Hareeraha madow ka saar markaad daawanayso Qoraal-hoosaadka Habaynta Qrl-hoosaadka Koromakaastiga - Habka Xawaare-kordhinta - Midig iyo bidix u jiid si aad marba dhinac ugu dhaafiso muqaalka + Xawaaraha muuqaalka + Midig iyo bidix u jiid si aad u xakameyso halka uu muuqaalku marayo Laba jeer taabo midig ama bidix si aad u dhaafiso ama ku ceshato Laba jeer taabo si aad u dhaafiso - Inta aad is dhaafinayso + Inta aad is-dhaafinayso (Ilbiriqsiyo ahaan) Laba jeer taabo si aad u qallajiso - Dhexda taabo si aad u qallajisato + Labo jeer dhexda taabo si aad u joojiso Gurmad u samee xogta Iftiinka moobilka istcimaal Cusbooneysii marba halkaad marayso daawashada @@ -285,7 +285,7 @@ La degtay Soo degaya Ka Saar - Kor ama hoos u jiid labada dhinaca ee daaraha si aad u maamusho codka ama iftiinka + Kor ama hoos u jiid geesaha muuqaalka si aad u maamusho codka ama iftiinka Iskii-u-daarka xalqadda xigta Si iskii ah ayuu u daarayaa xalqadda xigta marka aad dhamaysato kaad daawanayso Si iskii ah ha ula socdo appku hadba xalqadda aad marayso @@ -310,7 +310,7 @@ Safe mode ayaa daaran Dhammaan Kordhiyeyaasha waa la wada demiyey cillad timi aawadeed si aad u ogaato ka cillada sababaya. %s (La xayiray) - Marka dambe eed gasho isticmaal + Dib u fur app-ka si aad u aragto isbeddelada. Eeg warbixinra Cilladda Qiimeynta: %s Lahaanshaha @@ -424,8 +424,8 @@ Xayiran: %d Aan dejinayn: %d Waxa la cusbooneysiiyey %d sidkane - Ugu horreyn cloudstream ma laha wax websaydyo uu filimaanta kasoo xigto, waxay noqonaysaa inaad adigu rakibato reboositarradooda... \n \nSababtuna waa in mar dhexdaas ah na dacweeyeen shirkadda Sky UK Limited🤮, markaa si aan mar dambe taasi u dhicin anagu kuma rakibi karno... \n \nDiscord naga soo qabo ama internetka ka baadh. - Soo deji dhamaan sidkanayaasha reboositarkan? + Ugu horreyn cloudstream ma laha wax websaydyo uu filimaanta kasoo xigto, waxay noqonaysaa inaad adigu rakibato reboositarradooda. Sababtuna waa in mar dhexdaas ah na dacweeyeen shirkadda Sky UK Limited, markaa si aan mar dambe taasi u dhicin anagu kuma rakibi karno...\n\nDiscord naga soo qabo ama internetka ka baadh. + Digniin: CloudStream mas\'uuliyad kama qaadanayo isticmaalka kordhinnada (extensions) dhinac saddexaad mana bixiyo wax taageero ah oo iyaga ku saabsan! Boodhka Boodhka xalqadda Boodhka weyn @@ -465,4 +465,45 @@ Bilowga Bilow isku qasan Qoraalka dhamaadka + Seasonka %1$d Xalqada %2$d waxaa la soo deyn doonaa + %1$d saac %2$d daq %3$d ilb + %1$ddaq %2$dilb + %1$dilb + Sawirka + Ka bilow bilowga + Waxa soo degaya + Aqoonsiga codka ma shaqeynayo + Bilow hadalka… + biraawsar + Daaro musalsalka oo dhan + Fiidiyowgaan waa Torrent, taasi waxay la macno tahay in la raadraaci karo dhaqdhaqaaqaaga daawashada.\nHubi inaad fahamsan tahay sida Torrent-ku u shaqeeyo ka hor intaadan sii socon. + Dooro waxyaabaha aad tirayso + Ma jiro muuqaal aad soo degsatay. + Hadda ma jiro wax safka ku jira oo soo degaya. + Waxaa laa daawan karaa khad la\'aan + Dooro dhammaan + Ha dooran dhammaan + Daaro muuqaallada taleefanka kuugu jira + Linkska dib ayaa loo cusboonaysiiyay + Magaca Repository-ka iyo URL-ka + Waa la koobiyeeyay! + Ogeysiiska xalqada cusub + Ka raadi extensions-ka kale + Muuji waxyaabaha laguu soo jeediyay + Daaro + Diiwaanka + Wuxuu ku darayaa inaad doorato xawaaraha muuqaalka + Iftiin dheeraad ah + Daar filter-ka iftiinka marka uu iftiinku 100% ka bato + Iftiinka dheeraadka ah waa shidan yahay + Inta jeer ee la kaydinayo + Kaydka + Talooyinka Raadinta + Soo bandhig talooyinka inta aad wax qorayso + Tirtir Talooyinka + Muuji xogta dulsaaran muuqaalka + Soo bandhig guddiga dhibidda + Dooro habka loo kala saarayo soo dejinta plugins-ka + Soo deji nooca hordhaca ah + Nooca hordhaca ah horey ayaa loo soo dejiyay. diff --git a/app/src/main/res/values-b+sq/strings.xml b/app/src/main/res/values-b+sq/strings.xml index 828066ead..d083809bb 100644 --- a/app/src/main/res/values-b+sq/strings.xml +++ b/app/src/main/res/values-b+sq/strings.xml @@ -269,7 +269,7 @@ NSFW Tjera Film - Seri + Serial Vizatimorë Anime OVA diff --git a/app/src/main/res/values-b+sv/strings.xml b/app/src/main/res/values-b+sv/strings.xml index 0740e0b8d..0b35d39a7 100644 --- a/app/src/main/res/values-b+sv/strings.xml +++ b/app/src/main/res/values-b+sv/strings.xml @@ -722,7 +722,7 @@ Inget konto Dra uppåt igen för att gå över 100% Startar plugin uppdateringsprocessen! - %d plugin(s) har uppdaterats! + %d plugin(s) har uppdaterats Inga plugins blev uppdaterade. Spelarnotiser Spelarnotisen för att styra uppspelningen i bakgrunden diff --git a/app/src/main/res/values-b+vi/strings.xml b/app/src/main/res/values-b+vi/strings.xml index 3e2a93fb4..a68d45183 100644 --- a/app/src/main/res/values-b+vi/strings.xml +++ b/app/src/main/res/values-b+vi/strings.xml @@ -660,7 +660,7 @@ Cập nhật tiện ích mở rộng Không có tiện ích mở rộng nào được cập nhật. Ngày phát sóng (Cũ nhất) - Đã cập nhật thành công %d tiện ích mở rộng! + Đã cập nhật thành công %d tiện ích mở rộng Xếp hạng (Cao nhất) Cập nhật tiện ích mở rộng thủ công Tập (Giảm dần) From 8a01c6c9628e256a3e990dea51b3a1f964acf3eb Mon Sep 17 00:00:00 2001 From: ByAyzen <189399597+ByAyzen@users.noreply.github.com> Date: Sat, 29 Aug 2026 01:56:49 +0300 Subject: [PATCH 19/23] Open in browser is now directing immedialetly. (#3137) --- .../cloudstream3/ui/home/HomeFragment.kt | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index d0b4b0f20..c9c9adfc2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -32,6 +32,7 @@ import com.google.android.material.chip.Chip import com.lagradost.api.Log import com.lagradost.cloudstream3.APIHolder import com.lagradost.cloudstream3.APIHolder.apis +import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.MainAPI @@ -67,6 +68,7 @@ import com.lagradost.cloudstream3.utils.AppContextUtils.getApiProviderLangSettin import com.lagradost.cloudstream3.utils.AppContextUtils.isNetworkAvailable import com.lagradost.cloudstream3.utils.AppContextUtils.isRecyclerScrollable import com.lagradost.cloudstream3.utils.AppContextUtils.loadSearchResult +import com.lagradost.cloudstream3.utils.AppContextUtils.openBrowser import com.lagradost.cloudstream3.utils.AppContextUtils.ownHide import com.lagradost.cloudstream3.utils.AppContextUtils.ownShow import com.lagradost.cloudstream3.utils.AppContextUtils.setDefaultFocus @@ -869,26 +871,16 @@ class HomeFragment : BaseFragment( homeRandomButtonTv.isGone = true } } - + //Open browser directly, without a menu. is Resource.Failure -> { homeLoadingShimmer.stopShimmer() homeReloadConnectionerror.setOnClickListener(apiChangeClickListener) - homeReloadConnectionOpenInBrowser.setOnClickListener { view -> - val validAPIs = apis//.filter { api -> api.hasMainPage } - - view.popupMenuNoIconsAndNoStringRes(validAPIs.mapIndexed { index, api -> - Pair( - index, - api.name - ) - }) { - try { - val i = Intent(Intent.ACTION_VIEW) - i.data = validAPIs[itemId].mainUrl.toUri() - startActivity(i) - } catch (e: Exception) { - logError(e) - } + homeReloadConnectionOpenInBrowser.setOnClickListener { + val currentApi = currentApiName?.let { getApiFromNameNull(it) } + ?: homeViewModel.apiName.value?.let { getApiFromNameNull(it) } + val mainUrl = currentApi?.mainUrl + if (!mainUrl.isNullOrBlank()) { + context?.openBrowser(mainUrl) } } From 92b3ea92efe1f5a423fab882ab62cc57e80b1e48 Mon Sep 17 00:00:00 2001 From: Osten <11805592+LagradOst@users.noreply.github.com> Date: Tue, 8 Sep 2026 15:50:26 +0200 Subject: [PATCH 20/23] Compose work (#3151) --- app/build.gradle.kts | 7 + .../lagradost/cloudstream3/CloudStreamApp.kt | 4 +- .../cloudstream3/actions/VideoClickAction.kt | 2 +- .../cloudstream3/network/RequestsHelper.kt | 2 +- .../source_priority/QualityProfileDialog.kt | 7 +- .../cloudstream3/ui/settings/Globals.kt | 32 +- .../ui/settings/SettingsAccount.kt | 19 +- .../ui/settings/SettingsAccount2.kt | 7 + .../ui/settings/SettingsAccountScreen.kt | 143 +++++ .../ui/settings/SettingsGeneral.kt | 178 ++++--- .../ui/settings/SettingsGeneral2.kt | 7 + .../ui/settings/SettingsGeneralScreen.kt | 268 ++++++++++ .../ui/settings/SettingsPlayer2.kt | 7 + .../ui/settings/SettingsPlayerScreen.kt | 353 +++++++++++++ .../ui/settings/SettingsProviders.kt | 3 +- .../ui/settings/SettingsProviders2.kt | 6 + .../ui/settings/SettingsProvidersScreen.kt | 88 ++++ .../cloudstream3/ui/settings/SettingsUI2.kt | 7 + .../ui/settings/SettingsUIScreen.kt | 289 +++++++++++ .../ui/settings/SettingsUpdates2.kt | 7 + .../ui/settings/SettingsUpdatesScreen.kt | 222 ++++++++ .../ui/settings/logcat/LogcatDialog.kt | 258 +++++++++ .../ui/settings/logcat/LogcatParser.kt | 140 +++++ .../cloudstream3/utils/AppContextUtils.kt | 13 +- .../cloudstream3/utils/BackupUtils.kt | 3 +- .../cloudstream3/utils/BaseComposeFragment.kt | 43 ++ .../cloudstream3/utils/PowerManagerAPI.kt | 2 +- .../cloudstream3/utils/videoskip/AnimeSkip.kt | 2 + app/src/main/res/drawable/article_24px.xml | 11 + .../main/res/drawable/aspect_ratio_24px.xml | 10 + app/src/main/res/drawable/autorenew_24px.xml | 10 + .../main/res/drawable/battery_alert_24px.xml | 10 + app/src/main/res/drawable/cast.xml | 10 + app/src/main/res/drawable/colors_24px.xml | 10 + .../main/res/drawable/construction_24px.xml | 10 + app/src/main/res/drawable/copy_all_24px.xml | 10 + app/src/main/res/drawable/dns_24px.xml | 10 + app/src/main/res/drawable/extension_24px.xml | 10 + .../main/res/drawable/extention_download.xml | 14 + app/src/main/res/drawable/extention_renew.xml | 21 + .../main/res/drawable/extention_renew2.xml | 11 + app/src/main/res/drawable/face_24px.xml | 10 + app/src/main/res/drawable/filter_alt_24px.xml | 10 + app/src/main/res/drawable/folder_24px.xml | 10 + app/src/main/res/drawable/hard_drive_24px.xml | 10 + app/src/main/res/drawable/high_res_24px.xml | 10 + .../res/drawable/history_toggle_off_24px.xml | 10 + .../keyboard_double_arrow_right_24px.xml | 11 + app/src/main/res/drawable/label_24px.xml | 11 + .../drawable/language_korean_latin_24px.xml | 10 + app/src/main/res/drawable/light_mode_24px.xml | 10 + app/src/main/res/drawable/match_word_24px.xml | 10 + app/src/main/res/drawable/memory_24px.xml | 10 + app/src/main/res/drawable/memory_alt_24px.xml | 10 + .../res/drawable/mobile_arrow_down_24px.xml | 10 + .../main/res/drawable/mobile_code_24px.xml | 10 + .../main/res/drawable/mobile_wrench_24px.xml | 10 + app/src/main/res/drawable/movie_edit_24px.xml | 10 + app/src/main/res/drawable/movie_info_24px.xml | 10 + .../drawable/notifications_active_24px.xml | 10 + app/src/main/res/drawable/palette_24px.xml | 10 + .../picture_in_picture_center_24px.xml | 10 + app/src/main/res/drawable/pip_24px.xml | 10 + app/src/main/res/drawable/play_arrow_24px.xml | 10 + app/src/main/res/drawable/plugin_lang.xml | 12 + .../main/res/drawable/quick_novel_icon.xml | 50 +- .../res/drawable/responsive_layout_24px.xml | 10 + .../main/res/drawable/restore_page_24px.xml | 10 + app/src/main/res/drawable/save_as_24px.xml | 10 + app/src/main/res/drawable/save_clock_24px.xml | 10 + .../res/drawable/screen_rotation_alt_24px.xml | 10 + app/src/main/res/drawable/shuffle_24px.xml | 10 + app/src/main/res/drawable/skip_next_24px.xml | 10 + .../main/res/drawable/subtitles_gear_24px.xml | 10 + app/src/main/res/drawable/swipe_24px.xml | 10 + .../main/res/drawable/swipe_vertical_24px.xml | 10 + app/src/main/res/drawable/tooltip_24px.xml | 10 + .../main/res/drawable/touch_double_24px.xml | 10 + .../main/res/drawable/visibility_off_24px.xml | 10 + app/src/main/res/drawable/wifi_proxy_24px.xml | 10 + .../main/res/navigation/mobile_navigation.xml | 12 +- .../res/values/donottranslate-strings.xml | 10 +- app/src/main/res/values/strings.xml | 5 +- build.gradle.kts | 2 + desktopApp/build.gradle.kts | 55 ++ desktopApp/src/desktop-icons/icon-linux.png | Bin 0 -> 8334 bytes desktopApp/src/desktop-icons/icon-windows.ico | Bin 0 -> 8868 bytes .../kotlin/com/lagradost/cloudstream4/Main.kt | 59 +++ desktopApp/src/readme.md | 1 + gradle/libs.versions.toml | 27 +- settings.gradle.kts | 2 +- shared/build.gradle.kts | 55 ++ shared/readme.md | 112 ++++ .../DevicePreferenceStore.android.kt | 19 + .../cloudstream4/compose/BackHandler.kt | 42 ++ .../cloudstream4/compose/ComposeFragment.kt | 51 ++ .../lagradost/cloudstream4/compose/Layout.kt | 48 ++ .../cloudstream4/theme/ColorPreference.kt | 72 +++ .../cloudstream4/theme/DeviceTheme.android.kt | 73 +++ .../common/preference/AndroidPreference.kt | 235 +++++++++ .../preference/AndroidPreferenceStore.kt | 102 ++++ .../composeResources/drawable/arrow_back.xml | 10 + .../drawable/arrow_downward.xml | 9 + .../drawable/arrow_upward.xml | 9 + .../composeResources/drawable/cancel.xml | 9 + .../composeResources/drawable/check.xml | 9 + .../composeResources/drawable/close.xml | 9 + .../drawable/default_icon.png | Bin 0 -> 8334 bytes .../composeResources/drawable/error.xml | 9 + .../composeResources/drawable/info.xml | 9 + .../composeResources/drawable/preview.xml | 9 + .../font/productsans_black.ttf | Bin 0 -> 91660 bytes .../font/productsans_blackitalic.ttf | Bin 0 -> 95404 bytes .../font/productsans_bold.ttf | Bin 0 -> 92096 bytes .../font/productsans_bolditalic.ttf | Bin 0 -> 95504 bytes .../font/productsans_italic.ttf | Bin 0 -> 95744 bytes .../font/productsans_light.ttf | Bin 0 -> 92564 bytes .../font/productsans_lightitalic.ttf | Bin 0 -> 96000 bytes .../font/productsans_medium.ttf | Bin 0 -> 92312 bytes .../font/productsans_mediumitalic.ttf | Bin 0 -> 95656 bytes .../font/productsans_regular.ttf | Bin 0 -> 109128 bytes .../font/productsans_thin.ttf | Bin 0 -> 92836 bytes .../font/productsans_thinitalic.ttf | Bin 0 -> 96540 bytes .../composeResources/values/strings.xml | 9 + .../cloudstream4/DevicePreferenceStore.kt | 202 +++++++ .../lagradost/cloudstream4/compose/Colors.kt | 24 + .../lagradost/cloudstream4/compose/Dialog.kt | 491 ++++++++++++++++++ .../cloudstream4/compose/Modifiers.kt | 63 +++ .../lagradost/cloudstream4/compose/Screen.kt | 8 + .../cloudstream4/compose/Viewmodel.kt | 145 ++++++ .../cloudstream4/theme/DeviceTheme.kt | 17 + .../com/lagradost/cloudstream4/theme/Font.kt | 71 +++ .../lagradost/cloudstream4/theme/Palette.kt | 84 +++ .../lagradost/cloudstream4/theme/Scheme.kt | 162 ++++++ .../com/lagradost/cloudstream4/theme/Theme.kt | 132 +++++ .../src/commonMain/kotlin/com/mihon/LICENSE | 176 +++++++ .../preference/InMemoryPreferenceStore.kt | 125 +++++ .../mihon/common/preference/PreferenceData.kt | 71 +++ .../common/preference/PreferenceStore.kt | 87 ++++ .../kotlin/com/mihon/material/AppBar.kt | 403 ++++++++++++++ .../kotlin/com/mihon/material/Constants.kt | 26 + .../com/mihon/material/LabeledCheckbox.kt | 50 ++ .../kotlin/com/mihon/material/Pill.kt | 61 +++ .../kotlin/com/mihon/material/Scaffold.kt | 85 +++ .../com/mihon/material/SettingsItems.kt | 474 +++++++++++++++++ .../kotlin/com/mihon/material/Slider.kt | 58 +++ .../kotlin/com/mihon/material/Typography.kt | 14 + .../kotlin/com/mihon/presentation/Modifier.kt | 120 +++++ .../com/mihon/presentation/Navigation.kt | 9 + .../mihon/presentation/settings/Preference.kt | 198 +++++++ .../presentation/settings/PreferenceItem.kt | 188 +++++++ .../settings/PreferenceScaffold.kt | 46 ++ .../presentation/settings/PreferenceScreen.kt | 96 ++++ .../settings/SearchableSettings.kt | 39 ++ .../settings/widget/BasePreferenceWidget.kt | 127 +++++ .../widget/EditTextPreferenceWidget.kt | 97 ++++ .../settings/widget/InfoWidget.kt | 53 ++ .../settings/widget/ListPreferenceWidget.kt | 110 ++++ .../widget/MultiSelectListPreferenceWidget.kt | 97 ++++ .../settings/widget/PreferenceGroupHeader.kt | 28 + .../settings/widget/SwitchPreferenceWidget.kt | 73 +++ .../settings/widget/TextPreferenceWidget.kt | 86 +++ .../src/commonMain/kotlin/com/mihon/readme.md | 4 + .../cloudstream4/DevicePreferenceStore.jvm.kt | 16 + .../cloudstream4/theme/DeviceTheme.jvm.kt | 19 + shared/src/readme.md | 11 + 166 files changed, 8041 insertions(+), 178 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount2.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccountScreen.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral2.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneralScreen.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer2.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayerScreen.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders2.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProvidersScreen.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI2.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates2.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdatesScreen.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatParser.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/BaseComposeFragment.kt create mode 100644 app/src/main/res/drawable/article_24px.xml create mode 100644 app/src/main/res/drawable/aspect_ratio_24px.xml create mode 100644 app/src/main/res/drawable/autorenew_24px.xml create mode 100644 app/src/main/res/drawable/battery_alert_24px.xml create mode 100644 app/src/main/res/drawable/cast.xml create mode 100644 app/src/main/res/drawable/colors_24px.xml create mode 100644 app/src/main/res/drawable/construction_24px.xml create mode 100644 app/src/main/res/drawable/copy_all_24px.xml create mode 100644 app/src/main/res/drawable/dns_24px.xml create mode 100644 app/src/main/res/drawable/extension_24px.xml create mode 100644 app/src/main/res/drawable/extention_download.xml create mode 100644 app/src/main/res/drawable/extention_renew.xml create mode 100644 app/src/main/res/drawable/extention_renew2.xml create mode 100644 app/src/main/res/drawable/face_24px.xml create mode 100644 app/src/main/res/drawable/filter_alt_24px.xml create mode 100644 app/src/main/res/drawable/folder_24px.xml create mode 100644 app/src/main/res/drawable/hard_drive_24px.xml create mode 100644 app/src/main/res/drawable/high_res_24px.xml create mode 100644 app/src/main/res/drawable/history_toggle_off_24px.xml create mode 100644 app/src/main/res/drawable/keyboard_double_arrow_right_24px.xml create mode 100644 app/src/main/res/drawable/label_24px.xml create mode 100644 app/src/main/res/drawable/language_korean_latin_24px.xml create mode 100644 app/src/main/res/drawable/light_mode_24px.xml create mode 100644 app/src/main/res/drawable/match_word_24px.xml create mode 100644 app/src/main/res/drawable/memory_24px.xml create mode 100644 app/src/main/res/drawable/memory_alt_24px.xml create mode 100644 app/src/main/res/drawable/mobile_arrow_down_24px.xml create mode 100644 app/src/main/res/drawable/mobile_code_24px.xml create mode 100644 app/src/main/res/drawable/mobile_wrench_24px.xml create mode 100644 app/src/main/res/drawable/movie_edit_24px.xml create mode 100644 app/src/main/res/drawable/movie_info_24px.xml create mode 100644 app/src/main/res/drawable/notifications_active_24px.xml create mode 100644 app/src/main/res/drawable/palette_24px.xml create mode 100644 app/src/main/res/drawable/picture_in_picture_center_24px.xml create mode 100644 app/src/main/res/drawable/pip_24px.xml create mode 100644 app/src/main/res/drawable/play_arrow_24px.xml create mode 100644 app/src/main/res/drawable/plugin_lang.xml create mode 100644 app/src/main/res/drawable/responsive_layout_24px.xml create mode 100644 app/src/main/res/drawable/restore_page_24px.xml create mode 100644 app/src/main/res/drawable/save_as_24px.xml create mode 100644 app/src/main/res/drawable/save_clock_24px.xml create mode 100644 app/src/main/res/drawable/screen_rotation_alt_24px.xml create mode 100644 app/src/main/res/drawable/shuffle_24px.xml create mode 100644 app/src/main/res/drawable/skip_next_24px.xml create mode 100644 app/src/main/res/drawable/subtitles_gear_24px.xml create mode 100644 app/src/main/res/drawable/swipe_24px.xml create mode 100644 app/src/main/res/drawable/swipe_vertical_24px.xml create mode 100644 app/src/main/res/drawable/tooltip_24px.xml create mode 100644 app/src/main/res/drawable/touch_double_24px.xml create mode 100644 app/src/main/res/drawable/visibility_off_24px.xml create mode 100644 app/src/main/res/drawable/wifi_proxy_24px.xml create mode 100644 desktopApp/build.gradle.kts create mode 100644 desktopApp/src/desktop-icons/icon-linux.png create mode 100644 desktopApp/src/desktop-icons/icon-windows.ico create mode 100644 desktopApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/Main.kt create mode 100644 desktopApp/src/readme.md create mode 100644 shared/build.gradle.kts create mode 100644 shared/readme.md create mode 100644 shared/src/androidMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.android.kt create mode 100644 shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/BackHandler.kt create mode 100644 shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/ComposeFragment.kt create mode 100644 shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/Layout.kt create mode 100644 shared/src/androidMain/kotlin/com/lagradost/cloudstream4/theme/ColorPreference.kt create mode 100644 shared/src/androidMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.android.kt create mode 100644 shared/src/androidMain/kotlin/com/mihon/common/preference/AndroidPreference.kt create mode 100644 shared/src/androidMain/kotlin/com/mihon/common/preference/AndroidPreferenceStore.kt create mode 100644 shared/src/commonMain/composeResources/drawable/arrow_back.xml create mode 100644 shared/src/commonMain/composeResources/drawable/arrow_downward.xml create mode 100644 shared/src/commonMain/composeResources/drawable/arrow_upward.xml create mode 100644 shared/src/commonMain/composeResources/drawable/cancel.xml create mode 100644 shared/src/commonMain/composeResources/drawable/check.xml create mode 100644 shared/src/commonMain/composeResources/drawable/close.xml create mode 100644 shared/src/commonMain/composeResources/drawable/default_icon.png create mode 100644 shared/src/commonMain/composeResources/drawable/error.xml create mode 100644 shared/src/commonMain/composeResources/drawable/info.xml create mode 100644 shared/src/commonMain/composeResources/drawable/preview.xml create mode 100644 shared/src/commonMain/composeResources/font/productsans_black.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_blackitalic.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_bold.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_bolditalic.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_italic.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_light.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_lightitalic.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_medium.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_mediumitalic.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_regular.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_thin.ttf create mode 100644 shared/src/commonMain/composeResources/font/productsans_thinitalic.ttf create mode 100644 shared/src/commonMain/composeResources/values/strings.xml create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Modifiers.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Screen.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Viewmodel.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Font.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Palette.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Scheme.kt create mode 100644 shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Theme.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/LICENSE create mode 100644 shared/src/commonMain/kotlin/com/mihon/common/preference/InMemoryPreferenceStore.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/common/preference/PreferenceData.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/common/preference/PreferenceStore.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/material/AppBar.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/material/Constants.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/material/LabeledCheckbox.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/material/Pill.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/material/Scaffold.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/material/SettingsItems.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/material/Slider.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/material/Typography.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/Modifier.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/Navigation.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/Preference.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceItem.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScaffold.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScreen.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/SearchableSettings.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/BasePreferenceWidget.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/EditTextPreferenceWidget.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/InfoWidget.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/ListPreferenceWidget.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/MultiSelectListPreferenceWidget.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/PreferenceGroupHeader.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/SwitchPreferenceWidget.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/TextPreferenceWidget.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/readme.md create mode 100644 shared/src/jvmMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.jvm.kt create mode 100644 shared/src/jvmMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.jvm.kt create mode 100644 shared/src/readme.md diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2ad8451a3..c714368ee 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,6 +9,8 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.dokka) alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.compose.multiplatform) + alias(libs.plugins.compose.compiler) } val javaTarget = JvmTarget.fromTarget(libs.versions.jvmTarget.get()) @@ -288,7 +290,12 @@ dependencies { implementation(libs.work.runtime.ktx) implementation(libs.nicehttp) // HTTP Lib + implementation(libs.bundles.compose) + implementation(libs.activity.compose) + implementation(libs.kotlinx.io.core) // Logcat parser + implementation(project(":library")) + implementation(project(":shared")) } tasks.register("androidSourcesJar") { diff --git a/app/src/main/java/com/lagradost/cloudstream3/CloudStreamApp.kt b/app/src/main/java/com/lagradost/cloudstream3/CloudStreamApp.kt index 466ef5a00..6ff3ab54d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CloudStreamApp.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CloudStreamApp.kt @@ -170,11 +170,11 @@ class CloudStreamApp : Application(), SingletonImageLoader.Factory { } /** Will fall back to WebView if in TV or emulator layout. */ - fun openBrowser(url: String, activity: FragmentActivity?) { + fun openBrowser(url: String, activity: Activity?) { openBrowser( url, isLayout(TV or EMULATOR), - activity?.supportFragmentManager?.fragments?.lastOrNull() + (activity as? FragmentActivity)?.supportFragmentManager?.fragments?.lastOrNull() ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt index f4e8768d8..0fdccb476 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt @@ -98,7 +98,7 @@ object VideoClickActionHolder { ?.second } - fun getPlayers(activity: Activity? = null) = allVideoClickActions.filter { it.isPlayer && it.shouldShowSafe(activity, null) } + fun getPlayers(context: Context? = null) = allVideoClickActions.filter { it.isPlayer && it.shouldShowSafe(context, null) } } abstract class VideoClickAction { diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt index 203a503e9..e49245904 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt @@ -37,7 +37,7 @@ fun buildDefaultClient(context: Context, ignoreSSL: Boolean = false): OkHttpClie safe { Security.insertProviderAt(Conscrypt.newProvider(), 1) } val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) - val dns = settingsManager.getInt(context.getString(R.string.dns_pref), 0) + val dns = settingsManager.getInt(context.getString(R.string.dns_key), 0) val baseClient = OkHttpClient.Builder() .followRedirects(true) .followSslRedirects(true) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityProfileDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityProfileDialog.kt index 6a0f12e9a..e1b8ad479 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityProfileDialog.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityProfileDialog.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.ui.player.source_priority +import android.app.Activity import android.app.Dialog import androidx.annotation.StyleRes import androidx.core.view.isVisible @@ -26,7 +27,7 @@ data class LinkSource( class QualityProfileDialog private constructor( - val activity: FragmentActivity, + val activity: Activity, @StyleRes val themeRes: Int, private val links: List, private val usedProfile: Int?, @@ -34,7 +35,7 @@ class QualityProfileDialog private constructor( private val useProfileSelection: Boolean ) : Dialog(activity, themeRes) { constructor( - activity: FragmentActivity, + activity: Activity, @StyleRes themeRes: Int, links: List, usedProfile: Int, @@ -42,7 +43,7 @@ class QualityProfileDialog private constructor( ) : this(activity, themeRes, links, usedProfile, profileSelectionCallback, true) constructor( - activity: FragmentActivity, + activity: Activity, @StyleRes themeRes: Int, links: List ) : this(activity, themeRes, links, null, null, false) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/Globals.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/Globals.kt index 93e469a4d..70fd2a19d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/Globals.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/Globals.kt @@ -1,19 +1,18 @@ package com.lagradost.cloudstream3.ui.settings -import android.app.UiModeManager import android.content.Context import android.content.res.Configuration import android.content.res.Resources -import android.os.Build import androidx.preference.PreferenceManager import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream4.compose.DeviceLayout.Companion.isAutoTv object Globals { var beneneCount = 0 - const val PHONE : Int = 0b001 - const val TV : Int = 0b010 - const val EMULATOR : Int = 0b100 + const val PHONE: Int = 0b00001 + const val TV: Int = 0b00010 + const val EMULATOR: Int = 0b00100 private const val INVALID = -1 private var layoutId = INVALID @@ -22,18 +21,9 @@ object Globals { return settingsManager.getInt(this.getString(R.string.app_layout_key), -1) } - private fun Context.isAutoTv(): Boolean { - val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager? - // AFT = Fire TV - val model = Build.MODEL.lowercase() - return uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION || Build.MODEL.contains( - "AFT" - ) || model.contains("firestick") || model.contains("fire tv") || model.contains("chromecast") - } - - private fun Context.layoutIntCorrected(): Int { - return when(getLayoutInt()) { - -1 -> if (isAutoTv()) TV else PHONE + fun Context.updateTv() { + layoutId = when (getLayoutInt()) { + -1 -> if (isAutoTv(this)) TV else PHONE 0 -> PHONE 1 -> TV 2 -> EMULATOR @@ -41,14 +31,10 @@ object Globals { } } - fun Context.updateTv() { - layoutId = layoutIntCorrected() - } - /** Returns true if the current orientation is landscape. */ fun isLandscape(): Boolean = isLayout(TV or EMULATOR) || - Resources.getSystem().configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + Resources.getSystem().configuration.orientation == Configuration.ORIENTATION_LANDSCAPE /** Returns true if the layout is any of the flags, * so isLayout(TV or EMULATOR) is a valid statement for checking if the layout is in the emulator @@ -56,7 +42,7 @@ object Globals { * * Valid flags are: PHONE, TV, EMULATOR * */ - fun isLayout(flags: Int) : Boolean { + fun isLayout(flags: Int): Boolean { return (layoutId and flags) != 0 } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt index 75ba55674..6f67ab826 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.ui.settings import android.annotation.SuppressLint +import android.app.Activity import android.graphics.Bitmap import android.os.Bundle import android.os.CountDownTimer @@ -13,7 +14,6 @@ import androidx.appcompat.app.AlertDialog import androidx.core.content.edit import androidx.core.view.isGone import androidx.core.view.isVisible -import androidx.fragment.app.FragmentActivity import androidx.preference.PreferenceManager import androidx.preference.SwitchPreference import androidx.recyclerview.widget.RecyclerView @@ -29,8 +29,8 @@ import com.lagradost.cloudstream3.databinding.DeviceAuthBinding import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.animeSkipApi -import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.kitsuApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSubtitlesApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subDlApi @@ -42,8 +42,8 @@ import com.lagradost.cloudstream3.syncproviders.SubtitleRepo import com.lagradost.cloudstream3.syncproviders.SyncRepo import com.lagradost.cloudstream3.ui.BasePreferenceFragmentCompat import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR -import com.lagradost.cloudstream3.ui.settings.Globals.TV import com.lagradost.cloudstream3.ui.settings.Globals.PHONE +import com.lagradost.cloudstream3.ui.settings.Globals.TV import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.hideOn @@ -76,7 +76,7 @@ class SettingsAccount : BasePreferenceFragmentCompat(), BiometricCallback { /** Used by nginx plugin too */ @SuppressLint("StringFormatInvalid") fun showLoginInfo( - activity: FragmentActivity?, + activity: Activity?, api: AuthRepo, info: AuthUser?, index: Int, @@ -121,7 +121,7 @@ class SettingsAccount : BasePreferenceFragmentCompat(), BiometricCallback { } } - private fun showAccountSwitch(activity: FragmentActivity, api: AuthRepo) { + private fun showAccountSwitch(activity: Activity, api: AuthRepo) { val accounts = api.accounts val binding: AccountSwitchBinding = AccountSwitchBinding.inflate(activity.layoutInflater, null, false) @@ -153,7 +153,7 @@ class SettingsAccount : BasePreferenceFragmentCompat(), BiometricCallback { @UiThread - fun showPin(activity: FragmentActivity, api: AuthRepo) { + fun showPin(activity: Activity, api: AuthRepo) { val binding: DeviceAuthBinding = DeviceAuthBinding.inflate(activity.layoutInflater, null, false) @@ -273,8 +273,7 @@ class SettingsAccount : BasePreferenceFragmentCompat(), BiometricCallback { } - fun showAppLogin(activity: FragmentActivity, api: AuthRepo) { - + fun showAppLogin(activity: Activity, api: AuthRepo) { val binding: AddAccountInputBinding = AddAccountInputBinding.inflate(activity.layoutInflater, null, false) val builder = @@ -315,7 +314,7 @@ class SettingsAccount : BasePreferenceFragmentCompat(), BiometricCallback { binding.createAccount.setOnClickListener { openBrowser( api.createAccountUrl ?: return@setOnClickListener, - activity + activity, ) dialog.dismissSafe() } @@ -391,7 +390,7 @@ class SettingsAccount : BasePreferenceFragmentCompat(), BiometricCallback { } @UiThread - fun addAccount(activity: FragmentActivity, api: AuthRepo) { + fun addAccount(activity: Activity, api: AuthRepo) { try { if (api.hasPin && !isLayout(PHONE)) { showPin(activity, api) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount2.kt new file mode 100644 index 000000000..0185370ce --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount2.kt @@ -0,0 +1,7 @@ +package com.lagradost.cloudstream3.ui.settings + +import com.lagradost.cloudstream3.utils.BaseComposeFragment +import com.mihon.presentation.settings.SearchableSettings + +/** Empty glue code to connect the old navigation graph to Compose */ +class SettingsAccount2 : BaseComposeFragment(), SearchableSettings by SettingsAccountScreen \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccountScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccountScreen.kt new file mode 100644 index 000000000..47dac104d --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccountScreen.kt @@ -0,0 +1,143 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.content.Context +import androidx.activity.compose.LocalActivity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.fragment.app.FragmentActivity +import com.lagradost.cloudstream3.CommonActivity.activity +import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.animeSkipApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.kitsuApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSubtitlesApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subDlApi +import com.lagradost.cloudstream3.syncproviders.PlainAuthRepo +import com.lagradost.cloudstream3.syncproviders.SubtitleRepo +import com.lagradost.cloudstream3.syncproviders.SyncRepo +import com.lagradost.cloudstream3.ui.settings.SettingsAccount.Companion.addAccount +import com.lagradost.cloudstream3.ui.settings.SettingsAccount.Companion.showLoginInfo +import com.lagradost.cloudstream3.utils.AppContextUtils.html +import com.lagradost.cloudstream3.utils.BackupUtils +import com.lagradost.cloudstream3.utils.BiometricAuthenticator +import com.lagradost.cloudstream3.utils.BiometricAuthenticator.authCallback +import com.lagradost.cloudstream3.utils.BiometricAuthenticator.biometricPrompt +import com.lagradost.cloudstream3.utils.BiometricAuthenticator.deviceHasPasswordPinLock +import com.lagradost.cloudstream3.utils.BiometricAuthenticator.isAuthEnabled +import com.lagradost.cloudstream3.utils.BiometricAuthenticator.promptInfo +import com.lagradost.cloudstream3.utils.BiometricAuthenticator.startBiometricAuthentication +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogText +import com.lagradost.cloudstream4.AppSettings +import com.lagradost.cloudstream4.compose.PHONE +import com.lagradost.cloudstream4.compose.isLayout +import com.lagradost.cloudstream4.rememberAppSettings +import com.mihon.presentation.settings.Preference +import com.mihon.presentation.settings.SearchableSettings +import kotlinx.collections.immutable.persistentListOf + +object SettingsAccountScreen : SearchableSettings, BiometricAuthenticator.BiometricCallback { + val syncApis = persistentListOf( + SyncRepo(malApi), + SyncRepo(kitsuApi), + SyncRepo(aniListApi), + SyncRepo(simklApi), + SubtitleRepo(openSubtitlesApi), + SubtitleRepo(subDlApi), + PlainAuthRepo(animeSkipApi), + ) + private fun updateAuthPreference(context: Context, enabled: Boolean) { + val settings = AppSettings(context) + settings.security.biometrics.set(enabled) + } + + override fun onAuthenticationError() { + val context = activity ?: return + updateAuthPreference(context, !isAuthEnabled(context)) + } + + override fun onAuthenticationSuccess() { + val context = activity ?: return + + if (isAuthEnabled(context)) { + updateAuthPreference(context, true) + BackupUtils.backup(context) + context.showBottomDialogText( + context.getString(R.string.biometric_setting), + context.getString(R.string.biometric_warning).html() + ) { onDialogDismissedEvent } + } else { + updateAuthPreference(context, false) + } + } + + @Composable + override fun getTitleRes(): String = stringResource(R.string.category_account) + + @Composable + override fun getPreferences(): List { + val settings = rememberAppSettings() + val activity = LocalActivity.current + val context = LocalContext.current + val hasSecurity = remember(context) { + deviceHasPasswordPinLock(context) + } + + return persistentListOf( + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_accounts), + preferenceItems = syncApis.map { api -> + Preference.PreferenceItem.TextPreference( + title = api.name, + icon = api.icon?.let { painterResource(it) }, + onClick = { + val activity = activity ?: return@TextPreference + val info = api.authUser() + val index = + api.accounts.indexOfFirst { account -> account.user.id == info?.id } + if (api.accounts.isNotEmpty()) { + showLoginInfo(activity, api, info, index) + } else { + addAccount(activity, api) + } + }) + } + Preference.PreferenceItem.SwitchPreference( + preference = settings.security.skipAccountSelection, + title = stringResource(R.string.skip_startup_account_select_pref), + icon = painterResource(R.drawable.ic_outline_account_circle_24) + ), + ), Preference.PreferenceGroup( + enabled = hasSecurity && isLayout(PHONE), + title = stringResource(R.string.pref_category_security), + preferenceItems = persistentListOf( + Preference.PreferenceItem.SwitchPreference( + preference = settings.security.biometrics, + title = stringResource(R.string.biometric_setting), + subtitle = stringResource(R.string.biometric_setting_summary), + icon = painterResource(R.drawable.ic_fingerprint), + onValueChanged = { _ -> + val activity = + activity as? FragmentActivity ?: return@SwitchPreference false + + if (deviceHasPasswordPinLock(activity)) { + startBiometricAuthentication( + activity, R.string.biometric_authentication_title, false + ) + promptInfo?.let { + authCallback = this + biometricPrompt?.authenticate(it) + } + } + + return@SwitchPreference true + }) + ) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt index b04f9a4a2..91d729b91 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt @@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.settings import android.content.Context import android.net.Uri import android.os.Bundle +import android.view.LayoutInflater import android.view.View import android.widget.Toast import androidx.appcompat.app.AlertDialog @@ -17,6 +18,7 @@ import com.lagradost.cloudstream3.CloudStreamApp import com.lagradost.cloudstream3.CloudStreamApp.Companion.getKey import com.lagradost.cloudstream3.CloudStreamApp.Companion.setKey import com.lagradost.cloudstream3.CommonActivity +import com.lagradost.cloudstream3.CommonActivity.activity import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.R @@ -175,6 +177,96 @@ class SettingsGeneral : BasePreferenceFragmentCompat() { putString(context.getString(R.string.download_path_key_visual), visual) } } + + fun getCurrent(): MutableList { + return getKey>(USER_PROVIDER_API)?.toMutableList() + ?: mutableListOf() + } + fun showAdd() { + val providers = allProviders.distinctBy { it::class }.sortedBy { it.name } + val context = activity + context?.showDialog( + providers.map { "${it.name} (${it.mainUrl})" }, + -1, + context.getString(R.string.add_site_pref), + true, + {}) { selection -> + val provider = providers.getOrNull(selection) ?: return@showDialog + + val binding : AddSiteInputBinding = AddSiteInputBinding.inflate(LayoutInflater.from( + context + ),null,false) + + val builder = + AlertDialog.Builder(context, R.style.AlertDialogCustom) + .setView(binding.root) + + val dialog = builder.create() + dialog.show() + + binding.text2.text = provider.name + binding.applyBtt.setOnClickListener { + val name = binding.siteNameInput.text?.toString() + val url = binding.siteUrlInput.text?.toString() + val lang = binding.siteLangInput.text?.toString() + val realLang = if (lang.isNullOrBlank()) provider.lang else lang + val simpleName = provider::class.simpleName + if (url.isNullOrBlank() || name.isNullOrBlank() || simpleName == null) { + showToast(R.string.error_invalid_data, Toast.LENGTH_SHORT) + return@setOnClickListener + } + + val current = getCurrent() + val newSite = CustomSite(simpleName, name, url, realLang) + current.add(newSite) + setKey(USER_PROVIDER_API, current.toTypedArray()) + // reload apis + MainActivity.afterPluginsLoadedEvent.invoke(false) + + dialog.dismissSafe(activity) + } + binding.cancelBtt.setOnClickListener { + dialog.dismissSafe(activity) + } + } + } + + fun showDelete() { + val current = getCurrent() + val context = activity + context?.showMultiDialog( + current.map { it.name }, + listOf(), + context.getString(R.string.remove_site_pref), + {}) { indexes -> + current.removeAll(indexes.map { current[it] }) + setKey(USER_PROVIDER_API, current.toTypedArray()) + } + } + + fun showAddOrDelete() { + val context = activity + + val binding : AddRemoveSitesBinding = AddRemoveSitesBinding.inflate( + LayoutInflater.from( + context + ),null,false) + val builder = + AlertDialog.Builder(context ?: return, R.style.AlertDialogCustom) + .setView(binding.root) + + val dialog = builder.create() + dialog.show() + + binding.addSite.setOnClickListener { + showAdd() + dialog.dismissSafe(activity) + } + binding.removeSite.setOnClickListener { + showDelete() + dialog.dismissSafe(activity) + } + } } private val pathPicker = getChooseFolderLauncher { uri, path -> @@ -186,10 +278,6 @@ class SettingsGeneral : BasePreferenceFragmentCompat() { setPreferencesFromResource(R.xml.settings_general, rootKey) val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) - fun getCurrent(): MutableList { - return getKey>(USER_PROVIDER_API)?.toMutableList() - ?: mutableListOf() - } getPref(R.string.locale_key)?.setOnPreferenceClickListener { pref -> val current = getCurrentLocale(pref.context) @@ -226,83 +314,7 @@ class SettingsGeneral : BasePreferenceFragmentCompat() { true } - fun showAdd() { - val providers = allProviders.distinctBy { it::class }.sortedBy { it.name } - activity?.showDialog( - providers.map { "${it.name} (${it.mainUrl})" }, - -1, - context?.getString(R.string.add_site_pref) ?: return, - true, - {}) { selection -> - val provider = providers.getOrNull(selection) ?: return@showDialog - val binding : AddSiteInputBinding = AddSiteInputBinding.inflate(layoutInflater,null,false) - - val builder = - AlertDialog.Builder(context ?: return@showDialog, R.style.AlertDialogCustom) - .setView(binding.root) - - val dialog = builder.create() - dialog.show() - - binding.text2.text = provider.name - binding.applyBtt.setOnClickListener { - val name = binding.siteNameInput.text?.toString() - val url = binding.siteUrlInput.text?.toString() - val lang = binding.siteLangInput.text?.toString() - val realLang = if (lang.isNullOrBlank()) provider.lang else lang - val simpleName = provider::class.simpleName - if (url.isNullOrBlank() || name.isNullOrBlank() || simpleName == null) { - showToast(R.string.error_invalid_data, Toast.LENGTH_SHORT) - return@setOnClickListener - } - - val current = getCurrent() - val newSite = CustomSite(simpleName, name, url, realLang) - current.add(newSite) - setKey(USER_PROVIDER_API, current.toTypedArray()) - // reload apis - MainActivity.afterPluginsLoadedEvent.invoke(false) - - dialog.dismissSafe(activity) - } - binding.cancelBtt.setOnClickListener { - dialog.dismissSafe(activity) - } - } - } - - fun showDelete() { - val current = getCurrent() - - activity?.showMultiDialog( - current.map { it.name }, - listOf(), - context?.getString(R.string.remove_site_pref) ?: return, - {}) { indexes -> - current.removeAll(indexes.map { current[it] }) - setKey(USER_PROVIDER_API, current.toTypedArray()) - } - } - - fun showAddOrDelete() { - val binding : AddRemoveSitesBinding = AddRemoveSitesBinding.inflate(layoutInflater,null,false) - val builder = - AlertDialog.Builder(context ?: return, R.style.AlertDialogCustom) - .setView(binding.root) - - val dialog = builder.create() - dialog.show() - - binding.addSite.setOnClickListener { - showAdd() - dialog.dismissSafe(activity) - } - binding.removeSite.setOnClickListener { - showDelete() - dialog.dismissSafe(activity) - } - } getPref(R.string.override_site_key)?.setOnPreferenceClickListener { _ -> @@ -329,7 +341,7 @@ class SettingsGeneral : BasePreferenceFragmentCompat() { val prefValues = resources.getIntArray(R.array.dns_pref_values) val currentDns = - settingsManager.getInt(getString(R.string.dns_pref), 0) + settingsManager.getInt(getString(R.string.dns_key), 0) activity?.showBottomDialog( prefNames.toList(), @@ -337,7 +349,7 @@ class SettingsGeneral : BasePreferenceFragmentCompat() { getString(R.string.dns_pref), true, {}) { - settingsManager.edit { putInt(getString(R.string.dns_pref), prefValues[it]) } + settingsManager.edit { putInt(getString(R.string.dns_key), prefValues[it]) } (context ?: CloudStreamApp.context)?.let { ctx -> app.initClient(ctx) } } return@setOnPreferenceClickListener true @@ -355,7 +367,7 @@ class SettingsGeneral : BasePreferenceFragmentCompat() { (first + ctx.getExternalFilesDirs("").mapNotNull { it.path } + currentDir) - } catch (e: Exception) { + } catch (_: Exception) { first }).filterNotNull().distinct() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral2.kt new file mode 100644 index 000000000..37b1812b9 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral2.kt @@ -0,0 +1,7 @@ +package com.lagradost.cloudstream3.ui.settings + +import com.lagradost.cloudstream3.utils.BaseComposeFragment +import com.mihon.presentation.settings.SearchableSettings + +/** Empty glue code to connect the old navigation graph to Compose */ +class SettingsGeneral2 : BaseComposeFragment(), SearchableSettings by SettingsGeneralScreen \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneralScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneralScreen.kt new file mode 100644 index 000000000..7ce49e8e9 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneralScreen.kt @@ -0,0 +1,268 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.content.Intent +import android.net.Uri +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.integerArrayResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringArrayResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewLightDark +import com.lagradost.cloudstream3.CloudStreamApp +import com.lagradost.cloudstream3.CommonActivity.activity +import com.lagradost.cloudstream3.CommonActivity.showToast +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.UnsafeSSL +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.insecureApp +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.network.initClient +import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.isAppRestricted +import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.showRequestIgnoreBatteryOptDialog +import com.lagradost.cloudstream4.AppSettings +import com.lagradost.cloudstream4.compose.ActionDialog +import com.lagradost.cloudstream4.compose.PHONE +import com.lagradost.cloudstream4.compose.isLayout +import com.lagradost.cloudstream4.rememberAppSettings +import com.lagradost.cloudstream4.theme.CloudStreamPreviewTheme +import com.lagradost.safefile.SafeFile +import com.mihon.presentation.settings.Preference +import com.mihon.presentation.settings.SearchableSettings +import com.mihon.presentation.settings.collectAsState +import kotlinx.collections.immutable.persistentListOf + +object SettingsGeneralScreen : SearchableSettings { + @Composable + override fun getTitleRes(): String = stringResource(R.string.category_general) + + @Composable + override fun getPreferences(): List { + val settings = rememberAppSettings() + + // TODO Refactor entirely to use a different file path selector ect like QuickNovel + val selectFileSelector = + rememberLauncherForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> + // It lies, it can be null if file manager quits. + if (uri == null) return@rememberLauncherForActivityResult + val context = CloudStreamApp.context ?: return@rememberLauncherForActivityResult + + try { + val settings = AppSettings(context) + // RW perms for the path + val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + + context.contentResolver.takePersistableUriPermission(uri, flags) + + val filePath = SafeFile.fromUri(context, uri)?.filePath() + println("Selected URI path: $uri - Full path: $filePath") + + // store the actual URI instead of the path due to permissions. + // filePath should only be used for cosmetic purposes. + val visual = filePath ?: uri.toString() + settings.general.downloadPath.set(uri.toString()) + settings.general.downloadPathVisual.set(visual) + } catch (t: Throwable) { + logError(t) + } + } + + val bananas by settings.general.bananas.collectAsState() + val parallelDownloads by settings.general.parallelDownloads.collectAsState() + val concurrentConnections by settings.general.concurrentConnections.collectAsState() + val locale by settings.general.locale.collectAsState() + val downloadPathVisual by settings.general.downloadPathVisual.collectAsState() + //val downloadPath by settings.general.downloadPath.collectAsState() + + var isBatteryShown by remember { mutableStateOf(false) } + val context = LocalContext.current + + if (isBatteryShown) { + ActionDialog( + icon = painterResource(R.drawable.battery_alert_24px), + title = stringResource(R.string.battery_dialog_title), + text = stringResource(R.string.battery_dialog_message), + confirmText = stringResource(R.string.ok), + dismissText = stringResource(R.string.cancel), + dismiss = { + isBatteryShown = false + settings.general.batterOptimization.set(false) + }, + confirm = { + isBatteryShown = false + // The og impl never modified it to true? + // settings.general.batterOptimization.set(true) + context.showRequestIgnoreBatteryOptDialog() + } + ) + } + + return persistentListOf( + Preference.PreferenceItem.BasicListPreference( + value = locale, + entries = appLanguages.associate { (name, code) -> (code to (name to code).nameNextToFlagEmoji()) }, + title = stringResource(R.string.app_language), + icon = painterResource(R.drawable.language_korean_latin_24px), + onValueChanged = { value -> + settings.general.locale.set(value) + activity?.recreate() + }, + subtitleProvider = { v, e -> e[v] ?: getCurrentLocale(LocalContext.current) } + ), + Preference.PreferenceGroup( + title = stringResource(R.string.title_downloads), + preferenceItems = persistentListOf( + Preference.PreferenceItem.TextPreference( + icon = painterResource(R.drawable.netflix_download), + subtitle = downloadPathVisual, + title = stringResource(R.string.download_path_pref), + onClick = { + // This is not a ListPreference because the old selection system is + // broken af. This needs to be refactored to QuickNovels download path + // system. + selectFileSelector.launch(Uri.EMPTY) + }, + ), + Preference.PreferenceItem.SliderPreference( + icon = painterResource(R.drawable.arrow_or_edge_24px), + value = parallelDownloads, + valueRange = 1..10, + title = stringResource(R.string.parallel_downloads), + subtitle = stringResource(R.string.download_parallel_settings_des), + onValueChanged = settings.general.parallelDownloads::set, + ), + Preference.PreferenceItem.SliderPreference( + icon = painterResource(R.drawable.arrow_and_edge_24px), + value = concurrentConnections, + valueRange = 1..10, + title = stringResource(R.string.concurrent_connections), + subtitle = stringResource(R.string.concurrent_connections_settings_des), + onValueChanged = settings.general.concurrentConnections::set, + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.battery_dialog_title), + icon = painterResource(R.drawable.battery_alert_24px), + enabled = isLayout(PHONE), + onClick = { + if (isAppRestricted(context)) { + isBatteryShown = true + } else { + showToast(R.string.app_unrestricted_toast) + } + } + ), + ) + ), + + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_bypass), + preferenceItems = persistentListOf( + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.add_site_pref), + subtitle = stringResource(R.string.add_site_summary), + icon = painterResource(R.drawable.copy_all_24px), + onClick = { + // TODO refactor into compose + if (SettingsGeneral.getCurrent().isEmpty()) { + SettingsGeneral.showAdd() + } else { + SettingsGeneral.showAddOrDelete() + } + } + ), + Preference.PreferenceItem.ListPreference( + title = stringResource(R.string.dns_pref), + subtitle = stringResource(R.string.dns_pref_summary), + icon = painterResource(R.drawable.dns_24px), + preference = settings.general.dns, + entries = integerArrayResource(R.array.dns_pref_values).zip( + stringArrayResource(R.array.dns_pref) + ).toMap(), + onValueChanged = { + (CloudStreamApp.context)?.let { ctx -> + app.initClient(ctx, ignoreSSL = false) + @OptIn(UnsafeSSL::class) + insecureApp.initClient(ctx, ignoreSSL = true) + } + return@ListPreference true + }, + ), + Preference.PreferenceItem.SwitchPreference( + title = stringResource(R.string.jsdelivr_proxy), + subtitle = stringResource(R.string.jsdelivr_proxy_summary), + icon = painterResource(R.drawable.wifi_proxy_24px), + preference = settings.general.jsdelivrProxy + ) + ) + ), + + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_links), + preferenceItems = persistentListOf( + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.github), + subtitle = "https://github.com/recloudstream/cloudstream", + icon = painterResource(R.drawable.ic_github_logo), + onClick = { + CloudStreamApp.openBrowser("https://github.com/recloudstream/cloudstream") + } + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.lightnovel), + subtitle = "https://github.com/LagradOst/QuickNovel", + icon = painterResource(R.drawable.quick_novel_icon), + onClick = { + CloudStreamApp.openBrowser("https://github.com/LagradOst/QuickNovel") + } + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.discord), + subtitle = "https://discord.gg/5Hus6fM", + icon = painterResource(R.drawable.ic_baseline_discord_24), + onClick = { + CloudStreamApp.openBrowser("https://discord.gg/5Hus6fM") + } + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.cs3wiki), + subtitle = "https://cloudstream.miraheze.org/", + icon = painterResource(R.drawable.baseline_description_24), + onClick = { + CloudStreamApp.openBrowser("https://cloudstream.miraheze.org/") + } + ), + ) + ), + + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.benene), + subtitle = if (bananas == 0) { + stringResource(R.string.benene_count_text_none) + } else { + stringResource(R.string.benene_count_text, bananas) + }, + onClick = { + settings.general.bananas.set(bananas + 1) + }, + icon = painterResource(R.drawable.benene), + ), + Preference.PreferenceItem.InfoPreference(title = stringResource(R.string.legal_notice_text)), + ) + } +} + + +@PreviewLightDark +@Composable +private fun SettingGeneralPreview() { + CloudStreamPreviewTheme { + SettingsGeneralScreen.Content() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer2.kt new file mode 100644 index 000000000..3c6a272d7 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer2.kt @@ -0,0 +1,7 @@ +package com.lagradost.cloudstream3.ui.settings + +import com.lagradost.cloudstream3.utils.BaseComposeFragment +import com.mihon.presentation.settings.SearchableSettings + +/** Empty glue code to connect the old navigation graph to Compose */ +class SettingsPlayer2 : BaseComposeFragment(), SearchableSettings by SettingsPlayerScreen \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayerScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayerScreen.kt new file mode 100644 index 000000000..309b65cb8 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayerScreen.kt @@ -0,0 +1,353 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.text.format.Formatter.formatShortFileSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.integerArrayResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringArrayResource +import androidx.compose.ui.res.stringResource +import com.lagradost.cloudstream3.CommonActivity.activity +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.actions.VideoClickActionHolder +import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize +import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment +import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream4.compose.EMULATOR +import com.lagradost.cloudstream4.compose.PHONE +import com.lagradost.cloudstream4.compose.TV +import com.lagradost.cloudstream4.compose.isLayout +import com.lagradost.cloudstream4.rememberAppSettings +import com.mihon.presentation.settings.Preference +import com.mihon.presentation.settings.SearchableSettings +import com.mihon.presentation.settings.collectAsState +import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlin.reflect.jvm.jvmName + +object SettingsPlayerScreen : SearchableSettings { + @Composable + override fun getTitleRes(): String = stringResource(R.string.category_player) + + @Composable + override fun getPreferences(): List { + val settings = rememberAppSettings() + + val context = LocalContext.current + val defaultPlayerName = stringResource(R.string.player_settings_play_in_app) + val players = remember(context) { + mapOf("" to defaultPlayerName) + VideoClickActionHolder.getPlayers() + .associate { player -> + player.uniqueId() to (player.name.asStringNull(context) + ?: player::class.simpleName ?: player::class.jvmName) + } + } + + val playerSeekTime by settings.player.doubleTapTime.collectAsState() + val tvSeekOnTime by settings.player.tvSeekOnTime.collectAsState() + val tvSeekOffTime by settings.player.tvSeekOffTime.collectAsState() + + var cacheSize by remember { mutableLongStateOf(0L) } + var cacheCleared by remember { mutableIntStateOf(0) } + val cacheDir = LocalContext.current.cacheDir + LaunchedEffect(cacheCleared) { + withContext(Dispatchers.IO) { + cacheSize = getFolderSize(cacheDir) + } + } + + return persistentListOf( + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_subtitles), + preferenceItems = persistentListOf( + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.player_subtitles_settings), + subtitle = stringResource(R.string.player_subtitles_settings_des), + icon = painterResource(R.drawable.subtitles_gear_24px), + onClick = { + SubtitlesFragment.push(activity, false) + }), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.chromecast_subtitles_settings), + subtitle = stringResource(R.string.chromecast_subtitles_settings_des), + icon = painterResource(R.drawable.cast), + onClick = { + ChromecastSubtitlesFragment.push(activity, false) + }), + ), + ), + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_player_features), + preferenceItems = persistentListOf( + Preference.PreferenceItem.ListPreference( + title = stringResource(R.string.player_pref), + icon = painterResource(R.drawable.play_arrow_24px), + preference = settings.player.defaultPlayer, + entries = players, + ), + + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.source_priority), + subtitle = stringResource(R.string.source_priority_help), + icon = painterResource(R.drawable.ic_baseline_people_24), + onClick = { + ioSafe { + val defaultSources = QualityProfileDialog.getAllDefaultSources() + val activity = activity ?: return@ioSafe + activity.runOnUiThread { + QualityProfileDialog( + activity, + R.style.DialogFullscreenPlayer, + defaultSources, + ).show() + } + } + }), + + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.pipEnabled, + title = stringResource(R.string.picture_in_picture), + subtitle = stringResource(R.string.picture_in_picture_des), + icon = painterResource(R.drawable.pip_24px), + ), + + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.resizeEnabled, + title = stringResource(R.string.player_size_settings), + subtitle = stringResource(R.string.player_size_settings_des), + icon = painterResource(R.drawable.aspect_ratio_24px), + ), + + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.speedEnabled, + title = stringResource(R.string.eigengraumode_settings), + subtitle = stringResource(R.string.speed_setting_summary), + icon = painterResource(R.drawable.ic_baseline_speed_24), + ), + + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.tiktokEnabled, + title = stringResource(R.string.speedup_title), + subtitle = stringResource(R.string.speedup_summary), + icon = painterResource(R.drawable.speedup), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.autoPlayEnabled, + title = stringResource(R.string.autoplay_next_settings), + subtitle = stringResource(R.string.autoplay_next_settings_des), + icon = painterResource(R.drawable.skip_next_24px), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.skipOpEnabled, + title = stringResource(R.string.video_skip_op), + subtitle = stringResource(R.string.enable_skip_op_from_database_des), + icon = painterResource(R.drawable.keyboard_double_arrow_right_24px), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.autoRotateEnabled, + title = stringResource(R.string.auto_rotate_video), + subtitle = stringResource(R.string.auto_rotate_video_desc), + icon = painterResource(R.drawable.screen_rotation_alt_24px), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.rotateButtonEnabled, + title = stringResource(R.string.rotate_video), + subtitle = stringResource(R.string.rotate_video_desc), + icon = painterResource(R.drawable.screen_rotation), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.previewBarEnabled, + enabled = isLayout(PHONE or EMULATOR), + title = stringResource(R.string.preview_seekbar), + subtitle = stringResource(R.string.preview_seekbar_desc), + icon = painterResource(R.drawable.picture_in_picture_center_24px), + ), + Preference.PreferenceItem.ListPreference( + preference = settings.player.softwareDecoding, + title = stringResource(R.string.software_decoding), + subtitle = stringResource(R.string.software_decoding_desc), + icon = painterResource(R.drawable.memory_24px), + entries = integerArrayResource(R.array.software_decoding_switch_values).zip( + stringArrayResource(R.array.software_decoding_switch) + ).toMap() + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.extraBrightnessEnabled, + title = stringResource(R.string.extra_brightness_settings), + subtitle = stringResource(R.string.extra_brightness_settings_des), + icon = painterResource(R.drawable.light_mode_24px), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.episodeSync, + title = stringResource(R.string.episode_sync_settings), + subtitle = stringResource(R.string.episode_sync_settings_des), + icon = painterResource(R.drawable.autorenew_24px) + ), + ), + ), + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_gestures), + preferenceItems = persistentListOf( + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.swipeHorizontalEnabled, + title = stringResource(R.string.swipe_to_seek_settings), + subtitle = stringResource(R.string.swipe_to_seek_settings_des), + icon = painterResource(R.drawable.swipe_24px), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.swipeVerticalEnabled, + title = stringResource(R.string.swipe_to_change_settings), + subtitle = stringResource(R.string.swipe_to_change_settings_des), + icon = painterResource(R.drawable.swipe_vertical_24px), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.doubleTapToSeekEnabled, + title = stringResource(R.string.double_tap_to_seek_settings), + subtitle = stringResource(R.string.double_tap_to_seek_settings_des), + icon = painterResource(R.drawable.touch_double_24px), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.doubleTapToPauseEnabled, + title = stringResource(R.string.double_tap_to_pause_settings), + subtitle = stringResource(R.string.double_tap_to_pause_settings_des), + icon = painterResource(R.drawable.touch_double_24px), + ), + + Preference.PreferenceItem.SliderPreference( + value = playerSeekTime, + title = stringResource(R.string.double_tap_to_seek_amount_settings), + icon = painterResource(R.drawable.go_forward_30), + valueRange = 5..60, + steps = 10, + onValueChanged = settings.player.doubleTapTime::set + ), + ) + ), + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_android_tv), + enabled = isLayout(TV or EMULATOR), + preferenceItems = persistentListOf( + Preference.PreferenceItem.SliderPreference( + value = tvSeekOnTime, + title = stringResource(R.string.android_tv_interface_on_seek_settings), + subtitle = stringResource(R.string.android_tv_interface_on_seek_settings_summary), + icon = painterResource(R.drawable.go_forward_30), + valueRange = 5..60, + steps = 10, + onValueChanged = settings.player.tvSeekOnTime::set + ), + Preference.PreferenceItem.SliderPreference( + value = tvSeekOffTime, + title = stringResource(R.string.android_tv_interface_off_seek_settings), + subtitle = stringResource(R.string.android_tv_interface_off_seek_settings_summary), + icon = painterResource(R.drawable.go_forward_30), + valueRange = 5..60, + steps = 10, + onValueChanged = settings.player.tvSeekOffTime::set + ), + ) + ), + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_player_layout), + preferenceItems = persistentListOf( + Preference.PreferenceItem.ListPreference( + preference = settings.player.limitPlayerTitle, + title = stringResource(R.string.limit_title), + icon = painterResource(R.drawable.match_word_24px), + entries = integerArrayResource(R.array.limit_title_pref_values).zip( + stringArrayResource(R.array.limit_title_pref_names) + ).toMap() + ), + Preference.PreferenceItem.SwitchPreference( + enabled = isLayout(PHONE or EMULATOR), + preference = settings.player.hidePlayerControlNames, + icon = painterResource(R.drawable.visibility_off_24px), + title = stringResource(R.string.hide_player_control_names), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.showName, + icon = painterResource(R.drawable.label_24px), + title = stringResource(R.string.source_name), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.showMediaInfo, + icon = painterResource(R.drawable.movie_info_24px), + title = stringResource(R.string.video_info), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.player.showResolution, + title = stringResource(R.string.resolution), + icon = painterResource(R.drawable.high_res_24px), + ), + + // Unsure if we want to have MultiSelectListPreference or boolean + /*Preference.PreferenceItem.MultiSelectListPreference( + preference = settings.player.showPlayerInfo, + title = stringResource(R.string.limit_title_rez), + icon = painterResource(R.drawable.ic_baseline_text_format_24), + entries = persistentMapOf( + ShowPlayerInfo.Name to stringResource(R.string.source_name), + ShowPlayerInfo.Resolution to stringResource(R.string.resolution), + ShowPlayerInfo.VideoInfo to stringResource(R.string.video_info), + ) + ), */ + ) + ), + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_cache), + preferenceItems = persistentListOf( + Preference.PreferenceItem.ListPreference( + preference = settings.player.bufferDiskMB, + title = stringResource(R.string.video_buffer_disk_settings), + subtitle = "%s\n" + stringResource(R.string.video_disk_description), + icon = painterResource(R.drawable.hard_drive_24px), + entries = integerArrayResource(R.array.video_buffer_size_values).zip( + stringArrayResource(R.array.video_buffer_size_names) + ).toMap() + ), + + Preference.PreferenceItem.ListPreference( + preference = settings.player.bufferRamMB, + title = stringResource(R.string.video_buffer_size_settings), + subtitle = "%s\n" + stringResource(R.string.video_ram_description), + icon = painterResource(R.drawable.memory_alt_24px), + entries = integerArrayResource(R.array.video_buffer_size_values).zip( + stringArrayResource(R.array.video_buffer_size_names) + ).toMap() + ), + + Preference.PreferenceItem.ListPreference( + preference = settings.player.bufferTimeSec, + title = stringResource(R.string.video_buffer_length_settings), + subtitle = "%s\n" + stringResource(R.string.video_ram_description), + icon = painterResource(R.drawable.history_toggle_off_24px), + entries = integerArrayResource(R.array.video_buffer_length_values).zip( + stringArrayResource(R.array.video_buffer_length_names) + ).toMap() + ), + + Preference.PreferenceItem.TextPreference( + icon = painterResource(R.drawable.ic_baseline_delete_outline_24), + title = stringResource(R.string.video_buffer_clear_settings), + subtitle = formatShortFileSize(LocalContext.current, cacheSize), + onClick = { + ioSafe { + cacheDir.deleteRecursively() + cacheCleared += 1 + } + }) + ) + ), + + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders.kt index c8478a840..08b147f52 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders.kt @@ -109,7 +109,8 @@ class SettingsProviders : BasePreferenceFragmentCompat() { return@setOnPreferenceClickListener true } - getPref(R.string.provider_lang_key)?.setOnPreferenceClickListener { + getPref(R.string. + provider_lang_key)?.setOnPreferenceClickListener { activity?.getApiProviderLangSettings()?.let { currentLangTags -> val languagesTagName = APIHolder.apis.withLock { listOf(Pair(AllLanguagesName, getString(R.string.all_languages_preference))) + diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders2.kt new file mode 100644 index 000000000..adb44a94d --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders2.kt @@ -0,0 +1,6 @@ +package com.lagradost.cloudstream3.ui.settings +import com.lagradost.cloudstream3.utils.BaseComposeFragment +import com.mihon.presentation.settings.SearchableSettings + +/** Empty glue code to connect the old navigation graph to Compose */ +class SettingsProviders2 : BaseComposeFragment(), SearchableSettings by SettingsProvidersScreen \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProvidersScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProvidersScreen.kt new file mode 100644 index 000000000..48c305a32 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProvidersScreen.kt @@ -0,0 +1,88 @@ +package com.lagradost.cloudstream3.ui.settings + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import com.lagradost.cloudstream3.APIHolder +import com.lagradost.cloudstream3.AllLanguagesName +import com.lagradost.cloudstream3.CommonActivity.activity +import com.lagradost.cloudstream3.DubStatus +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTagToLanguageName +import com.lagradost.cloudstream3.utils.SubtitleHelper.getNameNextToFlagEmoji +import com.lagradost.cloudstream3.utils.UIHelper.navigate +import com.lagradost.cloudstream4.rememberAppSettings +import com.mihon.presentation.settings.Preference +import com.mihon.presentation.settings.SearchableSettings +import kotlinx.collections.immutable.persistentListOf + +object SettingsProvidersScreen : SearchableSettings { + @Composable + override fun getTitleRes(): String = stringResource(R.string.category_providers) + + fun TvType.toStringRes() = when (this) { + TvType.TvSeries -> R.string.tv_series_singular + TvType.Anime -> R.string.anime_singular + TvType.OVA -> R.string.ova_singular + TvType.AnimeMovie -> R.string.movies_singular + TvType.Cartoon -> R.string.cartoons_singular + TvType.Documentary -> R.string.documentaries_singular + TvType.Movie -> R.string.movies_singular + TvType.Torrent -> R.string.torrent_singular + TvType.AsianDrama -> R.string.asian_drama_singular + TvType.Live -> R.string.live_singular + TvType.Others -> R.string.other_singular + TvType.NSFW -> R.string.nsfw_singular + TvType.Music -> R.string.music_singular + TvType.AudioBook -> R.string.audio_book_singular + TvType.CustomMedia -> R.string.custom_media_singular + TvType.Audio -> R.string.audio_singular + TvType.Podcast -> R.string.podcast_singular + TvType.Video -> R.string.video_singular + } + + // TODO Move this into general and layout? + @Composable + override fun getPreferences(): List { + val settings = rememberAppSettings() + + val default = AllLanguagesName to stringResource(R.string.all_languages_preference) + val languages = APIHolder.apis.withLock { + APIHolder.apis.map { api -> api.lang }.distinct() + }.sortedBy { fromTagToLanguageName(it) ?: it } + + return persistentListOf( + Preference.PreferenceItem.MultiSelectListPreference( + title = stringResource(R.string.provider_lang_settings), + icon = painterResource(R.drawable.plugin_lang), + entries = mapOf(default) + languages.associateWith { lang -> + (getNameNextToFlagEmoji( + lang + ) ?: lang) + }, + preference = settings.provider.extensionLanguages + ), Preference.PreferenceItem.MultiSelectListPreference( + title = stringResource(R.string.preferred_media_settings), + icon = painterResource(R.drawable.movie_edit_24px), + preference = settings.provider.preferredMedia, + entries = TvType.entries.associate { + it.ordinal.toString() to stringResource(it.toStringRes()) + }), Preference.PreferenceItem.MultiSelectListPreference( + title = stringResource(R.string.display_subbed_dubbed_settings), + icon = painterResource(R.drawable.ic_outline_voice_over_off_24), + preference = settings.provider.displayDubSub, + entries = mapOf( + DubStatus.None.name to stringResource(R.string.none), + DubStatus.Dubbed.name to stringResource(R.string.app_dubbed_text), + DubStatus.Subbed.name to stringResource(R.string.app_subbed_text), + ) + ), Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.test_extensions), + subtitle = stringResource(R.string.test_extensions_summary), + icon = painterResource(R.drawable.baseline_network_ping_24), + onClick = { + activity?.navigate(R.id.navigation_test_providers) + })) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI2.kt new file mode 100644 index 000000000..86f3371fa --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI2.kt @@ -0,0 +1,7 @@ +package com.lagradost.cloudstream3.ui.settings + +import com.lagradost.cloudstream3.utils.BaseComposeFragment +import com.mihon.presentation.settings.SearchableSettings + +/** Empty glue code to connect the old navigation graph to Compose */ +class SettingsUI2 : BaseComposeFragment(), SearchableSettings by SettingsUIScreen \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt new file mode 100644 index 000000000..3e67ed15e --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt @@ -0,0 +1,289 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.os.Build +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.integerArrayResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringArrayResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream3.CommonActivity.activity +import com.lagradost.cloudstream3.MainActivity +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.SearchQuality +import com.lagradost.cloudstream3.ui.clear +import com.lagradost.cloudstream3.ui.home.HomeChildItemAdapter +import com.lagradost.cloudstream3.ui.home.ParentItemAdapter +import com.lagradost.cloudstream3.ui.search.SearchAdapter +import com.lagradost.cloudstream3.ui.settings.Globals.updateTv +import com.lagradost.cloudstream3.utils.UIHelper.toPx +import com.lagradost.cloudstream4.compose.TV +import com.lagradost.cloudstream4.compose.isLayout +import com.lagradost.cloudstream4.rememberAppSettings +import com.lagradost.cloudstream4.theme.CloudStreamPrimaryColor +import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors +import com.lagradost.cloudstream4.theme.modeToTheme +import com.lagradost.cloudstream4.theme.perfToColor +import com.lagradost.cloudstream4.theme.perfToMode +import com.mihon.presentation.settings.Preference +import com.mihon.presentation.settings.SearchableSettings +import com.mihon.presentation.settings.collectAsState +import kotlinx.collections.immutable.mutate +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentMap + +object SettingsUIScreen : SearchableSettings { + @Composable + override fun getTitleRes(): String = stringResource(R.string.category_ui) + + fun SearchQuality.toStringRes() = when (this) { + SearchQuality.BlueRay -> R.string.quality_blueray + SearchQuality.Cam -> R.string.quality_cam + SearchQuality.CamRip -> R.string.quality_cam_rip + SearchQuality.DVD -> R.string.quality_dvd + SearchQuality.HD -> R.string.quality_hd + SearchQuality.HQ -> R.string.quality_hq + SearchQuality.HdCam -> R.string.quality_cam_hd + SearchQuality.Telecine -> R.string.quality_tc + SearchQuality.Telesync -> R.string.quality_ts + SearchQuality.WorkPrint -> R.string.quality_workprint + SearchQuality.SD -> R.string.quality_sd + SearchQuality.FourK -> R.string.quality_4k + SearchQuality.UHD -> R.string.quality_uhd + SearchQuality.SDR -> R.string.quality_sdr + SearchQuality.HDR -> R.string.quality_hdr + SearchQuality.WebRip -> R.string.quality_webrip + } + + @Composable + fun RoundColor(color: Color) { + Box( + modifier = Modifier + .padding(start = 15.dp) + .size(20.dp) + .border(width = 1.5.dp, shape = CircleShape, color = colors.onBackground) + .background(color, CircleShape) + ) + } + + @Composable + override fun getPreferences(): List { + val settings = rememberAppSettings() + + val overscanDp by settings.ui.overscanDp.collectAsState() + val posterSize by settings.ui.posterSize.collectAsState() + + return persistentListOf( + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_looks), + preferenceItems = persistentListOf( + Preference.PreferenceItem.ListPreference( + preference = settings.ui.primaryColor, + icon = painterResource(R.drawable.colors_24px), + title = stringResource(R.string.primary_color_settings), + entries = stringArrayResource(R.array.themes_overlay_names_values).zip( + stringArrayResource(R.array.themes_overlay_names) + ).toMap().toPersistentMap().mutate { map -> + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { // remove monet on android 11 and less + map.remove("Monet") + map.remove("Monet2") + } + }, + iconProvider = { k, _ -> + val color = perfToColor(k) + RoundColor(color.color) + }, + onValueChanged = { newValue -> + settings.ui.primaryColor.set(newValue) // We need to set before we recreate + activity?.recreate() + return@ListPreference true + }), + Preference.PreferenceItem.ListPreference( + preference = settings.ui.theme, + icon = painterResource(R.drawable.palette_24px), + title = stringResource(R.string.app_theme_settings), + entries = stringArrayResource(R.array.themes_names_values).zip( + stringArrayResource(R.array.themes_names) + ).toMap().toPersistentMap().mutate { map -> + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { // remove monet on android 11 and less + map.remove("Monet") + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { // Remove system on android 9 and less + map.remove("System") + } + }, + iconProvider = { k, _ -> + val theme = modeToTheme(perfToMode(k), CloudStreamPrimaryColor.NORMAL) + RoundColor(theme.background) + }, + onValueChanged = { newValue -> + settings.ui.theme.set(newValue) // We need to set before we recreate + activity?.recreate() + return@ListPreference true + }), + Preference.PreferenceItem.ListPreference( + preference = settings.ui.layout, + icon = painterResource(R.drawable.responsive_layout_24px), + title = stringResource(R.string.app_layout), + entries = integerArrayResource(R.array.app_layout_values).zip( + stringArrayResource(R.array.app_layout) + ).toMap().toPersistentMap(), + onValueChanged = { newValue -> + settings.ui.layout.set(newValue) // We need to set before we recreate + activity?.updateTv() + activity?.recreate() + return@ListPreference true + }), + ) + ), + + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_ui_features), + preferenceItems = persistentListOf( + Preference.PreferenceItem.SliderPreference( + value = overscanDp, + title = stringResource(R.string.overscan_settings), + subtitle = stringResource(R.string.overscan_settings_des), + valueRange = 0..100, + icon = painterResource(R.drawable.arrows_input_24px), + enabled = isLayout(TV), + onValueChanged = { newValue -> + settings.ui.overscanDp.set(newValue) + val padding = newValue.toPx + (activity as? MainActivity)?.binding?.homeRoot?.setPadding( + padding, padding, padding, padding + ) + }), + + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.advancedSearch, + title = stringResource(R.string.advanced_search), + subtitle = stringResource(R.string.advanced_search_des), + icon = painterResource(R.drawable.search_icon) + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.searchSuggestions, + title = stringResource(R.string.search_suggestions), + subtitle = stringResource(R.string.search_suggestions_des), + icon = painterResource(R.drawable.tooltip_24px) + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.trailersEnabled, + title = stringResource(R.string.show_trailers_settings), + icon = painterResource(R.drawable.baseline_theaters_24) + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.kitsuPostersEnabled, + title = stringResource(R.string.kitsu_settings), + icon = painterResource(R.drawable.kitsu_icon) + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.castEnabled, + title = stringResource(R.string.show_cast_in_details), + icon = painterResource(R.drawable.face_24px) + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.fillersEnabled, + title = stringResource(R.string.show_fillers_settings), + icon = painterResource(R.drawable.skip_next_24px) + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.showMetadataOverlay, + title = stringResource(R.string.show_player_metadata_overlay), + icon = painterResource(R.drawable.metadata_overlay_icon) + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.showClock, + title = stringResource(R.string.tv_layout_clock_settings), + subtitle = stringResource(R.string.tv_layout_clock_settings_des), + icon = painterResource(R.drawable.ic_baseline_clock_24), + enabled = isLayout(TV), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.randomButtonEnabled, + title = stringResource(R.string.random_button_settings), + subtitle = stringResource(R.string.random_button_settings_desc), + icon = painterResource(R.drawable.shuffle_24px) + ), + Preference.PreferenceItem.ListPreference( + preference = settings.ui.confirmExit, + title = stringResource(R.string.confirm_before_exiting_title), + subtitle = stringResource(R.string.confirm_before_exiting_desc), + icon = painterResource(R.drawable.ic_baseline_exit_24), + entries = integerArrayResource(R.array.confirm_exit_values).zip( + stringArrayResource(R.array.confirm_exit) + ).toMap() + ), + ) + ), + + Preference.PreferenceGroup( + title = stringResource(R.string.search_poster_img_des), + preferenceItems = persistentListOf( + Preference.PreferenceItem.MultiSelectListPreference( + preference = settings.ui.filterQuality, + title = stringResource(R.string.pref_filter_search_quality), + icon = painterResource(R.drawable.filter_alt_24px), + entries = SearchQuality.entries.associateWith { stringResource(it.toStringRes()) }), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.bottomTitle, + title = stringResource(R.string.bottom_title_settings), + subtitle = stringResource(R.string.bottom_title_settings_des), + icon = painterResource(R.drawable.title_24px) + ), + Preference.PreferenceItem.SliderPreference( + value = posterSize, + title = stringResource(R.string.poster_size_settings), + subtitle = stringResource(R.string.poster_size_settings_des), + valueRange = 0..15, + icon = painterResource(R.drawable.baseline_grid_view_24), + onValueChanged = { newValue -> + HomeChildItemAdapter.sharedPool.clear() + ParentItemAdapter.sharedPool.clear() + SearchAdapter.sharedPool.clear() + activity?.let { HomeChildItemAdapter.updatePosterSize(it, newValue) } + settings.ui.posterSize.set(newValue) + }), + ), + ), + Preference.PreferenceGroup( + title = stringResource(R.string.poster_ui_settings), + preferenceItems = persistentListOf( + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.posterShowRating, + title = stringResource(R.string.show_rating), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.posterShowEpisode, + title = stringResource(R.string.show_episode_text), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.posterShowTitle, + title = stringResource(R.string.show_title), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.posterShowHd, + title = stringResource(R.string.show_hd), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.posterShowDub, + title = stringResource(R.string.show_dub), + ), + Preference.PreferenceItem.SwitchPreference( + preference = settings.ui.posterShowSub, + title = stringResource(R.string.show_sub), + ), + ) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates2.kt new file mode 100644 index 000000000..50070a384 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates2.kt @@ -0,0 +1,7 @@ +package com.lagradost.cloudstream3.ui.settings + +import com.lagradost.cloudstream3.utils.BaseComposeFragment +import com.mihon.presentation.settings.SearchableSettings + +/** Empty glue code to connect the old navigation graph to Compose */ +class SettingsUpdates2 : BaseComposeFragment(), SearchableSettings by SettingsUpdatesScreen \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdatesScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdatesScreen.kt new file mode 100644 index 000000000..dc03174ff --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdatesScreen.kt @@ -0,0 +1,222 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.content.Intent +import android.net.Uri +import android.widget.Toast +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.res.integerArrayResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringArrayResource +import androidx.compose.ui.res.stringResource +import com.lagradost.cloudstream3.AutoDownloadMode +import com.lagradost.cloudstream3.BuildConfig +import com.lagradost.cloudstream3.CloudStreamApp +import com.lagradost.cloudstream3.CommonActivity.activity +import com.lagradost.cloudstream3.CommonActivity.showToast +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.plugins.PluginManager +import com.lagradost.cloudstream3.utils.BackupUtils +import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.InAppUpdater.installPreReleaseIfNeeded +import com.lagradost.cloudstream3.utils.InAppUpdater.runAutoUpdate +import com.lagradost.cloudstream3.utils.UIHelper.navigate +import com.lagradost.cloudstream4.AppSettings +import com.lagradost.cloudstream4.rememberAppSettings +import com.lagradost.safefile.SafeFile +import com.mihon.presentation.settings.Preference +import com.mihon.presentation.settings.SearchableSettings +import com.mihon.presentation.settings.collectAsState +import kotlinx.collections.immutable.persistentListOf + +object SettingsUpdatesScreen : SearchableSettings { + @Composable + override fun getTitleRes(): String = stringResource(R.string.category_updates) + + @Composable + override fun getPreferences(): List { + val settings = rememberAppSettings() + + // TODO Refactor entirely to use a different file path selector ect like QuickNovel + val selectFileSelector = + rememberLauncherForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> + // It lies, it can be null if file manager quits. + if (uri == null) return@rememberLauncherForActivityResult + val context = CloudStreamApp.context ?: return@rememberLauncherForActivityResult + + try { + val settings = AppSettings(context) + // RW perms for the path + val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + + context.contentResolver.takePersistableUriPermission(uri, flags) + + val filePath = SafeFile.fromUri(context, uri)?.filePath() + println("Selected URI path: $uri - Full path: $filePath") + + // store the actual URI instead of the path due to permissions. + // filePath should only be used for cosmetic purposes. + val visual = filePath ?: uri.toString() + settings.backup.path.set(uri.toString()) + settings.backup.visualPath.set(visual) + } catch (t: Throwable) { + logError(t) + } + } + + val visualBackupPath by settings.backup.visualPath.collectAsState() + var showDialog by remember { mutableStateOf(false) } + + if (showDialog) { + com.lagradost.cloudstream3.ui.settings.logcat.LogcatDialog { + showDialog = false + } + } + + return persistentListOf( + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_app_updates), + preferenceItems = persistentListOf( + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.check_for_update), + subtitle = BuildConfig.VERSION_NAME, + icon = painterResource(R.drawable.mobile_arrow_down_24px), + onClick = { + ioSafe { + if (activity?.runAutoUpdate(false) == false) { + activity?.runOnUiThread { + showToast( + R.string.no_update_found, + Toast.LENGTH_SHORT + ) + } + } + } + } + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.install_prerelease), + icon = painterResource(R.drawable.mobile_code_24px), + enabled = BuildConfig.FLAVOR == "stable", + onClick = { + activity?.installPreReleaseIfNeeded() + } + ), + + Preference.PreferenceItem.ListPreference( + title = stringResource(R.string.apk_installer_settings), + subtitle = stringResource(R.string.apk_installer_settings_des), + icon = painterResource(R.drawable.mobile_wrench_24px), + entries = integerArrayResource(R.array.apk_installer_values).zip( + stringArrayResource(R.array.apk_installer_pref) + ).toMap(), + preference = settings.updates.apkInstaller + ), + + Preference.PreferenceItem.SwitchPreference( + title = stringResource(R.string.updates_settings), + subtitle = stringResource(R.string.updates_settings_des), + icon = painterResource(R.drawable.notifications_active_24px), + preference = settings.updates.showAppUpdates + ) + ) + ), + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_backup), + preferenceItems = persistentListOf( + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.backup_settings), + icon = painterResource(R.drawable.save_as_24px), + onClick = { + BackupUtils.backup(activity) + } + ), + Preference.PreferenceItem.ListPreference( + title = stringResource(R.string.backup_frequency), + icon = painterResource(R.drawable.save_clock_24px), + entries = integerArrayResource(R.array.periodic_work_values).zip( + stringArrayResource(R.array.periodic_work_names) + ).toMap(), + preference = settings.backup.frequency + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.backup_path_title), + icon = painterResource(R.drawable.folder_24px), + subtitle = visualBackupPath, + onClick = { + // This is not a ListPreference because the old selection system is + // broken af. This needs to be refactored to QuickNovels download path + // system. + selectFileSelector.launch(Uri.EMPTY) + } + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.restore_settings), + icon = painterResource(R.drawable.restore_page_24px), + onClick = { + activity?.restorePrompt() + } + ), + ) + ), + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_extensions), + preferenceItems = persistentListOf( + Preference.PreferenceItem.SwitchPreference( + title = stringResource(R.string.automatic_plugin_updates), + icon = painterResource(R.drawable.extension_24px), + preference = settings.plugins.autoUpdate, + ), + + Preference.PreferenceItem.ListPreference( + title = stringResource(R.string.automatic_plugin_download), + subtitle = "%s\n"+stringResource(R.string.automatic_plugin_download_summary), + icon = painterResource(R.drawable.extention_renew2), + entries = AutoDownloadMode.entries.map { it.value }.sorted() + .zip(stringArrayResource((R.array.auto_download_plugin))).toMap(), + preference = settings.plugins.autoDownload + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.update_plugins), + subtitle = stringResource(R.string.update_plugins_manually), + icon = painterResource(R.drawable.extention_download), + onClick = { + ioSafe { + PluginManager.___DO_NOT_CALL_FROM_A_PLUGIN_manuallyReloadAndUpdatePlugins( + activity ?: return@ioSafe + ) + } + } + ), + ) + ), + Preference.PreferenceGroup( + title = stringResource(R.string.pref_category_actions), + preferenceItems = persistentListOf( + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.show_log_cat), + icon = painterResource(R.drawable.article_24px), + onClick = { + showDialog = true + } + ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.redo_setup_process), + icon = painterResource(R.drawable.construction_24px), + onClick = { + activity?.navigate(R.id.navigation_setup_language) + } + ), + ) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt new file mode 100644 index 000000000..a157a0cda --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt @@ -0,0 +1,258 @@ +package com.lagradost.cloudstream3.ui.settings.logcat + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.DialogProperties +import com.lagradost.cloudstream3.CommonActivity.showToast +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper +import com.lagradost.cloudstream3.utils.downloader.VideoDownloadManager +import com.lagradost.cloudstream3.utils.txt +import com.lagradost.cloudstream4.compose.Colors +import com.lagradost.cloudstream4.compose.circle +import com.lagradost.cloudstream4.compose.ripple +import com.lagradost.cloudstream4.compose.rounded +import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.OutputStream +import java.lang.System.currentTimeMillis +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + + +@Composable +fun LogcatDialog(dismiss: () -> Unit) { + + val list = remember { mutableStateOf(persistentListOf()) } + LaunchedEffect(dismiss) { + try { + // https://developer.android.com/studio/command-line/logcat + val process = Runtime.getRuntime().exec("logcat --binary -d") + val items = arrayListOf() + LogcatBinaryParser(process.inputStream).use { parser -> + while (true) { + val item = parser.parseItem() ?: break + items.add(item) + } + } + + list.value = items.toPersistentList() + } catch (e: Exception) { + logError(e) // kinda ironic + } + } + + val context = LocalContext.current + val scope = rememberCoroutineScope() + AlertDialog( + containerColor = colors.background, + onDismissRequest = dismiss, + title = { + Text(text = stringResource(R.string.log_cat)) + }, + text = { + LazyColumn { + items(items = list.value) { item -> + LogcatItem(item) + } + } + }, + confirmButton = { + Button( + onClick = { + scope.launch { + withContext(Dispatchers.IO) { + val date = SimpleDateFormat("yyyy_MM_dd_HH_mm", Locale.getDefault()).format(Date(currentTimeMillis())) + var fileStream: OutputStream? + try { + fileStream = VideoDownloadManager.setupStream( + context, + "logcat_${date}", + null, + "txt", + false + ).openNew() + fileStream.bufferedWriter() + .use { writer -> + list.value.forEach { + writer.write(it.toString()) + writer.write("\n\n") + } + } + dismiss() + } catch (t: Throwable) { + logError(t) + showToast(t.message) + } + /*try { + val date = SimpleDateFormat( + "yyyy_MM_dd_HH_mm", + Locale.getDefault() + ).format( + Date(System.currentTimeMillis()) + ) + + val file = FileHelper.logcat.createFile(context, "logcat_${date}") + ?: throw ErrorLoadingException("Unable to create file") + val stream = file.openOutputStream(append = false) + ?: throw ErrorLoadingException("Unable to create stream") + + stream.bufferedWriter() + .use { writer -> + list.value.forEach { + writer.write(it.toString()) + writer.write("\n\n") + } + } + dismiss() + showToast( + txt( + R.string.logcat_success, + file.absolutePath ?: file.uri.toString() + ), + Toast.LENGTH_LONG + ) + } catch (t: Throwable) { + logError(t) + showToast(t.message) + }*/ + } + } + }, colors = Colors.whiteButton + ) { Text(text = stringResource(R.string.sort_save)) } + Button( + onClick = { + clipboardHelper( + txt("Logcat"), + list.value.joinToString(separator = "\n\n") { it.toString() } + ) + }, colors = Colors.whiteButton + ) { Text(text = stringResource(R.string.sort_copy)) } + Button( + onClick = { + try { + Runtime.getRuntime().exec("logcat -c") + } catch (t: Throwable) { + logError(t) + } + dismiss() + }, colors = Colors.whiteButton + ) { Text(text = stringResource(R.string.sort_clear)) } + }, + dismissButton = { + Button( + onClick = dismiss, colors = Colors.blackButton + ) { Text(text = stringResource(R.string.sort_close)) } + }, + properties = DialogProperties(usePlatformDefaultWidth = false) + ) +} + + +@Composable +fun LogcatItem(item: LogcatItem) { + val interactionSource = remember { MutableInteractionSource() } + + val color = when (item.level) { + LogcatLevel.Fatal -> Color.Magenta + LogcatLevel.Error -> Color.Red + LogcatLevel.Warning -> Color.Yellow + LogcatLevel.Info -> Color.White + LogcatLevel.Debug -> Color.Green + LogcatLevel.Verbose -> Color.Gray + null -> Color.Transparent + } + + Row(modifier = Modifier.fillMaxWidth()) { + item.level?.identifier?.let { value -> + Text( + value, + modifier = Modifier + .padding(2.dp) + .rounded() + .background(colors.onBackground) + .padding(4.dp), + color = colors.surfaceVariant + ) + } + Text( + item.date.toHumanReadable(), + modifier = Modifier + .padding(2.dp) + .rounded() + .background(colors.surfaceVariant) + .padding(4.dp), + color = colors.onBackground + ) + Text( + item.tag, + modifier = Modifier + .padding(2.dp) + .rounded() + .background(colors.surfaceVariant) + .padding(4.dp), + color = colors.onBackground + ) + } + Row( + modifier = Modifier + .height(IntrinsicSize.Min) + .fillMaxWidth() + .clickable( + interactionSource = interactionSource, + indication = null, + onClick = { + clipboardHelper(txt("Logcat"), item.toString()) + }) + .rounded() + .ripple(interactionSource) + .padding(5.dp) + ) { + Box( + modifier = Modifier + .fillMaxHeight() + .width(4.dp) + .circle() + .background(color) + ) + Spacer(modifier = Modifier.width(5.dp)) + Text( + item.message, + color = colors.onBackground, + fontSize = 14.sp, + lineHeight = 15.sp, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatParser.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatParser.kt new file mode 100644 index 000000000..df1dc7077 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatParser.kt @@ -0,0 +1,140 @@ +package com.lagradost.cloudstream3.ui.settings.logcat + +import androidx.compose.runtime.Immutable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.Closeable +import java.io.InputStream +import kotlin.time.Instant +import kotlinx.io.Buffer +import kotlinx.io.asSource +import kotlinx.io.readByteArray +import kotlinx.io.readIntLe +import kotlinx.io.readString +import kotlinx.io.readUShortLe +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import java.util.TimeZone + +fun Instant.toHumanReadable(): String { + val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).apply { + timeZone = TimeZone.getDefault() + } + return formatter.format(Date(this.toEpochMilliseconds())) +} +@Immutable +data class LogcatItem( + val date: Instant, + val pid: Int, + val tid: Int, + val level: LogcatLevel?, + val tag: String, + val message: String, +) { + override fun toString(): String { + return "${date.toHumanReadable()} $pid-$tid $tag ${level?.identifier ?: "?"} $message" + } +} + +enum class LogcatLevel(val identifier: String) { + Fatal("WTF"), + Error("E"), + Warning("W"), + Info("I"), + Debug("D"), + Verbose("V"), +} + +/**https://github.com/brudaswen/android-logcat/blob/main/library/logcat-core/src/main/kotlin/de/brudaswen/android/logcat/core/parser/LogcatBinaryParser.kt */ +class LogcatBinaryParser( + private val input: InputStream, +) : Closeable by input { + val source = input.asSource() + private val buffer = Buffer() + + /** + * Parse one [LogcatItem] from the current [input] stream. + * + * @return The parsed [LogcatItem] or `null` if stream reached EOF. + */ + suspend fun parseItem(): LogcatItem? = withContext(Dispatchers.IO) { + val firstByte = input.read() + if (firstByte == -1) return@withContext null + + // Read v1 header + buffer.writeByte(firstByte.toByte()) + buffer.write(source = source, byteCount = 19) + + val len = buffer.readUShortLe().toInt() + val headerSize = buffer.readUShortLe().toInt() + val pid = buffer.readIntLe() + val tid = buffer.readIntLe() + val sec = buffer.readIntLe() + val nsec = buffer.readIntLe() + + // Read additional header fields + buffer.write(source = source, byteCount = headerSize - 20L) + + val additionalHeaderBytes = (headerSize - 20).coerceAtLeast(0) + buffer.readByteArray(byteCount = additionalHeaderBytes) + + // Read payload + buffer.write(source = source, byteCount = len.toLong()) + + val priority = buffer.readByte() + + val payload = buffer.readString() + val texts = payload.split('\u0000', limit = 2) + val tag = texts.getOrNull(0).orEmpty() + val message = texts.getOrNull(1).orEmpty().removeSuffix("\u0000").trim() + + // Clear buffer + buffer.clear() + + // Convert raw values to item + LogcatItem( + sec = sec, + nsec = nsec, + priority = priority, + pid = pid, + tid = tid, + tag = tag, + message = message, + ) + } + + private fun LogcatItem( + sec: Int, + nsec: Int, + priority: Byte, + pid: Int, + tid: Int, + tag: String, + message: String, + ): LogcatItem { + val date = Instant.fromEpochSeconds( + epochSeconds = sec.toLong(), + nanosecondAdjustment = nsec, + ) + + val level = when (priority) { + 2.toByte() -> LogcatLevel.Verbose + 3.toByte() -> LogcatLevel.Debug + 4.toByte() -> LogcatLevel.Info + 5.toByte() -> LogcatLevel.Warning + 6.toByte() -> LogcatLevel.Error + 7.toByte() -> LogcatLevel.Fatal + else -> null + } + + return LogcatItem( + date = date, + pid = pid, + tid = tid, + level = level, + tag = tag, + message = message, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt index 8d322fd04..e0661721a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt @@ -86,6 +86,7 @@ import com.lagradost.cloudstream3.utils.FillerEpisodeCheck.toClassDir import com.lagradost.cloudstream3.utils.JsUnpacker.Companion.load import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.downloader.DownloadObjects +import com.lagradost.cloudstream4.AppSettings import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import okhttp3.Cache @@ -481,14 +482,14 @@ object AppContextUtils { fun Context.filterSearchResultByFilmQuality(data: List): List { // Filter results omitting entries with certain quality if (data.isNotEmpty()) { - val filteredSearchQuality = PreferenceManager.getDefaultSharedPreferences(this) + val filteredSearchQuality = AppSettings(this).ui.filterQuality.get() /*PreferenceManager.getDefaultSharedPreferences(this) ?.getStringSet(getString(R.string.pref_filter_search_quality_key), setOf()) ?.mapNotNull { entry -> entry.toIntOrNull() ?: return@mapNotNull null - } ?: listOf() + } ?: listOf()*/ if (filteredSearchQuality.isNotEmpty()) { return data.filter { item -> - val searchQualVal = item.quality?.ordinal ?: -1 + val searchQualVal = item.quality //?.ordinal ?: -1 //Log.i("filterSearch", "QuickSearch item => ${item.toJson()}") !filteredSearchQuality.contains(searchQualVal) } @@ -500,17 +501,17 @@ object AppContextUtils { fun Context.filterHomePageListByFilmQuality(data: HomePageList): HomePageList { // Filter results omitting entries with certain quality if (data.list.isNotEmpty()) { - val filteredSearchQuality = PreferenceManager.getDefaultSharedPreferences(this) + val filteredSearchQuality = AppSettings(this).ui.filterQuality.get() /*PreferenceManager.getDefaultSharedPreferences(this) ?.getStringSet(getString(R.string.pref_filter_search_quality_key), setOf()) ?.mapNotNull { entry -> entry.toIntOrNull() ?: return@mapNotNull null - } ?: listOf() + } ?: listOf()*/ if (filteredSearchQuality.isNotEmpty()) { return HomePageList( name = data.name, isHorizontalImages = data.isHorizontalImages, list = data.list.filter { item -> - val searchQualVal = item.quality?.ordinal ?: -1 + val searchQualVal = item.quality //?.ordinal ?: -1 //Log.i("filterSearch", "QuickSearch item => ${item.toJson()}") !filteredSearchQuality.contains(searchQualVal) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt index 0a561b471..d0c79c43c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.utils +import android.app.Activity import android.content.Context import android.net.Uri import android.widget.Toast @@ -282,7 +283,7 @@ object BackupUtils { } } - fun FragmentActivity.restorePrompt() { + fun Activity.restorePrompt() { runOnUiThread { try { restoreFileSelector?.launch( diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BaseComposeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BaseComposeFragment.kt new file mode 100644 index 000000000..3c3f55adb --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BaseComposeFragment.kt @@ -0,0 +1,43 @@ +package com.lagradost.cloudstream3.utils + +import android.content.res.Configuration +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.TV +import com.lagradost.cloudstream3.ui.settings.Globals.isLandscape +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout +import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding +import com.lagradost.cloudstream4.compose.Screen +import com.lagradost.cloudstream4.compose.createComposeView + +abstract class BaseComposeFragment : Fragment(), Screen { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = createComposeView(inflater, container, savedInstanceState) + + override fun onConfigurationChanged(newConfig: Configuration) { + this.view?.let { view -> + fixSystemBarsPadding( + view, + padLeft = isLayout(TV or EMULATOR), + padBottom = isLandscape() + ) + } + super.onConfigurationChanged(newConfig) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + fixSystemBarsPadding( + view, + padLeft = isLayout(TV or EMULATOR), + padBottom = isLandscape() + ) + super.onViewCreated(view, savedInstanceState) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/PowerManagerAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/PowerManagerAPI.kt index e3c7d68df..af3795834 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/PowerManagerAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/PowerManagerAPI.kt @@ -63,7 +63,7 @@ object BatteryOptimizationChecker { return isRestricted && isOptimizedNotShown && isLayout(PHONE) } - private fun Context.showRequestIgnoreBatteryOptDialog() { + fun Context.showRequestIgnoreBatteryOptDialog() { try { val intent = Intent().apply { action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/videoskip/AnimeSkip.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/videoskip/AnimeSkip.kt index df9d56217..687f746e3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/videoskip/AnimeSkip.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/videoskip/AnimeSkip.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AnimeLoadResponse import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.LoadResponse +import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.TvSeriesLoadResponse import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.app @@ -25,6 +26,7 @@ import java.security.MessageDigest class AnimeSkipAuth : AuthAPI() { override val name = "AnimeSkip" + override val icon: Int = R.drawable.animeskip override val inAppLoginRequirement: AuthLoginRequirement = AuthLoginRequirement(password = true, username = true) override val idPrefix = "anime-skip" diff --git a/app/src/main/res/drawable/article_24px.xml b/app/src/main/res/drawable/article_24px.xml new file mode 100644 index 000000000..5b0f45801 --- /dev/null +++ b/app/src/main/res/drawable/article_24px.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/aspect_ratio_24px.xml b/app/src/main/res/drawable/aspect_ratio_24px.xml new file mode 100644 index 000000000..429ed7237 --- /dev/null +++ b/app/src/main/res/drawable/aspect_ratio_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/autorenew_24px.xml b/app/src/main/res/drawable/autorenew_24px.xml new file mode 100644 index 000000000..4ac69c08f --- /dev/null +++ b/app/src/main/res/drawable/autorenew_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/battery_alert_24px.xml b/app/src/main/res/drawable/battery_alert_24px.xml new file mode 100644 index 000000000..3675de44d --- /dev/null +++ b/app/src/main/res/drawable/battery_alert_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/cast.xml b/app/src/main/res/drawable/cast.xml new file mode 100644 index 000000000..7600cb706 --- /dev/null +++ b/app/src/main/res/drawable/cast.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/colors_24px.xml b/app/src/main/res/drawable/colors_24px.xml new file mode 100644 index 000000000..d86304cf9 --- /dev/null +++ b/app/src/main/res/drawable/colors_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/construction_24px.xml b/app/src/main/res/drawable/construction_24px.xml new file mode 100644 index 000000000..00c976c90 --- /dev/null +++ b/app/src/main/res/drawable/construction_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/copy_all_24px.xml b/app/src/main/res/drawable/copy_all_24px.xml new file mode 100644 index 000000000..8e4db679b --- /dev/null +++ b/app/src/main/res/drawable/copy_all_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/dns_24px.xml b/app/src/main/res/drawable/dns_24px.xml new file mode 100644 index 000000000..2f9b4c8bf --- /dev/null +++ b/app/src/main/res/drawable/dns_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/extension_24px.xml b/app/src/main/res/drawable/extension_24px.xml new file mode 100644 index 000000000..ce2cfc260 --- /dev/null +++ b/app/src/main/res/drawable/extension_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/extention_download.xml b/app/src/main/res/drawable/extention_download.xml new file mode 100644 index 000000000..97f3dbd67 --- /dev/null +++ b/app/src/main/res/drawable/extention_download.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/extention_renew.xml b/app/src/main/res/drawable/extention_renew.xml new file mode 100644 index 000000000..a9e6e07f9 --- /dev/null +++ b/app/src/main/res/drawable/extention_renew.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/extention_renew2.xml b/app/src/main/res/drawable/extention_renew2.xml new file mode 100644 index 000000000..d34e91355 --- /dev/null +++ b/app/src/main/res/drawable/extention_renew2.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/face_24px.xml b/app/src/main/res/drawable/face_24px.xml new file mode 100644 index 000000000..152c278e7 --- /dev/null +++ b/app/src/main/res/drawable/face_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/filter_alt_24px.xml b/app/src/main/res/drawable/filter_alt_24px.xml new file mode 100644 index 000000000..0afa9b51d --- /dev/null +++ b/app/src/main/res/drawable/filter_alt_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/folder_24px.xml b/app/src/main/res/drawable/folder_24px.xml new file mode 100644 index 000000000..a1e978b69 --- /dev/null +++ b/app/src/main/res/drawable/folder_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/hard_drive_24px.xml b/app/src/main/res/drawable/hard_drive_24px.xml new file mode 100644 index 000000000..45fecb4d2 --- /dev/null +++ b/app/src/main/res/drawable/hard_drive_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/high_res_24px.xml b/app/src/main/res/drawable/high_res_24px.xml new file mode 100644 index 000000000..183f2ae69 --- /dev/null +++ b/app/src/main/res/drawable/high_res_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/history_toggle_off_24px.xml b/app/src/main/res/drawable/history_toggle_off_24px.xml new file mode 100644 index 000000000..30186016c --- /dev/null +++ b/app/src/main/res/drawable/history_toggle_off_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/keyboard_double_arrow_right_24px.xml b/app/src/main/res/drawable/keyboard_double_arrow_right_24px.xml new file mode 100644 index 000000000..2e564cea4 --- /dev/null +++ b/app/src/main/res/drawable/keyboard_double_arrow_right_24px.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/label_24px.xml b/app/src/main/res/drawable/label_24px.xml new file mode 100644 index 000000000..fb1d1b506 --- /dev/null +++ b/app/src/main/res/drawable/label_24px.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/language_korean_latin_24px.xml b/app/src/main/res/drawable/language_korean_latin_24px.xml new file mode 100644 index 000000000..55bf0cf21 --- /dev/null +++ b/app/src/main/res/drawable/language_korean_latin_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/light_mode_24px.xml b/app/src/main/res/drawable/light_mode_24px.xml new file mode 100644 index 000000000..8d278ec46 --- /dev/null +++ b/app/src/main/res/drawable/light_mode_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/match_word_24px.xml b/app/src/main/res/drawable/match_word_24px.xml new file mode 100644 index 000000000..d2a7334f8 --- /dev/null +++ b/app/src/main/res/drawable/match_word_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/memory_24px.xml b/app/src/main/res/drawable/memory_24px.xml new file mode 100644 index 000000000..7e4dd5e9b --- /dev/null +++ b/app/src/main/res/drawable/memory_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/memory_alt_24px.xml b/app/src/main/res/drawable/memory_alt_24px.xml new file mode 100644 index 000000000..21807a9e9 --- /dev/null +++ b/app/src/main/res/drawable/memory_alt_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/mobile_arrow_down_24px.xml b/app/src/main/res/drawable/mobile_arrow_down_24px.xml new file mode 100644 index 000000000..caab6850e --- /dev/null +++ b/app/src/main/res/drawable/mobile_arrow_down_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/mobile_code_24px.xml b/app/src/main/res/drawable/mobile_code_24px.xml new file mode 100644 index 000000000..9b78af094 --- /dev/null +++ b/app/src/main/res/drawable/mobile_code_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/mobile_wrench_24px.xml b/app/src/main/res/drawable/mobile_wrench_24px.xml new file mode 100644 index 000000000..5cc975528 --- /dev/null +++ b/app/src/main/res/drawable/mobile_wrench_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/movie_edit_24px.xml b/app/src/main/res/drawable/movie_edit_24px.xml new file mode 100644 index 000000000..41df964e6 --- /dev/null +++ b/app/src/main/res/drawable/movie_edit_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/movie_info_24px.xml b/app/src/main/res/drawable/movie_info_24px.xml new file mode 100644 index 000000000..7d7dfe9e9 --- /dev/null +++ b/app/src/main/res/drawable/movie_info_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/notifications_active_24px.xml b/app/src/main/res/drawable/notifications_active_24px.xml new file mode 100644 index 000000000..b3982c522 --- /dev/null +++ b/app/src/main/res/drawable/notifications_active_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/palette_24px.xml b/app/src/main/res/drawable/palette_24px.xml new file mode 100644 index 000000000..f701027cc --- /dev/null +++ b/app/src/main/res/drawable/palette_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/picture_in_picture_center_24px.xml b/app/src/main/res/drawable/picture_in_picture_center_24px.xml new file mode 100644 index 000000000..60d7e133b --- /dev/null +++ b/app/src/main/res/drawable/picture_in_picture_center_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/pip_24px.xml b/app/src/main/res/drawable/pip_24px.xml new file mode 100644 index 000000000..5fba5b85e --- /dev/null +++ b/app/src/main/res/drawable/pip_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/play_arrow_24px.xml b/app/src/main/res/drawable/play_arrow_24px.xml new file mode 100644 index 000000000..b7b67fccc --- /dev/null +++ b/app/src/main/res/drawable/play_arrow_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/plugin_lang.xml b/app/src/main/res/drawable/plugin_lang.xml new file mode 100644 index 000000000..ac33489ac --- /dev/null +++ b/app/src/main/res/drawable/plugin_lang.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/quick_novel_icon.xml b/app/src/main/res/drawable/quick_novel_icon.xml index b19aaafea..8f0b3cf3e 100644 --- a/app/src/main/res/drawable/quick_novel_icon.xml +++ b/app/src/main/res/drawable/quick_novel_icon.xml @@ -1,31 +1,23 @@ - - - - - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="1000" + android:viewportHeight="1000"> + + + + + - \ No newline at end of file + diff --git a/app/src/main/res/drawable/responsive_layout_24px.xml b/app/src/main/res/drawable/responsive_layout_24px.xml new file mode 100644 index 000000000..4d3ea085f --- /dev/null +++ b/app/src/main/res/drawable/responsive_layout_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/restore_page_24px.xml b/app/src/main/res/drawable/restore_page_24px.xml new file mode 100644 index 000000000..65a7d274f --- /dev/null +++ b/app/src/main/res/drawable/restore_page_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/save_as_24px.xml b/app/src/main/res/drawable/save_as_24px.xml new file mode 100644 index 000000000..0c3c01414 --- /dev/null +++ b/app/src/main/res/drawable/save_as_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/save_clock_24px.xml b/app/src/main/res/drawable/save_clock_24px.xml new file mode 100644 index 000000000..c86924cc1 --- /dev/null +++ b/app/src/main/res/drawable/save_clock_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/screen_rotation_alt_24px.xml b/app/src/main/res/drawable/screen_rotation_alt_24px.xml new file mode 100644 index 000000000..a1d01a5c6 --- /dev/null +++ b/app/src/main/res/drawable/screen_rotation_alt_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/shuffle_24px.xml b/app/src/main/res/drawable/shuffle_24px.xml new file mode 100644 index 000000000..d25985f8b --- /dev/null +++ b/app/src/main/res/drawable/shuffle_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/skip_next_24px.xml b/app/src/main/res/drawable/skip_next_24px.xml new file mode 100644 index 000000000..3beb9c760 --- /dev/null +++ b/app/src/main/res/drawable/skip_next_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/subtitles_gear_24px.xml b/app/src/main/res/drawable/subtitles_gear_24px.xml new file mode 100644 index 000000000..d84e9f883 --- /dev/null +++ b/app/src/main/res/drawable/subtitles_gear_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/swipe_24px.xml b/app/src/main/res/drawable/swipe_24px.xml new file mode 100644 index 000000000..9b2183713 --- /dev/null +++ b/app/src/main/res/drawable/swipe_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/swipe_vertical_24px.xml b/app/src/main/res/drawable/swipe_vertical_24px.xml new file mode 100644 index 000000000..48048f334 --- /dev/null +++ b/app/src/main/res/drawable/swipe_vertical_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/tooltip_24px.xml b/app/src/main/res/drawable/tooltip_24px.xml new file mode 100644 index 000000000..5770d07b4 --- /dev/null +++ b/app/src/main/res/drawable/tooltip_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/touch_double_24px.xml b/app/src/main/res/drawable/touch_double_24px.xml new file mode 100644 index 000000000..f008bd374 --- /dev/null +++ b/app/src/main/res/drawable/touch_double_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/visibility_off_24px.xml b/app/src/main/res/drawable/visibility_off_24px.xml new file mode 100644 index 000000000..50ba19a57 --- /dev/null +++ b/app/src/main/res/drawable/visibility_off_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/wifi_proxy_24px.xml b/app/src/main/res/drawable/wifi_proxy_24px.xml new file mode 100644 index 000000000..92798c29c --- /dev/null +++ b/app/src/main/res/drawable/wifi_proxy_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 27f186a00..970850882 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -137,7 +137,7 @@ Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. - In case of copyright infringement, please directly contact the responsible parties or the streaming websites. + \n\nIn case of copyright infringement, please directly contact the responsible parties or the streaming websites. - The app is purely for educational and personal use. + \n\nThe app is purely for educational and personal use. - CloudStream does not host any content on the app, and has no control over what media is put up or taken down. + \n\nCloudStream does not host any content on the app, and has no control over what media is put up or taken down. CloudStream functions like any other search engine, such as Google. CloudStream does not host, upload or - manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, + manage any videos, films or content. It simply crawls, aggregates and displays links in a convenient, user-friendly interface. - It merely scrapes 3rd-party websites that are publicly accessible via any regular web browser. It is the + \n\nIt merely scrapes 3rd-party websites that are publicly accessible via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream at your own risk. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d530b59f..87e447ccb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -139,6 +139,7 @@ No Plot Found No Description Found Show Logcat 🐈 + Logcat 🐈 Log Picture-in-picture Continues playback in a miniature player on top of other apps @@ -458,8 +459,8 @@ Random Coming soon… Cam - Cam - Cam + CamRip + CamHD HQ HD TS diff --git a/build.gradle.kts b/build.gradle.kts index f58fb584f..dc98269b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,4 +7,6 @@ plugins { alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.multiplatform) apply false alias(libs.plugins.kotlin.serialization) apply false + alias(libs.plugins.compose.compiler) apply false + alias(libs.plugins.compose.multiplatform) apply false } diff --git a/desktopApp/build.gradle.kts b/desktopApp/build.gradle.kts new file mode 100644 index 000000000..0008a854d --- /dev/null +++ b/desktopApp/build.gradle.kts @@ -0,0 +1,55 @@ +import org.jetbrains.compose.desktop.application.dsl.TargetFormat + +plugins { + alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.compose.multiplatform) + alias(libs.plugins.compose.compiler) +} + +kotlin { + jvm {} + sourceSets { + jvmMain.dependencies { + implementation(libs.bundles.compose) + implementation(compose.desktop.currentOs) { + // compose.desktop.currentOs imports the wrong material 2, so we exclude it + exclude(group = "org.jetbrains.compose.material", module = "material") + } + implementation(project(":shared")) + } + } +} + +// java.lang.System::load has been called by org.jetbrains.skiko.LibraryLoader in an unnamed module +tasks.withType { + jvmArgs("--enable-native-access=ALL-UNNAMED") +} + +compose.desktop { + application { + mainClass = "com.lagradost.cloudstream4.MainKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "CloudStream" + packageVersion = "1.0.0" + + val iconsRoot = project.file("desktop-icons") + macOS { + // iconFile.set(iconsRoot.resolve("icon-mac.icns")) + } + windows { + iconFile.set(iconsRoot.resolve("icon-windows.ico")) + // menuGroup = "Compose Examples" + // see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html + // upgradeUuid = "" + } + linux { + iconFile.set(iconsRoot.resolve("icon-linux.png")) + } + } + + //buildTypes.release.proguard { + // configurationFiles.from(project.file("rules.pro")) + //} + } +} \ No newline at end of file diff --git a/desktopApp/src/desktop-icons/icon-linux.png b/desktopApp/src/desktop-icons/icon-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..394459001fdb5237f04624560e66550660044d05 GIT binary patch literal 8334 zcmZ{JbyO5i)b{SOz|y6JNVt@A%MYZ~rI8M4kj^Eggk3_A5^0bwl~}rS6{G|f>5yd+ zkdp3NKHl^F_5SgmIWu?anVIv6G8LP7d~6aWBFsHwvA?j-tuGKA=^)cfs5 zd?&z;&t5(Q0BRG-u5AeK+U&NfdM^QhAZ`Ev83_Pf-gO~2002J`0AR}+0FcQ902n-T z+H_^_28dp3sK5ZX|C0(jzoY;FY^7?jX9fZDJGp@$IgLZs7540nmgMB*&dO{#m5ZW$NlIL%X||&d!1v z0*?NUWJ|%Z9fAM9!rz+;59e|>TWjztb{8Bi{_i&U2)v>C(J{K#TJj}c$Q@JdQ?kFh z4|a-EsU}|KvIcAr2O%wkj{#vJEbJiK0&>l{VscHu%IQhaz%oY^4>~!(g0R=8;|tp? z7V#R5RXo0~^i zJ%XoU+sH-q6#a3}5oguWU}DgM*;cAe;8W+p{r~nw_8;$?r!AGdwc7^|$Z|+)FFA(c zm2oH26V(-mbG_kCwNrV@C{sUG>FytmD`+~jf7sru*P7GkH+cYloD?x7vfl1Q2KNRx zf=*LBdx)Ho?C@+Kp1PWXh`QU4H;2LJHmFA?xUuPl!-Em8iT4+#U!?n3O4i1}pO-e6 zl=Q5IMqHWB;r}i*xqnQIo$s*^9L!;70Ac}evD`^QBCq+l)Jz;yUL(wMg7NR&8}EUVM07T4&#n`+HR6W>@`8z zEcnIU44$=O8Q@ww(@i&<-)@3k5zdtu!Y{!WuqqMst~bM@gk3omTb!(41$hhG42y*q zf^~OT1I?g=KE$50KXX4MDMjzd9V9!&aBBZGj6z}pTPJGkHiUD!BG4K|f7SCAOCnl3 zX7>I$)iBJiaOwJRVX6sivD*x<1K!OBpDr4mXI0A?AXUx!yQK(?aIK2?NnmH6Wc~Xum_HWGbYA_) zVsAVGVV{D7cuuH43Nu^3ZPgA4dF{q##ctv4ZE{9uS2}8gQ(?Wv57^-P(U%X6(i5D& z93B}82VaXuKWysTW)zfHX*kW#GzBC)Kwb~YMRR#=aky=zjsnPq`w@$dgPEu zcaVo+6C>sh^=ak&aknwg;%0B+*0G9Qv8pyfwJRY>x>&E>PxorjZnHgxQQ^VZ!gadM zD#?Pj@A%M~U72FrWZNww`SdhsKe(dVnwB72tvRpl%liw(LCgK%j@`3<=usGzsX$sR zGz?_6Hb?rFZFaY_yID!Kum?R-azq09i$&FQaDMbw&p~+5F;RuWgZQa(Wh>Uc@)4`NV$Qkzk*F^Rx(i73`XRF9>V&&eG%aJ(thm zp|&ao!j8o#{=20{(1xH&uEn-Zv(t;u{~YEjA=LA69bvu-^PxNp`+wc z-y(;=ogc}MHMHbCzOe=G2b+aQwosiXegK;`5Wu6rL!$>;IEAMr`vi*}l25G+(S#*O z#CbKnAAg9s*5*r~q}x4~-0aE~{ycSg)48@&Yg zZ9eJHPsLrkef}x`I)D5811bDnB51Cl#foExGmd`6_XIkZnQ+$H7NgcE1wXMq)x!}C zTbiTn!2=zL4Ev%PJa#^b?3y&K+;+?b^cZKAB;>r@*xmeQ6=bY$Vy@8`3&^6JC|MEj zI6IpjeeB7T_TkkODaeih>jCySnqM-@_6f^Y>xT$BjiR3;UB9Nvx92$FM$9TmKE4|3 zPMo`6#KobgI)Bvk&i7NX6g<}&4&8Z_u3U_M?t^Rwt_NJKpkCOG(c`T2v*Nlq!2?lp zPUBt*@)sx5YJ>YbM#)kVvPHUg=OX1}eiXd7hte-X`<3(@EDAP%Qb)Qu$1bSOTpq04 zbo!A3$wdGiX+s^ypJ^4JlJuE9d>(K#O_FYNU^HIqtS8*mLqy`qv`GpN*jdUn*x6dr z!fg3$Y_T~w9hq%doIUuC1%_9#rW$eTJ{Obxv6*3oJJz5Q6~E9=-qeTb3tJCI!oSbA z0!mgQstCHaVNsu{*{{O0QP(uuNK?VjzifnGWLsg{;^2;+KTGM47qup&6O-|%D~t-& zu5wnS-6f~EtwQPBYp=eQd;5)ybnvDR)q5d3fi%&ljqfo|j{=OPU$lp56g}Mz#y0l;X2sXve&mCRRlT z9xyKtr)LI=UDQ*w3^gD9HrlelwB>g9HqC@0v65!GHnRauoKs7c&qvUw3mXe9(VKMI zVwczXB64^-n8Vv1OjDd%GcKr&mC1k#C}!nLu_+d9d23vY_nWC%p|zs+*JQ`qS}6YJpKHRXwYgvCax6?1@d2#jR2h(Oc`$z$@3>+o{)(f(mMMh60Mv z;zbVPh$nDl=ayt~F(-9^lR~08e77;9=Ac}m2(gnLVqIl@q&{wGQ=<=#*m@n|VxLTh?AfVpC{Juk2E23$%T zOPzXC_FBYi_&AG+TiNo@tPi4pzZGsX(b!z*d-UlAt+N1VI2jh<)^CfzT?isEsqOVz4KjEM*k;EMrl3th-1ck*P(9QCAqf;cjEK0JSrCeLpI0g6%h z9eDq`wZRZInIm}2;2jaW%Rq;jwQa7CrpeQ)(0Xl*)JF+M zc~}?>G8{f_ODeT_9kjk9>L#7@++;Bi>UU(4btL24(7ex(K?nOpXGDYveA@Y9rrHkV z2AtNn4QQx(6n4*fea2Hr_%Y>qghvzhe6Mw5vHQi@rzw^Y)Wpij0$$yps^(PEzwiXD zK1{&QQr{HuK;!d9pcd_aV8+K#VCwGcqx)>QYpHIk#i`|BKZvHbg#TOZv;BAEZUM~v zl?e8$+MAMzHx>d?IRuZH)eDduiqWHUJ%Moi{qVf158cyFd>*m*e7~db9#Ku5#HH;< z4}f)1zlWef;o@kxt(ps~hN}g^dd>D?exa+=SLzuvdGQH}n>|xZ6#oWu2e8y=(!=o2 zD)4BOI=k91J|slr7o;G9wQ297*|&N}xQ~!0dF!^+$%c@*>Vo{>m_KunWT^jCOx{~K zsUtqgE8V%;>$LcZF7LZ1Fu+b^il5TN?G3Y#;}Hd1`)N6;Cn9B5M6Stq<*KKPA~J;8 znlb1K{(58eDl3-FR`!CPZm`?>)f@3qu}Vt)GJrjq^HeU_aj-|HFtk;u_ReaiJ!Rlo z&A`AolbkJ>%UCbN?;b*R*F>Wd_LD<(nC+A6qO8 z_xWCF=Qv?X=#^A6>7fxW{j|qr4PbHlSRI7MHBtO%l~d0APk!-Fd~m4lB*Q<4 zmSfjV&;37UgUJQUdANE)Fn!J6pmsOUm63bS*E6%fyfsp+tnDZ(zY%T?u*z9VGxiczEN3AxSw+mHnynft~qT|jd1C&Ukuy~NJcrO7^ z`pxiNtGND`qG|4K$L^NVCId4j2-e6K)N+o>23$zdf36UL#1)#Y)GZ$`mz_5JGq|ym zq3(#$apxBUmJl_q#{+gH>PkcujMW}_44x->yYee&fqoCjm=SfKOO_sl*_6`Kd?+86Zz$pdk)Jle5e+8Eq{N<4%7v z%AcK(@BMyJ)(x9cbj(0%a>lb}e(w!;=^5Z-%7|JCwDKhTF0pv|8q`RSA`%r-7KrBM z(H0Zb7IV-0p{nwM=To8bB7bFJE|f1W{*AJ!)t2oD;hy!vHugvP&N`iMyYvT_L`T#F zQvzFV;ahH(nVFt|l>=D=W=7H_mFaf^5YrR7@`U4!3K8!p=fHIFgC%{fJlMw9Ms;yw zc}F2Leo*SpuSb>$W(F+2-K`}^stwjtA>A-_KY=Sp-duT}>oJ`*34P3%%STH;mPLyU z5mrRD{=vGa*c|VO{L6$kV-!S(NaoMXG5L<&diRBJRE&I=ZH)J6WRf)Z_`sO%5+ z6^&Pjg6MDiZ$OQoer*V=uEKaEZc-zbqLOjL+ArtPb4Fz971DbYY&Ii5eh_qXdTxzL zOswp@9J3!L4KMA~KPC)*#nRQRA7!i!bo9=wiUTm&_DVlnaNADk{?@nc;imDY<<)Y? zmUUJwhxkqx`G3>p!urS2a69%66rBe;a|BqD1o_x09lYaY=7(5%(zJL;>HF%xf8wE> ze}+{j@m0ONA8V&AU^1zHGRDex^wpGE1dO?!p_a!$@eM}|ZY_ryo0b+GNiiurw#rsH zmXoIWFIWD6{iwp6J~8UKZj!xezWTye+~>l5at!YNLtWH}?e-_z78b&8Inednez>jt zjV+n!%;IP$eOhn4ge+=iQIPK0QWXM8q!MJBgEQ-&1`a|*{?lCgyz@g>4y4aRrzNYf zD2>YpXf>*JZTUbZJ)erP8h1jDvEBQkYvd}RU&ec0S`QCq*lI#^ z^--UQoNY;7l(G5-b5c(`vklm^RzcXMNm6sauF;@5Q&?mhzFDrsi?k`cKDM5uA=#^B z4$OJtZ_777=Ed;=&o-h~5LK?2l`e)pat9jA#`NZzUFS0~SOz*o=|}%LrSQu9`B{;& z(JXAl+OFKf3c;QbMrQRuH>W>eCenEh+Uh0kaOsU(0r(-B9z6$ z(GWtdA6&C|;1 zf}lvrhSY2_S)1%94Y$n#XUu`e)f-IM0qJIJ*Q#M-04cW?dh$9GFh2V0m7T8m z$l49IOjTC5=ratUJJC2pok$EC_JJGk5-v5Rm}8tUtP(>;Xb z=I1%VCE?*-x}2b88<8Gzw|TX((#%erabx!pS-4@IIF07RnH=d7M!5pU#p+BM3hul` zy9!*~|K>3WHoon~LtO$E?<6pPaTB#jWhk~!& z`5;-J`-ZrmvEXJ($l_hbS)@FCR&_Ph*u{D_38;MjZ1$6=Z}Xm1@oZ2EQkT;y#zT4e z9k?adCO*}20D1Ce9$uNCvyAWQf4S$?+Gy>;2mI{rmgfZK8F_@Hruzv=8H|WmMhvH3 zJaRWx9-6wf0W9@6z?hA&E*Vc_Zkfy4!Bt+Gx=8i5 zJ$PFrINbMIJ7?nNWgMXP0^N6y8)^Htqzd*V0c=WaBTirLp*WHONzq>`tt5N?nZors6#qC?B0yTfOslBe zLls_0qTjZnS^uedPC$_w;k{x4k8-_yx_v~k9gUv=AL;E*O*NJyVwd9k`y7 zz0d~11&I(}Q^lZ%etd@cEi@PPA0as9u-Ur3N9%rYtSR(rXT=sBLLVnSId{>h1H!EW zlX_Z&=Vm}`gdL3oSH$YPuvLD8piswG=)5;%F@U~bW;D)w3qA6rcOO(|OB$x{V>0Fb zI;wFXv&kK~e!cg-Zk&CLQ^UFQeq(!F3WIgQ>=*O1lv9@8(X!{0X9xm!zlf=h{$z)1 z@FHqj*QEqcKhq~)3q1j~v?@+NJDo|7k!RR%N}We2!|+sTxmM5zl{ZomfGqM@cg9#X zo=0ydmU!SAZdvmK_f$J{F6wnaEt~^=mUf8tI{;-4iP7#Ln*ZY4Z9 zYUV66PtV2{paR*u=MwuQ<6MY(ikc&u0(;s4d&fT;qP2;)KP_|Epv&&H<2(lHO3=e@ z)C>b(T0Wuct%EXBHtYGgg?$vV!l~zw7n`1N4Ae2*u8tX47u?{fJT@5{i!M!7oLeg7 z>aoQ6p_PT-bqJ2#ab1=lGtggoJh+A5t%+7r0xWWVc3`nh;&{g$rcif#)dhr;0dE`L zUyG)_d|Uyhm;cHAbyR_Q)qF(ulQnv%5!{O$If8+8I94_ofP+BpKkk6sxa z$_hZ)2tO>Wb9-YEx-oltv<06!HbLAC)50+W%saP&i&ki}LRo|f?};-_;H12i&Cx*R zM1?cXY6zkz#P&F5;HT+!-TRL8q3N_dFjj2ewe+5P8HUMr_l+z#UM9!G~7yV>DHGBdR)t(Lq9f219LpAgSsx}C_S zcG%z-zk(t{R6q*6y$86SWxUUUgj-j?|5wYo@9h-$Gf#)t&%pJDEtzx9F`uni@5FiQ zRlZg{-~vUg0reJugtMiLa+=L9S=YLF5%LILyKUFnK=C0&O zVx3Lk-A3wiG7*V0Je&pVXYYMsBwgI5Q6H*i$L0 z{Lbb)cKVXd{obrI+l@f|bYO^kW2lL1h(9&ByE-+9H?}CAF>4(YvE)m8{xl76MDxuq z`7tCM=6XY5%~q4^E2y_7QOEQAC@lY_>w>yvhlF2+{TB=M z#$wZCS@buvFtLDGf!3*#qu3V(c0jCsqDMi60Ut?0f~YaGEtaao2&R528{@+YZwzDm z*meyGc;$LSh&m}odJSbW%{o48rSmVx3iAQYY3=w&?KwSTbU;A>3%)dfZ!%}|`vDeo zBa}7>DgnM`Jg?Q;`uU7n);cV|Q<&em4V_qS5Lg01^Ch?5!N$@Q?%v?sKoaNR2=q`0 z7euo;xW-UvEtYBCUX;p5kjPis#R~rBFV1B9O+kOnGc5le;kgnQ+WZ&0c4YF4NX>q} zJC)IwX$3xgKQMoRtcgeQ9i-_Mqt6;v|ds3jcWoEifK)IhraQ<~@iD`e4 z(>RutlT;HdNktU#}&DsF&(Z`&7n@n!#4N zY33-*&EJuG6wNx*d?$Q81zrlrGnGzKrRpU*p{z#bZSA%GAmxX`6h?td_gKzOL-pJ@)q~;KRoM{yo;}s|nkLc|HjtrXLE8cwg!Tvw zazG=X-g)o;0fg=)q0rpcf@3_;PL0cAy-s+ItwVYDs>*7c#s3r{qO}7#my;Q zu&$Byll7cG3~(2jTbz7aNy`FS*LCzBJmt^)eZg*V+Gz}nQnCwHCMumIvK@9AuT{G- zkooufs68%ocNT~cTz9PlwZ{|Zq!m-hTVK>$AV0=eLV(!_1 zIsmT@Rg{FT@~d17P#ZjYeu|)fnY!bZS1pGLO?>aW07*JAbW&+|#J*AHl_b#QEB(B# z_Qdf9{>m;x^w)xNKya5zE{ATTk732+2H*Wsm;0YP{L_ECP9d#! zehqsG8A12ufLlBfLt(N5Ycy@@_jHdQ`ZRVLltD)%d7Xqns#^WbfAbx-;BD~mPj;D! z_gpCVs@H)CNm|wW&;i*zt8&=;AoT9DJ(8NQPs*birz~w*X+@~cvj~P00E-b=x&p3} z%sBQ&dGUPm*Ob;8Ecpk2Nybj!7*M=*kl$C2zyw!J;1wKK&5?}31Ri%Gd@f(5SH5;O zzVT|uI7q1K4Dhg67*iNC z8EuStjQJY#^uNv2-)Ydl8LY3-lewSEio#k(%xvAevQoxa#u1D&8CNlWz<7x93}YkX zO~wa|4;lYp{EM-lp)~gEfBT32&IkH88}&6F($~C7-{S~<&(dz5%j_krSxeeL7$J|_ ziWw6bCo!&J{FL!BV=p-bRvzJB`kpWAXTYeGx{ z$Z3t=r>==9E&2a-fqx~5?tZ_&oBO}7pYay`%oDnJC)+bvi<gUl37rC5VsoyIrr})eNo^m5H@JX43VWhCuQn#^ZD#>7|7YD&dnAguH`W43Fz~ zAL}X;>mKznb9eJ-i~cdL*2G!IjJW%hSBr#C`ym)*mzuuOw&m^Y0X^tbdaCr0J_L%kuBK ztSh^y3u{?6cdLH}V>`uHmTmDf4{?p;@7s0Ri`m}512g^3TE<-ztB^zkJ{FmW46e!F z@6vUVsE({<(9BljHylv%9E6Yblu7jV@vQFU1Pmw)(+?Os`P7u0GdA zU0O@Rq`)xr{tZgp(pUr&W*vQ#Vm*KrL6Rl~HH7gCiWO#8i#{xZ2eYp3XAE=It+g&p z3JAL*$W#WK-poyy>C$zKgIslN%`kItYdT{aIR$6^{oIC`CE8BTwl6R{Xl9t1<~*G7 z4mkz(5AwePEiwbCOVsTGUDq~EO0mtRV8=85LYDk}6;h|DFK+WxwAA4e&16+Prj)k`(fIEbk zpkIkV$luJ^Lzevg6;kKemEut9Cn7NO{9DN>02}c3Ur5~}?Tu|5VSQEvlIp~k-)qT| z-y)s^>lwqzu|79}PEwud=6+7j3tP9xLLgkgvW?su-@Q2)D3lS~Bne=+0t`eCf^Z{Zb$Yw$cI7VxweKpM(sek}Zg$+bn~aM82TVadPZ zh7an8u&uv^*AUJoC{&OgHFtr`^weT9kIc%$f-Q_&X2<2zCUK=?QlJWg@xA;?qSi)c=8}41OY&0ON@uG!LNm1Fu8XLIo!-zFdU+o z!hWr9YJ*=3zabo4P0o}Y;c3QAT7R73^(TE(8~j@M4dEcR*>5L@qgio-q&6xq zenYtU6^b>~88zuv(8a9QH?zU7MNlAIe3qPz*fQ=0iK?*0p>=;!MxH7xtx*F;x2j>| zk5l#SH>=4DA6AW@ep*dGd!1@oyk0e(xmHa*^$Arw`!+SGYOyLAGFKIqPEnyyX#k!- z@G#w=!zc@G5H1E0T&!=jpKSK0d*p>wC|s%r*Bqn9Ex1`VpS4=ex#UgNe$_t4yBhCl zysz3d&iiah=M`3;X|G!Gy`j>&W41#snbU z2}ao-&^NK}Pd6wmtyZIFUZ`4@yvXFXiVdp0P720RRn*>O<~R#bu4YmqS1x+{BLL;H>N(k+#VMBP@J*RA1b> zKN|Uk16BRJudBIV-ooU6Ga~|+{HYzu3*Z70U4Opt%>!}e1 z8aURp-}wKLK~vPg(F@ejhSSxc>f=<|@Hr}!UlRA7P(i60zu;CT?+#7=3<%)L57fb@ zZckYNz}R`utI{Ed_jeJPalkanKsG3>f%(?bz4WQ{XHi+L8q#p28aMx&YWk8V)tt-L ztG25)FezVSvTky~f1Z2!1~v17b!yVW`_zajr>KLD{XYL6$iFLN0%*JJeKld>wzLEQ z)E~Z1l?_|qdztzO;U>6xn&xiWae!<;hYNhkxUk3bRb2;u0Bn!9pXOUfqeEaCtEKwmWH-JgQu5vpeHGBxWmhd8%6%_upCBB)TXR>|u%v7v0Ia%y(sl8iLtZGX22>rw zE&d~_{km6}_!|Q!fSDKUVX|-TwE%z-ek)4;ObDRo;Ih=AJESp{x zQ4Sw>lou*gl}$_3+^g0x@izop09SsX8jjnVQ2{h8cv%e?b&Bd|sZo=opVJ(UP>O-D z2mttEf(z#a2Gw<_*~_2O=}dn!4^RCXW9SP0dOV-FpmGdbl}`*9OQSMcnIO@_ce!$C=CH1 z1}J6RsV|l@0dOe4Sk=$JR<+;sQf>&~iucr{Ps0Dtf&ds}<~^yZXWy+xH{Pa(j=o40 zg(j*{9@3qaOCB=M-M<`xZfk%184Apui`~6>eIVS|rx_qB%ICg8+ z1P~V&%z0gnEqPv56yB=J^G{GA>XR$ZgsV@JvqUmXDXtTi8K46h&$%udXdvG|apA2@ z{+Dt_04+;*Yw~*|fcnx`RDEQF8XH-!s*CPZrTHBykNQ-cAY9$R7(uZS;iL@$fZtTf z*yy@wpiwq*hMINB?{Y@~E8bHRPyK^00;rF?%y?1N6+fYdhL@@Qyn(*gUxpxD#o59Q zn#)NG0KQV8{?Ry!ZGo5*0;rmGu4=#W#hekqtc&-m`cC)90|Crp0Yu^jP#<}Ldjq&d zH?s(a`d)__fN&LCCN^p=Cmjfj0FXYy(z3m-iv}7^=RC~B&m93wJ#&|;o4?sR0UXkm z0M;|ss!@gCQU!SFj#x40fndI0av@72)o#lF{T8Zd+4`I^fq^#CG(7RLLoiv|MW zNQD|Q{zz4S(&NZ0CO+jt0tVXUDY1B*%tvcOvfm2Y67S({)HM6 zK3`Q7-K?sL?^HFB2cw^(ioU@y!Xg#PpRPiAftybxT>T?CyG5rg0C9n^x60c=5t-sc>~4Ic|K)E|3TSYbv(~MF8PQm72Kl+p7JB zwQAm187rU983D|?c()pR%$6wmeGvd(v!OEc0;r2T%6No(hWn#EMJTVtSB|*2`X)Jp zxhV^vNdKso@oEqRP&{zFntJAsnbglY5rp|f!qqK|iJHsBsR%$8!whGv@mm1-;X!J`iQiToD?$F;57tM3s+xejMiK(OjQ5~8ir8-7R1!FE?4b0uF>SrB>}vx zrY>Ug&)@8?0Pz1~yb{2#*d4l0l@}Zv1OZ?{%y7-+aIylBEz!%g0PgTp0O67v(Cb9eTbj}C>csz%j*!UJ`v>iM*!8ucd5cq zz1IQ|j{cc(g66QC7L&99WI6=84h)8E^FsiG$IfG-KO1-fv|qi8UA671cK#MsefZ|! z2mrbNq5cT~gJZ>^$$kj{gJ6xC!!ivb>AituFbr`(1!J|osHdA_4Xiyxb*v1w0NSqD zsiu5po2p?_gZx1fK*Ka4zh44)Ko#dt^;!VJ(Y1^k&Ecd6!At?jzCa_%*<#Lf0hEuP z5@;7__9Z*i_>;DF5l)@mGDS)v4F*X*uL|@cA zl0NH*44tSBz2x!02>=JZiq zQVN4$V&+%?1`>`ZA6AB83dH2ozY`n*T)SPhUcOyTJZ)>#<*7Q%LEr>%1m^viwMWH>A4mc4 zcN@mJ$?uN60_SVa4xuzMK&Ai?JtK&~lu`^T_!0*I z;mAnt;=k^sMKdUX_8VSTZC7t%H*1rcbOw|07$*8*4*7FN07q-rr>raTdm{i0d<_nN z)~{KhQBLON<-z}-qdA*a21rZ-Vfav(QHSGF@AXRnKq$XV)y=y?wXRsxYXP)fvr)Bt z;dM3XGfc1(9I{um$B3U(0+`ExAJQH9voCF?vQF8qMp_;oX2f(HP z5IkTM4e2DD6(HjQfG>yU;jF9D;WO04lW$S8mp{#f?p^@xH*8?RyrK@h^i|bz;YKy( z945huo789~YK11fiJTL_;n?h|C-P@U0BH3`7vHXihA&d#=muK8?Imh*ai!+!aLwJ6 z17K4C5(glf9!1VhyYG;Xy?`K;U#iMRO;?pI=ctJb?^I1oex;hv{jHjM)+1`t>5r-j zr#z+_j^|ct{wh_~zDiZhUadyXSgl61!~_3kV${q7QDNec15tTsJVv`QYqhE}F*_dB zz?^v0w8o=$ZUVHfQZ=<4bQa&Qs*94rFBtbSe$KduaW~^d3{@B9jc+J(p6!ZVn5&%&w z4#0Q-6jjIgt-fi_9GYj*=Mm*13*Z_4tS5)7;B4CdznH`U5q(j_0hjBWdY%rout)%t zhgiJ`j)JS;Y})?6n8X1x9su8}gL3TD7>hm)aev<9&tu8qC<0(`Hf{gkG>HR{4#ni6 zF^mVyn_F1;2ob<)a@-%80GD?D-)&|(6oU(xWBqye16Wx20F!$w$l)de;9jLa-Dajk z#SLm=yq@48EG)c)wEo-4InGPmpI)Uu6PdU{<@zRYgZ{&O2n!1zAl&;OaySWYf}Sa!k`$Gh7-8&(+?w)kX0Bqd}E`pQbX2!BVZZikP5NOmf&Y;+UnC%5Dyn?j;_&kpsE@Jv~Mh3sSGILN2 z!EhC0JmYuzq3p=8g;$V~-{<&q0y$hn@SBmrudd9T8-zdutHj1K&NUv)_5v2!hP3|p zGbU$(gAjlj{ANV}MDf8eW{4v2fC8V<4`+J;i)=$${}=f4V2VwB$V%|*HZvF=sBezZ zH~3-Zo{WAMl11M{lv_-Gd@d)4dl>uz7qb@py3Kup3?2Z-(5%uAX?p>SY(ZN8-;>k& zgL~j0xR|xz*KOv=Fw#!&zmSId4CTd&<0HR4S)z3Uhz0I3 zAJ)PmgAf5=dmeDDjt-pn@0kE37&dzW_+uCywo|1oG7TaByZm_)Ib1Va|EveUZZlJ& za+VmteRgGLEPyC{w0}d(Obxr z2b@=pL$ARxcpTuI=f1yb(hEpN2qSUQ3oIgMgHpVJb%iX_g}6T85;nU6x4uj6!0y0Mk>jj`xq^cG%J|yRbY)@d3EX4Ks zh(DK-!zBm++>$vzfoT23%$cG{O<`&(W{Mun-Iuv?%w~lwdP3Co!DKh)&JV~L@dcN_ zDR3*RGd|O`L;-OvIS-D*HJ%{n<*X}Y(G$$6&d4Z&Lv<9Oz_%+i17d8Ug5_!A3LU}N zNyZCWSR{au|1JJJh8*sIL*NoP6_~)cD}ypkR|w10kh+2^g!vnPBj-hJJYeC5`28O; zSC*0E`hYvK)KOfYKm@*BSt3^mgBDQoWPY))lIw*nEXY{o!W{WF|9LXSuk;GE>*F@l z6_Tt_9XT&Fg>eTtFKwqvSak9Lfh)b>|*jiMb0Ti?g1}fCcE|4Ojihl7TBx3Gad+s!-w+cI&uoZ2F3js67+5; zC@5&9c)uTHdZ`Tm@5{l(0nSO&@P!bo+A2tJ|Dc$I>(*`dCJ{S+hrZlmR& ztT_NK$Y58_2E9F5qRbE`IH3Sg7*i1w=;^O0G@Byr8!%IyZ)l%?s>T@2ft=6yBx_xz zN3qH$S|~a@Bq`F)!n4RpbT(p4{+GCJkJ9x#P1m`Z?#%g&PqJpFM=``Lr_P{2qCEi@ z)GUf^v<29>Aon0H5AY<{=^To+{6hXAngjiY{9@)J8dC(FF+n&S#)`6^kQG5L!X*Da zx-KQ)m6Kdd^7n~!XR4M+1QHX72pU77(AT&SWfrLoj)M&|*u|{QZ=nt;KH#k&Oa4xC zi$DV7I&ua^L!ZfK`>yp`oJ^!`T577&d8bEHbK{=q&g+rW65urTLdy#COb?-gC{Q3 zCT>~2!-f1;inThnH}DUg<&Qeyy7`{=_a)dJr|WQ}uG4;z>P*+%gJmc%%nQLDkeD7D z%@9EZ+`^pA1uk2z02p4ky@7YwPq8Ttz(%f<&!TR~A^sGK?cO|`Vk5kk{5_aCZ36Q_ zFewUqK+17Um@x>KsF5*~Tb$dC09jfjzsb?p(OJd_beZ^fV0ydu_owMP8msGUn65)} zZjU8@4`zx0GXgO!Rx(6&rX@jI6;GG4?wZ`6U|@5>Gj zlIbbab!f?-qKOF13=sk-_>$rhA+098K?~NBVum5bDhz>{6+&i+-#G?@GUl?r1wF9Q z>QAPa%QI5fm6_?Wwb{ zdzN7?w6Q`+j`;s%tl=`e*dTtRF5~eO-|9<>)4DuaHZxUf_68Cg$kyx<8=zO1u7&Uf zvKADpa2ZkV&tj1G?@S@1j4H zsbq>U@Pe+nj+~K5_^NIpoX9QQtz6-+Gu%CZxJZ3T3_|+28MXZG|Hs(GW$|Aq6BqFt zb=i&6WjR`xtz>xI=}k+1AI$6x+-?yTSWA2$LJ$!|g@8HgT=|zW34V>GddP26;+`Sb z1Yx%F2e^3pU4PB*z8qy>5Ff3*3}%g^n4dp_4s>^Hs~;dUdjoXINW)Cw_RQ_RPg79V9M z#4p!nT1Ym-6IgS*MQ$NTk5EgFKa2b|G8#>$5VRPMXQEuggn5Xu#h858oglpEDcmi4 zcZC0|e)fm>U9RJIIzb1uCh=wN&OsFO@y!;$E8<)72iSTN0{mD6a>!p}&Pwzo8**B} z1H`~CLI;NY88b7p3cQD?>d>Y0+P2_j=pZDnh z+o8X^LI3sP12bV30Ac+d5>c(#FmiQ3{2w` z{cp$NJNT}iY}R7gc7&*W>&`%zmJ;;!R!@~ z*LI5mtt4Co1cvaF>nJ7&=^@}UVF8Ft6=>1KZAwJeG$7Y#!L^P1OrhA0$_G(A;cN6W zkJ9hpCVdgbxAr!x(@!l?uV9J+13qvOF!%#MSpW_pYl2)khzAz~K_$qD78t~g z?t)DYS*N+sA&ARb%{w7MW8T3`^fjvSIA(;^}fjB~;Kzk28+(U}h9DqXT?CeBfk8~P#vmyM<3>?TD1u52 z^WWrqBlT~G>uU|w*B+$rRj%({La`P;5#cTAt@Xt4e8Yt=l=6B5B%Jtktw`8{#2+QjN mp>B}xLU-To>iX7tY5xzaVB_zSkpQ^>0000JnE1D literal 0 HcmV?d00001 diff --git a/desktopApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/Main.kt b/desktopApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/Main.kt new file mode 100644 index 000000000..f40cc9f8b --- /dev/null +++ b/desktopApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/Main.kt @@ -0,0 +1,59 @@ +package com.lagradost.cloudstream4 + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Button +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import com.lagradost.cloudstream4.compose.Colors +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.app_name +import com.lagradost.cloudstream4.generated.resources.default_icon +import com.lagradost.cloudstream4.generated.resources.preview +import com.lagradost.cloudstream4.theme.CloudStreamTheme +import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors +import com.lagradost.cloudstream4.theme.CloudStreamThemeMode +import com.mihon.presentation.settings.widget.SwitchPreferenceWidget +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource + +fun main() = application { + Window( + onCloseRequest = ::exitApplication, + title = stringResource(Res.string.app_name), + icon = painterResource(Res.drawable.default_icon) + ) { + CloudStreamTheme(mode = CloudStreamThemeMode.Dark) { + Scaffold( + containerColor = colors.background, + contentColor = colors.onBackground + ) { + Column { + Text("Hello, World!") + Row { + Button(onClick = {}, colors = Colors.whiteButton) { + Text("Hello in White") + } + Button(onClick = {}, colors = Colors.blackButton) { + Text("Hello in Black") + } + } + var checked by remember { mutableStateOf(false) } + SwitchPreferenceWidget( + title = "hello", subtitle = "world", icon = painterResource( + Res.drawable.preview + ), + checked = checked, + onCheckedChanged = { checked = !checked } + ) + } + } + } + } +} \ No newline at end of file diff --git a/desktopApp/src/readme.md b/desktopApp/src/readme.md new file mode 100644 index 000000000..0bebb8638 --- /dev/null +++ b/desktopApp/src/readme.md @@ -0,0 +1 @@ +Based on https://github.com/JetBrains/compose-multiplatform/tree/master/examples/imageviewer/desktopApp \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e38895451..4652cf938 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ # https://docs.gradle.org/current/userguide/plugins.html#sec:version_catalog_plugin_application # https://docs.gradle.org/current/userguide/dependency_versions.html#sec:strict-version [versions] -activityKtx = "1.13.0" +activity = "1.13.0" androidGradlePlugin = "9.1.1" animeDb = "1.0.2" annotation = "1.10.0" @@ -55,8 +55,14 @@ tvprovider = "1.1.0" video = "1.0.0" workRuntimeKtx = "2.11.2" zipline = "1.27.0" +composeMaterial3 = "1.12.0-alpha03" +composeMultiplatform = "1.12.0" +lifecycleViewmodelCompose = "2.11.0" -jvmTarget = "1.8" +# jvmTarget = "11" is needed for compose, otherwise you will get +# "Cannot inline bytecode built with JVM target 11 into bytecode" or +# "Error was captured in composition. java.lang.AbstractMethodError" +jvmTarget = "11" jdkToolchain = "17" minSdk = "23" compileSdk = "37" @@ -66,7 +72,8 @@ versionCode = "68" versionName = "4.8.0" [libraries] -activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" } +activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity" } +activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activity" } anime-db = { module = "com.github.recloudstream:anime-db", version.ref = "animeDb" } annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" } appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } @@ -136,6 +143,16 @@ video = { module = "com.google.android.mediahome:video", version.ref = "video" } work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" } zipline = { module = "app.cash.zipline:zipline-android", version.ref = "zipline" } +compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "composeMultiplatform" } +compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "composeMaterial3" } +compose-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" } +compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" } +compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" } +compose-ui-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "composeMultiplatform" } +compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" } +lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" } +coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" } + [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } android-lint = { id = "com.android.lint", version.ref = "androidGradlePlugin" } @@ -146,6 +163,9 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm" , version.ref = "kotlinGradlePlug kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlinGradlePlugin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinGradlePlugin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlinGradlePlugin" } +compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" } + [bundles] coil = ["coil", "coil-network-okhttp"] cryptography = ["cryptography-core", "cryptography-provider-optimal"] @@ -153,3 +173,4 @@ lifecycle = ["lifecycle-livedata-ktx", "lifecycle-viewmodel-ktx"] media3 = ["media3-cast", "media3-common", "media3-container", "media3-datasource-cronet", "media3-datasource-okhttp", "media3-exoplayer", "media3-exoplayer-dash", "media3-exoplayer-hls", "media3-session", "media3-ui"] navigation = ["navigation-fragment-ktx", "navigation-ui-ktx"] nextlib = ["nextlib-media3ext", "nextlib-mediainfo"] +compose = ["compose-foundation", "compose-material3", "compose-resources", "compose-runtime", "compose-ui", "compose-ui-tooling", "compose-ui-tooling-preview", "lifecycle-viewmodel-compose", "coil-compose"] \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 73bf5a195..dd92448b0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,4 +18,4 @@ dependencyResolutionManagement { } rootProject.name = "CloudStream" -include(":app", ":library", ":docs") +include(":app", ":shared", ":library", ":docs", ":desktopApp") diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts new file mode 100644 index 000000000..1b7f0f29e --- /dev/null +++ b/shared/build.gradle.kts @@ -0,0 +1,55 @@ +plugins { + alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.android.lint) + alias(libs.plugins.android.multiplatform.library) + alias(libs.plugins.compose.multiplatform) + alias(libs.plugins.compose.compiler) +} + +kotlin { + android { + // Must be unique + namespace = "com.lagradost.cloudstream4" + compileSdk = libs.versions.compileSdk.get().toInt() + minSdk = libs.versions.minSdk.get().toInt() + + androidResources { + enable = true + } + } + + jvm() + + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } + + sourceSets { + all { + languageSettings { + optIn("com.lagradost.cloudstream3.InternalAPI") + optIn("com.lagradost.cloudstream3.Prerelease") + } + } + + commonMain.dependencies { + implementation(libs.bundles.compose) + implementation(project(":library")) + } + + androidMain.dependencies { + implementation(libs.activity.compose) + implementation(libs.preference.ktx) + } + } +} + +dependencies { + androidRuntimeClasspath(libs.compose.ui.tooling) +} + +compose.resources { + publicResClass = true + packageOfResClass = "com.lagradost.cloudstream4.generated.resources" + generateResClass = auto +} \ No newline at end of file diff --git a/shared/readme.md b/shared/readme.md new file mode 100644 index 000000000..084244cf3 --- /dev/null +++ b/shared/readme.md @@ -0,0 +1,112 @@ +## Motivation + +This readme is documentation for humans to understand ***why***, and ***not how*** the core architecture +for CloudStream will be designed going forward. + +## Design decisions + +The following design decisions has been tried in the sister project QuickNovel with great success, and a +seamless porting process. For inspiration and code in a similar codebase, take a look at QuickNovel. + +### File structure + +The `shared` directory is supposed to be for shared compose UI between platforms, and can act as a glue for business logic. +This is separate from the `library` that includes the primitives for a headless, cross-platform plugin system. +Note that we do not want a shared screen layout, because each platform has its own design language and quirks. +Instead, the ViewModels should be entirely detached from the rendering process, and each UI component should be shared +between every platform. This ensures ViewModels cant cheat with dirty escape hatches, a leaky api or +with UI elements that hijack business logic. + +**Include**: +*Viewmodels*, *UI components* and *Theming* + +**Avoid**: +*Entire Screens*, *Plugin APIs* and *Android Layout XML* + +Please also note that while it would be nice with an e.g. cross-platform downloader or video player. +It is simply infeasible and undesirable to write such a system. Therefore, each platform should have +a seperate implemenation of platform specific behavior that closly interop with native systems. + +### MVI + +MVI (Model View Intent) is the core structure for building cross-platform UI we will use. +The reason for this decision is that current MVVM architecture created a lot of boilerplate, +and is very unwieldy when working with compose. We will instead use a single immutable UI state, using +kotlin immutable collections, as that creates thread-safe and performant UI. Additionally, because the viewmodel +is shared between platforms, the Viewmodel must be provided with an implementation specific +database/preference/downloaded by using dependency injection and composition. + +**Use**: +*PersistentList*, *val*, *dependency injection* and *composition* + +**Avoid**: +*List*, *var*, *globals* and *inheritance* + +Please note that this process aims to replace the current UI in a backwards compatible way. Only +the navigation graph should change to point to the new fragments. Then we can roll back if some +critical issue is found with the compose rewrite, as well as compare functionality without a checkout. + +### Resources and assets + +**Images**: For this project the accepted image format is `XML Drawable resources` instead of +`ImageVector` or `.svg`. This is primarily due to make the review, cross-platform, and edit +processing easier. ImageVector is undesirable because file must be reviewed to not contain malicious +code, and does not have a built-in preview. SVG files on the other hand are currently not natively +supported on Android. + +**Translation**: CloudStream already has a huge translation, however to make the porting process easier, +we should limit any copy-paste from the android project until everything is converted to compose. +This is in part because weblate is not set up target the cross-platform compose yet. + +**Styling/Themes**: In compose there is no `styles.xml`/`colors.xml`/`dimens.xml`. Therefore, +styling is done entierly in code, but should use the exact same colors to avoid a styling mismatch +when porting. + +**UI/UX**: The intended experience when using CloudStream should be the same, but the UI should be +refreshed to a more Material 3 style, such as rounded corners and a greater focus on light theme +support. However, no glassmorphic, blurred or gradient UI is accepted. + +### Settings and Mihon + +The settings system has been entirely forked from Tachiyomi, now Mihon project. This is because +Mihon has one of the best FOSS codebases for Android, and has a great declarative preference system +unmatched by any other tested Compose settings system. Unfortunately, this is not provided as a +library and is therefore instead forked by importing the exact files used. + +### Why compose? + +During the lifetime of CloudStream, the app has been redesigned and rewritten several times. + +**C# Downloader → Xamarin Native → Xamarin Cross platform → Android Native XML → ViewBinding → Compose Multiplatform** + +We have experimented with different UI technologies such as **Blazor**, **Electron**, **PWA**, +**WPF**, **UWP**, **Windows Forms**, and **CLI/Console** for CloudStream. +We have also investigated projectes written in **Flutter** and **React Native**. + +However, none of these frameworks provided a good developer experience, that was also performant and +cross-platform. Compose was the only real option, but did not exist until after we started on +CloudStream 3. Android Native XML was simply the least bad option when building an Android app. + +### CloudStream 4? + +The current android app is named CloudStream 3, and this Compose rewrite will be named +CloudStream 4. However, the android app will be upgraded, not replaced with Compose. It aims to be a +seamless and gradual rollout of updates for everyone using the Android app. Therefore, CloudStream 4 +is used to refer to the new compose UI, features, and cross-platform support. + +## AI Usage + +Due to the prevalence and popularity of LLMs, CloudStream 3 has been rewritten entirely 4+ times by +independent actors using Compose to target desktop with different LLM models. These have not been merged, due to the +absolute mess of the AI generated codebase, both in code quality and performance. + +Therefore, I (LagradOst) will not accept or allow any pull requests concering the Compose rewrite +before I put the core structures are in place. The compose rewrite is supposed to be a refactor and +quality improvement for everyone involved, to solve the massive tech debt we already have. XML and +old plans forced bad design decissions, which we can improve greatly upon. However, in no part do I +want AI to lay the groundwork for something this important. + +The idea is to provide a solid staring point so it is easy for first time contributors and LLMs to +understand and work with the code, not ship a product as fast as possible. This is not a project +with only one person involved. A messy AI generated codebase makes reviews and contributions very hard +for everyone involved. diff --git a/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.android.kt b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.android.kt new file mode 100644 index 000000000..04ba9dd36 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.android.kt @@ -0,0 +1,19 @@ +package com.lagradost.cloudstream4 + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import com.mihon.common.preference.AndroidPreferenceStore + + +@Composable +actual fun rememberAppSettings() : AppSettings { + val context = LocalContext.current + val value = remember(context) { AppSettings(context) } + return value +} + +fun AppSettings(context: Context) : AppSettings { + return AppSettings(preferences = AndroidPreferenceStore(context)) +} diff --git a/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/BackHandler.kt b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/BackHandler.kt new file mode 100644 index 000000000..8f9d096c4 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/BackHandler.kt @@ -0,0 +1,42 @@ +package com.lagradost.cloudstream4.compose + +import androidx.activity.OnBackPressedCallback +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState +import androidx.lifecycle.compose.LocalLifecycleOwner + +/** https://dev.to/pawegio/handling-back-presses-in-jetpack-compose-50d5 */ +@Composable +fun BackHandler(enabled: Boolean = true, onBack: () -> Unit) { + // Safely update the current `onBack` lambda when a new one is provided + val currentOnBack by rememberUpdatedState(onBack) + // Remember in Composition a back callback that calls the `onBack` lambda + val backCallback = remember { + object : OnBackPressedCallback(enabled) { + override fun handleOnBackPressed() { + currentOnBack() + } + } + } + // On every successful composition, update the callback with the `enabled` value + SideEffect { + backCallback.isEnabled = enabled + } + val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) { + "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner" + }.onBackPressedDispatcher + val lifecycleOwner = LocalLifecycleOwner.current + DisposableEffect(lifecycleOwner, backDispatcher) { + // Add callback to the backDispatcher + backDispatcher.addCallback(lifecycleOwner, backCallback) + // When the effect leaves the Composition, remove the callback + onDispose { + backCallback.remove() + } + } +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/ComposeFragment.kt b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/ComposeFragment.kt new file mode 100644 index 000000000..9a91e3e16 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/ComposeFragment.kt @@ -0,0 +1,51 @@ +package com.lagradost.cloudstream4.compose + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.ViewCompositionStrategy +import com.lagradost.cloudstream4.rememberAppSettings +import com.lagradost.cloudstream4.theme.CloudStreamTheme +import com.lagradost.cloudstream4.theme.perfToColor +import com.lagradost.cloudstream4.theme.perfToMode +import com.mihon.presentation.LocalBackPress +import com.mihon.presentation.settings.collectAsState + +/** Backwards compatible fragment for compose, before we switch entirely to compose navigation */ +fun Screen.createComposeView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, +): View = ComposeView(inflater.context).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + + setContent { + val settings = rememberAppSettings() + val mode by settings.ui.theme.collectAsState() + val primaryColor by settings.ui.primaryColor.collectAsState() + val layout by settings.ui.layout.collectAsState() + val layoutFlag = DeviceLayout.layoutToFlag(LocalContext.current, layout) + + CloudStreamTheme( + mode = perfToMode(mode), + primaryColor = perfToColor(primaryColor), + ) { + val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) { + "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner" + }.onBackPressedDispatcher + + CompositionLocalProvider( + LocalBackPress provides backDispatcher::onBackPressed, + DeviceLayout.LocalLayout provides layoutFlag + ) { + this@createComposeView.Content() + } + } + } +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/Layout.kt b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/Layout.kt new file mode 100644 index 000000000..96838ae5e --- /dev/null +++ b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/Layout.kt @@ -0,0 +1,48 @@ +package com.lagradost.cloudstream4.compose + +import android.app.UiModeManager +import android.content.Context +import android.content.res.Configuration +import android.os.Build +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.staticCompositionLocalOf +import com.lagradost.cloudstream4.compose.DeviceLayout.Companion.LocalLayout + +@JvmInline +@Immutable +value class DeviceLayout(private val value: Int) { + infix fun or(other: DeviceLayout) = + DeviceLayout(value or other.value) + + internal infix fun and(other: DeviceLayout) = (value and other.value) != 0 + + companion object { + val LocalLayout: ProvidableCompositionLocal = staticCompositionLocalOf { DeviceLayout(-1) } + fun isAutoTv(context: Context): Boolean { + val uiModeManager = context.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager? + // AFT = Fire TV + val model = Build.MODEL.lowercase() + return uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION || Build.MODEL.contains( + "AFT" + ) || model.contains("firestick") || model.contains("fire tv") || model.contains("chromecast") + } + internal fun layoutToFlag(context : Context, from : Int): DeviceLayout { + return when(from) { + -1 -> if (isAutoTv(context)) TV else PHONE + 0 -> PHONE + 1 -> TV + 2 -> EMULATOR + else -> PHONE + } + } + } +} + +val PHONE = DeviceLayout(0b00001) +val TV = DeviceLayout(0b00010) +val EMULATOR = DeviceLayout(0b00100) + +@Composable +fun isLayout(flags: DeviceLayout) : Boolean = LocalLayout.current and flags \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/theme/ColorPreference.kt b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/theme/ColorPreference.kt new file mode 100644 index 000000000..17d715372 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/theme/ColorPreference.kt @@ -0,0 +1,72 @@ +package com.lagradost.cloudstream4.theme + +import android.content.Context +import android.os.Build +import androidx.preference.PreferenceManager + +/** TODO: This should be replaced with a better system */ + +fun perfToMode(perf: String?) = + when (perf) { + "System" -> CloudStreamThemeMode.FollowSystem + "Black" -> CloudStreamThemeMode.Dark + "Light" -> CloudStreamThemeMode.Light + "Amoled" -> CloudStreamThemeMode.Amoled + "AmoledLight" -> CloudStreamThemeMode.AmoledLight + "Dracula" -> CloudStreamThemeMode.Dracula + "Lavender" -> CloudStreamThemeMode.Lavender + "SilentBlue" -> CloudStreamThemeMode.SilentBlue + "Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + CloudStreamThemeMode.Dynamic + } else { + CloudStreamThemeMode.Dark + } + + else -> CloudStreamThemeMode.Dark + } + +fun perfToColor(perf : String?) = when (perf) { + "Normal" -> CloudStreamPrimaryColor.NORMAL + "Blue" -> CloudStreamPrimaryColor.BLUE + "Purple" -> CloudStreamPrimaryColor.PURPLE + "Green" -> CloudStreamPrimaryColor.GREEN + "GreenApple" -> CloudStreamPrimaryColor.GREEN_APPLE + "Red" -> CloudStreamPrimaryColor.RED + "Banana" -> CloudStreamPrimaryColor.BANANA + "Party" -> CloudStreamPrimaryColor.PARTY + "Pink" -> CloudStreamPrimaryColor.PINK + "CarnationPink" -> CloudStreamPrimaryColor.CARNATION_PINK + "Maroon" -> CloudStreamPrimaryColor.MAROON + "DarkGreen" -> CloudStreamPrimaryColor.DARK_GREEN + "NavyBlue" -> CloudStreamPrimaryColor.NAVY_BLUE + "Grey" -> CloudStreamPrimaryColor.GREY + "White" -> CloudStreamPrimaryColor.WHITE + "Brown" -> CloudStreamPrimaryColor.BROWN + "Orange" -> CloudStreamPrimaryColor.ORANGE + "DandelionYellow" -> CloudStreamPrimaryColor.DANDELION_YELLOW + "CoolBlue" -> CloudStreamPrimaryColor.COOL_BLUE + "Lavender" -> CloudStreamPrimaryColor.LAVENDER + "Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + CloudStreamPrimaryColor.DYNAMIC + } else { + CloudStreamPrimaryColor.NORMAL + } + + "Monet2" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + CloudStreamPrimaryColor.DYNAMIC_TWO + } else { + CloudStreamPrimaryColor.NORMAL + } + + else -> CloudStreamPrimaryColor.NORMAL +} + +fun Context.loadThemeMode(): CloudStreamThemeMode { + val prefs = PreferenceManager.getDefaultSharedPreferences(this) + return perfToMode(prefs.getString("theme_key", "AmoledLight")) +} + +fun Context.loadPrimaryColor(): CloudStreamPrimaryColor { + val prefs = PreferenceManager.getDefaultSharedPreferences(this) + return perfToColor(prefs.getString("primary_color_key", "Normal")) +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.android.kt b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.android.kt new file mode 100644 index 000000000..73b188c25 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.android.kt @@ -0,0 +1,73 @@ +package com.lagradost.cloudstream4.theme + +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource + +internal actual object DeviceTheme { + @Composable + @ReadOnlyComposable + @RequiresApi(Build.VERSION_CODES.S) + private fun buildMonetScheme(): CloudStreamColorScheme { + return if (true) { // isSystemInDarkTheme() disabled until everything is Compose, as otherwise you get bugs + CloudStreamColorScheme( + background = colorResource(android.R.color.system_neutral1_900), + surfaceVariant = colorResource(android.R.color.system_neutral1_800), + surface = colorResource(android.R.color.system_neutral1_800), + surfaceContainer = colorResource(android.R.color.system_neutral1_800), + onBackground = colorResource(android.R.color.system_neutral1_100), + onSurfaceVariant = colorResource(android.R.color.system_neutral2_400), + icon = colorResource(android.R.color.system_neutral1_100), + primary = colorResource(android.R.color.system_accent1_200), + ongoing = CloudStreamPalette.Ongoing, + isLight = false, + ) + } else { + CloudStreamColorScheme( + background = colorResource(android.R.color.system_neutral1_10), + surfaceVariant = colorResource(android.R.color.system_neutral1_100), + surface = colorResource(android.R.color.system_neutral1_100), + surfaceContainer = colorResource(android.R.color.system_neutral1_100), + onBackground = colorResource(android.R.color.system_neutral1_900), + onSurfaceVariant = colorResource(android.R.color.system_neutral2_600), + icon = colorResource(android.R.color.system_neutral1_900), + primary = colorResource(android.R.color.system_accent1_600), + ongoing = CloudStreamPalette.Ongoing, + isLight = true, + ) + } + } + + @Composable + @ReadOnlyComposable + actual fun resolveDynamicTheme(): CloudStreamColorScheme { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + buildMonetScheme() + } else { + darkScheme() + } + } + + @Composable + @ReadOnlyComposable + actual fun resolveDynamicPrimaryColor(): Color { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + colorResource(android.R.color.system_accent1_200) + } else { + CloudStreamPrimaryColor.NORMAL.color + } + } + + @Composable + @ReadOnlyComposable + actual fun resolveDynamicSecondaryColor(): Color { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + colorResource(android.R.color.system_accent2_200) + } else { + CloudStreamPrimaryColor.NORMAL.color + } + } +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/mihon/common/preference/AndroidPreference.kt b/shared/src/androidMain/kotlin/com/mihon/common/preference/AndroidPreference.kt new file mode 100644 index 000000000..b766dd4b3 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/mihon/common/preference/AndroidPreference.kt @@ -0,0 +1,235 @@ +package com.mihon.common.preference + +import android.content.SharedPreferences +import android.content.SharedPreferences.Editor +import androidx.core.content.edit +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn + +sealed class AndroidPreference( + private val preferences: SharedPreferences, + private val keyFlow: Flow, + private val key: String, + private val defaultValue: T, +) : PreferenceData { + + abstract fun read(preferences: SharedPreferences, key: String, defaultValue: T): T + + abstract fun write(key: String, value: T): Editor.() -> Unit + + override fun key(): String { + return key + } + + override fun get(): T { + return try { + read(preferences, key, defaultValue) + } catch (e: ClassCastException) { + //logcat { "Invalid value for $key; deleting" } + delete() + defaultValue + } + } + + override fun set(value: T) { + preferences.edit(action = write(key, value)) + } + + override fun isSet(): Boolean { + return preferences.contains(key) + } + + override fun delete() { + preferences.edit { + remove(key) + } + } + + override fun defaultValue(): T { + return defaultValue + } + + override fun changes(): Flow { + return keyFlow + .filter { it == key || it == null } + .onStart { emit("ignition") } + .map { get() } + .conflate() + } + + override fun stateIn(scope: CoroutineScope): StateFlow { + return changes().stateIn(scope, SharingStarted.Eagerly, get()) + } + + class StringPrimitive( + preferences: SharedPreferences, + keyFlow: Flow, + key: String, + defaultValue: String, + ) : AndroidPreference(preferences, keyFlow, key, defaultValue) { + override fun read( + preferences: SharedPreferences, + key: String, + defaultValue: String, + ): String { + return preferences.getString(key, defaultValue) ?: defaultValue + } + + override fun write(key: String, value: String): Editor.() -> Unit = { + putString(key, value) + } + } + + class LongPrimitive( + preferences: SharedPreferences, + keyFlow: Flow, + key: String, + defaultValue: Long, + ) : AndroidPreference(preferences, keyFlow, key, defaultValue) { + override fun read(preferences: SharedPreferences, key: String, defaultValue: Long): Long { + return preferences.getLong(key, defaultValue) + } + + override fun write(key: String, value: Long): Editor.() -> Unit = { + putLong(key, value) + } + } + + class IntPrimitive( + preferences: SharedPreferences, + keyFlow: Flow, + key: String, + defaultValue: Int, + ) : AndroidPreference(preferences, keyFlow, key, defaultValue) { + override fun read(preferences: SharedPreferences, key: String, defaultValue: Int): Int { + return preferences.getInt(key, defaultValue) + } + + override fun write(key: String, value: Int): Editor.() -> Unit = { + putInt(key, value) + } + } + + class FloatPrimitive( + preferences: SharedPreferences, + keyFlow: Flow, + key: String, + defaultValue: Float, + ) : AndroidPreference(preferences, keyFlow, key, defaultValue) { + override fun read(preferences: SharedPreferences, key: String, defaultValue: Float): Float { + return preferences.getFloat(key, defaultValue) + } + + override fun write(key: String, value: Float): Editor.() -> Unit = { + putFloat(key, value) + } + } + + class BooleanPrimitive( + preferences: SharedPreferences, + keyFlow: Flow, + key: String, + defaultValue: Boolean, + ) : AndroidPreference(preferences, keyFlow, key, defaultValue) { + override fun read( + preferences: SharedPreferences, + key: String, + defaultValue: Boolean, + ): Boolean { + return preferences.getBoolean(key, defaultValue) + } + + override fun write(key: String, value: Boolean): Editor.() -> Unit = { + putBoolean(key, value) + } + } + + class StringSetPrimitive( + preferences: SharedPreferences, + keyFlow: Flow, + key: String, + defaultValue: Set, + ) : AndroidPreference>(preferences, keyFlow, key, defaultValue) { + override fun read( + preferences: SharedPreferences, + key: String, + defaultValue: Set, + ): Set { + return preferences.getStringSet(key, defaultValue) ?: defaultValue + } + + override fun write(key: String, value: Set): Editor.() -> Unit = { + putStringSet(key, value) + } + } + + class ObjectAsString( + preferences: SharedPreferences, + keyFlow: Flow, + key: String, + defaultValue: T, + private val serializer: (T) -> String, + private val deserializer: (String) -> T, + ) : AndroidPreference(preferences, keyFlow, key, defaultValue) { + override fun read(preferences: SharedPreferences, key: String, defaultValue: T): T { + return try { + preferences.getString(key, null)?.let(deserializer) ?: defaultValue + } catch (e: Exception) { + defaultValue + } + } + + override fun write(key: String, value: T): Editor.() -> Unit = { + putString(key, serializer(value)) + } + } + + class ObjectAsInt( + preferences: SharedPreferences, + keyFlow: Flow, + key: String, + defaultValue: T, + private val serializer: (T) -> Int, + private val deserializer: (Int) -> T, + ) : AndroidPreference(preferences, keyFlow, key, defaultValue) { + override fun read(preferences: SharedPreferences, key: String, defaultValue: T): T { + return try { + if (preferences.contains(key)) preferences.getInt(key, 0).let(deserializer) else defaultValue + } catch (e: Exception) { + defaultValue + } + } + + override fun write(key: String, value: T): Editor.() -> Unit = { + putInt(key, serializer(value)) + } + } + + class ObjectSetAsStringSet( + preferences: SharedPreferences, + keyFlow: Flow, + key: String, + defaultValue: Set, + private val serializer: (T) -> String, + private val deserializer: (String) -> T?, + ) : AndroidPreference>(preferences, keyFlow, key, defaultValue) { + override fun read(preferences: SharedPreferences, key: String, defaultValue: Set): Set { + return try { + preferences.getStringSet(key, null)?.mapNotNull(deserializer)?.toSet() ?: defaultValue + } catch (_: Exception) { + defaultValue + } + } + + override fun write(key: String, value: Set): Editor.() -> Unit = { + putStringSet(key, value.map(serializer).toSet()) + } + } +} diff --git a/shared/src/androidMain/kotlin/com/mihon/common/preference/AndroidPreferenceStore.kt b/shared/src/androidMain/kotlin/com/mihon/common/preference/AndroidPreferenceStore.kt new file mode 100644 index 000000000..192bc77ed --- /dev/null +++ b/shared/src/androidMain/kotlin/com/mihon/common/preference/AndroidPreferenceStore.kt @@ -0,0 +1,102 @@ +package com.mihon.common.preference + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceManager +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.callbackFlow + +class AndroidPreferenceStore(private val sharedPreferences: SharedPreferences) : PreferenceStore { + constructor(context: Context) : this(PreferenceManager.getDefaultSharedPreferences(context)) + + private val keyFlow = sharedPreferences.keyFlow + + override fun getString(key: String, defaultValue: String): PreferenceData { + return AndroidPreference.StringPrimitive(sharedPreferences, keyFlow, key, defaultValue) + } + + override fun getLong(key: String, defaultValue: Long): PreferenceData { + return AndroidPreference.LongPrimitive(sharedPreferences, keyFlow, key, defaultValue) + } + + override fun getInt(key: String, defaultValue: Int): PreferenceData { + return AndroidPreference.IntPrimitive(sharedPreferences, keyFlow, key, defaultValue) + } + + override fun getFloat(key: String, defaultValue: Float): PreferenceData { + return AndroidPreference.FloatPrimitive(sharedPreferences, keyFlow, key, defaultValue) + } + + override fun getBoolean(key: String, defaultValue: Boolean): PreferenceData { + return AndroidPreference.BooleanPrimitive(sharedPreferences, keyFlow, key, defaultValue) + } + + override fun getStringSet(key: String, defaultValue: Set): PreferenceData> { + return AndroidPreference.StringSetPrimitive(sharedPreferences, keyFlow, key, defaultValue) + } + + override fun getObjectFromString( + key: String, + defaultValue: T, + serializer: (T) -> String, + deserializer: (String) -> T, + ): PreferenceData { + return AndroidPreference.ObjectAsString( + preferences = sharedPreferences, + keyFlow = keyFlow, + key = key, + defaultValue = defaultValue, + serializer = serializer, + deserializer = deserializer, + ) + } + + override fun getObjectFromInt( + key: String, + defaultValue: T, + serializer: (T) -> Int, + deserializer: (Int) -> T, + ): PreferenceData { + return AndroidPreference.ObjectAsInt( + preferences = sharedPreferences, + keyFlow = keyFlow, + key = key, + defaultValue = defaultValue, + serializer = serializer, + deserializer = deserializer, + ) + } + + override fun getObjectSetFromStringSet( + key: String, + defaultValue: Set, + serializer: (T) -> String, + deserializer: (String) -> T?, + ): PreferenceData> { + return AndroidPreference.ObjectSetAsStringSet( + preferences = sharedPreferences, + keyFlow = keyFlow, + key = key, + defaultValue = defaultValue, + serializer = serializer, + deserializer = deserializer, + ) + } + + override fun getAll(): Map { + return sharedPreferences.all ?: emptyMap() + } +} + +private val SharedPreferences.keyFlow + get() = callbackFlow { + val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? -> + trySend( + key, + ) + } + registerOnSharedPreferenceChangeListener(listener) + awaitClose { + unregisterOnSharedPreferenceChangeListener(listener) + } + } \ No newline at end of file diff --git a/shared/src/commonMain/composeResources/drawable/arrow_back.xml b/shared/src/commonMain/composeResources/drawable/arrow_back.xml new file mode 100644 index 000000000..196b4c087 --- /dev/null +++ b/shared/src/commonMain/composeResources/drawable/arrow_back.xml @@ -0,0 +1,10 @@ + + + diff --git a/shared/src/commonMain/composeResources/drawable/arrow_downward.xml b/shared/src/commonMain/composeResources/drawable/arrow_downward.xml new file mode 100644 index 000000000..061a6db1b --- /dev/null +++ b/shared/src/commonMain/composeResources/drawable/arrow_downward.xml @@ -0,0 +1,9 @@ + + + diff --git a/shared/src/commonMain/composeResources/drawable/arrow_upward.xml b/shared/src/commonMain/composeResources/drawable/arrow_upward.xml new file mode 100644 index 000000000..fbf89a796 --- /dev/null +++ b/shared/src/commonMain/composeResources/drawable/arrow_upward.xml @@ -0,0 +1,9 @@ + + + diff --git a/shared/src/commonMain/composeResources/drawable/cancel.xml b/shared/src/commonMain/composeResources/drawable/cancel.xml new file mode 100644 index 000000000..570986990 --- /dev/null +++ b/shared/src/commonMain/composeResources/drawable/cancel.xml @@ -0,0 +1,9 @@ + + + diff --git a/shared/src/commonMain/composeResources/drawable/check.xml b/shared/src/commonMain/composeResources/drawable/check.xml new file mode 100644 index 000000000..6976b0df2 --- /dev/null +++ b/shared/src/commonMain/composeResources/drawable/check.xml @@ -0,0 +1,9 @@ + + + diff --git a/shared/src/commonMain/composeResources/drawable/close.xml b/shared/src/commonMain/composeResources/drawable/close.xml new file mode 100644 index 000000000..14beedf9b --- /dev/null +++ b/shared/src/commonMain/composeResources/drawable/close.xml @@ -0,0 +1,9 @@ + + + diff --git a/shared/src/commonMain/composeResources/drawable/default_icon.png b/shared/src/commonMain/composeResources/drawable/default_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..394459001fdb5237f04624560e66550660044d05 GIT binary patch literal 8334 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 + + diff --git a/shared/src/commonMain/composeResources/drawable/info.xml b/shared/src/commonMain/composeResources/drawable/info.xml new file mode 100644 index 000000000..32d30b88a --- /dev/null +++ b/shared/src/commonMain/composeResources/drawable/info.xml @@ -0,0 +1,9 @@ + + + diff --git a/shared/src/commonMain/composeResources/drawable/preview.xml b/shared/src/commonMain/composeResources/drawable/preview.xml new file mode 100644 index 000000000..d56fbde6e --- /dev/null +++ b/shared/src/commonMain/composeResources/drawable/preview.xml @@ -0,0 +1,9 @@ + + + diff --git a/shared/src/commonMain/composeResources/font/productsans_black.ttf b/shared/src/commonMain/composeResources/font/productsans_black.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5864e2690837d17e7d01f323345e821eb96d3fa6 GIT binary patch literal 91660 zcmcG%3w&F}oi9FfBula^>uFiCUgyY1FI)0cwk1DeD}KdsUQXgTb_l^voREkBsh}jK zBuydE;HH$O*(_z#Hd~%$DSro8no`=OEZb$dS(eMPTy8JrLYwWfEVq}kZMU0Cr2lv3 z967QR+J63@`?q~GI&)@zGxK}RZ+`QeV?qfbc6?}wG(0@M=I|5A`v~3nA#!~y28V{t z%dIO3wcm&P(G_FsHf~hHgARPl*2QbsL*nv%i-25kenD z`RN_k&+PmAzxu0bLMmP&MDxUsTW*$;BeClVshvfc@m>3_z5eWz>ve=!4&b`!+L@d7 z5k2t&-Z9iSUAy-)yPjVY`Da36rwI9{4|eaInXR6F=ML0O;CaVxWLW;C5`Ck9M?$-= zzxlxbJU;nNLIeXL<HU+quut{G^?bzz696e!_4?g|$Ebt5^H3vGn|g zh?@71L;i8%)8j1tZ%>7H7sm3hm|oZX7`Fx@uw4A0p62<2219ziFjg2dz0M&i&-kaX zkLBR=pNWx-gBAj@k|uIL>g^_9CK^1|3Y#=XNEu4a36rR_N=fN9GEO7NtuSgdWr9}F z90n;$K$8{g)~%q2NoB25>4WCSv{fP$SVG6%7tSG(h*jt!Bc-+I8r;2w5)x^q_mPC~ z4dN1dh+8v3B;ij;h`d7*^wT7RtBSr&VnRPjQyF>Zkg__zAztBIxPK1M8gTWIknm}w zD{w7W^Tw3#ygcd(5=jedaY>{{Fp-cZhrIK|B-}+tgm+0;_$ktHTp2Q=X~O+a$Ot%L zMCc}N;c28XYx-=?OYN8TvPY{m5$;6G1HnwK%=bGVKJ{w$H`Er5kSBb5XX zG0~4P_E*WY5F$Q2PYVg2`h*VRqo)988u#ccttJh$y6}onhwnO))*MpDFoNgPq>YuM z-@&tA5jXud>a9iIDA8(qK~Wbm2t!0m-$VOX;kgHWxC-refFdgH1sQN;wG*vAx5MW zIDQx7d;>VwA^kPd3XGp=Mj5FS=`h0wlMOt7h06zcRH()UIbpo2rY$^uh^%I*aER2? zzajxfJKo+Wq?~@h>(W$v0JMG*x` zk}zmFB0P-ib?7Zszd>Isef%-6u$IE3C3f_TQ{=|7M%y%@pwU8wgV?isuf zptG4?`x$A|)FYqCBFkrWkg`1CPjNrbbYS5OPfw%osxHw)0jC4wVe-XzMbRn$#`svf z8P{6iL`E5gk2i500lpXxn4BI#dKWI>Q4?2SYThOS+79uutURlC9`o2Cqzu0UXouBj zIAm>Z2W-I9jHo!YBmV{x)0{=wU*M`iUDn3kynKlDN4Sp!IUaTcA7<#>7m?nFaYF7j z=EBFoX$)ge^NZo~5UCS>032+YVIXa~C6js9l@E$07Lz+o5D4fFh&kIy;E{B)l2t!9qYq3=wND5ojw6Qe6@KMh`H z{bq8~iuP6cS(@_atj=Ea=UW&no9op}uww3Dmw>!^GNv&gJq~#|PI}0HDU8t^`iJ=l z@FmTEg1_dT+a&0Bn4E6#0z%V5)o z_}JPse+&4lh@{<*zD$xB(lkpowfrIr=^#mH{%>B4c0VERvT9reef=w3&tn|_1L;|$ zVWcmh4924QI?{hZ3fyZ!6U`>{5p`J}?8AiSYRFMPsRj>Klkbyy;M7b1j+_LpGxRJu z%K4bt7SK10zX?o5_3|-W5;MVr+-@Yq2MgmUGPaQnDI)=Vt0aeL3+{s?N_vC0(S=fU8<5iQb4MeB2t$$DBUbQ zD1BRcTzWz}BRwnqAXr^%uXPp*tYyHL$Z6U}m(x+ezaY2)|Hs0cfPXXK9|8P(G!JNg zp!u(WPqg=H=L3fWA4v|$E%`Y7P7eP8!2b&l|7;0-P2oQZ|6F*n@Iv87h35<3DGV1H z3y%Dq`A_F>zWCFN|8()yi~n%(m5YCW@!Z9iFaF)d(--$$-0<@wKX>K(g+H$H;@e?D}AkpTvI7_CZw zC@HQLAj`k^htn2x)WOO(kaA)q6_{ZviJ4fiX0Q=EagZwF1lPETo74~w)>A&>2i>7X zNRZSL8Qc;k5fUXaQb+1ZoFqsCX~cS?nY55rk|b@Uopg{CNs~^}MYk?}K)9LgBG;1ZVDH{a9w0e_RV6t<_TqDnV6{j-jf;>yAl}8nsi0U=k_?n_w5rf(6n?$Y0Ym zxHuqGLnkXgpB1cv17GhV_mDfWMmt8nLXMNKl6%RYlKYT$uHb>=aD;Z|NP<49Xfd1XK($?f&I69`sSN*pW1ih4cG6z zF1u&YB+fmG) z0t8e-RX|v^QC_uv%cL|kH_a;{XME9}@;uD|@@ML|d0}9Da(+0DeEvkaU%~HDP`Mph z@_1$Ou`FTgn46nDLo{K4$#~CD{-tc-i&GfLlsvyZE(hhwod9*lKup2$=>dE*6~9qw z1zM2KTFG`?J2uN_srqfp3L~~B+cu>^7gUGx!^pV zmc8ozhDjOsGv2wN9F(S}&K7>;W2NOFKnbM(j7*QNKa-(HH*T3koAc7q@yRCzDhy2b zPn`+j+2mP?%x8G6z;an8Dk&qSVMRs;E7gwlHpH~r$wGY$vdo+j;W;$cw%KwzDKa`1Ln|YysO)3N5bZG>ewoE<+)keMi6D9iDPeWqpjIcH?Ut;R@lOW*G8M-z;4aQ!DTTOUK z0$0yxHcqm#(_V0VaB6C>A;FkSnv{2X<*74ORdf4>07gDDql-+(=O*VFk1@`YtzF<# zO?c%Fd3c(YfSllOC9-x*NYnG%r{nl4S%>HFSlTheN|Wj{f+l>1YQwab^a4{l)4WmM z**{+)_b)u@BYnyf9ebje`{!x3g6AQ5NOJF)+aYfU&t=9Yul4SlngQ(jj65^1mHWMC zw4@*66Bej?t_F*R6|>T?MIR!L9z}jU_u|>u?rs%gzeMvb|r&}JD&Gqdv=@Lf=vol=;DjB!QriG=_{221cl5@^bS&sn<}r?5Lb=Xbcl z-`xDQi*I)?+z#V0m_ZwERCr}x8)3XQ8Jy30=l4!Aeo>0d%pcw^%}G|dOJ*OO&sN~+ zis|{X@QV4vJ7zErXd&<(GDeXhO>PJG0^soUT#@5YO&eKgY5oS#z62z6C>=-BLYQG; z{_vPIJta*~BNvlE&@0WCA(eK`Fy>-zgHtFXgc;r$;-DY4vyUBf;Kuo@A#G)0+njw)>YRg~x*9508`&{2jd?(_O2ZN- z^$a+fAz>xEn*t!EU^yg~*9QpJo9p9euGWVy%ErQ0lK_RlRDCS`9f|)3Rw2 zGl|lL3Z=hFqdEqE-o*dP!}EqP2tBVwAC$*>Hk=|%{vDlG>Yz*Lt}+08M^kKOV}E=g z)9Q^j4739tp9|Rl809lFkop=OXf$dlg4th=j%+LkMhAW~% zZAodoB0OJ#QfvrHhVp3)&*Kk+LZ8_)1#xHsS-iij9Tj`%6}toGINmtwSOrb^3ycsD zQdRDDdgpgf#b*^@Iwe^tL8C%*?^w^xi>n|Fa!?OF1`I%zQAilz~W3h zFs`J-G8qOBsb64X&l7nBD_wbnK(yqMc>%Wz-{dC+LJe}7CFOGZj6n6!_RztywbBGd zKeuCgR+*ZCWs>&xFxv?D0HN^*+TOxc)A(eWSIZ;|5;ebHAt}TPcwq$=p6`byWY|(L zX<*Oh79KK0^2!X`5KaDhOWXi>m)Dvz{Beze6*x8L%XvqbHi{Tn$3D3!jY6PN)&P{N zpbTS_4W$f-hD^`R?U;d0b+wsEs3~GYz8z!g#Lzm`u>m)?VccV^b@TxP=C|XMZ1@#m z0?s@Ycwhw{f26Q>1s+;4@E<{;sr^1%C@=)7r4`fxzktX2Tz$+1g8^gp_Q$92ZJ1rt zC^gJ3Rgx-HDK#z8`YL#Zrsazt$_v2QL@zHuWOrw%3D&RHTZR@Rk`?Ic;-rgUB)FgJ zIz#mlwE#HMieYxm%~iNwBMLo#lRPbntXLB<=mbCJ3| zE=hY}XAMwTTbL;EU4k}1X+wl7xj7g-duA{-Lk}^%Gv$UYwSn2gu=!=HM0-e&vJR3J zj@yViKpURy@pevOC2_X!uRf-)fEodlJg)IMNwV7TXilv@wqvr9J9hr!rZEjKmuE$8Pi@P`n0g%x-b6w9%FJE7>B-^zeYX~WT=>*kUs-s zVL+ajq^(ei$Xwx@nw!IPIw!M5)C61B;FCR~2_Lfzm=&Xz_4=?VyL4+pRgCCt;VBgU3<~_NO^@L;!-yXdnJ~~JQ z;x~s1;x`5g;@5jC#Z&zU#XsvWi1#l)D1L2uLA)uB@hd&E;=Mf=#4q<0#Ctjm;+MKc#A96r@r&IBajqjR-krK29!(X*FLV^dyV?ul zovnT1k=BBEN9zUg_O^of`L+w<;kJX~=aLu1L&<`8u(ejat>vKj*_I39tt|!dGx&a> zrBU49;u3FZE{LCQDu_2X7Q~wxE{M5?g7~RKLEM*kM7%LEBHmDcP`tjrAnuJ9#OvZ0 z#BALK^n6cULEK$;P`tLTPuvx`AnuG5#MxLu+z}fWw?{9CGq|S11@W5jL2+AnT)aBu z6Ss!?#H&IDaZBieI3*txC*^{;x%LrpqP8Gzsy!%<2MglHKtbFPI4G_c3*wk~L0l)! zifjD^agDDauJ&CJSNRT#qbnN4l_QUcBP%Y5!-EIKp+Se(-zSO7Gifo?H!Jq_9u#}h zQL(%0pxD(ZiJj?cG1c*i*w*@pm~3qon;K`u#zd>wkeC${-QJq5@%pq_@4%P3sHbLY zEHWZSBW^Jw;3iyC5JOT_l%)$|tu!kJC9f#CGuK}b#lWl>a1}(qD=TQsdEL=@ zvT4gzlV>QsXX?%`eTn${SIytMaq>wx2mNpyz*De(@{Cq<&lFsA@i;E!7hn0e;wOz? z`eo(#4^@f~JY#=q9+HI;Jp0mJNtw$sFV$WIU{N-^xne~(!CsVs?MDfZ6DxfmUP@B< zuy_?>g%4T2a!V>t!0pUL3tnjk%3*myM zS2LlxU-O~Xqn*`0t$nR5U3RqW>9X@>7j)ISgzi>dUSF@@r+-}kj=^B4H{5FYmf;!0 zJLQIQseH72U-{GJ?-{#{w;EqFeo*15NLOSl?yY#D;?;`xEAl3{snyhP8aF*^`gLWZ z^47{9SAJ~XYCdLu&YZV|EFG5HERR}#Y3;GjS)aCPZ9TT#w%cuw+g`POU^m+P?c?@i z_UG*{**|bJJB~WeSJhT^RXtO6-nqs3xbv*@73Uk(gVht&_f|h!{X+H2)o)dwcbQxf z*R*T5>v7jv*GsN9UGKR&+-u#t+=tvJ-Sh5`Yr1Mq*PN|+wdOsK;PH4`J)@r6JuiCR z^8DIs@`k)U-U;sk?;GA<`gFd4FYO!i9reBK`>o&XkNA82lm49lLSS3qmcTuMhXc{Q(i_q*gSwy;Ob71@o(`T3z8ZY5meh{ao~?bg z_C1-%Zn;?=k*DPY^1bq-^82Cw(3VgxG#C0-=-JR~VR!h}@bU0t;TOU`34atZMrtEn zk@3h4k-H+_h`b#QL_4BuquJ=~(fgxMMt>Gd#0Fy%u^VD{#ZJebjlB^2N$jIKQ(dI4 zw{D{DhPvZ*^K~!Py;FCgzN$V^KUjaL{<-?s>pzHV_nHmy@346UiSZ-)_saO}6FQj6g;4b{aajbRO?~yz^}5tDWz4kuG0X zsOz?_Q(f}AJ=&i7o{pa0o`-v$=sCZvdD$(?o>}&4uda7P@0Q;Cdwy`a=*#xq-uGI@lZj>8GRHCxW`4L_SRPuwYxxb!Z(07t@@M)@{Tup^_21V&-~Uqo z2mQYuXdLJmSUYfJ;Ml;61Lp=l8Z-{t2ZsmO4o(bi8@zS!_QAQqd{sC)#=q^tFx=0TmAZ)>NSBiku}q6 zcCUGE&Btqd){d{;yY|T1hu6Na_U(1Xbsg*0uG_Wl(7KcB=GVQv?q};RjG4v)W0|q( zvFz9_W4DjpJNES0i(@|-`*6K(y?K3R{o@<78+LCvw;{iAcw=_s!yDfmm&T8bpB(?j z_(z*+H|^bY`=+Ni{bHhRV#~zgiN_}1+B~}X^ycR_zrOjyNyDTx*)=&ndBfyKQ@2dL zyrp-`D_141dU9)e>#?gtS3h?3TiYVr?%npvHL+`sUi0j9 zIvvAx%PI3=9shT8SCjGzy{NO;vla^}x8``C;R(A>XX8 zE^EntKX+B`N&F!B{`bF65+}pS!EXVPFYh`?P@nuM_VN7?4`e1ca!%>d1GxwaCuVn zwya1@$9m#k!(qMK(LB=Z65EEN)9r45jqe4!x2nceF*B74b(aTjVA;f! z&L3^8?sAC6`>X0&x}yy%l9Fyx*yiwA4;cLUC!Id?ZRHXpe;JVqA80y+=P@D|_CHhD z!%SxUnh-UI(%Ig|Y;SMK?AB)8?vOc0Ot#kWGT{>y2_9llDtF~6{H`Ka3 zzDo1C^mX~~H%gB&14-) zggTQ>*(tZn?a6k!^Pz{1KJ*a%9s4}`5W`{N0Qq<9l$(Jgy_t{A#fhpaFbhre)&!W& zGFD!{^_q$%KhtG)kK+L*C#?OC=m~l&&w9&p8eWZ63E$ySC%rphk$ifQfu#LFyD zdHPPzmz`III&SJt*=xPls2pi9+zsFCnwI3oF3I8!Sekol`?grOZRxzGUS5CHVDmXW zv3h^n+Y$Hrn)@Sx)~Hu+bc`=gtV)TjuL8rduo5^nf@8fIbEQzO%a)fLfo>zmTaoL7 z?ZI}6xdyDS!K_BF$^RX#e_(w4r11-1`Sfk{`}s}BK2872%YX^G@$o;77#BAF@~p+j zX3r{Dwn|&%p@s1`3T)!y7*GbzxLxn+MV6x8X|Qf=pta&|W4bYxPSyMCE%%1$m-D~$ z7#*E;^=rCAq5dt_>c-)w_NMl9eSNyx)=xJczDHy61bZgh+c)>tD&s_-Y6Qf7VJ7uv zEXJCwOtP|Ubd=?coUPeFK@qAptLo!EXc2Tq!zok3-|M0))3tSjQ%#*$_lMGr-@NIN z-cS{GM0|F;F9JqqX_$UL*tMahW3o3G>7Poj+@E=S-CybjA^!ujC+u>EYs}`Fu-g^( zm{pvWfew0N%ajW=D`<0NinyUqFtVI9O6kV@KMC}Itx#Q;^gEy1~BBNXQvhG-qwj2jA?lIhe@@}0)It{Di~{2_Dokk-Mu^Ku0ZRcO}uVgFrP4m@kD4tr|cQ7O_?{&_O`w?GXx|2?4@ z7M?_m-FgzwxO>{OvSk=5tcTRB&eqiQka(6@b3FtKgE1pFP+J%2j!0CeC=h0*R=big z$Ey@Ie@TZx=P}EvSZ-v}N zUXy4ZyEauf=naRrG){eWPiN=HBDmX+y=kkcF3RLoQ+Gq_+u$kCu)rcG4FBmru3j|A|)BH#SCYyK`pe=Y?~D)`3v>T8Gg&(bu>#>80}# zS&kgfzcn^?8ax|@?)(AxP+~TN;Ln;IS(7Fwlou_X@FGHTn_Mw*82N-B+;C=WZf955 z&bhJ4&$nOiTiemJzNfafXMI!Wcu$E0VS>k46Hz{Lh<1WsCahg)6pE$LRT%4GjP9~@4CNBP_Ydq!gq{h3ZgmZ1% zj*N{R+17SKOs{EdT$2{_TR}pMkF_JAoeG9$Ls?c!vszfSBwH@z42$vXgjBO%NcVZ_ z%b%w``51juNFP0M^n`Hk#A0~mY!=mPvwD3w(Xft}UjoKxrh>wD{G0g~8MIRXaqo%z z2MW$Ldf+_H`eY1e<0Ko88>_t8DoxI)U|yZ8Xpqrdr@tg7Oq>=`0hU}!rr*HAq%2Y2 ze01~LFYfN?+5N?}SA8LwmDhDQu3sk0y&IagrfmN5f1vkZOMCS#4av^*j=MK(xO+#s zw_~>)4t6urbO&234LJMAaduqAnN?p=M#=@)L=_lh1s|ls_sUYk7R1U_#-}`TirP<| z%Ks7r&3{0vF1FE79%slDc;r0bX<)|!h=v1U1ZI^`oSWS!=f(-D0?Ij<4FJ2zEaZrS z4#nawT1se<*@LFUnoN`F9%E~LUE`_fz@)XNX?d(G($bQo=kuOrTYE)yEKdTa8C+&G z8q8(rfSGpyP7A(RvL&%#(opyZ;3F70W&GfK1seF_xj}Ut#{5t7*BPn2?)BnW5O;eH zDuqIJ$k~d`vl{Fd_)Eze{JMaU z`_CEA-G9H)_`uc&jK=$c%Wu(bz~!<7`JYq&f&5>qW4aG8D;Z2fr4BGFOJUMvlo5q; zcu!LSa30M6Qh_!+N1Fle^||~r7!S^P6h73rz>AC5I+`NlA%}l(t@Dlbb2~dbch0RZ zrj3dYXlxo|x?s#&R0GBIL{SwKQ&k@fb2_=0WL3Jdm6e26XE8RS;8(GGk~T#hngy=^ znCpXUK$5z8FcKNOIyJkerMbCfk8rMb*+g60#Io88^q%H$xcM z@vJGBbsCt)gWjuVa~giIRw~hZihbzPYf>{kL`t?WtJgEiL5Kx-u7>uwIz_D;6L%^ocOLPc{V*3`T#an)?YiYe=Lvt7x4 zgUw^J`D<#-R-@dyJQiP_lKc&6Pp1?Odc5^ci@19-y`h6G>0v7qVG#JzLmrGejgZwW zFb(__kc9T2Q?|YHl8|^wNS`=y@g>wrW8MQIa83bUDXWfDWOWPLhG{m=^-R%lvqjZ7 zo0h|3IMv_R*MAE3gSBq#rcwHEeyo3DqMy#^w+!YsG_w9MyMg(U%3;|tYeAVBnEy5D za3xa?m6p#M%(`bHj7eC= zf*sy@^MM2Tk2LPvF3#fk1BIWv#1EuVS1>_Xada^M-%ipxWk#a#*&YE`tFW;O&|o2; z!tSPwt878(zyWH`ef#e)&CmH#Y zjClIwB~OS9jUyLL!J?<_#bYOpM%#*xritZ|$nuG%jukfJes?_Ra0KISH5JaiHtes9 z3{7`+O%Fxt{KGb}!RJdzHk*|2`Iz&UGW-cuz@ML4qZ&`PvMO7t&uP^DU_Mxkt_9~b zH-oD}`!{vYtcXTe%yf=_#y@9mkXx27Z;@MV2m4n=ho(C_r-!1W%e(x(<>~ZtF`&>( zNL<2$)}1xiW~+@kvp(k}3&i3x(Gp4}qwP>>(WM@l(@U{nJb7#>B03gf;kAoxw_Sz_ zm6rq$a1nt26Beq*n{`LAHKC~tmEfevN2PdCCQ6+XRx85`He;ACPq2qRy|Hs+4h23c=!yy5%18{=Sju@HL(7*Q}_I4PD)Cu=0wPMyUFLW z#PEIz&E&T3TnJKoB7glN%D*7+%%b}+G+zq+l1 zw))zgaiiPf_3z(*a$;hy@Op*e&a$#vzyDDF1l@ZmG_zB|fm?_H2O)R^f*B{QQENEs zY03t4tR)W^)}pq@XvAg<)jC~9dYJ=FY#;BdGu~_N33dz^8@JVO=#Rzv zH`H%yG~OE?+MaUO1*@upbxt*X&l_*|c1FU(%e}qw@@?JS+m_3{jrmN^&XqB`&*D$` z@n2E4KukTs2k~UBx97 zyhO@lM)#^@del+eaYj2reU>tbV_&g;f zI9$HBVyw!N0E9SaT+p^W2vSK6;*99b=66ByHw{=Q+lDOa*JvH_ABbSwn9u8 zL|@do2nrZ#xLTJpSLC!%!{(xZGrMcaGz4FoE$Ld~ZGHZaP{H)+MLPH;;f33Zvrw^* zT)uqh8^e>@H%yXjs;tOSUCyD;2_=2Qu7up4S~^MSm-LOE{v#$z`u@eJ)jY zbDU4%y}>35~O8T(0zO2ZMofol|H z?f>5~-X;dq6U;IVTb?BgTv4K7VX9nOZ7kX+Ru>yWa&2eC_yu#T90*GCfV;KcgAJ<0 z>KlBYuWWLAq@WvzSH;eCR(CY%>8uqU7MlZ}f$C6;BzMND+k4wRZhNKKrnB3}I)Xio z%qQY5{Eq&Ka2U2DR;SgLtUz_;S)DGYt!7<>z1f;%CMTO@Fna6~VMCZL9TXR56k829 zOiY~o%x4Tn>^xT+>ODQR>l)o2OgJY`9(u^?Ew41_Za0}5-7DZ8LwjwoN0<+q%b!ip z9!nI^(DAZuzF;{-ZaDuBI^Ke%E37gyX+taytPlajM#8Sb2( zQ%zZ$z!jm9wNKn&-1qpKC!mPmr`5$enl99_fHuYoOIbPUS(JLkRSEZFuM%|xY;=I06@VZ#II_WvhiW5D_8(?VGW|oRVpCa$WkzGSpk6}RZsrsuWq^JSO2+xzoMhHkdx(rW59TH5Do*E42&nG;eDd=>#u(2Q^vjD z&j0;`PWn&zkEr=#FWQ&TiI-5Xobj8w_R!_Z7kIdjvQ?uV%LnP-eojyQ+yBl_9{BBT z_(NSc)&<>I7s$l)w9cM$8`z?txy4@Xr&ZP6@FdknmQhTn?TjXE9XtnS2-{;i=a2c! zbyv-XT&~dURdr@x%wJD?dy;q7#%{ZI>MJlp{Qj>@U3*)s_ReHag;5#fNZ}}X4ehb@ zfolJ|FmH^^9njqS@jj&tTdNGHee&9KSC%?j@bIyemA1cQ5t5`YUy6 zW5p-+3Jo&+{RI4Z4^H=6pTP!sMTMDdc<`;MFy>CE-Na2}L-O4Q8zpMw6;$4Ck&GlhLO(Z8lNuzvAMOsDPz81VJH7N9|{iaT{dIYR%Oy=8;RYKwcAb1 z=Teq4DSP+K)=sh=7S<~lyiRQ2B-q{y+Ct%6+QwXPydV?`yDm;j1&>4bg*J1}HoiLl z!-nR`M!O_+j+zx~5-i>17$wSRQKZBWwg^AEPhM$*BWx>TUu z*VrICLqV(6x(|5kAsZ0gn+ESUk!Z$I>#-UF(QF{#F_;>#%kMF;ojcZFzAFSSWzx)g z2)r@(8S`JKTzbW=tw#Ptq6lbE_lFqUeA;5OSvrThJj;T?WuC5~&L#I>=}IKJ*!5p) zx+ST*rgON{YO`69EV=(qLr+fwF5sE@WadC`$VH~6Trk_5)mf+0E*L9o2==t>*l)I% zZ1^jYZK`&QCPkX;qX=k&UT2pQ{335%ed@VuYm7BJp8ML$Z+`Qeryh9V0s6gv|IYC6 zcmDlo`_Y?kKHAPW7keE{euZB{e%bCLRU&>Izzs9IN9N|>RkOoQ=N74f!g1*D7xy}{*p@Nx{0@ott z`-@G~OuHkJ7zy`oXh^P2*Xr)p*VhNIKB@~OuG&!R@dV`F&B@ecUrc|uskbSVFbA50 z;n9tAZBS3FHb3nww`f>MQ*p4&)U09? zUaA-jiSONw!C)i1UM_wSzN%yt@8OYSdu}0Wr5~z(?jdll6G|)AmyN}Uv9&YX+Dcf6 z4^v^6wlpDcm&emBj`O*+3twV~sIxvbHgD_;h59x&H;uD&yeZP(>hrbsM-V;e_qX=f zdE(4&jeEqXO{%D__9qg4r>~;c+DNUX)fkv&lnWx0$_8 zc(xe-V66Xuu?8Wd*tgWivte^K9M<}rS)aBzL^ir5M&m;44U^K#oIs;SS7)TQJ5l3n z?3Kf5PeX&Z)wEm6c4lw7v#QA{%Rygt$WuuNJsxK?6_h%nPE$o?AmMSrWxj2?Kpr!c z`&}-tr6z#4^u+K+tmX8AHG=m*5vv}b|AxLJwu|f$?DXr{UokyR%CNn{`U}Gf8%*uN zX8NstOIGEjk)scIn`Ng{ zZuWYcSlZ;Ra=WXlYHH}SRk%TBRWTj++8rK`!>;fe^D`KQ6_8mA<28#ZOSR<&Y!?y> zcdTM2;FfV|)S!krH8gFkfN5`QR+#rwifZCCuEsKeHFu%7{=mDh3Op81;XuvaX^WXobg0B5o3smxZ4 zK}dqj@JkKWhV{$B;brR^=-{oHHwEJFSliOFw!{DNR!#HAud+T=k@Mtr`XI)QXcYzu z(_Bel#?5Nz#rdM?!Ro9iIH(7`@k6Uu!f{ny$pWnHz!M0h#$O@WdqG=s6AGbzdMvXQ z4*X7}y<%8vHJL3Iv!PNu5E^yrv>I(J)%Cx^K7WPTR#D+Fl!;rLXlp}#hl)$`bKxBD zz}6LrJArQaSk;5p@2bmhBSQsK2#bQhuCVmBCMTFGn@ASZs)!%EG?6Mb72c{{Hl9q5 zFRLx4`FV>s1}o2NDW>QK=+guF-^Aq~(dUqV7n6VFHy1t--V*+&H7d9%3_j?GxSKt1 zqIVUaYl!;1uH<X?lXh+i1M)3gr*r1ea3YY}JQ8sr->5e7Xu}yp-}T zyIw+h7vLwB!auQ4evF(dmJjjrtUql_%imutk8@;qpu8tz)d*&ZRnYJjnpq7tgf`|4 z<72)9^5ns&{TYk3%I<*yT%`|Y^;K-=OP!G+s1){nG45TD?Wu*38%JrJURiB5_B;J; zQCIDME(*h0Z*#e9xP<$n?SZyvH7nL0y|}v=nnSw<=VE#yTyXH1oMpg=6?&qwgKdWUWWEbqjJiTQ3tO=20RULh#ZA~H zK_YNNAVhFMjnHo_(Ieu}&Erj1j^erTx^J7Wq+`|_m(LtpafRrfJ)bKR74^g9k?mzm zoTuwmp6*q6h*nw*9{px&DqqCYOUoa?d)qjVQj^&b`K0oUr#XD^biZ2OX*EbF?*#l3 zo>t&99##6Y7jKl~Jc@XdfI-x67I=+IJc==38;_jlW5E2wNL%ll`p)F@?#n{1`eODT7X|^z;WKq6p;`MV<4IWG$mcGImzv0 zFMQJ#POk+@FQ@S;@}jH{G$m$osmZJdH#!rdm|)lQ#VB#EUSBl}9$NLfv)97V^ zTwgu>l_DSAr(wDXv3WK<-e~tN6{oIHWe{{Wmo3soJg$Y)0Nd(Zm!4M23w7}_pJlTU zGvX!dmvq---Tk_~5(8@peF_t6LIf63Vv#zJDov)_ zxv|)Wf{31aYq*o;Tz81D5jam=)YkY=c&D-AAID0R3F3dw|6VMOI4DJtg0x z-kN_gi#8T2^ivi$q}+BTk54ewVm9>-m3*i7H?Hy1T$u&|Gx>jGQ8T#D-&2PEG9KYE z9h~M}D$TD}a79 zlAJE|htW{!&t9D3R&`Bvj8zbLjYYbK&4+jKG4%2=9Kd@f75!&4(XehaHGfG<`LnggT#V-Qs(+fb#!h!pEZP;uXy;x)tT7eW zqq5lK8;`{~U8koneyVoarnV}trJ~vv>*#BA*&{8Da-Pc|%XIQKE-h4mYh! zi-Pb4LDSNZXuCZw`|CPlc@zC@>v(Uiw{8cu~d^%Y!p?n(fhcAc!(}nV58Oq_O zhstYJ`0Fk&|FdFwvNuE&YpQ8^c~FD~U@eSZ(f~{b_!u-5#X5A|@I|FgTQ6%w!$;j- zQU@}RI-0GpiPON-I?|BwM1z|4tfss_+ta!ZzLE0PK~FZQ&tdzC9ZGCvv`yld=>Z-W z5m`9Er^I_NawHTtDB=EIhztlb{lE?^vhb3y5+m*Oe8Vg9mjzQ7HX16d)phN zM&pg*bpt(HBxx$SBzz!$q$K`?eo6@@SjK`0TI*Zl#b}gDqb}A|<8JEfjD{lNMd1aN z3$ZD86$1@eEQo{0f^@Oy2WEdIi}u&Twm)a@KgI%62FbU9N)52YQUk1MZA!d6d}L1E zyY|Y~mmx7Qkz5j1($hPtl~mJ9FY&GoV32`4+%mwuGMnpYgNOpS>`_`060m#VTM%Lyg7!%-+*M-r!^S zjWULNd7mi4@J$+6UWg^xUfMAA5ne?>*EOF@ad}imGYGa zBB4AwL?;&D!}dCl@~y(#s_lh!ywh#b)L>tZ=G@BW(eJmuSR{n6Xknc#e%X3b`05hl z>*6sb6aqS)op16Z?j;1Ri`_>TBF2M=7Y*qQ;R0$;(6g}SLxv< zdUHk8b8EPzs?O1vUAZfqUfvrW+?GmC4Md`S8{$&4ZFRh_FOG};jVEsFn5fnpwKupD z4V!8*kL}9r91e$;Z|UgRvOHv|%s;cNt!){*_&kg`$s;g5w~^6|j&0_8j{56u%u=h; zV4<^sQ$=SX7K-ud_!2&~)RciwVf`-E(~HU1z`5HPmU3edUvo`P% z{AO$i3Z65IrJC?_(R`-H>JGjJ#qYynAWPj#G#GAc3**9mCLC!0s@_%eJmk|Ix1A|| zhX8RM{~E?r11lj6udtZ0StM8^~pT?EfMu(y`dO1*@*S9PS9vQa3_AAeHiqgqZdr^?gm*2hcnf8 z$K~lRro!_7CoKIvb%c_SIzp`y&ajv6PM^O$a)%nN){i<&i^4k(ky4S zHCZJfr5e#&C2=2Dj!AJY4N7_O@}QKH*Dnc>TpE<3dGNJkOQTX2#=^#rQD7`8p9{3y zOC=q~Z-=&AGJeIsv*2^WTZF!$*t6JzTks(jrBBfX6l+biq1bFUsdG?M>w=9mT}qQB z@adWb_zv#^{F3#*V&g5qr)x5bE@v)4|co}mE|VRtWkm+h*aXF z5*$F>x30XR0oITrv#F@!n@VDLjoUbEzgEK!hY4TrTM@Er0?P3)6HbcxBTh?&DP(Q_ z6+bKXPH%^twCGQtW-(#*I=9VHc9o$*Fe_)rSp1YESYfNRVt=D9Qxl7av)Z1lxg%>f z#}Vs`C||_-#?=@(Zl&;dEL>@)XCd0Rq?gJbdMf(I#{Vw#-qp84tTL2XW1kcayf7vV zw82qc5**BBo}WkK>z^2SOivDw7uEGoP}axDW*RgtTDOCq*ehjo2UbGh$pZt5%m*Yx z1f>H@Z5#zYTeoxgSi?Q0tlMdQSRX=-dcZGPw=3`&zi{|;m8yfHwT2){M*;s5zkq)g z_>UIhs8_F=~{AJEgy{X7T5@i>%9f|CyVguYViBZTx$hm z96sxRqbebaJOTct{;$T|#B^LBY^c3b|1nD8EcZphyAD6gYW(j6#YgN3n!FHl8FSf^ z{{J80oHc^~#DMa`9{Q5fe<4v#Y3qd0P=))V#0rJq0sa&J*L+F6!^2ndB|ZBrZ97ce zm-&*896rrtn$dJ8`XIu?A!n+!Qr29b^}C&W5GjxNYj;Wfbt~HzSsE*k2<(M8uRkgh z`|#nDot=Lm8oO<_t84b+YnMf2gNBSI5~m3aX2`@%n158CsD-&Kxev9-g(}Sv`wJdX z?ChIXDLO6|H?V1n+5L-j=o0wQp))FcTMhT2G9T>{nwP+*YtRg{ryAP1-Ou0`d&6Z~ zfzM=u_a8Fxu*%~W=K}nm#XRfbGQs-KWa7&zPsLjnMo^Rq_{&G26WQLLh#g;-pLnw6 zcDzFe8j%<)vp7y-RMoU{qL%4HuBY)944mL7`s$q}T9E#8--=2{b$R}=;^|G=v!Lmq zp{r`WReHGW*~!oaeTFV_-ok(HgMMz}apR5(vmu+V!>b6)f%a@bpEE0(x#%4Uv!k60 zQ-rE~i$45Mil+BBwYI5q2DEO_o!T}S`Msg_$xyVt#%5y^Nq2)kRVBoRwxvHQ(*AOO zgAOSA)J={n`P7*_mGZ-Cs1C*tv8IJ};39rtzE-`4>EHyX?<)Ma74AEw?ah{;b~A0S zTB3syi_iIaHOlK2?DzgE!SefbT2ix5e<+z`SAo z?;Up50)FR};WK{b@P#a84oId|o7ycB`p@7mk{1S_>3G(E#?LIyhqcht?+mIf6!{tW z59eQD=YtpEUx&9c68K_Gw7J7l3;0R+iq96_nJ=nUF7s$Z)2p^&5v~&i!SuYqTNzl! zTghM?{lzNGHf3^ooa+_rv!h?D@U}=ruQ;t0FzBdrv94$P03Kd`jaojVmKQzFUEZi2Y~Se8{%PS2l<((sS9}p;&nR3~3FA}sU2I#RAMmR~ z`H1ESR(Jg5BgGLlAxHVU3HWwC3lL$uJvu7AjqM$KE;oD;`^JS|qh5&16K{vyRP>SP z7uwmk&<^;d0_9~+cV!%F3V*GRqujPIjuL)n$ai0YPuHmM%WVtrOMEJn$OQ@ie(D@v zXOFX(Rm6r@PUUbspPSZ9TPrWXgH;W-C{dT+=BBjyRHJIBg%+brj+J0;2)0^`%yY9aFKEbR zr%QlStZv9JG)#vuoa%%uh{-gFzH$Mh#Tk+Fat5DoQ1(s|oFBnC6wbNI`H{>?)*fKG zp@(yvPED$O{;f(Uzq>4D!HO0$^Cdd5Gd7Cvi?VqZdi8##4>eVmMpzX!*lD;#C#Em* zuLw9xk3Pc)hOKuU-aV`6B}a|fUyl6}u9sdd)k|sA!5JFV8RO%;4q>lMlYK%BZZB{> z!(w+R;Ino?L@QqtAR>gV2^RKKb%ru4^q~%NRH8popSpPY^=kP?R6M{7*rG~ONqIId zoasj&8GM~^9jdWD8m)NYGJJr*!`Z^6QSm;t;P57N8|tt&Q0Lo99fO(kcm;ZNSsgx( zg?3;obG^=}j&UT>k{;+R+R7L=W()cQZd=3pzO_#CFPgEC!eLY zq>7g>x0E$8Je3LLcSXsj%o^kGA*m!d!BuHw+CzDd0BqhA9Emu8iG4$6Abq1{;O0}i zGmo-28@v~|ZY5p&!SdaEo;$(%OL0z+(8nOZ_&;!eSypnkseJ*4?Rfp@%_q~Nw|^Lz z8K*=4qi*{(`9~ILdmpD2-`B-@?=h8DW@}|s-PbLl73U4U-s8SSa^PwOBURNlR+{-1 z7x@YqZSk%I(22tb&;AMe%&2R3SMvA(!(vJS?~G?*!=VmuL%43OQo|jvu^QebHA=?8 zY!2QIY$g`_!f0i7^8INSxagAX70>4t+n}pE)p2-v8-9%AlFbAhj}!uI9PGd8u_!wo zHQBQsLk?SfCLZ0V&X{a}{aLxy9GFw)JztCYpgV_vO{6& zQM?G+<|hl)<6%!Khw#ue?d0cF==-1nv*BD^m$h*jzNu)#39+PxU!pqSr$ISu7X& zZ;g@zw1b@G{)}tD7qN^_lV~jSvbci!vIgwtC!jiG6PKbAc#KyZb=9#o6#e5^X&?9( zt_AU*2#E^Gl4O0TXYO)M0;*>NAn^pg$RZ^Et`F@X=D`5GM-3UT==Wr6suBFIsjRa* zvwlO)4h5jeY1D0gymElAQ*fAO$?4}L42y-|_N7OWh3)>mYew&w>Fk`jV|2}4zwy!{ zDj$1g$;&~yYGcJWW5jB^_=>7)wD>-ZV#@v3A8Jm57whqVAXl)rQpGhMTZQ3jQ{ev@7tVL;9O?@Foah!E#z?8qnM!`D7|REjOjmV9zw*#W%F2D{uOrq4|RAO!gY@;`qAaLo6&{`Zs5!6DC1!Ik+sA8N<+nQgd731 z6hY5Q^dsgb`fc7X-UeO$D|H+mdjM_VU(7DHO_=Q?b39>&;BvN3)xT{Wu5i<4+U8s2 zH$-{*-#MNp6+Fob{N`3?IKG)SW39Ak9#P=4H7kdYwNkyhW?hzc1pt2;;FtIf75EIV z96nuTRN!~EIYq!n#@UVk3*x03BSH>R?U8pROiqIl&UXYBW}*or^q zMJ%Oz&`nGCpx5R+f7Cl#>IOE}UwK#Byldi*dv#0uSchL&*sE5?Pucm;(c+%_02_aO zOSb-x*>hJhz+zc`@6piG;aiptf8CV*51bkGwyua@ap3DJ);0a1gQQ}*!LM%5L#`Em zV)CHy(`wkBioMX*?GjafDw%(l^3!T{z0jO;20n?O*gJ8;EMkwvFa#JrYpR`DEVrwR ziz^<=0c-CHk)-T}3G4+b@}=l=x@-x`LSgn!orWvNpewK1Q6tg0od_Q1XJ|VSQR}O% z%QktkO-;30mwK8Od)e$jZ7o}ru>%WiMXqvv$*R2MTy30Q>Kh9^Tl^x`ROE`MZjClI znl-s(FcBPixa>S`l!sH4#@27IaPl*{jE2z7gBs?Ghw;XB+W$-4mjJd^ocX_bI&44L zl6BaUElaj6AF_N)mgOss?f6RU-1h;ANu0!rlYor^|SPz-kUe`%{Sl7eDht? z11^F_+gytbu7FuNn=b6Nu?{64;?JW^bl|8web!QY%xHjwQZddjX{J=wzzT1PNI1uk zkTK8qC1&k6SH%A{n?tc&_RvEQE#Onk;YIlt(qx$sl_4fWNRwlX<$xZ94X3c}!K?|f z0bH3m32@=d%SVq=TOU2jIkU3=xkvSF2jCmnw$# zL|hJPO>Q9?6`5cCcRR`uQ` zekug|ZejapmA}t07jVQoQ{?gt!FBD%&!`srp+`A1$J=dTQqv;1YAp2oj7Nq{?N-7mt)#ue?=60vqfvwAssxREaz! zKow1erjOj78W?QrZ9&D9@Ale+Hka1SZGa^Nc}_L@qTMf*SfmQ>;c1$&K)R#KrBqK( zO{*N4@9&=?4rAY2JW}n@r?~$o#8d#Ps@0sA5e*`@z z`dE-I+eLak(syzGY@3=MOd#($B@U?q99juXvoz@u(&uBWd?(MpS53cJ&EIY&;{j%e z?LZ1$3%d#GHxR}rW&X@oz*Cc@ftEX`H<3Tt+VcFRX|A{Abgh&L>jWyHB|)WtsRA(d zaGnuDDb6#hx=yC#=hfuG))wP;itGXD*|VGuw-Zd#)nHO-tU&5IHFX+qE%GNBAJ1P- z>b(l=c|P?GP0A?NHUu2gEA_o&_fsmm=zGZQ6YN61>{ZOS#rjt%B|T9!1H^g4ppGEZ%8 z_a^qh(&@{U+}rZ&YDbR7Pu29bxan6_1FI)nCTi=`x~Cz}B`bCK3|Wip?wH+Plv^3g z<iK%!EQVC9F8oW42{Vu2X$I*@woa-IrWcOt)pufETlQz^R?{5t{%X-Mv>` zb@sLk27F`3Qs1RKR`J)7g*t|OgZIDNMgL=G1pUfI&iq!m;uNLZJ^-(}ZLp=H*#x>3 z^uYUpZ}dRWgU#Q9g<^Hes!gx@GMwO^Wvx;KlC#2McPy_l77md+L%5g(9mHvZ15Wxg zq*}hgyiEk0<30=&_`wd8!?6KQnVLt#_>we-hQ~?3QRaeE!*lRusPK|!!zbXGYSsK0 zO4D2#K9}Of@JiFThh342J8T~h>jxZ_&5_+6w;x+YHGVQtBFj`8|&s&%^@F0Tv*Rz?S*$XiFcsCrFYu8iYlJpld}aXBB&%hSVci-)R!(k~k3p4~bA z&Z};@>b-um|Gn}u==r+9yQICRJYr-#CIQ}w@DMdcc!$4m)h+Uu@q%(plr@U7cAzXP z{EZ6?*g1*Ef)px7QYwW7cM*o9_Fv1wwa(oRyR~StxF#(36;+4J3tg_l@^Ez#FTWc; z%s+x`AjQ`>HQxRSe<cES9>UNU6D8uw`BVM%D(b!7dip%HYSjVlT7JK{SHI`Phe2k2lv1^K*a&~8Dg7NIcoy*vxm{|Iigz&n z=gBW&1<>|iN!T#zkPw|gmR_gG(&y&RCPolZA6+rZsRdtnLdA$$BN)n|q+g&$Cg{<$ zl2jc00p(zxnL}D|pH~7Dlq1QlOxnIdV)}XIJc>@RD@Av|{x0l~iP)Jk({t@TE-;x1 zyDPD<5(sm}c-}#tcLnl--4x0K|ENB{lmCW22uk2O63{R0 zS;k}4BRrnO!2anGD$lH&(F;X^Dx)FGjQLzgdb|{t!T}J#=ov9 z1+??LnBf9miYpKJn-cKfN5AE1z|T0Yp5^0x;b-#>SwUe0#Mda1_Y2yZVYq3&% z_otcuQKtVidwSERI8Xw|SCK!(Zb$y1ME;~pG?I=FSSbVkD7HJb1>au)5*0nR7|_cU z0Rv0Tq+cvJjB5CUM}Z^4lk7bguV7z}V>`3E?z-#Ucizd|cY$C~riycvA?SnX;V6~K z{viPa{QdAe!s{47MdN_M1lx&x#G@BG8>k`aQL5dU15Hf?7QMcrpWc5zBDw`1d4zpi z?s@(PC+_+7GxwZ8nW|g}d3N$Lh{oQ@%fMQS)4?}&cKIxP2P#07g^yKLdp$@)dkWHq zV20EvS?Xqcw8@^emsNvfg2@J3F??WoAh7(v@T_+6S7QSMWASD5-LhYatlZfV-L*2J z?5FEp(T<&T%}(~Lw8WpYtn9&=wOLjIR;*Jgb~RvK#On_(M}7tMFGu`mixX{J&XFD> z#ShK1w2t@6aB8rUe4Kv4qoK&p7KgVlZJ0H?1EsU20k?U!Vd?hp>}+s+{~*hY|I!_* zaWcf1cGiU4%ohLe!TsaGSzaD`j*PJZ(pqqnW>d^uT@#bAd4+DKTC_I&;y_+%AdMWz z&Jd>ydlELmkAg%9h>ngsVHIWU&K0-pXm{4MmRukInypR?|*4V)YZ@>lXZ19z7{4V)Rz{d@90xSVAQs07X#EQUKTL9h{QlPZ-edsS0 ze0~(RPgg%A@=vi0Nn68{>?ug&I(r|V3D&!{alI^LD09NO2VS{Z#&upl@YF`CAI9xb zz)60y9*iA`&y5I|bpZq<(&e?pAoGdk6^Lmf?{6k#+G?IOAP3l`E5!8&F*(!Qu9 zd}Lcaksg27cwPM>PiHXD-qYezy=ZGzeOi8A^O22=7b(tlqv6$^-V)CjJ0qQl7z0<@ z^4bVgaolfZ1Nd%=k7zP7*c-v>C{Msh1E?BaQCH;u_wRrDO6BSYe|S=6KaKx{mBd}& zMSVs4ia6gGnes7n=rD84_sZ>QXpkAE5pl7NhJrf)zI6LOXCUtOv zatqkh7C^)oCl_Ks?k7SxCIo=0p)Q;x2^9Bc7V7^0Bd++6MnfcuURX(y2p% zYPZJ|8AUwWy0xuNhtsvU)ZujYY^@8eSytI?Rm^Pxu>OdD1-M*uo7jH<-z{XJhLE@g z`7z+Uz&KOK%{wrrgvlQ)O569fLwxQlBGX~WB~=UJZf(e+sFNfZcxKavL98O2rJjFe zvwh*>pw(u|mkX@r&Yiw!pt!WDx4O_`DO|N^EM&4-3vG_;uB@-Ll$r8#b6H-or5ItF z!{v3|*p;f#WWuNm7z z7t|=ij@uD*($~c1@4wi+G%x<+yrtc3gJwh$8JxfW;`Xtz?d;KIJw40#IMfG-u zgK>iDj!s+(Kq(fqDKDqMpf@KXmLkRidW5|Q5lf45M2IlI^?BvppZ?%|pT0|hb>4H& zDc}FS^6INpQH*hroyz_4mkUQow#i=Wlb*r~M!k>NtSECUFB3E&gWpAj63B>c{W0uf z+R>rZwooY8S~i2IO%PqE_^8%GH;Lx2*CN+N)tQDGB^e%4P7*X**EHCeAcyhpOZqAu z-Wr?LQ&iyZS<~D!6bV?T91&k@(B`SK?cUef+q?LX#T3$wr6M{O{x+0w*P;4J*ux8JyVouLK(bAjY~n zOoS-vCA5DSD|qiqY#D~gmsbC~uaN)l8c#)`EjMWEsR|A@mHhQdMASWcue%}FczvGL z8(G$Pe;kaJ>Q@KawL@h&ryhZKWq%7M_@no}L-ZFP>;lXL!%K zmh2j=tn6OhylAOYQBGD5?qCNrOAc)iI$k%tqg~y7;92&$-o0axu@DZ*=0;^R;0o}5 zUmQb>ArQKfGlSrLLNb-4^wjZ415EAhtaET`S&imEUOL>`KR#Ssv$&~L>*s}<^$E5i z=IK3k{ydY z1Ru#10`gO6E5Fo8(kNFj<}$%Lfs+r>X@U`kjtB)pKC4Bxpj}MC4s(I65nEYyT>>+_ z#%S4)KNxLsH!rS^Zd_DRjeR8BI%~?U&ePAsy?=9i+p0y4h1WWJn_Cu}|0TG%zPK6= z*U_y*O?l6=jpn6`gHgAe`c*D~Lo8*C8!(3CI0?wl04LSpiCFsdge7 z9w?e=3Zu+g@3B^U?YW3?{WqoF(D>$>dm`J0YERlc)pnQ}zrVm6uJLtsba>lu=o#JB z=Xv(o%EjBdm=4;MV5uV75&c|eM)|bUsJT4XQtOJ7oA z)uNB5v=S)}%1;2tx4 zW(Ds=K8+YBi3bL6OcI=h{M$ttNUxT0HI_!iGnaV{l}^}7LGhKw)AIHq9+=&Y{2?Qf zzJi9MHyWD_Rh1iAqc`OZnb+Y4%tPckjuu74_sVkYRw%jaV&H}$QcRtbsjp$dMWL@X zMRHCU+o(gFW*dwHF+u_w4RT2a!_TeWKYrcE~#*ew=&MOC4{e`_ap zrt&m+E#2K!1(9Xj+JpTK&aJCHbCvhXo8?ibv!pz{&Pnu6^XY?#k5El>bBPTWjrp*6 z!LmdJjn-poKs0Eiz?s0jfTC0L-EO>ymkXVhPF@TJON+s#Sly>*rR@1 zpth=Txw5>ls+NLMTK4Md`b;Tg4dvi`{IQ}I1SpmYyh@%Lm^PHO z)}i57aDje}gQkAM+G>4TR2ANvctfyu>OSWDrMgv1^UaAq!+KotYh86di@PZQcs`bh zzB=BGYDWFj)Be#K>Nw$GFwFGBgM-7N9#d(3`TF(c^`)jB&^5^=(&M@es2Z)t05Y+U1*{fb=kRYmb`?QUGv)(WBE&DxpZ_zMXF^AQ4HcJ=HejoY71k!6N(>fUI+` z?;ai|UlM5w4kbFqmPPd=ZJsZ+wx{$6cVuPwRgHG!`$%_7vQ`@5q+<}hi*exu-DjWU z{d@>(9ASTDu@;of_vgq5OJ-@!gP4^z{NwyGk6vPkkf#_O{vzi3=4^cSo2O1esEghI&b6IF^8rqhS)Jyz zM-3|a_%7s2an#U5O(ii`Ay%HId<$Vw?0c%sbBU$4a34;@P-8nImktL4!Zw^VI4zep2i}+n;~I@dPzv|TUKmhvxxEiTER%$ z5swQ93heNfvfr*a8DITjr|0!A+`}3MzP$gd=TOW&_sGY#KQw|E$Ld%iCv*pZ6)RTV zODG!=WuWzS78%SoNW6vInaZOTu@6JA4!bevBDBdV4%%qdPJ2|!i)NK?JoAU2ec`Lh ztn$?_{Ok|Ud_$oTOktbY4ki8-wunAT{G;(xxKHInZiDhMcF70PUIJGqP`=m$HQxwa ziLHZqT~S;PbRif8Xk*n;Y^TbUd-45;N70uazn#5^?GRgTj}Hr4AiST@-GcdgIlO~D z2PqJQ#!;f^8VOVr4_a$}5IuwTt*SR*a0SV9(Gu6cBC8 z$%}V+8z~@?*X-n?)fU!eS-lA7_%oK(f?hIp5Hu#ehWhg{37hJe~dYfrh{7hly840b$C_~3i7>v~Zpq_&Gev27Ts!2a zDXSwbe5Ww3kNSSjS{*Yh+BcN#i`=$e<3l&x-Lb!NTjz=yNBkS?lVj`Zu`NW8#)bAT znW}GEH%9m-J;<;0b%3K3I@{Vl7dE`dSZOp$)@pS}b}Rf{v_VsCYm(u>`I}Gby*ds3 z^W4rJ8a;YhziHF#?AC%M6W0!FYUT{}a$(b&E0)ADb;Zvt-nXJb19F~z8L;{Q@G7(o z?Zb?SY0RK2Ls(UVtvCipIaS3nZjs@aCe9{GDj9C!LiRAQc; zX>4o>(yq%2GvyW3hI{;>^}YW1GxyUT%ouO6K4;j=72_*SP-4&2arygL2SqX4TCfeL zt|bhz*);Mv|!3um`^(B>_szz^fm*l^VPO6R%Vf^NH1o|(2J4A zMQtU%#3DCiT3@BCze%w9wb`kS#Wg0H>q2XWZHT|IP3CqzV$XQSMAqZ}(F%z3$z5xNi&>w(u7 z6AT?8vi)bQ(LI}ZX&Ps3A)GL%lHjW9M4?<9D0C4 zBRj!=t9$(6IrL?m9}LLf$G9x)Q6TdJLzv^WOEWn_&W#l|ARviQ#ujcm14-otTUgY~ z@NELZDdn_F!$?U&kgo(ibOUJ1atrG-*STk8zZg# z`DY9IT3h=H&gSnnlyaVs@Tl>GwwP6pS*@HW z#7?b(;Ii|C25tIqG37|z5ZTKN)y3zIGUcG#?{~A;W{c~41A*T9Vs`E9fikzdOw3vG z<0vn|54xlh1_sok}J{()hpyN~ASgw$kR6G_M?@C6fQ?F~)Nq&;u!EE*2ObdwvX_^PaRZUS;;+rI(c# zJZ|l3Y+RK8)KmG38f$tjhdf8tb#7l$4Fy)``fI$8_-h**_B1rrc$)hYyPuB_pr4?g zeb5cn0tS`;gJQq1C{|dgNYydBafbGgP3{G(aRv!_3+DsHAl0kOWM|}Z%|E|&{gNhS z^yJAgQ}e*u=1eXou{&^C+tLOa;DUoK?kqb5L&5>1J%={20$NDc%%B5;RWiBwp5sZ{ z2ayT&_>J9)-RlcqmBv_E?P0lRrOG3Vm{gyNiCIO&7F(Rux%?C7?KJY$* z=G)(9uiblZ0+4SD2>#-+OQ<-49VZzs$wlzcLT~E6o>>_!;8&QswR`{0ycg@V=3x-2IjN!Oh&y zxtWiH^((#yeH7dsbcV}?eFxv%aYjywRtK|K5=f4+p4s@*kf+`h|M|IRsc)lh7z?(D z_XMZOII9WyHaJa$2jpwFrlYiYyZo1Y^+xZJj30K);7P#y=INCJhER>nj4)S1mPC^{ z5sw(xbey%;@yyOWaQF9~JgQv%#nUGrm}S!CY}>cK6~FiLIglpuCEPUu7ll|06y_u1 z7a4skGguO8`?8B%VXZ=eVrz9_Hw){qdo0^|^7u~6`|jVj|J3^}J5Qh9Rm~<4udpiq zk?LI-FCbTh4=(vkqp_ivA0nS=-YQO*NWllU7?!5@Bz-ra_a;3v(r;7Ke+2MRD_XJS zh7WEbyu-9s&yxSu%%kRC?2@W3od4B{cx8n)scND8NyjVTpO*fB{J9Tqae$nN#eO#@ zQhjhm9Vn)_r~~bDq*_Q6R+1yJ%sq*d780ljZ&!{d;p;nu@m1i-RKO6UIBky{q!+YK zc*6RD=G1C|CG4nEiqu5w8kTtbVqa0YjS1f(u!Q7W1aYIUgPn!R;8!*2cP>zoA za;gzt&Y8nDtpCn;*1vCN#m|4f;zLM^{)xRMSximK)b>Nl+Gr0?${tLnom(|`hP^}n zA)49zxLi2HbbJE_v{Ef|vWM8eY>7A5$&W8Ow+#3x!@i^&NM|Txa78&T8qct&v=uX{ zOJ>}Z*s3?>Vu@ze=RgTNV@>Ygtp>k<76?N^BJ40mn~fdTeR{At=B@Ve`6pBZPr zJodq3E5{*V;_E-T5_`IKAcCsLro?ncrz_@k+G^V+k2mJ=*kBG?Yf$&D<_t5&9-6j! zoDJCPVH!x&BJ~5%H(cco?(4+uBoDH zb!*GANF}xq57t&S6u`!H*yJ*g7kNwy-$XpWv1Gi!l|Sxi^pH10VjuC+pkticNKflp z-FJXHlDd~evf0{KA{z@GrXs7WK&sSxbMv4#^kQt5Ot|CFB-F%4w4@_B4MFX zvO@%f;k!Bv-=3)7S&fhG=-N+g->~bRjXRc)of;b2HoJ9b=+tOmyrj;eJX=ta$L?1u z^4W0w3Go?crSTtYpYieSKkw_4dojj@ReYLfH>l)jaSQX{? zaf%=w{|Ng<-2O{fT?O`yu5+p9c28Z^z$IN>mkdn zRU1^=3LVmII*7mo>rA`S^CemYv31($i27ZVw!V$~7B#I54A(>>n>}Cn5o164f@gE2 zHsb7U+|aUU-^M=M$M@TLj$=QAAOLG2~rj$d64&6@Q ztZwx4$BykCxN@{=>2*u*Ti<_Vv}zP>PW}4?@86L6VXaiE_n;7?$&v?6ds)n8Gn$;S z1c~y_mxy2t`ZTk8E>(uNkB{0Nl-Yz^PRMHfMe@4*8+TlL`}jE1@3=9)Cw}QmN@K;X zw^lSNFR@Q=*mjnEdi;@X8^*_-B@dQ3X}D2kAyjAbHiOpPh`<5Z<&AvJ5>|LAcCzGW z_8qEd{4qJ7U486W{5Cmw?3g-kbs--w^o#{2GrSEj9~uoYV=+xoG8*J;y0FXJ3YdugO{88Fm{6F^D4wvyltp`;3q}?&?q#An#pzW_N(w$*6g$03LQoF z%?A%2WOodG=eDuy#s}Q9b=Ul4q(kghp@BlWJG&D3*W2TxWY8Wimp0 z-W&fVzI7eK3Vd;mcJ|cOdCG#pUvAsReh?qNowYzw{NnBL$K{vYO}$lBy-jZI{6@g< z56}lR(;@w{tgyAHNAAP;O7cY5eu%9FPlWjq#-QxG+@{8nXp8lzdkNN>jeR9f>{C$c zsLIpp93`>5LUJF0-xCvJ6doT6HKs#o)6g8;E81DaWl$a=O^;%L5px#bSdi&YZrCur z$%0+43r%hh>pgku*IG>G+Nh<(4|jm7iAg5ESC%V19*Ci*AJxJ!M|t#SwLc6X&WS^U zpM}F@E->an!v|`au}L$gRQLcHHVZd$dsY%}6O54dum`8SzUgUz@**r_h69y0QXx$r zHJ?7$g8bLc{hRKX&MDQQznKxEBv40jK*(SW1_BA|*-YDdb*L3igQQQuw5@xGUe`JG z{w*8sxuU=SihDM!e^2{W{g;Nf4Aj;RYza4wcU9-S%)U|-bZ)+Tx`z@_!p`v3y@A24 z;qca>KuN=*s*myUlU+*tf+L1_?!(~0d3#d4-mvwi!Rniqu}jZ-*LTP7L_2amhXvpR zT?%`0EW<6im^27*Om5)_nci`;UM&y_dN753@m%k4*b8kX`c0;#;YG^|>_vG4$NH~# zxNZ4GPFH1P`=VxBrN@;wutAgo-U{Zm*~+lLxiZ+L*-Jq^z(Hakd103hxrf<8dqjAO zW3cZ}wgW;dBqPbCsI(IH!le8XR=1xFtu}8nAgoh~x1`$VEOnS2F8zjr!44X;>`rHe zvo_2&v_yPm#YHx&W4zF6hToLGs+s&_T(#ExMt51g7t22A8iD8y?0}?@1h`Lu&QJ#e z%z-?f$EW~1M=?qu`z3}k;>X;UQ})Trnwu~E)TflecmDL{RlU8dcI*HuDKEjNo;NR8 zs%FoNy_%AlSbP5!`<-MXsPc@k{iY?9RfUSLoski#o{JM5)!%VkaV%@JJC*$M;+_F4 z%6lssWW)Q6xnl$W4tgT}PchcdT$g6nJMv-sE^TnZ@!{l+J#^HiWgM_MF z-}sbr@?@|o965Ph2`+V177V>#{&M^mJa%xrxOs6NbQn?vlYrU^MTAg^5a5zg0ySh*8y?^;F6CE8B zw=7@xj__XhSX<-rPM@!Hd1L#UigPh{i@&JI-{N*R)43Ue23=51mmD}6ow#{y?BV~g+)GZHO3CVCvNG6h#>wr^} z*PH9$j+VKOD%i_vWG!YCn%TjZfa9bCwt45N65GqZ5`TeN;;*0l#|6NbAH3t;cf^0s zp3tDw&Piw){24>~#c~(MnZn08Nkgoz#s#U)V+03li=Rb6bJN-1hOnKZ{)xL#N76@M zCb#I8@$rcVY5_x%M?PQHfJg;xtUiABT$%hTPv3!ZMtC{4&vG%35gxsw>!4wM5nm*a z>6V=RtTq}PjeYaXj*wLYtS<+)!^$>v*- z{5R)*J0&Z(#7}5toIx4?fijfOFauwZ0xGOWoj&V>a>3NO|3cE!vT5qv8+cp1Bh8&b zIpQ6ItiHpnz9@?_`490Ex19U0DQvp24Og-_?wbn7Pt3{2`Bar210qTjTSBeG%AG!+ z6QBQ7&wsQ0w%y&`yKh^ro%^m>*V?-7ioV5HtZ!{yf5qa(yIf1G{WUfHg@dkL>^(l0 zv!cT3@@ePzWa`V5NgutHn#;NV_Htru%G>*aymUNN!=`aLxD{8v%hn>fj4d@?F;_XP z&}v!+V=Xm8IzSz10ZjN(u!21$)R}~$2IkD`ZRvQCt5pod|3kf)MpXw3sjAi`u3d{Y zglOl~Dxr$=QSs|!Jx^T_DHvBYdaz5iu+9Rn5|A1c1UFFp@IVnY=e1V7>QcPh7wardvQroQHj-5^@SDPc{$dKCa<^IXC+x+YCA50o>J}6gB%;nx?;G$>T)mE~0*)xcj3_ zId$PmeCzj{*T+w%&Y3ho8jY8f5{+-sWutNOqZfRq9jkrw zV~!$~x!|#+GMOaApLa%!=7C?h?566$dF}KicA$H`_bjb}lVvnf8Rh3G18Zsc|EfX^ zCtu2fjkU1(rAChpJTI7qvH5x|~J%Hn-#Y z{xq2SNF&`9=8az;Lhn|=)0ma{yo#`PuYp;uh$UsrNkB@1O3rzc84e^9^N_XNjR2F7 zPgL4`&1D_^1#9&#NGeL5zG}!VtTwMoFm!B9b_$A>4l$Y5|InxkRovzbD{tti9&(7`6K0WHv)m#eL<(A zRx$te%f%}ZVcb?y;;@$*HspG$gY!WAyw%t2^)^*lt=8o$zEEYd(wtXp$MjbW?Pwal znx$^ws|A~ho)=$9H;0^Pt?1(vFu^NFQ&t-Du(;YAswuLU*V;|FOP9GEn6^xXrH<;b zccJ;}^B5CL-HjDC{nqyKGFM4yp)*oh+FVtXIz6S)Fz>sl6<-)+Yw23re=wXg54OFh z;%|K5_&n4-!UF3z|2~}`2xC<`Mv|w#;kGVERiZF8@Lc9#p+@-7`UnYcig_%KbZq6E z4|8bpT32~#u~VBQoW-T(sgs1gq|}CqVjldTw_xzkIwwE{cZTKzyV+vHoM10@LO7U( z7TQ}X5tClUXEu!L65(m0IZnaTh`Q!z{U6ijJbbT8r3GwfX}=IH$kB9Sz>JuM7|37y zSGp%5HxEGL4k;ANud0h#%sC2#QC0TRFdW>g?7En}N)sON`5%^qns4YlL3+YhUvoP0 zi|EVLWrZY8?l=xn`~Lkx)UIUiZGTlpXr44#{I`cabu~jfy3{Z{^Bh*tKZQ>J+mI30 zlfJ!1%b4dQw=FRK!a1(zF(E?1thZM5>yF^%j~QLkK+*x`PbW#i{8{Ws#zK z#bt4OuDa^vsdU8!bQq@GTMY*2Ft};~uyXwC{{UEfuxrprUya<-9V}3Si(>7DPJNMt z=n+AsVgznpL8T;wpcp~L8dHL4jUieD@bh&&_axEO8YIn0NOZZHV}9$9-Bq#?GdIx6 ztRQqF^BRm=6N(eLKcnH;G*_UcsBX-s)7!5-*|xu$*Oa^+_0It}UDA?1e|1wQD^S`D zQGphwRMY`2OetvrS{R{)By%acH{!oUvyy`iWL+1;O4>HDsaVO11>5TIwk;wWnm|Cd z`o1w7YP)g=?8EF_w4JOkZ+lE8?}zNJ5pggV^;NQZLt#ZhQ=q-Tm8;7MG{z8F!c!IV z6a)~ZjHxa&XygRBk`#~rg|2O(>ZK6@B*s=hpLK{S=ju0k;N+<^1ste2O&3QxxJrkr ziOX!#l-A{nIfVc7msnJ_9H(zDuVWM|jQ+mLhPr%rUtM#|p^xe+!I&`Cj_8I8<- zcRGIJcIE!~i^?}zK9VHN%mx| z$#Ll*`|VMClc%QuGaaWfVJ1PK(%AVXdy=;A5xA3LGN<5%GjJY2P`mT>s8c7exh7*T zBuo>T)k^7A*d^(2)AmSuQk&y+cVOPGft=gvsmUwIH{`|)hCHjwhsx$zS~+%g}lX5ROwlM0rcEV`WzO>&kLYk^=_R5W2YlPXbfo0 zO9XsA44#KlGq9JPI8z6{`P2xTkq*pbgYt{8_#9&}nZd3Qt)NlFo3G@%Nd6O|vj+>I zGgPgfIp2eK^v6&?8|FUD?*3Y9ajV@M^wo7%$2vP<`^=Tp_T~t-m$TYSV|G$kXPw;z zT_Y?>rUpA1Ff2jvDWgv+U(}wV?AE=S#>Vm4vD~dV!PcFcPSb?TFQ&0{T0b`4$=N(~ zW3s8~fWJn+d?Rdiffv|>Zze1OKk!AS==H#lWUG^<+$4T5Z@^cTc-Us@-;H_s6`p)U zEa0fOyPDCz^NBA=#70XH0QwL`>*nK85gGD{@d(|V7VN?jXC4PjqfvpQz~OhUY;u)i zyS7X&_Nz4f7>#%27@Dwa-jmq34fRT^Hq8-$_XYVz%yX~;h0Ak+zYN|e1-h1)-Bhet zg7Cc46_Cx5#+He;2yg}KxFSc>x4a%LTVt3wogHq+kZnifloy$SSHz*TFViOZLD6@~mW@m3uzV^e| z*S>ZG@v1k>r)JLVoc~Mh`RQ5n#4S*-XrltGNbIz zDF7{$4=@=;B*07)SeV&i{MD(UD7D3%0oEQ`+*%UUmotMpJ?iYFFgD9h_T2gp%nS_7 zd|>^h?+&f3+PS26->SyORr`7aeW4mlYkvGU>>8s|TwT_FTwWL5F&Yex?ucq*2$!w_^vwKrMmA)rx3kZhrhDbsHOdom))R(a{Da;x7)>m9oPAgLkiA zfA_)u{+5-MP0g#PdwZu>H(Q(@OY64Fy@86sq5iVYuE4N}doAK$4+r+Tg5CbA?x52d z?5^^62VHwFm?Hjwr?I0nSXk+{t{ks%RUr^9>{LeTs+{%lnx%V}l(Y4&V3*(DUGH+$ zcl-TaLDwc-?%AK#jqK>?o*1sFYU?WY-nlB^>nW_Oa>Q|+megmM9jdlPMYaoD4EQZekE1DHQZPa*cf@v7iuQJ^GcZK7AOa*Z;y za|VSJUJ(5P1|8yhVXtni)FJovdn8lLUWX0KBu_Zi_Kf%#RcH5Rx(zFO(`0(?P16Lm zcMz@qVO=gHSFqouCWeU?y6lG!AI44K+6`P|M^V^;RuQi4F{L~^uCsM)-^FyA3lSxg zU~I<1uxlh3VSOX)8pAm!Kh(-zd*u~W0QKktxG(EY!R{sZVT|BBO^QhHgs-{ThmS6o zpKytHZo}SHwR7J@yfOIU6o_|H{BSJnwpNF;v7(}(U|an4<$2Z4ipIu@;-cHBJlFus zh!+OSKJ%mIJc=UO+$`MAFol8nfE^SCvG9-1DnI=~e2zJu`key*H}XnQ;`heyRakrc zYrwA>gFz}rOThlS*id9K_+l1k%tD4s!XgEna3srU%My}9F^aQ~W{S>^$IoTRVBlAf z6qmJQg+4E;HR`7B%f#^w5IHWxIsU`ghe>`;#kU%}4c5;zPH1+QK3B|_K2B)XGEl{E zO9TRy7Ur-lRipkSV#BcN9q%eVp5N2h&^gc<4HeaTZOy@8v*{+KrfYd?WJ8ay-rv|+ z)qhFh_ASv+sI4{-IjR&@IxCtQJP~)a-tB1{33d&aD$b3I>sR)`e;0p+(le-$_9v^? z418vtdM(5Deq6n#{a@}-uk}dtFY2{HDnKS8AH+3cA@$k}|El|dC#HwFKK~Qfh|Pq% z;#vlc^r+VqKWCkKtw)*%)N2E@X0NK(MyZmS)oZh~gAG!?tE3qOQQRdRk#yU5*mEz(Y4nSMj3>xH*Vp5AZVyjO@hq zemvdHp*W1x&3H0}`;#2qllXmkT2mZS8$#N9`0(Y2x|*Be-O#k>;K4o9lTAAh?r+|G z@WA0i&C^pmCl4H&Y`$Ey&>>)qQws)G`itMeBd~}cDc>>~0x~8u9lD_o8fOHe1GVO2 zw87R1@R=+CZCOC$R?w;)w(dn3N1X6+DFHpXLCYTagOmfCKIrlMptows2Wl~A)nT-$ zhs|^&>f8(}XhrnFFsy1Lu(*kWMmvF}Zcyza?69?1>cegY1CXQ)!h3ZXbTTR}MP!08 z^qS@H(p(9uSq(Z{i#n_aue=eS@|&?U@>Y0%UIOpT?a~fug6XACNViD0N?(vpN|=|j>drF)q{x?TFNbhGq+>A$33OMfMOK)MaFtA8N9E`1n#LH-s#gKtP5mp&!^ zlk`>T)6!*VixcPw|BM!SR{EOs4e2@Q>(cYm&(LNsNZ&+{{X4YgucY@$-#%+C1~L_L_57!x&}L@T`ye+&(n`#kLjb* zjo7LAjPy(C31);fC^pex2=67Glj1BNTYs6EMVfH56N?0j# zvohvkUg>wz?^!vkU_Mrfy{W2LHLHQSXn@tRAiQxJSR-qKSM9&CX4b+G)*7xMZ7jmt zVddGuI$4+Wiu50>oAp4H)XNsLKGx3$*b+9#hS)G0VWZMR(!*>iTgJxNI9twE!0l=k zTg}$6wQLp)*>1K6#=%o8CjC9z z$EMkSSgrq9`UyJ-L)I(c&vA$yW=GhS>?(FOy9OSzr?FS(_oSDk?@Qld?_k%l>)BCu z1G|yk#E!9>*)8l=_D=RLb{l&)yPds<9cO>V?qKg_C)oSgo$USWE_OFN$v(jDVSmm3 zhJBFDvJbHjvyZTkvX8Nkvrn*l+26AJ*#Bdv*eBuD`T*j4eTsdW{T*C4A7+oR&#*_? zW6}rNJH2X*P4Erj3mi-g^8vAEN{P;S1 zo_<ll=?(7JGrc$o`dmoBbOi5`Bk#mwk`D#J(S$csC7YcBUR@o-o;W1St7t2oBC6^#-hg&X_J=k@u9G<5>c!v7r zD!E#&k!!)_)?ufjdh8b3C^yN?a*Nz5hvcx_COsm3MtWTOtn{?>sPvfhkJ$110qKia zpFW8liQ46;+#z=&?qoOif?Fi_%8TVbxnKHEc|cx*D4#>}uskA<%1h;C@|Zj>FPB%y zD-jiMwY)}NE3bpRZUZ(I+$3+7x5!)NZSax2RNgM{kSFBJl6P`_gR__+Ad-=dxebNcsB?3_7xAZJ3Hjs2I+OkO!@oZzRN z{)2lC9+=#hGa=5VB|E2Pb{^TkdwTL})6V3zvSio6!xK9(03TL%CN7PGJ13B}ahEs^ zqMV7tIYVlJlWKuOqQFUhQic+5E0c-KoFNsANpUs~i5w^S$uyD##Iz@Qt&AixQ1&D) zdBs~pA@v+KjV9kP?M+_mM=zV0K}5fa8ROE!Q`5U9jZ^%Tvs8tDN`-%^fPYFLY^e(A zl!~xr$sA3w=lSXhCIH^en)bkSKxTyRz zKk3I&Gvf;J!hw_*+9KkGwszwR@xlRq(hVKhqnkXiCugNvkArGGR*HHY!vzOZe;L<^(q>Xhi>Nh;M2%}iX*1%qR-`?|Po}j=444ijuLY!$A@w{g z&h2U&wTF%CL}rKiNxzO-<}kI)deJgRM9ZvKYji}d(Rxv%Bm87oKQnb;kKqV?7pzaM zPr;GYUped57CfT1;Kn4JrmIq}H>X@*le{)=5jDP+pOh^L!c(qIT#EX2hLnDSMkJg_ zT;}u-@mo0)ll*L2d1!j#&|YzUFnP^$3AJ{p=T7z9ubu_Lgu+q&I~;9O&w?(((ILa( zg9i>CD%dqOIWu`^>Ja~>^iN;DcY^Uke45kR~(TyO@Wv>1oEmW-I~1z4Qr?N?4QuBn>doA zLZe%?cS^SepH+vZL^1uH`IMgqkws0Y{Z=Lth{&HzsZlNf5$K`%@GHj=@mzbA-!$;| z^_L;JVGmW#uxoPq@I;Q9m;PEj(9y%gylRx0VIQyBw5ZyFBl6W#IR{0(bTfMo3b2=i z42O8-4x@5v!Me*)v7Pt;4#Pphcs}K)!DGpQFJH}6G?)62|4{hL{8E%Q*s7jIR}2q^ z)!!;H4T^5x)*|MUHtZ}-lTBNTNZ-~X(zmsU^ldGI_}W^u_XKgawFn|^YY{};)*^_w ztwj)VTgwtP-$C^(hKjZpF;uj*s^zz;<+rNkx2om0s^zz;<+rNkx2ol2RV}|&Ex%PQzf~z|rk3BPmfxn9-=>z| zrj{R3%a5q#N7V8oYWWeh{D@kAb-HScsO3l0@*`^b5w-k?T7E<=KcbeePE~CYwfs)C z{7$v}0rmGlt8wF=nF;VySBcX`K8hQ!;-|cgS{Kc`O1m;{774H6C!+RHix|2?E&aSU zIP==z%%R13KtFV3h7S<pRe4jkD(IfHR-#&Y(K)(y!JvU6yxA}@A;0R`>_Ut`OKl2XrHpdH))RBZwf!@`^rFQv{sdPb} zL3f#4i%&ofN-|bikm_M=wHw!a50P!AS2-prYKp*-;yu*yHJvsuN<$C%GRu0j&B};L3@`p$Bd*v zJ5fKBHa)dRYQooq)HZ!^=QQdh(XMewlPW8s-{LMwC=u-vvZ5+Te%c`28H5C99V9fn zArrY0(hic!oP^ZnK1i7;?d{^s3f1#F)$P6S$o20u8GAjCB#=i&ox44sKgk}?HU zGztmWI!L>A0E^R*3|@^C>5!z9EAQg@y!F!I0|7}LgA^|hQo3HW;4(($-hOJZ>6p}wFEuNqjEbSe@rhepO>~k-fH=&P>7mm$853`ijbvG)Q)OZ zz{?S}+MQCj)V#CjYDmEoMW_-rA?lVOJ0)CYdylY(5dh!WN+T*L#m9k96+RLlSs0|9 zq+ck4%$3Fp?TTc#UX)9=e=_K%Bkc|9tp?C?GJUo?cY)^ef%_Spb5ViPGW9L9Bke~(^73L1&VrNbCC7`qfB zsRL5d-$7CxfNXLc(&!q{OD`bX0LnZ7$?7|$JEgyY9QS@muAY#-49V@c2h%<7Rm*9+?a+LdUMsD(AoGB;F1acIZ^7E@Ment4S;q3~! z7pZJWy@;O+@Eh76{E|^mYB{sykh}5AA$Q`tUyk4#VS|uo53c<97^Qr<181My&hIeb zwg71ykmLIxho3;|FC(vf=`P59Eue(}r>Q=SM5{8#MVUsdF=?~BiJw=?8~J&ayn&xr z%Io=gg-klBF=@F>v4F;;5qS&BUM>?oZ=dzHKu-z()c_#Ts2<9kG|!}n@AfbSJ} zE0-ur-p=12lXu|mX8BTlkKsdgx{TkM;CG02#-Jylp6A6Um!~mfbkq^;V%*Sis;1sa zW4BTE;aiMqG=t>gqesuw3!3N8XbdvS)GC?j_9ERf^i9#v>Dvg(q;yn21o{+GbhZ?C z^a$8fPEvaGVCvTjO4yY)zt`~jJ;dktUgV5FJ};Ax%Z0DmzR8&bfM4S!2sfDX`EQ5x zEpgQ&^}LAEKg@G9qbIchUK;CHV3vvTK6eY|<`d|9WDD^MY^~k^#+-n*6dyOJp5gu* z{OKFgFz9Ey)%*%_vrms@croA^Zc9g@1Hv@4bD9z z-HbF0c%u;{scZ6rdaI$uIU#*quECZ^_Y?H+_xu|0j%{M*-k5tG#~GZ}gRu3N|3h8V z{^njmbmcQR;1CO_Pow_$=GVBJmh{i~LzKu%1$;>u=bp|il*9K*=H0jFH?T81ca*RK zTKS9pWtI*40yJnrv|Rchzn{_G=_&qHzd%I~QC`$?z>-Q${LcIq#2uo!KULZ1%V!?0 z1ZBUHq&Mwty7TtHB(S8FdvRwPLoP1m|1{ObmGgFC>VI0B|6T8+75RMd=Ys0u+Ag(p zz@zrVKOco``>_v%=%%ho<*BIjpH;v&kH>9 zxdo9G#SG17Wg0>6qfd5B;%yvwPW%QRhciohbH5kDRpywF5>=jp_dk^VcD87Go&GW< zqt(RhOqhQ@6+c<{E2N|rN{2h`*RHf*Z}|scc>-m9AIDFKs^+jqiujv*3@!D#_KVJQ z|A6PuYuMAEo_i?0Hrg9_`+1^58r>GE2`KsrtQ)fY32G7FL_0(^IKQ5Tw=;8pjfG&Ne6<{tXSKDc?ZteXzz*Rk znb-Vx=id=n72nCHS{}(e$)mO6WzXH7|cm-;+W;|p|7LPPK+e=SsQ)?5=@1A0peHK)=g&pB;;OqKvjr|}mv z4Pc=Dmdua$!&Im+2NAy`sP~MRKk@Dp+4~lMa~98OYS{5MuL?7l>UUj z@e|;MWPa5DL|;@}R-_eh@sugBr|0vh{6)A16;mI48vGL|7vJ~IQ8aYW0dK!2@O>wq zFG#UG?JJmZR4T%Ilp9ZvD|JLiUwVj_ladehPfaG2Oqr~oz((@7c9q1EcIT~~sRdHU zi1X*daYfuQ^*ZWjZvbBp3A#k>sD965)VPmQoG&L{YmI^kQ{dPrJz+qt8DXCII2sw% zy!cxhF6nK2_*(=Cy-9sea1AdYPjJn+|NPuP@zyvENEaCIcqWvV=c$%Y`Dw5qU!E7W zD!A+PeA1v2={US|-{A?HAvQlzoXSrJ<~n$hq5_^@iKuK^)827 zOOtX>z@cJKy-)8yfLfde%~Olfd3NqLdWv?N1=gR&^I58?cq98ao~tQ14G@ne>X+yx zJSVJ!k#7P54*e6HieAV2G)nV2sNZ5lAdIEeJo$chST5#n0#^xGF6J%3PNOK^q3={U zaG%d1d?xxMv@37=PmB(4`Ze_hK=V3aPMXdTu`MgbYVRy`P9JZO zOO{bWjFBlhV0}r_m&}m?(h|Xs;TzB1iWK7_=qd${v`das(1eL z)bx1j&{BxAhE;K<@9eXK_EsV{wI34}vh_FZo{FW58AUVFWVT$Qdx z$P+c5hsn%5F8o`|>%u7)PQmBQG}>Oc=2|Y<%DwPg+S{q;j9DSUp9mgHOUYrRKA1Lp zYfrUvCZ@IfZ{;lLYW|xVUCn>;cHH?BK^6GZ!V0JsS|=tr8I*|Ko-X*8Wp;k6t-8>A z=$Txyz0mW8VbtaYPK|G|_ww;53$`@6;?Kzn=xzO@(zD?s1FE+drdJ3iTFs=-JK*ls znUBu=Lvv4}J(6QYwoyt{A^RM2vDE|rOB^!s?P=qtnm2LCe4fNB;3tmPu-d3^2schm2r*E#v@e5EH-y2%3b(0}E@;W=z3fzRVOY}3)F8{u zEn7Zf)ZtvPkQb3A={A8M;Ewa%No=K;qy6Iji{v>8E{L$NJ-yI56UyxAkvo@OWq9E>b!Wu?w8X$5F2 zue6)le_Uu3&HfhM%~~?uPt0vR6Sc?%| zT`SIi15YUhWUKh5k)7t>CqV5&vJJT{m51j8cyitqC*T)yN6Ig4^w8c*j5Pdd5*7{W zWX+HRmvW@`PLxkF;B3+lqK$-{Gc}k0`SpC*0Jp+h*8na!h4Qr7mh*d-3vhk(p7w51 z15Is^Mju(=Ux0c6QPw9KR5(-47sIz_6q??a84oY;bD`RBu8ZW#3Cg@cMp+*V>JXHh zHNgTm7lewqKt5vsY3e+mb`}7L-(Co)a02dGk>S1O~1~bGW*^1H~(+^%8s$OuYPVvPG4IWc=9DVSbbHMkj2*+A@^SSW61cwQJQo>Wp4KJoL982L7=s{^-9QizA z-g8lw8V7l%)*!!H91i%~1(07I4vN_sgb!3b4i{i)K;9HluM{!$nvpBTSM(sZ zUMq5?_N6ezQ#Q$8M}d?Wby$E zAR6Cdl-!4-0p5W9z}Endb?^=wM7@V`1QDTc1Q3nlXhhAH!UuC1j#l_#jRB5v9AQN6 zTaH{;;E3|*kDc%yT!oTXX7h1H212;uyhs6Y}1SV;r8(TaeRM z94p|3ybXC>f@2l@voA$X+i|Rc*V+z1dl?S$rrQN5CUN8;8Xv`4r1*;w#N)dH*qXu7 zj3|89AkTN;=tmU38-S;qaaa&P@ZD&I+i^G%J@7qfh2zqDas58T4s^pG@-DQ<-8f3& z#r`+I?JSORL>c@j@cA(u0r;PO9KLIxz|kh%i|B(v_@mwr%shajQF;)u16$!$`4DjO zFygCqAhz10puWd&EJC!kCxDG7ar7dJ;ZuO{OE^};8}rM+_E&JMMzq4;NE|>ND&cNqLGS77uJO$Zro(yJ|HRyh?{H#aqU)r|NqR~JNNaa zNwx2B@0~ky=FFKh^PhL;+?n%?#zmv?38V2bTChhS78>V_#?wY)$2i1aHDCVdhwqrt zRTx+LkNeYjs}!PUji`Aes%b0x6@yQ$C71B9iPv9wBWzsN`- z{0tdCleGRbc*GQbmW-eK>GRJLBR`lcM$VLxBk%EREh9NGY_OGFr|XEhq4R@HQie{F(NQoTrSOr}1O?4%&Q|Z$Qxn zqv&y?sACizG>QuLY<@_MKLSPPjGV&ADU6(XBWJ>xSp_p+p_7~7WXd>M1}EP#Hpv^| zveELW(XwK+Oc^axM$53#GHSG}8ZDzn%Zkw=-Y;8P8ls1@TYjRaSY#y%Z)QePFFq@*)1&0oCPzl5Pp?jz9MgijGj&U#{v4b zY#)=f?jd?=ho08sPUHVv*&AM?M`;XujuGq)T3Dmkn%mI3GVOH zGV6?9U$fu7#Z#8fn~hfS-amg8A8=gf8LL&WyO)?9Vsv-KcYnvUtK1RqyC!Mdgz)5H zD&KmT1nYqOs zBCPDv`UJh3*~sO_qOxgC4_*(KtQQtH$fw-o*ijVn28%;h9j9BXqdNP4L8(K@iiGp31EpmNA-oC3c{shMZ?mZ^{0>-cQpDP_t7aK3~7XW69}%b4>? z;k(GstJ$p`^%uQAqNbs2!UH5ov57Ltl*=3R%X&>&*@C9U-JC1>5E3o@#yx=F?)a&f zZ22~WbWq22+2XByoZWAvf5K@sH&(wom&Dstakn=1h_Isi_o{a#+hd}SHPHUMOdk$s zJ(^bYj)^3duGJBENjj&Onhi0jq;y3m-?PebgAzBQRrMS0%bkDJnv&zYj$l(^|F>4l zs`lgx?;C<0RwafNqv4%WXkYCmi5AyXt8jJiE8js#qPon!+h!%FYfq59?&LK2~dOH+9cdtA&scN#lA=m!Y0lYK2sLNmILBLyxFZZ{<|Qr21FX zAyiT5K9?_O&YG~bJmm~DL0`%d@}<=3iaxZKUvG+TME(6hj7Jgj#g(qHebutE^xu$O z-D7Uht}W*k&mdv_KEJDncdK)tijtS(o%#g)H3kNpiVS;};{7ct>!nn8fDw#Rp_RV^MONhM0p{hYDp})^Sk-clYEx&AKhI(I+B=Jph)#RrC zrFdVnUn;j&xzeLXRlBK47k^a~fu3hlc(-ex1U0h$-0-{2(_Jm=z#IMM3t%~<3( z?sQo%dQi+@!3+PpA!|p^JeHxGBu$ztX%Jd(+CCduTTX_{A)Hci_kP5MmQw3MOH5?%$` zQlLPM6%lJetWtT)1@E*VR#ZeqF4v2S$mMboxfZ=%FQPw0MXYq@|J~=zBxzdI|L^nv zCpmM@oU_+nd#~4Cd+oJP2qlD=@u47MZ|}&818Ni)esfnlm6numbii1pUy>x2#zm+{> zCZy;ELijIlz4}VA+0?L&kU$1yZrZ;4qDxPmT%{tUWDkxjE}FVxH&GKe>Z=62{-T}N zZtwZljxP~X?j+|UiZZ2v26A}1d;|}@B!GnHw{*NbG z-MP~25B0C`&l8SlaL7M^(7YV(Wm7#BQFXAK;BiBOu7edh$`1S@$peVhYEakpH%v@XUEI&rtxX+-K zKPOA+Riu$#m3x`KNrt&D;-SIZ8}vmIlwkgpd!4(F1n4*LtSa|Mu9NWGRkO0L zV&JNXlN*+OvdV1Ak-gj7&aW%SRan%jr=Yyqu7 zd+FXIv;HqUGdX1V zKkCe6^cQ&k6Yy>$&WuK^t$*h%$unf?qs~m$rT*m460PmvA)`B!!MStphmgl-3*VV; zNb>n4;31=&9S8J;$!h-ml5~C$^um2^Kjh_Gq=fNK!uJtpiGQHs;bWPIth35k#Qs6U_ zZzf}fe$BNr-yTLw`b)AI$5j~RZh`IE348D<`WRUTo7qWMAPeNtC#5; z_to5Me0%OS`bnItaxd_4oXc`ApuYc+a5Edn=>G=fjmZjZ65}~PKzxjs+(S5C$^D>U z-?*0C&!jd!kNZq#T)F?1>>K(CJrB)VM*)7fwo-*C}Gi=#(?iDVK>+OJhm)NEH-hsVh zcI_?LxC(d{44&a(FgUY&++DzxAq{*-?ltLroKzKT2_J!tZbW}h0~eE#yj@{(tXP|S zT`>fj1W|_3<24)yA-B&HheX4J_@1XDAH#S8I`VJkz6;!(RF;)zaKFsG&hN*W(RV5I zxj;jv(_2tC>g4;0U!tKIbzeq0`BNzSD;%Yy1M;EaK8E9T?ysyp+})rbvk^NWM+Vs3 z=Rl*oVQZoHydn2Zj3pm%y|~8?rhof$zu~@_`xCbs*NiUxIJ2<=c7?tMIznD~4*h3z z+>K`f@$xMgTejeOCusLOoPh`9>>>27}V~#XF$zv2hoBIua zE1sc@)DG4jrW*`@6ZDm}`%aQVJ+z=di_elCF_@hs$loW8^@GrXRaDHK<&2OqBU#09 zxwABb@k9gP;ZEYjQAGYNS4w9{i0jV%mW$`!<^KYi7|s1B_gTnC8>!%qlQNv$+-jUF zbHCU*UnD}M! zg!r`hO}{;04%l)zh8gvXwgB=|7!*@AUsU{j<}r zoPO~1HK(sQJ#+f<)4NVzeEOo(B|o?R+$_^g>cRg%A1Y4ADh7KPuNMALiG%D;Va~Ad z>B4J?x8S1+UcQEC;fWU!Jt%G|NBXN=-^`wC`k|b#&&7_62k~Y##I!GtvX9?*dOG!8B zA-$vzGlpekfDDr5WCa-_E6Fffh1ts*GD1el7+Fik$pl$P){_lnBiTeYlSwi~wvesp z&LQ$K@^SJB@)>duIZhrV50Hn*BjjQ7CGtkuc3+8Bt$*0JjN9_m7e{tVeAA5wZrHzX@AcPRyXTs#ue$Pznag+Yy6n=Om!vP=anbf|(_6Pp zO>W+_al`s`6XR>gMn~4H8eTcHV)@{}vi`o_s9@A-E9r?M-O}LFZMv#Taza;x%c81E zno%vysHK~kl`%1sS~cz;Ts_{??e_a8+(G||lp@rV!7(*GyR~qC0yO{#pr~PRO>l74 zx^b~*c2WYuoso0C$9||bYvo!*%Ax-gX7y!)d>yJ`$s01;!0n*qGBH~h^LHX3y!U8 zgQsZzYTbB7oZLRqkJ5w-Wu(6~nTFsV`EoKNZWYChDiqu@JU;8s(8-`X|9$m%5Z|ZV zv;Lr8oR~P3`?iOb4*F3QN4ib~>7i98QuNT8b>qO95f6=wpX4aFbh2yWL>ZoqpAtzX zCEexNU3QZd5!n|qNWqtroJK0|K9wSw1JYB4bVK^S6`hdo$%Vd_f)Uwzij(gdrE(|2 z3=y7ic%qP>q@*Xfr@%dpeD8o}n7fCTvtkP>0!q2# zqw_r&T{r#&OdEAepD5AAeyS>aPH;o9;Ji?;8V3V=PSBy)Bm{d9U-?jv2&vAb){L{V zlWs`7e`2D$s*(wpI3C>Q4o;k~T4#6npqk)`DOGqfHanhSGR7n;Xl#L;@}Xs0gT0fi z1oQ-d%W!M!m^hi)G8x0AXzZQEV{z*gD^2VtI6icODnhh_bbwMSeMT4D)|Dv=cFjHM zB%SgT6?>u%c4eqtrgKlQM|561yEV84GM5@2zsS9PVhVL#TPNJJ6I&%3 zqd90Asj5`YTcf-&;+WEhwr&Iu+ja*?Ubfi2>!XbiRl*@q-&eR$e8nNf!N zG6%L!p&zh9kUiWOzzuPH3#1nn_D;?gBo1JT@EoShW#IjSDq%zE2vBn&Mup6QVR3Ro zoSej63<7?)m{H;^Zl7Yp#q8`b@Z)b7#vZ4s*)@PAOl8~|HBA2Ysck_&tU2yYNOYCh z2kZlokns$0&&~#CGZak>^`bDU4`)>2W$c8%-Lc@*HZYOl6Q{OGu)S!mL><;XcTdni zfg)Up(VYE{o&&BScY>n z;UQxXU5()cRsoD+#8ZZrfxIb*o|{3784hEYWHe;>Wen7vzmo^U9m&$NVG<*W+=L>z zy)sf2t3RWc{8U660cpSn;6f9Mq2%$~`F0}9-d+RO0dsfSjvZ=jq+H;Hr%fDhfq7y%{C>)keY zX2(QqTCPkbpO%TRsIc5yS4r;0dT2w?uZA511z^y`%$gX64XJOpNf2R(II|8+Drs+! z^g@R67jUsNB-oFcZm^#~wSxT_4qxZ4f+smbHNhr!(gvGOa8wOz4;vgb7VBZ?XSYsH z%R@7$Oq$$n%r^o(z-at|w^uXMG%~JqE0|_MqcYdXEQMNuF08=Z^J`!U8MS0eYS^>c zxrdC95-_7SR8#uAI;KH==R?hE{xM__1&Ny(t<(@^jlvq%u#ZN?%M2P|2w+?}xf=tl zD@qU=Iz2nPbqYS!Mg!AOeb|KiX7s5UU2D$w4Yb*deh)M3XahPdeH|HR-7kU=NWxQu z2S(8G+p=gE;h_;7|27O-zTKyCIYz*IX_<7OFY1%zJpWiy3|1L~yCyb)t6p|YqEs(C z@|skX*HZl=tDmo4=Bf7FhrzkZ*gzkgtC4*>LG|!{6>cRkhD9UD)grMjjGo~8Y|9C% z4(AI%B8})~%j|4XP##i(%=~{1vkW4+lVk$c`P>_sePI3Utfu(FPc)0m7fS$4`N8Gl zxihOJuMWDUnWCl4e*ptCT4sl;!QXw~W8wy>klcv{!=ZGGsp7&x(#dEKLIVU8-z6C{ zR~ig6vT>87Qa8m! z@nZN{ODVi943xMQVGU4P6PAqJEF7MTr!X|b4l%nk;e;=>n)$=<`GZE0wvje@9u$-% zZVko&MQFUu-8_Mr#Hrl>dYHWeX*dk>I7Vhg(P+Y>S<#HCMdlW!ii-TRpmYzTxGJ14 z#d>f{Y<5;I$GmVoH#@inL|{`iU6WgfHqA}A?vDMf#~7TJj=)}JHV60k87VSj!E50x zEDdHvaRW>uZuWU5W@j;+&IZ{eYK%>5@W~$0N)Pi4m=}{T>-Jz$Hvd%*Q1s|j?gT&#cM`-yA+8d}BB#e0{|};pr7Q;cF{%!l{*Kgs%?m z6P_A6BYb5jCp@|QVd078IpO4BPB<}mM#v20gn#bO3199h79Q{0Cp^}d6aJ}ZpYUi; zPWV!9PI#nuM0mKjOn9h!M0l|KVc~)9oN#~YjBsBnCwy^fPB`9mM!2^tCwu|t&o9Xd z$CmU9pX;m^?&%y6KHGUlxVtka+|_YLINFgDKGWJM9BIu7pKd)P+}WNJKGl9kxTAfa z@X59_!tHH2;S;R^;c#=O@bTt-!ff*y;bYA?;ZRFXxUJ=kaBEXexTP^J9Bk|tZf@Ks z+|-y8ZcOITiUSSP!VL{MVSmFJVP8Y1us5+!xIU2+u1lN|u8p4&_QZ3-H8@`#-zQuZ z?-Z`AIU`(AlM`m@a>C_xBf{?5Gr}$$msO7lmsXz>8hM?ab-Z*QQ0Y6 zRJl*sUYQfNRh|*1V>w}KY@e_tx=)yj=7h=U!@}l@oUp0ljIc4bVn)+EGyIiVstEtE$iLR6d7zq zRrsW_`CnE}`eCMkCi@WS+l#2< zA@UX=)*zagM)dGXasy!RL`3i{}5)L~rTA=S$uZe_>fv@-a&P>?!=wFQtu8U3i}rNvQ_HMrRdGoh^X!Z zX4VqclRH6+y9oR7;Wvz!aW&$^8&K{!T0tM8A0l2F=f=5%+#}ozSWkGL*YN}V7XC4X zLQ$*Ot9U{2YvqvgZsl{zKdSVq3RSo2PIW*%q&}v8Lu1f1X*OuSta(lIj@GVCY1e6Q z)jppJUG*7t2j+hN<&_FDU_{T};C`!kM8M~7pN;~~dM$1{$X9B()kPM>qkxy5F(0QrH_<8@3OkaT+g^(cfId6yKCLc-8!0*r=Re~Avj6)5F0d|eW#IO}BZ21vZw8~m?%-tby5P}ZCip?wWo5Hv50pJy_Ila- zA#!L@a zC!>EX?=GJ#zpni5@{{E+mA_NrtGKMu)$Xdq2W}cs&TCG-K0JlP0l9YZc;S`nmU@+H63U=*7Q`< zTTLG}*Eer(o@u_R`DF8R%^$WnTjDLlEtj<%Zh5rjg_d8ns#?vhzSfG?{??J!$=02% z&$NEO_0`t*+qgDETT5GS+v>JWZ3o-#Xgk*StG4&rYugXDf1~~N_P09}9lnmHj^5{)bRX`1rTbUi@Ar)L%=FyR^LkIVx4U<^cSG-y-s8O=_I39i>btw|$-bBR z-tXu7JNgIur~B{g|8oC3{U0n7m$fbHUv_ZW9m|d_dt}-3%U)jg=CZd3JOk;0s|St^ zygKmVV8!6{;6sCNEEkvWS$=Byb1MQXcC5H*#WO3uKcpHOAKE)~bm+dJ*HxX-XcMcyLer1()RsX8lRd25LtZrPrdi7JQUs_{c<6Bd) zW_r!;H7|_lM>dRHJ#xp$qa!bk{BBe~+A_Lh^x)|6(Qk~tHv0aUb*y%5U~I$KWn*`Y zWyYQv`{CG|WACoDu8pqkSUa*dz4q$0kBvLWcaOh1Q9qHMcxvL!b-s10*BxK4TJKz6 zwtnyWC)Z~;ST_uBxP8Ox8@Y`Y8^<=@xry7fZqwD9Zr}9irtfTeebXN|8#YHbKQ>uA zd2sTBsY6r0+Ol)Yds}zQ?_YwK_F1ewA0GagipZCRP)xt+V(|{v?im`e=46MJuweaxb2( zZ$rufe&jVec7>n1zyBQpg4`ME1ui94@N`d5m3fB5YJeq~Q1d}cAly*jn26hLR@LUf zu|WHQV6>?zii6WU_gwZ&_5YW!>TP&E52 zKIq$T;fKK>884^ChI`qyb}GvRY*bH}(U-UtsSj`c`KN}H@`wAED9+A3JT z7+!|iQO!q^KI%wT^9}VKTqjMaeY`_mO(SX_wOO%{M}rot-H|jn&cx^(tTXD^|DuMoo~3f|9i5 ze$AJ1--b*%unwEVT52NY<;$p{tSQ}5o$lx;GdLA#r?br9OB+lxWfn&37^AU7=$PEs zBy~_rpqgVHO~id1>s}<@*iau122?hyo&JqyJGr{$XMa=~?x=Kn8wP8Ftrbq8ps}Q|&?O=n~DYUGL5A7+h zDz6wwidKK6v(@SG+YP0rKkewf-~L>z(^KI=ZWMGE`a}L8>5t}wq&u?aEX^y7RHiYv zhu+8Qp{<$|d@3Ahgvyxq=PopS91_(o_3sD5k2y`?0e_d#1_5L?qNDi zUq`zwME`_di`0Oc8m1VQgxZmGlu*z&sE#CSK*r>m9ail{o-0*N8prM?Ff?h=w0+W=#B=#TM6FcSNmJ(250sGdfg#L1K3dsM-i;3O0rbojAMcQ zoXT5H)dG_VsMlft+!D`WeG70wnxJ9&@IBdaX4}ySjszgbd%16b#>mS}>4`P1^Q3jk z8B#1MDh3ASdAipDJ%wT#pr&@}YuL~oiLbwLxN$>wxPIe}9p&A%?#@JIcTMRMV|e+@ z;HuBe^n_PjJ~)2YuFk{deUr)2@tVO+H6s&@hRCoW^oy|5Dx&|2LNTM#GqfJfX3`L>#F?WI{4fbZL1qm5_2oXT&e9!kT-)Ji zJkMqEkp#_kkX(1lP|RsnX{}ZV^6Mlz7PJ8VK$0eSHstXb^{8OWe&0@K9vK`d9kKQ3 z9{to+H`1rFqld4e|Mq>xOAY7{MZFOsq|8K{Hu=&f6S0@3?TUgtF~bioIUrLq9p?Mb zL|^Ub0BvOZ|6ch}XO;Sss)kA%%uRLJTW!8=EB&NB`@4kJ+z^TM)(hdjZAM3RPfdMQ zqrqG54#w@KCc5#!?FysIQ@^}6IoV&%WZZyuskrYTb0~`bmgwARKS}%jI-7DvCy6=h zFj__Pm}Q&IhFk}05T752WO?7{Yp?Z%`Y!HHtZo(Tp@i?p+rFSuTSwzv^%c$v!JMRD z_ap|Y>NodSl(h|2#QIY0{Hj*I+IXQ8m^J##))=yf?jH3N@+Qo<`gqZ zS?$mk5MNCrlwO|wPZ#}ZX?F55x`7i9-+A`cmGnOV&a4LDt08xOWIMG=G_OwA2h#QR z#1u`N=5%-g&0(54DYH<8VOCW_Wm&T<7;Vq#@E?+E+r?!dMIhF{xuI%#5>Y2A#wrGP z6jddwA{A|Q&O~org`+ZHt22rXz2T0{E}f|*TsPb-K>W%YN8LJWSF(PjO{8C`47nra z72aU>-)j@za=kO?D2uSZaHJ{sHdl+j(C}-!XtJR0 z^rC@%01b69<_1~#PC;bo8J@D~)3!2+vkz!1_g_Z%LiaZPY2~B?TsMuF{G~#9<1%T>hBYD703(ORH0n=8>qW z)akY<7tJ*Sxi9kr+)3mBt|yU{J)&EFv1@f&=hC@IS8KY9)TIe#3^Dk4=H_NbA?7X2 z*^Tg=$Hx@R?qD+p=0VwI$*kt&QPYQqjm)FV4^~&UYMle+ zWvM!kJGnX;t~KaXyDBKP87+dn$lo=U>f5unF50!Ww!F_5h;FD`ebc(y-s>MOscNnf zOCz2VgVV>ItXy;LipDJiu~0A+bk}r6%2zKb_gDkJQ)(1;i>tEPSGRUaxOVL|eU0mv zge;~htEFkv-tMi(ceQ0*?TJv#EJW=XREi|o{Q($wH~b1UGBh!EsClYMQ_3s28D7Ka z!b~Z1(n5YUybVi`Pt?$$n!b`fyZR)(`D>qi=(Xc+jV^rrIP*Yg_0jQhRLg7`>fMQY zZHU83Z*R-~JDsX&iU#(UuBvMn~H0kby5Sk}+L!@MTUiHgop|x(7#Ud#_!W z=)C+3n>XEDvDdw#J-)uD+!OCDZ`)9LMOmF_s9w8oc=YJ5)RNZi!B}|drlzW82~WHV z_}RWY!aW1p=wXM@0=*+m?2IOQnI?=Lg+Vju56Lb?jPkerdiB21Slh09w`TuLOFFjp zgf>i140114ueqvk)g70mjs!YJ6YHn48>F!tI5q=E5gSJ|%Cv%}74VEnTFcF7&Y?ZU zFfIMUDp0MHdfeGxI%%6L8@++P=Hi+T9XWJ_d+`YK2l91l*{G{lq}6II;aS7A^A*Yl zhE>Q1Zv%YxIT!V~vcIf5hC1#zl6?pD2*|EigZ@>lZAIa96-ifB71=y#8$VMdb0I&* zQ9nWo2r5A7{su?j7zXKsbdp=zT7S#n$iaNG&&bUKYei3S*?o**zHO~UvDA`;Z14#F~{ zJ+(UOc!tZq_zL&bJtJY(mv^aAmwi2ZnU026zf$NA+nq~ZAU+$quS#)+|wlJ zezN{bT~X6LXv%$b6Uf@WC;JQP-IM)po?rigdW%`Tnqn2|Ek3tiMz4IGk9|XDXuRi! zWB;Nvw6f}YXK5|!du2BJ6w^=G2i^zyw7_!Z=TN@1jxR6)`g!3w)ckNjuO8UHp}u|B z=Qj`Cu&xg0)r}i_BEH6fSpICRo!Gm4^fSAcB-UTw&(238y^}4`o`g5jv$?6Fx89q# z6O$6p98BYi9c(5-?P&^mkm)S@P#{{~d@eMXnj3x}AMFTMEWfyOrZSW0h{i7VaW4wZ z!?kUjI{jzp?KRbb*rRfPPfGoDVElHcjKrDNh0_*b(9WRORsc{wwOF=lgUEdO!QcT(N0E^yhP89DNLyGJ2#}<_M4`4odI7w72pnWkKEQ+ zeTZK(WF_(p$)Ue-V^;>49IDXHLGfwYTB zMLMsYvCbbXR8SV~s-e|Vw2v{!p)O3ag}jsghpN?G)!NS}S{iCwYdQsAW7G-uH#e=U z_o_Z&9ySTClFCH+;BaKwMMhU*sJ<=HT^{t;ld%?w3x?b4EH61x}_65MP z70Q!(MA@>(!y(0gM;!g!s5UQ3tTOjg5cmo zT{>O$%a3LEKL|a&dU~49G9yW`mxJ(s0j!Pbdx@Uf2U^C)vd@FxFXY=N*%WQD3VOG| zrcge?{6T67qVgc+9)Dq@?*13EKRxyBUAnLQh+aomzx;Ccc=pJze#P3$`ue?mUs*-+ z7|i;buZ9nP@c38O>AvvY?2lTH(G~OtK;M{s0a&rY-5AQXFJ2)zsVc^3KS^Rp&DpRiE%qjx--AGT2ck5K=OR~vMev&{Nb&j^$a9hlA-Hu?Jhv#Y|p=Rr3#h#@XU&eEF zM+4P$q1uSo9`RZ#T>)oBlYeBhUQ?`7xXV0_-ujxY!}?M~_MLBAe4@)^@*qmj<<3Ay zn?WxJqGsNd+2TxV#k5vELv=G8nX%*>%-EU$1xsjRJ_r*Ggd@RZAxb4XwYMJLe>7~@ znFpH6mnFs6vZ;pTpjG#APdsFAsR`7Vna#ck_u|XFrB!0fX#J9%LzU&;UR$WnQ`_nY z$34!7i-n~jC!i%~1}*(;)Z~5XVr#lsJ;UeQ!~E*M<;BqtDsEj>KfWYXF*MV^dd6{B z-w+hyL5tZJww5PN`}%tQ?PCqe9joe=_auEjds(f^UG6ai*nUBYkJ5a@fiW92Q8?2E zG40UJ7}OZR=9nmX_j4S~&*>Tvm<&c1(!@?L(V2T&DhC>Uk^bq9x?!U(baemWwa(g* zqdo462P~b9uFA5`k-Cnp@>+r{hF6~OLY4&O~*}J%o z^4eCLCSF&$wD>wPQDzCH#_F1OtgP`j4MaUv_S#BOyvbKvErLJDPN6SxuRw0RDU-!q zWPtvsi&Qg&&KPyFl*i(*p0qIOK{N@$LbzjW&8&mcot}DYyVh1B1eMBb?AN4@rpD}J zD$^C*E5@P&Ds{l?-Jd-|ckVCSHpS%h_cx)?yxkMFs@!SafmT%XlrmvN@#rw5Gw|)~8qN?8{;-Vo=sIovQA)=`?qY zSU=HMS`%`Zy&;RcuFPighJIaI(dcUk2U@zqqEBpDRo8IQsyd%ooh`faCc4{F78ji% zr`{f}b(e-p^{{DwK}RgmHG!0-3?@PC#H?qAkQpIQFnP3Dpkpc+082jNA+70Mb2Q-A zxmL#`EfEXP)gP^+RPcLaf<+ZQ%DwoF=|}`~cER8-$-Y4?iV~G8Y75m1ry)Q1p-%W4 zY%LBRHdW0Su!~qmTwBNH4U5;HFULN9^=~%pOx6WvZXg3_)pbgY?gdL3*x@-~l zAzhp^o_(jWufn9!aPM898F?DVL1P12p~u`r0g)s#2Dzhivy&t&863F?W+&Gi?bLn# zo7_)6&3$RI#RDp4@4i*{&)NUf{iya{xjtHq`q(^+B6~=xsJ0HakXP?Nx_@q-1>W*K zkk4A?O*+eJ4Jv`e(nK|*QqR=p`713OOCE(7L5JE_Q{LOiI+Y~4#y7D$38+1 z5l1Z8G9Pf~*OheEH1WWE%;@H<`+U?)UiY<}Yf*Oc1Lh+b?rhADaLCJB`H1pc^X7e9J2UUWqkGRW@AS!Y?E4GccP?n(qg=PtCTSeT zAk2nh5JWX&!ML}eO>9nkzEM*zY*RHyPaVGgXz=e0#Psd69WO{j@kqmkhhoWQ(XU}W z)Xc_VQk<7)b6!dqg$lAPuU+Q_*43MEhxCm)(b3#rBnhTiS*$sz)D3mQ1BLwk9=_7c z=x+_H*X2*UU5d?g4LAdE{tgI zqwZ_%*LtbVWprrmk%7LoJcm*3=+XTTs(4;u^=eeN*#=e=yYqgJ6@E`O^u)u)2v;%m z5*k?yym`tny19i6QS|88;f^__L=5S-(%`VN<8&6U%wYW$ zpqLwhSB?1-_S?^>B@WB;Cl*Kv3XeDHzL?4W1V$$NHnkVvJg|GK09OJ&>WWH~TELdb zu!Y$Zz}YXGHSRrdY00 zvPy8fS?t^)&6V!G;mLa4x~G4#Khvn&{58t|&(&A|{x5swxrP>cz8Ups&?gl}JdLD9 z=u;B&4gAvJ=db^#lv>sqlU8`zz+$fKLwnY$!30zq?*yr$$TWlX1q#F zakSL*Uv_%Tmi_H-?AeJu@9)K*Oiw-LFCWMJr3UjOV{tkZO^5K3GkJc*R@GqHrQm-n zRm;-SN#XuC)We|$BRi;cUn-b;U>+FVLROQe{ zE9o*#*jH>VzD23kxJ&o0>eCqwpHf1^V*9<`dxxg?MgzAb+7PH`bGJh@fVQ#uPJS+f zd?j9UeD?Sr{*DiKvodV{HI#3EIFc?gq)Wnu_Dg9DtdY=6X`!0sYL!q)0}|?h)N1Lz z)RW}X|5*Fi{KM@(LtY0BZb$x>9q~sUDN(^XVNI|GK@-GMp&&5T)hv@n9b_Y{-646u z$%KK@^}!mmC1AICRM)G#)k|V!wPrH{!yeV%l8M(%MLM(H9BPX>&AK9!-8`^P(hbJZ z{15WmxZJy#FX~~tSpFK;=-9jzE~h%klu^oClR5h3bF$a0&pk&~R5QQW$06Q8I2+b@ znNN?%LqHicVU<@}W|wpaa~zn{g*jeWbViOBk05nK&hDar2<;d|B3H1z%2n3AsTG&4 z!?qigUR$-d()c^Ct1@2qR%gFzbV3_x>aR#{9ZG1%)@r*O8!UcR_OD!-r#9SZIBQ(N&T3G>8@T(Xw_gWR%03I#zQPToXLb8mz}z0xqM5 zHPz?R=&RCty-UMZPo)^1^dV)=$*ZSwlu?>=JEV-cd;pS-@klO_f>H81<5eXlQ%Q4A zi>ux5Z+EryG%x!8sg}yh7Iyr&v{e*aOPhO}jV6;3r$yf%uWD$gy_5Adnu_& zm1;SI$!1U6Y-UbZTuRcVrDm-4n-?To$XSPZYZ^%iG{N%yFqvAAh!jjOzx->3#4rW-4l&MqzKpY5l6=-%wXk?b^H_yw5Ln12N^e^`QdpOmKbJ+b9QvHEn8HC+^w z%tAg_52N0NM4&KK=d%;p`pbe84;I)=S_Rpl)W9x=64iEBMeq8$+TMhqy;B{Eh_&H{ z*g&_j)Ne9bV|^PicWn=Fx2Rid%t4ni(HwJC`YpGQH=As+7N6bsr^Z!YM@yh~MWeSg z)+{u5D+RYJXfAP$b@&n$e!VRjs9Dt>^f&fLY7#9vv&$3=*^D}|BHr36S`B5+vY1V$ zFN2I-2CwW@?k#DZ-|(bOTf(zcGf9*zg)8YWz~LvH9NjvxZpl5-za>qX4Ztq z`wWfr-Tc}>4v?7(^*#5xu&*egq=rf!d-EXC*Bnfb;Z5jcvrbACYsG+%q2ujVv*hMt*rFgJVgOx zH8m~-W7X86d}twjr$wxEmc~Sj-Q+DQ#wNp<$>6qO$p~}=JpDcT8piu}ur{oyO;;Gw z6%`7>nHCgud=_6qZLtiqiIcW zXXDg%zWNrQrOxK7F7uiLZZqw6cx`31?oeBW!=YF59PcV~IofM0R`;7MIr6AR>-IVv zC8dZ1A$!4zw{xwgXN+MEPLfHD&)=Xg%9C!-cnqczZx_0lbJNdb9MC^Wlvp=l{eV-5 z#k-`xnZDI{NXR}!Kc9Uu12EK=&*`%uS8qW*igV;3Nm6-9s86wM9bI{6y1e}VP7)#w zau^ZmkTy6=Hnn%n@%|F2Xp0~;cM@Yqua3dr8ONw=7 zM6M{gh+CJ>&z|!&EO4GKveH^Bw{8ZLbC$#XG+mBy2kXmhg+Wg+8B5zuGkR&Y2n1b_ zf5hem^C?OO?z=Xx&0_aDHO`pdVvjV4^0|>Sc=G6#2A^GY6CLJWv8XtmZ+48Xh`eG9SW%%a#b9NcepJaN4o zTh8TjFTt0^ya08^&w+K~ejDNwW@3D*gy>Cay<{+$qpUQ|t#kWLLHtsUv1(O&DAc~H zigsU@dW|FA#-X~pp+@hA8~EA}Ut;_+kYAD?(g#2ncs8smmR2>Nk$+h}b>^4zehl(_ zau)I$TE*rp#RvnktQ{LMfsSBcK7S5rsdLGVQqmH*HAFE+b1#e7Ho zfLEp96~U^dzYV*+MFyk6qfrVQYG_?qA_+P!1APPBi=YM6aE^`~Nm3(X|7@)_2(LRI zR?sn}WQHZbf>)r)qL4yw_3|bG$&#(rE0XN2Ev*XJYNDl8K`WN&-1P&s^;rBboU<8= zztReXBKRQW1+3U^MTwu+%LHQGDbKj#rThM*x-k9HT2f` zKA>C|%Kau^4hf2Lu!x_m((mx5# zl-Xn}6++rnA^(W^aQQTqMmTbv)Uv>hrM>@PI@NpUW>bkRi?XTaonJ_dlq3mN)mpek3QC*CYH5wTUvoAvGHuBp7KS3bdi+lk@pB#EP&-w&21oHq5eW%)k3S8 z&#~HNw@2!I-uj56)OOQDa<A{JK-Sg6!3tk}L@FaRTF+i3rC?Ew^cY3ZHZJ!p z-Ysr`y(uAB%TKwH4W!ZQjA@-tUo52s>5ECacrN{Df%DB&u#g>u{V_p`3?VGzsku`N z(}cV~z52UcDc9gSd+8^ti>u^}q5JPgzE3ts@k%-Tu$(K4lylWdyvQ(6r}a(n_K7=? zc4GydjTw1QNqL^lDJ-DpJ)`+VEGGZ+_K2p=HApqd&ArhJCumIvdIm@9EElj>Yjnwx z%X==6x3!z)bP19%LwBId$YbF)OS({z*M(tO#%OVg#tBiOdhG>t;XJrK*zqdKCe<4> zWq%Kj>4Jn0x-gN4vz2I=Xi^*Sou>AfeTF{@dyYIAtRWQH+$Fpd307{F@W9eRq!`o!cR#t{TFqj!m4Z;oj%N!O zdytUD56U+S_bMk@?g59lQ4;P9^vMX{2eWRYfJmdyjY+Ig;YuTO(m0=qBF(y4#BnY~ zXF*1vG#A{n+%ed2q0By4#*uN+&;c4*l;HR21D~3g!T;cyol7wO&|&gqo?eb%u_O}) zZ$7Eja-%jkf$=mU0iv)<3 zcR-2-$T>UCd?_RLhZ`VEh9^}T_J0}*Ge_)EVi`{eHAKGpvG1+u#{C`jg)J|Am?(p(+=&7wkln$h0%+}k*!qI`F!W| zwRQ7J{75N~qZ@4TBBIVO)klyp6tOyx>d!K)QVkVVEVrz3MWA_eqN18zkM*|xlI50} zjoJS)2EH1gqA_4%H>^u5ua+{*OeK9osj6#wS+%El9!L`WV%zgSLw1s4_%fE>HT83J>*y(&gD^-{XICQ_YywH$4z;-pjRm{ zxS-}dy@!30@i9Kj?c7QB$b9Cgt5WF$ASXdy$hHkahrcKFu|w+P9=vlvwrx5+4KjPG zgYGQyWuW`a?hi@2Ukcr~7)sLygOPB~G=~>U7gS$L4rK$Hyw)aW0|}+1JYdMw4G_kcLtC z82Jn{795lICrSHqAys3BE4H$Tt0)d$Y!!f1xsC(!(py!3J1&U z)Z<6TwG~|(+NuZY-AKr=(f#J3mc-i5AjjRt@dkIKw7l}BSlAnGjAr%p{rE^nz*pB9 z2sD&?Kr!%2%Cq5K1Fw2PZx!jmA_TJli3V+{07kKm1*^v#&NeOj@AXXW`YcyIJu1sJlkjL4p1 zyjv>(h?g)86rf}aQ|J?4RDh!^d(X;n$t8I>Mu~=V;b2ohKYkLrff8khxM^NPD_fN@0N>DB(x49HTatQN#Sm2IfbW zis{n1bdtO$dH(FR5aqmpCy7_e%)635KG&11T{(H-l%$)=5A|s-^UXFC{(23BVbq@h+fJu9hdOX!FyzwVm1CH0`L?A&)7md&>x0p z8~0}3M?}oS<}Be$RWm%DamtH?f7?ssu}FL*9>=Jh&g6OI9C;2!N=lWGvod(@7qIW=!0f?Xk=g39a7pNI!!X`Uc<3NP zYYe6;QX(>#D&V=0eaHW1nu8f0lI?p{vkb%9cH#ao9gzF8vmvhoWsMRLFJW4w1CTQT z`ySO?J+2U=WGjT&)6DaBh4dZ)*&{$ia^Z&Wg)h%&pfB6A)h~tW>q9t#`7>Ry_kW}t zxJmCWTrrVmpF%496&6{I+nCL&AEDV|F~kmf)vvk(3y)_&#O7&x=C zQRJ--mbGf&9Fn9()OK3uu5(najwV>nPHDBZVr|&v#q~Pv=Zf_1M8vt1eW>nI(la_u zQ*T|{2+QBms2i5l`j%HoIXwScYWoh4gu72WE0Utc{7tsIP|B5%cNFkR>OKOg&=i__tM2|OYD(j4M@Y#u7b1$zs#t-1N6>=VxUYUGOicn1G& z&hMHmP+K0G7UQBTfJvUaVhLvs*ZFafMY!mSlx#PWJ@dHEu^Zsg4|1ZkH_}4_1Q~)R zt07IjX-cgM1oSY^mv+X;uRSS5^%52XFRgLOyFsOtqR4rhCJjDkiOEn@>@d|@+fp%s zb1vLPsj)g-#fB2)MvbX5IFbR*=e z*X46JB`-*NZ^>LPt?c++AY=UkZT^3e!+vgWqJsJ#%xOQTpP^QZb}q>svMBA*l;&qq z$f6oEyI1q`v$C)*OrXJK@LbN1QGn0*uNZ6`>X&4A&r;b|1j;la1{HwbFCK?vTr55z z;llj$8*+StRz-9H22}-I=f@{xTugo?Ty!vR%VHso$e?1tb&gEU_3M!WE-p=tvbCU* zDj65+*9GMl{dyyhOHGH!oq4!WOvc7~*0fMtqh?v|Y=UTXEyuzCl0V1->WGiNm>l*mpwd|4kgOzT4Itzfc^` z`LzA(9$J5ZIv2&^4jwp;R>99>a_~8{Ly)p?>x{$NmH7g8c`G`7F!@ zoZErG66~e?kiWkZL3_Y_L($Qq7IX8Kb5d5%-H}i~-O@6B_T_W4RzYjVqs##}C%;CDQtS`Vt zR{$l$6>FZywOFs@elfk0`i0G?Uj=WN#Fp4JVxeDc7nJj2Nv~MHm|lHShU3a>q{)sn z6I-NLn1^aGHn9EG0#^PVUgAotjcI)en~Q3S(%5LncFjpk;d6EvD;q4qRC@VBeo~r(WD+`S4&zmYMI_ulSztW#GAMFvuSqo`ME;bel9Z<# zO4AJu#28N-&z%~xWoEf8g{75qCjLkbE37fHK@9IAV{5-u*Om&ps|OOE^LL+I?ryIL zHRcDji=-(&+8Rt+RplMSRf{&Gw1iu1<~ol&zHxSGUVpw^!gdtaH^4tRmVLqXjd>d^ z$t7J5)5Bz?z`ZEsa>^?uNTbKKza53LV#sQBeyU1`geXP%g&eOL%E;hEXelZEbR^AJMuV7xJx*W(?82eSeLfj1=hzDuy!r9`gu9| zv1GrHry=p~?mRthB}EeNY!@11q&aO!f?JV?Tb+k14+?@FZ7rAJ7DON=y<&ESt@T*2 zwiHV_imA$;)^RGU$&ogh6eZYntc54Yg86Z#>90H&#}!M#mMuDao1wgg3JFFONi>@} ztR2Ymqh@^Ia!h_x0R2#t{hfyXYWLOn*`0Nvn9N@ci|5{4YLwgCsw49A_B2@0rC$N~Ktc{cKvue>wh6 zZ^8eWW_ggvJEC~G4L3ujoy~HhcG8ay^TAbCDq7!Wi+JXlnO>~hp`DDFTj%3Of`GlMO|$VJDB5v(^tQ42;6IdM4|VegPwFj~kH z3oQGetzR%!$P)^d6961+ZyBT-xl~3QF{d@s4uSx-m=(6=0hb)9NJh@Zxp?1MTU#nD zC=m=>(17%!tzZ}uu=7>NVq*&$ggD9kUbVv3v^?G0cxKBO-Bn!LDa(esm=_8pKX)`o zRy=-wgFOJ!zAx+{le9wKD2Jv=K{+WOg}q?$$C|-Ah9Wd9%L-ULprs!Sig=hnXO`@UCK^-^6`UDf-R z?sS&Up6(=sB~8*vy67x)5)!rq0sn6gU3r zkjnR*b8l_k9TMmJegEGV=)SkA@44?;-}9b#dEW2xz}-MQa5#W$dl?Y31s-W}>4Qp? zvcnSj$eCvs;gNqwS;WPm>{`|bm*Ff!xhqPB8cNnPEm41mSPYAt+zeW>2iTv*K7ECq zso>2-&s1lAAhuhslo=(ciyZ58p3O^mAGv~+TPK$Lx>ycc+Dv5{FWeu{cACE(^{4dY z{ADOk>#sJ-qLi8r{k5Rwp!Y1w3GyGMhjKXT&~i$>px@P?maJT3yP3SXq)HFx2<|`7 ze#%kMz8s~fz-O*;GeW=Uz!%1b%_oJ_UC* ziNV8$mQxvIZLB|KJomRodXOF6*i#0vzI8OJQ>UgJOu!Yu*`NrO9OAPm9wzjJHy4Vx z^qfi^C_WixI4ZyO?^;Dxzc#de?_+n;rVwZuIr2E(g&dm?$!f8Vx$1iqlJ)6K|1I4o z5(E7w`(F27w3&?lr0L@AA6{yCejg&(s1VzjUpZbuvF$eNux>IX(_u=6gM5p2!UwO}FtsLl8{xn!F)OFOE2ov(1SgvbD}=LauzT*glg(@R2a0ee;;NxQ5`bSfNM8yM*?IlY$OB zgw3Ksu4HEdKW!iMzRWun5SZo?bwPn^6=U6^g#o@GwTRlH9d6zOM8LZF>|<&}?)^7t zqPZn0Z`#_Ux0zis`8P$Bnu%LU=(M|cbqhjiZ&W)xaRIOf^1&HD?HinY0)rNt*;$f? zN=@>)q?(3Cnk&p)*f}b(k%$hZ?t`!nqXNZ-fMrX4oM?9?w6}wX`WEYAmm6b79}q%( zZ&YVVS1pJc%oyv0;pwn3$TR+N0G_9UUI2b0{)*p7p>ds{9F^?_9mGb;2e<=ZgP3E~ zKEQPSRBu23%sG!1&YYsW&A0T_d`1??+w*CWRU)mXVvSbAcvss+{UOmVV}d(^ec=(N ztD@)J_Q8;(xmD(7?Mum;ag{WK*C!|Exz{i~GE<6*ZQfI{qd+5aHdP{;)6r^|XjS)ThtI61yx46%J1?GQe1OuU z`oVP~9|*=R9!fEQwk^twDQ*5r1P@8>#X zu~wAIxfjtN<6QbWd5-ntW}zP=0or8<|Mi{nFoEMH2 zP81b?TeSO+XDlrCd^z?w=Jzqonw}{&H7d8*iKW`cw`#QL1yUcCZXhs`2;mJ6G`6{Q zc)k7{(duDf&o0*B_1YDgb49Q>4Y#>T#WzHpPgG|l%%^dn@C!PlLa!`9=Tiq}#)3VJ z&D}rL&8Km>FrRW=Hb0$D*@g3o>%)Ay0WwRJda_b~8XxUfr_EY2E9Yz#B_`8tKssIM zQ$w*~D3Vy+botN5uFJnd1vii0VvGDFT5`!AQvWiHWEJloi? zu25_dTmX_}v$ie>W~XT=CtwADzxRQ^-yos(7zmV%2VNxFV>Bi{D8(qL;ZW7zC<|c^ zr`QI6!#GK}5bVV!j}7ozYF$Y!pCjCyN3`+_(N9o0$*S6)dHujt&}1@j!$A4rvmhPt z-p_yj^Jf7&AXQvB1GR{1^t=R)j|R0uUQ+OL1`%J54hBWtr-)xP*9PVXQ-Xb@c+`H> zDIm@{aa$yv*W7sLoi|=newvOub`}A@AqGvGGvI<(wMh>xTR>T9sxm+uiZRcMX+2m> zvp__&pD>^5#<6E@-Z){NR90K8++o=PzxO(Phwsm}(l)owYUV*aV9W$RQ|gBdhX9P( zT4b3L)?=iNxtTJ5*aiA6(bs|MN0~nXS&O3*-IL>4!3Tl*IyC6Fs!|gfq2v&G)SkXnnofhikZdK?`1GJP9;0VsP38HGgI13*Hs%jLO<3(O23= z)@Ha$2Gci&BC0&|NeQSNv}z&d&q8ST*IeIf zZ)E%~30o4yTujs0g(l9a<>CzUl^z;}Gh@YG1M~)ZbfkM0jSgl?ImCojJq}2wcQESN zc3A@uqbA-l(qs&|jiQBy+GaYf`s3T@12gt-9d0(rtuBvNu+C6kY(?EDwh6^Zvc#L* zx~51804MM@0KiF8grh0SDqF1q)vwjC1y)dayAr{IyGWvbE4a%#`vtk zVd3vg#_VNlbtO?%=Cq3oP~G05GA806fr-NQ&I^Tdh*1+(tINU#sFdl#iN8V0WOGyS zBd2SK`H@5R+bz>`R?R6|9tM)ZXIkL(72{<(G^WSq*WP@=KHo zRZ3M?x#$lhw2SsXqlGiY1!B7@w6xey7w|Af>s2=106B?oA@v*-a842RiS2=v$Ri4#M}4dn)RYSIge_91e8Dn~ zd}hDFHaF$US+gduzg4L(9UoEpGD})q{nnJp>q{tmHj_^;pWUxk-lA$<+qo4lo{+2;(#lJL>nn-&l-P7K`9ur%{d|vkrpF<{YsbbY$1rg zeB42XwpqKwiw7Yh8xO_Vlzam6OA;39<=g}}G?-Afm(s>kI<1rhU8NO;(ux&I$>7@3 z;Ghx^r%+!7fuh9%8>pB^dsL9~D$*Wcc#4v9dp9-UaZ2XJqBmrv`op%_jPJbhI+c#3 z-(>P99hIxrY}VpO@`=NT-@N5~z&y$E`r`C8YuOv{7P=n3O5--!n!Qz6v!pD{ZQkb4 zX6Sj@#(aL}#oN-p2FCV_bjRbcKCo)<6V@MifHj<@9*N0$r&#=5D#k6{0sV9g{60_R zOuMI~YA$8lOLhbG_oyw)8Xq$3lo&3bClAKO&C48o3l$ip()bSm2V$AVw|~9p(3`pBQK(yI9~7 zxtZa)O3`-e5Bs9%51XYLQKR4un`xsCu^CeJiDkC5f5Ib{w&A?JVqIv!Kj9fmen#t$ zG5;UvWN^L*Kcit8S|4@#eM?kM2l+&7p(};nUn`fQEUgQEjoY7DY!WbgN;YS0VBgjxiQOd|~YK6Wg6e8T)Q0BuZV*_S`MP)F~VG&^|P*b}A?gGgySCPvA zTE(4lHHWPHP6A{hmt6{>k?7%*5CCQZP4VRxF=wzW7edX54xbPbxib}3Rk?~Ri_e~&5fN@V zkQcwnY{j-g70@>1T3Cyzrm5{aV9JanNnmROau>Eaq&TD|1q$4?#2)c$wQ7fahjLk= z?w((!Gb-fn;bEOouD}KJX#hDPcQAa2z37)J&c>OS&z!`#3OUuVMKjbc9Qf||{JmH2 z{srvVG-unNp%>O*#s7bid*@RacL0oH->Snx$tgb7`^CfjAmcd_ZUeu9m!N+HpMT_k zRJe2&i9`njA4@?87$}^EDU@!aF77y1cTbFKU28v)_WRTHhxjXZ{7ZL?3Ex$|Nn@=u ztN|vI#j*CN9rtR1zf*r}HQg5s$c(40oTP-Mhq?Aodis z-;*!azQ=l^bL$Q^-&qzjPFk030uZ`@e~7`8DMxO4@a^SJq8U_E2b0^$HK>E&@5yMT zYVSndn2gT8hm1b2Zpg8My0z54$0}krJhq;wcM9~qK=bAZIF3{wF6kw6(#9My&@U~N z>Kc)v3G$J)I=+04q#Pu2(@o{?oI5jz*jDPJgSx{%WnP=$?*W{f+1mHm_h`S~!uA~F zmsB!&B~uF9Kd@23ISkq|a#F62|Kf|}`Y+vZA9-r?<}y%%DCB_9w={1G=w)l~75f}E zXMCTleUJ0NPeG67S)qPXEC&!YbyB1OlJXQWiJ#+Z7zNlgVv5iJ4{#53CHb5C^ci=j zi+s&hUhC$0_nmj%`Sy3en|SWLlh#8V8_+|&=CvDs2$)a*w%~i@Fv0V1Ag?@+{`ej0 z*N6IP;Nzr4?ie96k(fS)ZetE=X&ivLf@URp`*XzQE~niuzSBcqaPz(2{n;HSU-;CpsnX5ZtyT=^b75SG1v$YXbd*)>P|Cp!w${A@oxaJH6S7 zfeW&Mqb`Oqc9daSASQS~8&uH=QLGkE)9T(BFKIi~E=e^bD|g2dP5Y^wwtsR*&U@-q zsBd#o4?~@*{Ck@|-Aj-qV&woamtP*49t~6XPmIxD1bzmuc6f+sk%uS}EpbRzC7Fu| zvnNgQbOn$mdaOmJPTEa1SUZd%6u^R)G5PB9Ba>}AC-`d}Mf{eonG^g=O)F;xXYS0aZ+uvj?p@iO7-)7xdsa2Y`Z8hF!#Ao8&0S;7 zORwJn6%VRN`3^ZD`8ej%{}%sauoBQ&#s9dHx(;rh7umT-@FQHPhoQO@rptsLhAMsd zv*}@&pTP*-yZJqIhu~<;qv)Z<<}!zKGW0FRI5{r-9jAM$muPMc#;IPh8S?mi`{Un% zCk9^}?04|O!LD;7dIe>$!h%U?yf_Y9^}I5)EPYOi>7&C}~} zZsw1Hvk7?@+ubH0^G65m*p^~Nnb*rUs8)DsFW8{47O5`>u(H7y(e7%C`7^&j-6^g? z0Ot?n-|)stZLOuYHi@cG@@nQ(qza==kN=o$MWCzJ;TBZ$#ZgZdLHu5bbnt#@;I5;p zrlBpJ>G7_h#hQ8;K{O!aEp8U4_8{#TEe~fnsaU?=s50-aIi(L;XTDUo_Xx_oW2Bs7M z;|k%MtqfETu9*$a3pbF5?LWDyEtOuoZ%M~Qe<;wiA%FN?H2p!L#gmEJdiZCqIn=tf zzq_$Z_A-QbRIqfxti_2K6?px$VQ`&T9_4cKA}G1 zh|m)>5N@H_!U;owKo-^06l9hK8dOvT#NEF!c!@q2@wElLE6r;I*+eka8`zlHx8~yM zT!uHgP$Tk>RLe@c+v&F0cH6uTtKYvipIzP8I%H8J<8rL03BZm7lFDX6eLdTtGuLr} zk{&SUh_->(6LYn~Ovwo`4npo=YJ`&GKg2fBU#zAA-T?(~rid8t8(p8sm^~i1KloJ& z4?Wi9bUG7Soy2AdnAQdh3HzR{R=wS*U(uNw2x!bEquF}R73r`+uXiYwL~A!%&6ZHc zyMFzg-l)-`j_}LExu4@92NyTnmB^cEHvOVQpcWR2#G7rk+e3aHAzFb0~pbgi)yv$EHSu#lm3Cl8HPuD7|7EH*aK;vx@@clWNK z`!xNBF~c4Qo>S!kf`2>34yXAaG%AHoCdD|4=;1g!;fQTf;9O2KgtJ4vBk$b@eZQiI~xw zZ?F1;pC=*yYp45(w)_`l;YA$-C<{z=)NyNL9nWm$W8hPL)DB|oD`h)ilU9aOC1g$s z0e*$eIKqgMf+Ravlm-UR-=A-$GW0-*(h(1 zCzo#POpWAY+UxY~A#==UE`)YE_BmT)j{f%BOi0i(xna(TXi|UsaCFJefZE)bX<6MJ zvILrJ6XAfy9N8s=*N69S#>3u7fFnLNGc?s zvx+PT(G3_4!K91zzHp(VcW=Gi&VP>L=i8kIyDH-8ZC<`g!u#Ggcmg2$M?WExNTrUT zQhvmwpBOf}be}5UiPii()(Y}KKra-htHrG>8M%^MGl$rvP;Ir4`iu$jgb1ud?fERr zL%)P{(zHdrDQ_y z;st||rZscRCgz-XXbTZvd(=AIZH;s{*>_J_^^S-!GBBAh>{{EtaC!k#dPF|ngxASo*yCAjRFfs0?=6RJo*;v&Wtkx(>j3FP-COY`{dzQUG) zaC+73Fd4H4$N%hUMViGXPixrPN%kV)V$Y7{P2rw(ZC$(8v|gir(;CTmQymuiyMR8S zO!p6{M=hURCEoz`6Qq`Y(3R}zzsp6w>bk3+TzuPY<&U8pl^J{}7o_{5OT`&WDtP7# zm8T0*=t(S&9Q9Oy;)reE7$UaGz0tm|JIKq@Cz>TKkx+Zc=&*iNZ;7wmYxHKv(ptak z|NPCYX>Rdldi))i3{#3r(XlQF3bFDI{bQz#Imy220FG zL)<{u0m+p){!n3`dBal2kgvtEs|(&hVZ0^Z)|*bolWuZ&y~jgk{BNRS%rBp2RIBkn zU`8bQ$IwS9@GGJV$>$tGHJQ>#B8b$L9--j!g4~ZTqlRGQ9wsYJ4kdGbtyH0solqMR z;|Is1C!@*n?kF#5I?*E0I|C*}KfH(dGrj(Bp|#28zo2*ciYwH!L>M-TDO5LdQ1l7*XEuhcGpmCr;!FTITu;80 zSd#Vjciv}ji8}lqe?eov&Cc^dk1OudI;?j`Lc=?TgW0G<;h8jg@+&hN-n-N{v~)ZP z^r`-9uhpAXk%+gY)7QJTZRO$K{hK zAHWVLCc6}pOHel~1du|!qJCCvk8`} z7MbHc7Z8umqLta4MsLTeEHY#LsjQaC<*h9}JyC6{cf6%#eP3+tC{iVTTU^6Bp_oGNTkI8AD+3<=;O=0r50X;<&IrP3+>d9&einZjuO3(__f z{$e%CBzyE0r9BaN*9m{jrl9K{I#kI)s#7GVXv724*i!CZ(Q?y0nblbW+diJ5Pt0;x z<4jq3g6CSWChx$S^ukxyH>hv6`6ynqF0NLIs{btW(F4m6M@I0ws+|?E1jSU+iIBKT zYb*Azba4jELl((e%a}w`w-$+(E>M}Ie62OemaXA71C}jYqJvYB*;#Xwr?wnV&?V|` zvP8GE(RDj9!PYHpAJs$t@9|R6FW`KD(~GW+P;3YyI{(Mi`0jzA)8a@4ExAO?ge!4s`bjq* zcYnj-P)9ZlCB`}ef9XPI*;~}Mup=663A{v|8;`CIraZYZ3^>*o?T0mzJAgm0V$y}A z1m8JCh$xuqi`rJ%DGxeUR0b)}pLUm@pFTz66j%Hiq-d2$snLPu(B^-PbsDDnQAwcW zb(Oqc3C*}zb>y!|qx2M35%8Mk4hAanls%-^KY;liJz86Jo3pv}jX zY_U>Qg`fhT!cjs(S$H-2#IOj~ox)ijy@qry0Gtsp^MIuk!m)y$Ek)PTm;yaZPn8x& z5WAadj8|uF%x)P@q*m=6DSu(141=Vr~l5VL@IFal5IrY4C7OG@SrFtADG*;IaOGw1~!jiUh%Shwe+S~^f zpp)klPw1?{c-^{kwGJjUcI{8txrVN-?^AyXs(WX99>pJRn2-GT=-MPjVRXBYCgTf-FmQawnmbJZ+ z=J8#<xacaAPlne$=>0;TZX>ZZhmt}G%(q2)x zdn`VhvO_@tiI3w)G{4Oj@jF#9^4A@!J5G4Y>paAuvAF}*->-Q5Bxw$3a?*@&TPzeO#_I zS+nGS$`8}PbqN=O{VQH)|4PJt{&U#BBsd}i+Drx;7!IUFZ|S>aINUjPfsTv$BeJN*}tmp)y7>K^p*0BBr^cd&#~ME(6vX&EBY%8~73p zzNiEmGX~Z0KE^9+@0?ykyCqQfUe-0(PhctKe}j68`w8%^oxomj0v4TCS1(jolq_M5 zsFNomq_UrEA{VAE&21fRYF&T%lJc*1ewDl@zqB)R0P)nXc?)Z@gF8l}{9t))p_poU zN{|O7*Mg>mGh3|Avo1k9T>=aPYE?ZlNoOXRtMEVF!CG@` z#ApoP>+q?03GxzlY?~Jc>ryai(@2phWX{&$+hBqAohqVOc6qU})Y2tL?x8nGi{_FQ zg|*@7;_VaEygIP|eHUDMGJU0YYwyG{H%_GYuH2Xi?(FK#U2xUdD+7Au;IWJSDjXq8o=S-Pr4t$~lt+30~O*6*ge(;nY@;zz#>}Y7IU- z!PIs&!!Li!*p*w0vMqTGM*AA`@|l}ftv+@^JAmrfQ*TdyVMSWJ8k^V7EiJ!917s3L({@s66J4r&^Q$`DOlhd}gp|#qI%Of8IamZu02cU?>cY$X!A7-XEryFR+YRkhf{B`~s{-<0McsX>}x>QM$ElCt} z!8%`4tVZD!*W-~`TC73V;5S5AqiDAz& zrJi7Sl|$1k9(1ZViphVhT&1^r^}Huqx_F!MYDd)L*r?xQ_dCtxm63ui;l7`52P*kpM19}g{8NYd56`NTCz9$f-p9Xi znE%)gHL|%s{j~b$e^tMXf4~y>f%<7|n6&Ty27B!{pzm&>V`xHxzF;X3FmVZLuQdtk zc9CmW{WU7Op$b32d6j8EM1N7rbci{zB;#og@{VX?$>z-R9lnya7>N{GBi-gve{0+w z&Y34#canctqwW4wQLomwN7DmM)LzHi8%*;XOR!&uD%vNUf{Z z3Gru0j~b#8K6-fS&5J){WfoxKpiZwsW`(^Evj;M(!~kuf&Rjw=WJEWKRtg$}RTuf% z&(rw>?t-|hv^lDFs!TG2!(vn<$WxQI58P+dCOZb<<9h>=MuiGZKP%hGjw53icRGY| zg5{I{2kJ>XH_S{hX=0_2rn z|GI3yf7>(9Y`b50C+*jEyp#HQ88uS1j8`BkQ(9dmcB=9{qz7t8 zc^UhI;MWD+F9chK-vaxuU{8S?E?sxbp{pT9uS7;IK!SLyfa>?<)``Q*qbH*I$-YqQ z#=|S(Cla^~pMI{;(Gu)za*+2`Zgl;Zj-?gPd{33`Tbb&+bgWtN!jIH&Sz6xGr#mgP zhFXm+y|}T3S}||WV*i1kz;3>ojUUq!sHmPmJtx7;sL8f607r`Ap@dxFDes;wFD ziGh(U7}G1|Q)EZ^R2ds2cmapal9cg2HzXOo)S~8)>Pv2^43#h&&<|N^L5dVBB5q9^ zOt@Ht&WwPyIT9ai^YFa=q?M2apkf~?+dnN=*qZth8jJquKA9R?L`sv+4T-GM2DutY zOOm?nLy&JV^Qr=_V)ESs*A1|rO_lni4Rpw9e@K8623s)XTcuW_ThJrI`|A3HzFER= z^qju6_TGlRsr3(R99SD913s6FjtARg`R{<9gh|zqZ#e@o8q6h= ztb|0x#!B*aCSeNlEgkEMqzS?vde7K0d1cdWo60+Dv^QN;A&sS0CI3qKFID<1Zzg#W z^tBwa(+wIl`V!kBHCp+chBa1{ZQ*4hdM?yad}%RSm1fq{p%@LXlQ)>n6}{?}l;jFX zlU}o?WLC_otU8zh{pfEZb>RsR7JG(w(jdp|}(?WLw$iaVL?x(0stM-(PHmLp82-Cv~ zFr&GOIkjjzuhgch7h|%mz##i2jlvk;QbB;pZs?22Rs>IAXBstjEZZ!p1Aig^i1e** zqd@rnj%6vzQ=3NGJ>}!|U@vq|@MoUDME@_0i3PGOw5CQ_UENgbq8j|lmLLkV;91xj z6QXSmdk>g6Jacds?IFbh{&u4C*z|$!^;tqT-R5n-^%`GG%zmjw{b7~D8ESIIm$duJ z_78oCTov%T9kU{TNA3~$3qp2*24XbCv_AZ~bC3L@TIRfRg0Qs=HzM%l90x3ipf6G~ zG*{3oebStIP5`jA0z&N?3|egUrK z;uuyKEW+AJndxih$CZWF=5G7Zkrkod zjoF^d)@7Dsw3E5Su2fqrM(a)2B;vxbS8hf_YI`U?Q2a-Hzq+Wq@x%4b9=j{MX-qjlSK0Y{xY_X%sP5T4j33Uxo*Dc9Kz4l3L+J}X&; z`dT?=mr=_&dx?{3Y>S9vRl7``L{_aRJxd8yBqQ;M;@F9O5BW~`-X>JS*CW(Cv0gn zzWnNGdZ1%tNPfs)Ae0p(ZNd&q%nO>@hS_;|)Os`}oQU`TUN{ni?jR=PzhnFj^gN)F zs&xz$Uk{i`L4QZLdpZH3lK_vE2tkkRg|B|va@PkR-K;+Kwel+`ET3*YMY#Rs!tZ~- z{E7X{eUqSON#sm^1(FPWO0_`bq(LR>Id}&V(M?ZyGrYm*{+^KJJdiTM%19)BB7U2B zyK=+bT_3&gKkVDhFTS`VMy3FC94&u1wgdYTRzI*CD4rfB1nhUCoEYy+94^jFN7)V( zvy+W+Z7AnOxpj1Ia2JZ@K8!x3v%`e#m0~v-f!)wBJJ>mAKD{HPO|6Hman}~hb%=fy^V)zW&RJAWhzY%_)+$Z3 z6kR`!y-J`%`V(IyLcAun6B@4xHF2niyy+}U$yK+|b%AFjdlq>{l6AiMUGbTf$bjvF zZc@)_ALNWkqkvaI{YV73f{I%?agDnCG_AQ#e+B>PHJUab&2?^$o8V?cxOI3(fW1Tc z1%+Oy3)2Sz1o^$%lb=?|Aq1DsRBM2&A*e z$OVLAk1M~Rrp{M1uY1LlL(m3)AMUO;_>6kR>tl8I>^pj%Cujb_-^)D;*$rwoV*ht`*h@s6D0e|YJ%ZpIvy-rV7wac|mC{<1Yz#5rx3L_VQwmr5f; z7j|Zrw)+&f@fx$&(v+7HKPw{loT=-1Fy)%%Z*l~`*fSA&0SCBb}48u4LN;RF$I6 zlG$CT?>zmFZX49*E!e#kv0O()QHo+B>&KfWKkxzHHr=@M(=I`VY1Mxfef6%Z~~_(M#{E}A(2l;-mDSB@H(V1cX9jGs@YX_A8Bf*k1 zTGBTo=W2xISJBQ%b#qSXT!bk~(gI8!CRVJ93bI&+6&dCOsaLi8d9p+87;B9$?+nLA zxAzssOlswAy4!RobX~fxOKvN_i@atoTS>;#8nX1Z_}ao|L=hEyO}^s#LeK8iEzRzw znenAd$4~!yV~N1UyVafUv_@K7@SQ^>2wg9Tr{O<|IhDaV$YbF$aBsKU)6FGUIxUZR zOEKM?Tn9)u!PtVi1SebLcaTbeTJe}`vKV7S4+U`F!*6;tfbD<<*-)EIF;^%?S~fYB zU2=G|eM4luEf?;&IQR|oD-!8%%>U|Gov}x}E?qHkad+pXtBbl_rd33?ocy?bLvPsQ zyi#v$jqlcb^}CS*TBTK9<@N>P~B1o za0Q|?_@aT26cv#HUa|F9=m`g}bv$7{sJ31wKW4kmf2-l3+ICEF9cfzj<>SY94_~n? zy8N2ucTWr*S{7Xf4nZB9?gK3gs3D)upj+;O5<#hRYn*Bm2YrcB7bxkZbIRIk&t`K1 z92~X>=wx9jpkfVN`RKxu!PkIRs}&hO`JJk^k&T&mwA?yAu3s7a!}ddp0e$%=fr9>J zrR~ScWav8b+2-LE$D8D{;}2|GKb~>8GS}F#YM@u3IwRT#G@2G3A@Deb>4~GUv{kQI zjFspIuc3jo?^`0YeaY`=7my>zkC$)ZQ^$`B^MSVW0N&?^Mn=c^p_9>Q>~dL2Zl@a^ zWhPWtN$r2o$)jg!TM&IJ#K53!NFrKQeCI57|li1>Pp?jnv4E!NdPxSg=Wf zvI3(RxH^8T^G5rP8vIy$)~nv8?pRmqzrk~r{VMI%m>4m6EjQhbNzdj0j*Z`zQ(IGw)EKR4cgoV0p#D_XiX_4@nA z=g3iwGwKYdsp4Ep<#XwJ$OjT|L~ujQHdk|dECtVx62RP~PCX3Y?NWb9+HS_W6h?*d zd022LH;iFmBo!O9xG_5fJssogF|bF9(FT5r0B`=MM&dLMhPqSEU}1SvsMzFiwlBAZ zTSKm7bL6z@{>Rk%!uyY%`UZvj{{4j)lHc1Bo#E72&TC7;b~#!IztNiTMe`AV=-1_? zw~`E;4W7TX{4g6&jN`X?IoW`=D8GPT6}c-|T>zpCc@Oigu%b|9Z{@M? zQ(u&qa7}T@ENrI+P)kH5l~|l5m4WKb5Qj+wwJ5`0MC%1TWlmUX)QlveKf$-;w8%t6<$uCFKTz!v7qie4cn=0arUTr-}GrJf`HzJGe49ZmpHg4tW~`- zTuL^Tl1U|Xx}@3Rt6Id0J*^_4!Pgd`DAes8F&dOrM>-@|OC?=1@3~;|8QIy92wdi%y`rZmJp;;`>&eXs7zy?jB9@#3Uuzk91wQfLQszSp@8w{-2lafQwWq zM&Co%u7u0M9`Fd{t`OG(-1yj_6+R>3)>1f3^{}1EQfH?`N7*Iq`^rjQOdHFhSJ)~R zT#ZD@eco|Qa&`YZH;f(J(AvBAeHU)LvgHczNOykoQ1qe+e|vM%dip&!pUqWUP~pc-ka)XE?WAXf&BrC^m+2|E~VFXSchY=kYUZr>6j zNGS6b4w>Yml@9RT;9JIr+0^Ih zdSsP}0m(gN3z2;JO(KLmBV~mdZOHBPG2eN@M%-Fa?9ia{tkt})huM!O{_S&6~mftMD$>^*G zoq|9|h7veL(!EYaejz-wp8w@(FRdZSu;+g#3G*-4KW{O?D_H>?=>0eH`kB*zCi`)B z`i&VLd24VE_#N_z3C~M~Gqwtr)kMg|Uvc`+GrayrJpPx{f1TmgHX?hciedT zPcv{}xe#}}dge6T5@cL6)(XuDrGY$@hL_|jDzRzD%wcX!9(FdD#s*7cW6p4)6n3Ix zgw-NQ0_<$a;_U^dDa76j_vgaqDONcN^Ee4OLLmo!{vcl8zv`BqJv}>bSyj0XUcRxt zedFbW1D8*THhozabZ{dxz^5m&eUIPc{JpXYpqs`(d?3L)-2EUt+~1O{AQEO?}~*SZmZtvluqhKI=yWnGo+@VBau6Ir@WEr2zl)`WZ)hL zh_%70k3`!{-k8OiFlk#n-n8F{{*gA!*DP1S>A;L0Dr8&JC8ScEHwl=o?wm8An6m+` z*B0rB6rtfdj~36|dudzS#h>|%dgR>A+ARoP+qTV{!{e2)Z5QV75;li>2THxY^IK8d z%+F&!)7I7ompNXbeGvtf=ER-;+zXW0?5UVfIVp2ntS*r+X}wIc_o8?EkAdeoJMJyWD4=<9r?8c60iNtTs`& zU|4+fg(Oo%;Xrog`AgKe;`-P^j{5}J+cV*Rlb(}nvP>(b5qJw4;Pec~4%!eHaoACp zg{Cf~T0B0ivRQO1`f|%Po24$Z(PFh;yI{6FF*&Sq`oiY6zCugb>9A?d9_zJ34Rso% zmiq1-8~k(@t6QvIMOaxVs|XPnacFA<8LO_QX_yC?eUqM>u~#qXtW};|!hzj5Y!0<~ zJBM@|q)zP1ZbvAFUD{;!%P!QfThNLx8~tgor&XhD_FJPKV~XzFurZCD>s*U_Qe33jOoKK7R8v7q_)tdhaD8>IGHe&Oi_H z@^6*cK=H4Bj611!b=Z$%eytf3g)|fJ%=~<@?xe9`$k+L^aR8YmEE?tV6;3O*EsfD_1+4$s z_Ny;bBbKKnXqIj*1iVfcQss1n-EC1@{r1$*=WOkwRqSA}Xw5Cyec%W(f9>wRr~Jly zZ(GoH50K=<7PuAH%XK-NN>@_N)Zo&5w>4TN_Eue0TO$Gj2~{sO_LkzUdJaRLa6+x; zsB97ryE{<7NmyKN1gm)#)c?x{F#kpOgp|+l8r=^pdV?8zg2nEDD7?sAC}hIcx+X51 z3zDjKxu|dmGL66w6qWUlZF50?udSa8cALp^uDQS$8z%#H#6>0p>VTBCd_1L zqSQ^1R7t0mDN8b0#O%rfge77oB{Ph*b+#0Ed$MR4EJFBsHmK?A{5}!nN0oqzh=^fQ z(W{f^7SrP=>}vCh&L#wBrk7836jxf+s@q7AB7=3e0WZyL_9q%O{VyzawFJ8-@9&rtk;=v!@+~u93)z%Plo+A2@6R?8kbm7Oc$`gMgj*`W|U-S*hFFWIaw z4M>IZVl6@uDQo4W#lPJ(-#r3MaJ zoAnF(O4vhTTa)}7bbx8B;hw<|X_6WIx@JdSr%Wpxl9J9+@`g)ZodW=kWKJg7+5j=a zCcK~l!=#5sNQl3zSls4Kk=jSmScnvDRa+s?VR|n*B^I|IQoCW;Dq0WcHLErmm@N@l zsS%e!v?S6s;%99w;>0pE7*-;;;vf?FB!KW!FhHLo8-%~Q&nFvf&SIs=2B|u@Dv8%G z4?zc3pHG=t8_zn(b?r~ky{FoCNuj}AeJSmS^Xm&3jjG*{s^wySH|>zKc1oe4Lofjt zpOgL^>mkQ=4f^zLxfYevUga4ofmx_1vrsjxgxVs&*u!8`Go3hAWjXoG&ig^ZX$ z{yh8S6vyml$P?|*zdL+MeL5A1mm(2;MqtaCKq=G6kC7JgpW?!zI{<&ld9hsVQNqxj z4}Ycp1N2M+9>aVqqR>aS4LYMCb6bcn!DuN*EJ&!$2xMhrs4Y~_Ss)RKT6LO7g+)|~ zjtjNH35f~BYS~_xZkm{AmUhNA7VM~`_reNuA>0i8v#aMN@FT%QWa)J7lzooEZBHK$DVzu6v?OQWM9y)i>`|W^Lml# zPyL8l+~hU<^YF@D34dUi+Ib2B6nKz?SQ71Tv1}tLBdny8%=tLYe8TaFP*(Z~%EoCK zco46rZ$Md2Wv7`+SXoYPC){m|ltpaKAb(w@EVrdvwuP46Pw8^z6hI>8mBqRQPB7q7 zo(I}aV$f>zhur93eN(F$TJ{iJEnsUGg(TM7Y(*}Vb?cZBKq4XC~@(#U^PhY3Sj``c_GiR&*Vtkd%F4}~}W|11YEs6GdNfb7^VQ!KuaYvE6@+;iGaX;aH$-Q%M-DBLp zs*l}T0=x&u9aDexxzbm^dQ9E61StE*)SE6VZQ689{qB28?|%0&buQfJtPMtcY{?>n%zV)qRfI?Ous|;^V20o9w3Qwyl^r}#VRsSgXG)Y^`2g3wGGfZr z7%BwSRzn&|c={gP~8=6^>BaGrUr2 z9m{05E={GDZp~)KtkS*7(XDBJE^Y^Ozu(`{1Rtah@`Hm1Nq^_IrOD(Hc#N$shLzg# zhnqIF^gAxl#EQ$B)g2Lgw#iM5L;LQUn7C`-&`@SgxV3HFY=8gkx;BHuXK250zdsoq z86EO=btjijb#+aRB@$!wx-_}lnd*r|dr}Su03ahhDd+BUK2iR(ucgzSGKM{-HREw- zvSP`P-(kZJj++OXJaex848l zwaHMgu_@{#bFP3T(H&>&nCf&_vUyK^ygVhOCy7KjIz{V!ylNBW95e2p7~AvfJ-Ywf z;rE>c_oj9cAy=DUtllC9o0@Jv1C7V@F2cKUsLR2 z9)Iw_iyVDzDX~-slv~Z~>%|s!=zJEm~t1-EE>fBC!It7o2t*WlS=nfeE;j+S7s zS*azy1L1y{kfQ}uZ3#cdkg+#pnF3{ zAJ~)D;?@;#lHY5O<-D0rTPzo7=|c0+KVtqs*6*ky^M8gm@rz&h!a@ES+UEv{#M@K+Prdy(UloZ7jx~ITr6``U9uL zw~0MI%?4>2pZ7Nm-~n;?xZMX2?my7o)^_;t;nrRI_U)RTZr#3bPutFYdk-FH!&pu4 zJuuz2Ul`W|=w3_(Fy=FFa2!BtHnJHv?-x%U7YX5s1W!s}^#RBuqHUDmR7h8Yxu^s0 zGGJW*8xXsx75=Vv_$fLu+kta~_y-?iX#(io5Y|Ej^Ed{i+yppO6ZVZX5T0Am;x*twa3AA7!+n$c26CofiV?a4 zi|5-It*5zfao^*f;l9Itm-`(?>^bgPL>+$)WBEGwF75^HdF}_uJpU^9-`v}A0+_+F z+Jmv&%Y6i6dpR4$16YIyxhpUeUO_g1tGFwHFn=v~4I+U*${oX*;X3Ym?lo|ZM~NKX zt4e~XV4~qpb7i6>I-(~A?hL}ejYvUBbJJUhmDq?KPN+`eB5s0cO5!7a?r+@RNq_`N zh=fUmL`jUq;oh1gO(ca#re@MYTDgC4KZFl_h5&+yuCisBMr8kzulgjF3^Xl#G#O+~>J3kmY0rSxLspDzciaA#2GxvYu=p8*$qC6`7!Z z|70`Sg6Pz(0Vh}DN+sG8Tlx!zE$TZoB7}woohLpIkkjuy{*#mFC%2M!klV<=klV>S$sOcfwL*&EcBXFwz z82LE)1Y(r$CZ8hrkbB8}QkCLyD z$H?R4U&+6buaYOo*T|FPDe~{+>%e|{ntYRdi+medyS_udOTI^*CI12BkLSqqdkT=O`pi7+r)DD5PCE=yKjFIpQa8w{T%`WQEYH9U! z^;R(|R%2SY%14DaPP2<1r#hT8D9k!zOMop=9FHp}F#A{J|*{-?i z>Aka4dw0ximyb_vKXh(uZ(V_WhfpA2kgpaB z>}40p=-yqD>AkxYYs7Z!6Wg&yXvaQw(XH8iXz#A6xkGzqrw$#|?W_MJUoTLbtE1K- zwxpvdUoTLb6D}KsXAiK8W<#w9GzV(;LQOkH#p_bxS`f#mkdto|UUraOq#NnT9Hb*N zA&kr+VPq!679A2>G$FL;5WC1G=4STpk{zPIbrbdN(H*M)q?iy#@Q^ryn`-sc9Im_H zQg?r4?OuL?(BiAuMSVea;;FBy-U{vO%BqKGZG;D>s<(=vQTCN$YMNa&YYxm#9oQ}0 z@2lOjw`AKp#cP*%9TKm?gvsWL>~pS|7q7y+$Q4Ir2lwsWcR;seW_oV=z{~;mNj)^X zfAPcW`=kdS*&Hy8pn;EJQ5&ymIuQ`1j>l^6V?^nmsd=hX@}g>K<0F*)zRM zc*rz^#~WzL7%l1W^ua0F*wmgqQ$hv1@(Pij^aA`z0(!EIyKyI_1Z69y_V1s<3fXh% zjwycCA%68Ce)9|_Gphl=c1E&(_deN%nO%FPBpas=Da6`H*6yB>EWyv(12Y1dp)M_b zU!_I{@xjVx^;ESZw9|DhQlcV4_t5sRJL@6g@0FtLOBpLKy%dkjcG1Skc1+J6oKlGI zl3s;BNa!CAvZm3O$u46}n-!Y2_Yi+%MzK$5mt=1DKB3x6va$oLaR<>jk+5Vx8nzuj zsE2GH?Qt!AU!^{VdG*tZFBOJM{3H9IW`)_UKy9R5yb4P(H+E7H%3^s^%UtVlmA($9+R&x-W3BK@pLKP%GDiuAK0{j5koC(_S}^mAhS zb0YnmNIxgi7dM`~$Q<%Hk$z63pA+flMEW_AeomyH6Y1wf`gxIlUZkHF>E}iId6B-j zDdqEG`}1P^^CJDcNIx&q&x`c)BK^EbKQGeHi}X80`W+(u4v~I`NWVj*-yybN+^+H+ zBK;1LeuqfEL!{p!((e%IcZl@Gtt#Ik((e-KcZu|e#m~d-@=d$urXWup7A~6@7ndJq z7uBZ95>*|p+{w2H4_?VGbhc+R0_)CZhFEKGWv#)L)fU%b>FA+3#vt&4bxk%i%&LMb zYZ|WN!^5m;_{^G;Z5Nh8Hrw7I*?Gwfer74rrJYNc>LB^G?me_;dJcSU&T#PXzSbSn zd%-;R($6~jx$dvKA-~i<8+YzIG*|z)o_$e$UN-mtRCl(qSsmFOzt1&xj4|fTyu=VN z3n2~=0_Mer0O2h_uz8I`LPB^AFG*0+Mc$~}s@rrEt+MH=O{270rEWJ(SA0nJ1FLSN z%Bqw$%a`_prEazgQk5_M5@}^s;zLm-KDfXCnVILlJomAU0Vn&6?#$eoGiT16IcH|> zxp(GSb^21TDf8ML#@u$yQ`X!yZV+f+x|VKm z!rZ>W33Gc#r*#UU!`IR2@O5-Ld>x&RtsR{vB)~cR9i0w;N2kNz(djtY(P_c~oTI;^ z({Z$;)Ac|{rwK=Uhi^xRZ%4<*cDQ_o>wGQO$us6(H2)>@UpD_0^Dmj-3-m>aDp684_#owCQ7L@u#(soS&4g4bMd!3CFGaL$6swwzK&Of4@>R-HZl z2UF~Up?&pgUP9G&DZlo#{HE`Ij}^h?mo8lF)$Qb7>vIZV-HWlAa7H$kk`nfmMy^fR zGn872AL43@mq#&AS?$Ev#^;G&h%XSo7+)m*Qv4d_w6zs$jbRWL7i++3@XyhtUAwz` zqL#DYzkDfL&RY4EXw#+By{}>;u??}*Tx0O@sQ1&Oc>lrf)@Z?zgZuIIM!Q^A@sg}S zXK5d`U3%$4vVtXD`tv{?y#plh}$xr%RPpvt+iSrC5H}Vcpq<1*ZoK&3SAh zuVd|yRpxK7x?I7^B-nRcSeov`ANg>|h4B#|erRDF|4+>VYhkIT$A`ajVeJtYa*&I7 zSE_6G_Z$4Z<5a2cfWLP(*;3tkYkdt?s&%w)Dwe3-(0l|7(+O-qFJr@b9c#@Gu^0(& z1Ad8o`8_G$r5v^tS%*usT)sMSFY}`)?1f8!6t092~ZI4ukeEx?WxM=Q+liq{fvGk@*EPmF)!|^i~9*U1zcrbq2!UJ)Sh5KWjmDCmOiJznH{jucvSbQA+Q2adp z!T1P%S9}~!&-K$^FZ ztKMLm7^!L`yNnw(Ce_kTjmvfMVthBMX$Gn1nuKOfax!mu8iVR$^-3P@B5?cAO{eGb z>yS*rseK&Q>a6IcCrO#a8_Y$r(xbuB>jr9gF*Cm}v-!Q<=J%~&=4bP=_T-EnFTHZ^ z@~hAv))L|kbH3d-N565g&C%xHQv2T<#wIjrHS}sUJisjTvgzDg%+2qjd-|4QknbsP z!m+tf+rl*;spr4RNmK64=!aaBhz&(=5c@Nq;z}wd=G(PFz7rTS_#ysP?533;#Q)Xs zfce6|o&W6JHGDOC6UY*L(+HBhYg&l(KFC{~ccYKuWl_B8V^Pok)c=6*oV7Z9lT){E z3=a`{CvJW7KiZo44G#_vK;@ABpBSpI(|&x5lbQj&hc~W9s}=eZ8i%jtD>eEC^QjYa z4|n>8e;}^tU#PyRU%qbS3u@>=dM^93^jv>uA@0^4Qgl_g)N^pjOOvHk_8=*exw}>O zNO&rE$`OA z(Bt*+-bq-VD$O_v4K5z2NYW=hRyig(8uIktOUk|te8{(tvAT~wyN0{YHQ@MUa|=f= zxf$AKWsRU$bQN=w(m3+9-mq~vU(@Z~SL0QF%%?`brZD}Z?uqQtY@5EBn&~w+JB#xJ zY5o+fuYgn!W$Dh`y_mTh_YJi4Q`c9xuO(H(Uj$UcpVCt|!kxn5&&VGL+zV6>U(L2A zl%Vv0q)?+r5PXm>%2o1qe@4v^ zG!NP{MW$6s5k5z;^Q+;v)_97ukk$AUFsWpSC!tF-Oyb8a=tH~U`XIj1MX0#0tIkhy1CAsmsl8fEutt$+a+F;g z?ng>px}L%YNx!DNYB-cmZf$^2`vWcHaH=f6q`qR!Z8^pV#nL}8+;CV7n_<+waq*VJ z?r1_wQ?w=Zrm-1)B=~X~?X5NP)B2N?32jMIR0f*v>9ccDt!WC1;@=XgWNS_R0)MR+ z#pPR}_XL!*lhhIADuvRbMbO24UI;1OmCj2VzfhP^Bl?oNYV{UkEw38TmXtK7<b=vc%<={nFbo9ZwWrr!!%XQL0a!P=^b+OCuRGKb<0Zjk*|@jj5Vp*#fr;{Pod`W ztfEo$E6+8@Eu#hOseVj&q?q-*pIeeSMmRz%h!uZO;Lu^eLNTCBB9) z8n-lhS});S&pKE~mqDgz&*Hh8cd8pHmJVKH{eC8dn4{5+-YPkRWl26`SD20pBulUh6oGj2@Sj3cOTlE-NKUR8l(URt;aM z*DP;f_}{HJu0v^+@y=m!_(E-lFVF&B!=+wjb)ChNp~}G-z4|8s;q-L*r+G&+scseG zTadKH%7ixhP-*T*-8N2Hop8pdrg%|?RGOp1bI-%7{3o>HIx?>wQ`k5BwsPsWKDd63 z{64ib;3^8q_Yfun(h-AKx`Q`NJO>9llxo!Xn-tvuQlqrB!TWAR5XUkt5B*<6$5_+K zyvlH6^wKt4PzFj&+}vR^(JkJt-1aZ<>9z}60jRkF4Sr3U$3f1J@}M&{lV$m+ z7RafrwZb@)h9})iSQdXMl}$A~x5?c~3_eBwxl-j91*lJOywZ*5 z+K3wB3MFMc5)1NeaQF0CYG&}|3v@e;>0ZV~Ranv8%X&t}o#$16F5s2MwnB*wWvYeQ z@dhjfa_q*)6b7CzWqrwy3|LFtdJLbu@mMj&AXh0mGErp6uc|W1JoPfFGV+qEwS7VR z5_oc(1B43$S6wK-cyYEXcWH&HYR`$~H2^j0U&BWG70k}#?bT}K+}P@Lb2M_Y`C(o# zX&k5a+IUpJtsqyHudje6^rza3_?4q-Vrd!_O?sNi&O5B`{miG(F3mmV{z%4%qEX5< zp?HoNYx7wDYK2_o`Ia0%PX>7r-fWFl*3aOg+)+XbG*?PjA>6a{1 zAk2IE>&=RP2G&&zDG*YEzDC(CDxXAh;$g-U?jpfecm9#;3gY|ALtKgL6h1u5D zd(%c)^{HGao4kQmA7tdCfc`4d3q^%Ffhs~_9?QBtS828{bD5*uR%?x|x@1=_OXetu z3bUPbILR#psFGNfDy;%-#J)Riu9TfB1X*%bs#u{4Ao~r^NSH!isNXm=np}lKxzN3V z$R%=%9%`IW4~}CFvN1>Fgw2R<#aLN~Qi{1!oGR#QIR)4oOk;9SXMtx0@JW*9xr$$9 z&FRrA4W5V0-=|@-@nXp0)NIM$kGo=yO|DzJ-Uo5onYs*o58;~GZ`TIChjDW_MQ=HKpjP1KLCZ>T>qNa4&eVGptU8wQ0nXN2 z16G}{*TOF5bzonQYi0lN2I3vK#q0^~wdD;CA@61J}duuwAr!H*Ps6^zDJ7y|_nc*^}(S+=pAkKCCY2=*F$(t^Tkb6|QNcYs~U z$HDai?hyO4Pk`wp?g+cqPC@M%oOaW_2o>jWlR1q~CoSsq#r2%W_g%Pk8TTls@O>Yg ze~jDCDSUqlpWehxD zZQvBRhoXI)QP383a2g!{BgKhshoV)S)^?b^yGPiQ+0LnLPZ957ALkZMZ+jZZqwMkA z$SH2mM4LFt?O944V>f6wC%Qcc%=7H$+{npp$H{$xeV*Mq;SD^e*tyxwId7+lpJC5t zH`?|`riV*S4>y_~a#k{KmFeM9)4+YEfsdL7>MTl0qBL-ZX`s%fl*Y`#t)su@B2UuB z&8CeT=&u&|J0G{nv~iJX<71|cYw1CqDk^Q9XWH0l+Sp)vSTe2SFT-(-rd=h|tCDHd z6Q)sfO`}>&qhiyj`>AaUqlb1?Hkn2(H(gq7x}@FIyO03sQjO`-PNaPwBZc&5lIhPh zdVfDBVoHA=H2rBq<`0vib1+w!=1e!u(dqL?DJ#u+#58A?X-=(aPMvAaeWp3}ra7}s zb0(YS%rTwWXL|A-c2U0ymC}}#rY*ZoTfV~y2(L4O=rq$-)0`cqIeR(B@=xIA4{%P4 z_Lvr}H!W%~Et+ClRASHOyR`T{v}l)UPRTTiIRq_Gy+mhEF2y*mR*Z9vjmxzqSRuPhCxkSUE}Pced>qlYzm9ku>=hW@xr z-)h`?&ba@)ja$dz_y_cAA0x;2;Ki>Qp)^AD(dJpv-_hE+(a)gr;ph|SJHzRwCHOW8 zTBcIB&Y0M0BkV=6|Ae0DWAyq3`|WF3Wyv3iYD#Yp50(Cc>u0R7YD(vf~F?rXCagv8YshslPF_=Meu5*;-8&R|HQx`bRXOD2{x4?V}ei!>4u@WaL zN2MB02-OLoZ*uPEJDm3Ux18wtpQSIOcx-~XWZhnUdUv}yH1ihR2EKN5Sl>y^)@%M1SdST=``c#?{gGz#4 zXK_e1$MTJ<MfH| zn?T!ld}iTElU0t3_CQ5Bv1-I<2e+%a2<$H6v#;5WJCYZ@KQjB7r4-7K+98?(WI|s; zuj4P@Ys$*z`Bw?=N(vUaF$rMq_P+#|5_E@T8FfJW@3M^s%8_DnBmNT)Uvp#i%VG(* z4a)D<#vb9msQxOEu3&o%@V*7we`jrR=@w5(w8@M~NSOjr?XOVA-HXa}N~kG>@Z?jz za{P=MKl4`AtK64&{=qG3_U}5JO@;m6`ifh1Q_8(>aCTTx;Jz4j@048sYA=b|R2_|~ z+0kUuSAGW((nYb)7ryI!$!YXJr~AUQj}&`TMDEw*tLNQ!vid$&-`Wo7o={&4K_B@1 z_nKCR`nJp!a_#%U>KdgoBmJC2BoLLDl9bRF3ABqP5V}v(=QyWwvMwj(9E_zak1O~I zjHQVu`tEy>FJm45`bn`-gcANsSKq(tSy}q8*!xtCxsJPLIZxwF^UrFr`qL0eyjq(b zgr~eb-bp5?S7V^lQ-NX6Qo6Y^WKu&mM)_u2$@^qqe}PR5tXaA>Cgk?q2nl_VDxE9i zYjHuy^R%Z*Wx#UdyIPmYXC0xRu+i>pp36%6wT?ICBrNUu{t0=8G27;lRSrj&=Q8M+ zBBSE-tsN6GfGY82wFi*$*gh7VW572SG-Vj!b5g7%8=aJ*{gNrpyMC?KPi15lvHnd- zfz{w~2}9C?P(xm3fJnV-91&mY%M3}Mqa@%BbY4M&1mb>Pn@Lc!q4Ei6x$fA~4q_M(3CxgMZi_Y;gelS;n+ z^~0U(INQ?am@g~rLVKbWZx%2js||`b(<1o|TubpA;ZOrSX@)ALx0v6GznJs}+9=p5 vP&|_ssjO1OVT~Kj*zP7#4?PptSk0cY|N8k)|6BK8y!F;%gjpNWvzBBy51dLY9>wOB`~Mgb=bJ2_bCACgdQ=?y`Jg!;)i(gJ=G~s_v0S z!esaPp6@TIyZWeltLnY#t+(E45lRTL;iM+wz`*Fb6OX4qLg?NH@zl3!W&ePAxn(t> z)oXFTX4UY9OWyZ;*pyYT*_gy??0VN+8o-yrTFgg%1≤gS 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

X*Z`O$j36P^=p+8tIZ#B$0JU!)9i35SLug3 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 ZjE|EYKE{{aYUV^#nF literal 0 HcmV?d00001 diff --git a/shared/src/commonMain/composeResources/font/productsans_italic.ttf b/shared/src/commonMain/composeResources/font/productsans_italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5b44037be5907cc01dcbfc07f300ef417127c59e GIT binary patch literal 95744 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

G+NYd zMg4tNyTjx+g?7;zdEjrgyAI>z@W8jP;TtBIy=aQby{ugm;5B>zJT^b6-30kX0bWy= z)Q*j_{sY=E{VmOJA8*I?cdAM50YH(@u%*~?Nq^&=1$-;-+xV_`4beZKQVTLQtS?V) zSWtSE!n+enq&S|sw(TR|tbZ7vkJMB1r<*==d+TR!Yx?x<@TOTj>COKHHC}&0E;vl$ zSlcSsmDwZNvOrePi^|Hxd3&Komc{U_=zBRc4-kb4UY1N@87j(`C<`TL5hOELHP`X7 zxBB&=mY$Zn1WMnwHcW+@HQq{ks3py+-+sqR;e77gN1dID{@9^atP0LnKG3*)+)sBl zPkHI{Zq&l%o?(S?IL+1Q^xzL(f5iC%J{;$de=6`*rC089fUn&4bLq8}?GEGnIVHX3 z_Q(T&cRTRM#|rI2UU?;J7doe2TM2I;xe4vKUW+01>Rc4w*h1$ z>iIf4uPUr-@DQH)7t5Tq;ytaxt8mg9YT->Y)G>S^oU9(2OPM@+gTbx#x1-*=-)pFF zC95ks8&kvT&09CCsyf!UFu%JmPCsl})7CQ69T0?!AegI~gNgoWqqp8u7awjr{k|Zp zU)%0?!R1b`w{oAG*Zl}jLoSDaa|IcMyf7`$*==@;&;t1XCUkPz?pX2~!HX-~94@r! z=rRZX&Ng%mp%{ZR&~2_{ZIGX(nRCE-L-=uVY;>Z7N^K_0-XfftbF}$I$$a)S*SAYv~4zWQOS%=!NhO}F3&x&Q; z*^&5WWU0!6)tPK{(47q`=gTBcz}h+E1uV3I6L~eu%2zQ;E2vnqmPLAnN*A^q!3$kd z@|8)Rkd(m%`*cP=v})u$NBF|Qa8ErdtTJ(_!m6=)m3g%{yS{VM8rqwVk0)47%r%tS zLk*7P8t3U3&MLhU#?|h|P}?*slu0L&JtgH=YISWQJKid@)g5sX8TcFg?CDsER9OCgfCcTJYL_Za+$&7b-J@< zX=R(k15%s1237E{xA|@n4jmi*Uk<0GQRQQ8S^#HlkyDwcaE`I5tN!zR1T%@hVLjqBSa`lx|-EixJsb(&q5LN^N8c((>2ZR4uomV|Pex zbV|aaNqo^RrF%-@K=v3M5A;6EJ+t*>oz`JiRG_|q%sZsT%)jnoib(TsS>a*kPzH2% zM*8%ftRx6#hP~s7cTbYiO}B@#{<^H+Z!e?sb}rzBoDJLuw(i0cH_S&Vs{Q4f zx-+USP!94HO;yFU&aJVOw{gOMQN?s!ZD4Tka08Ob)gwDQ>eALbe4X{~elFDZNRr-N z)9Y;R2|M+xbx~(ScU5X(B7OOJZN9dNrj{M3`?Kc0=&Daw^{hh*JK}2!nt8m>YO>zv zI(0pxC!JW!bt(-tRrr~Ob%UTQbt-sNFmT#TNj%Um@o06-;FN>kqUW#?E884KQzi@1 z8bk2!Y(vLZOKqBB1}|&VbPgLK!C^LnkBv@DNH(IWOKQW$_P6G)Mcqx}Ht@(8ed_k4Ft9B{A ziHJ5@)8Aaz*66Hc)kp3oRMI2s6h(%`tgc}6lZvv; zTE=F)mF{b# zzbon_{JokAV$G^NEdQoatL8NbAlSRK(h?(E8sIsKRD``Wnn0-n&;Ft=K*6ek1XX=o zRaAP1^rk%@*Lz)Nt5KyXvv}t5M(L|9;pz_GV~??FOO?YDFq+MR*srJW6<#rgebMH9 z^cDD%ZTJ?t>U2dbNoF6M*T`A& zb0#b$U4#FuQEVqcV>4T_S4*<60*`c@{G@ZT+NyA6m)#e60`uD8_V#l zoSO=o@Kc;-_$mzKy~aT`X+6N@*Rt{*{Qs+S<-Gt)_%+w7P{dgC*-P(}<|af;Sfu4`AZEccx6T1<}M=k()ezi$EgVzjCjnI4yNgSZC{T2^Q1i92Q*-(*gMs z;yuf-&d}pa^J4PFVc`YchYNCIu&R8;d1cNiU(0bDldszgJlNQ>3~hyc5tjS$@psU@ zs64QB#qe~N4BSj7&^0tC96@)MDec(i*E)yoU14j;Z7kTliwfxjjC!C^ z40WxoWv|-sY(d1RRH_QE+C;-GrZTrTSTKizv6wAbqAAN8OZ+pw#H*kw=T-eV^NZ@4Jv&C`_TOhrw!|e`=7!Xs9 zVaIs2uhUiueJS+0J_r_*3klY0a-tv?@g50Q9bko5@@fGVlM4yf>KO@E_n6JgV6_3( zO1Y5c#pHs+g5ANM+nvUiWmsp~ofY$9a`DxITy*v=Lt7yi+co)F#5afDv zv?-;JGCU?hv*Y}>TY(1L4_ausi`5A-@=*xokQGw&Lze|Fe#vJ=fEkc1$zQXNK zU4eH0QoyIxpkdJ?J@kxy;^TYb7TqoS4|oTwVe`(kF0Gpt zj*d)z@S9kjtWZg!4?V!w#rT>foyb2{z_YFb0V(jz)-2CjC&mAGC3H&o5%_`UpXkJ0 z66fkFh_9GSIx{cC*8}LNcmQ!af6E)iRG_sm#Jq`p=N8O=YK_wn!Au%S}Ninq~~1zQ8sO5X`3y@j?E zoVb~3xn}m7bJ&tIViB-4{;weG`#JoJj{sI}b-a7UeB~MMx}6h9X}+VJ0Yoyb1YoxC z-kW5^SVpbHf{F3v_umRQB_h*V45tbqi%^^S*Giq}Eu!;GOOE0z{M?848u&YI$dPca zmtOS4K%@*F(=qJ(M-FnCZz#z8I|W|!yW}Y(L#ZFW=h?i7cL1rI>3RI`Z?5OzCa^I% z%tBomS~*lJa+GWy`7-wIJn{yOd>$8ECTT)Tg^ESY)CTGv&p&&nCd3&3oG+uO(Ly^U zr!fBu(41*dvqR(K+Dut!SF$w2=EiivuUV}dy#d&e-^Xtk@Od|%Ggir2OqGAGFlU8I zuEoL#RY3mEt`B1UHSu=q3hjz(|B#60B73%$N`xfu7(ZTz2mKg4j2|%s8^78puVT=2 zSOq`Vyr0dNvG)|wj=@7aOoO#k>GAJE(N2R=@^)v=oB5ga4@>Y6M=be%m|vVhR5JEB z)8G{F^)ZfT3EtjGiKa4v()H8$_5{XOrL!yIav2u+5Oh~LA7zq@>vwlA^Tdy1~$gx@McM3q4ZHzf*>mhx-TV zbneZufxYi%SYH-=>_%VsAD9oym*45+-+9UVQHq1{&gh}G+4}M78@s6|+`pO*%Ux%(vpT3W(U)f0|?*tvBj;%?qnlI5$tc_Wv;VCh%=tRUYuWPn#`S`z~4g z>P^;ad5gDrOYFp1oWxDsG*03sQQSCnoTe>>l&ytQpcGmPbjmtVplk(7Vc41JFr_f; zl(uw$VLD8KP8pa2g~;Fk-us><%TCkI_xpXnFRlD!S?`^@oO|xMXZau2e`Z4#u0?C; zU5$QsT(%+Cp0nvX_yoI$(GpJzw_-dvN9)5>H@9Q;tm6pEOKdG5zZ30b^u&{JfzvyK zac;AYG+dM?s{4mC>Bpr0nVov3JDN6%#b9fws()lsvLggyoSq+Rc8%`H&H{dh#X}Km z42A~4keW+w#G3&1idv7Pya0;!!v%AM=L+|eHRwEVr0KyyDO=cBey$3s=WkEZEMb~4 zyi**ekUcbZ)K6&MYaF9twV3QBrLPoIFGXP?MmGOyemL9h<#16A5S1j_MJo>lu1$j- zsBXm2#u>49$&DdrcJvCCTiCrJ5L+g z-pg$H!WjUWfUl^}fZ6}4RlfFo$!XEOm(jxcQjSMy>#oG3tka_eQ!-7ySl! z(KjD($Ct+CH0{lYz0p%!=#jU++HL;bnzU{4@`}^Et3BN*CvR`c^0>!dSe^P|}3kxwnt5>LLF%Vu`~)^pH8 zJ?@L`*VTQ4Y(6OUEu6HvD8&%&T(5&sedH~bzLCkl^1iuHh=tN~qHp`x_Ko9Jv2pB_ z#!-$5?d?-Vinyy6XS{W>bv&1w(ZU8t$1%idrP9_&6Yc_JW34?|qD5^8j26z5(~=Eg zq+jKuv_?Q{ojtjh7MTF(2;EbQ8Eb&jDuPzco&#D|hQqa&+Mj6r?oJxNo7(0rrMgPn z>X%Yd7_aQSU`K=6Oal0+fv8U5_IN%zD;hxUK^s7I0j^TsN4`)SUomu0_tG%Dipq@w zE_NFZZGWl2UcuEM44Qv$W%>7G*b^;uOor%~B-=~LwFa<@j|EMp0tML^!{*W*l1kP&eTPQ}&CK8-_UV(rw9II*q^5RjfUR!&c$z${Hg5BdP%9wLb9L zI{QB3wfif4O(q*TuVso>KjpRbdR`O8z-tsYErM}1({c6Fadp{kCA)O;%(4w+!*NBR zEk#wUjM{xQqn4BL)}k9GuW%H$)f5)0`vx)36w6=}jkz+|OWwMY*K24Ii>0PyTe{Sy zUJ`XnEe5ItQT2;jJyzQX{b!Yrk(#)EMbv;FNVvEh`#^<%ZXTkJl09ouZ#s}Pr(A=I z;E+KPNE7GL>YSV_4e4;5&B1{ZbVjWsDB~nRFBro{zM0K!7nNCY+Yy7d9na9Sy17<9Z_nC%M(7ZwwK8{g{?b))jjF}UmYI*ygWY+pRSVTW`MiDh_v1C3Nz zWBUT%a9Y&2!)QVGzC)sgc;5h{1>5U--wuz9puQbOOE?1tcCOvSrG9;NmDUu?H)2r>p(aL*#}!BA zQw5LQV0sjzr>I+{H-)}QcWMF%6yY%++hvNYF(15tRNqJcn7%*C^?l(t=qI#uF&I|0 zvN83mT)2us7K2S1F~k+MtfHOsz9W8&>RA|Ps_*}W>-*HB%b%!8vv?Q3#zmVJ#2g<+ z4IK=BCA_5aM8^Ly_@O8^Wh>3GXc2q{A;H4hS=a&#lxOh)9LSv=KN+e8_5Q<5K;1n2 zcN)Y%l5p>|Z10V%1FSS3*pcp#96QXRLjuLA%trW?bJzMUgCQQ*b$ z!;%(X%joq<_enZFvFb%XwWe5MiCRxjibnz;u^1jQ0W%w=W@%s{Jj8;#YGp$WEnz`e zM&H;Y~r2A2Y>E--F+(%G12<^NA z_f>Rg+!d4WsxUT-Rh4MnihCHXDnG8?TF;^sDXkfKrOWXS^uB6ZWIJrFbWE!6IxVkq zdKYx4ntoxQy-MmA?h4WtHmu)g`F(3>O-lND^<8VmAL}5o5)~8TccDYI47fzWW`N28kqTu040&DesnOo?k8)2Oy`lY;3#5(oV81J{py&hbpcX530g`IrS*#{$@IkD zedW~HONTeT!4YpX@nlkyFK)MZJQlo=yizLv{G|e)lIQ3nc-|?^PVMp1XIU&1W&%9R z@=e(qzJ;%0@>|a9A}j-GoSsEHTutlNFwb#dYxpD5+Q%uJt}MmuK|?47tJG|y<|K^2 zD|yZp=FSVxWR{`sIGo8+eWaAn(fz3&$J4r{Jj=VHIy(McGVdzIa!pqguN(kD)a=N% z=PBfP`@UFbe`iaY=3b?zB6$mV{#wsKXf^rD?O63cCt&4v1k`OLsGZaEq z#lmv4!jge!dPqH@h@e#g;A=v2omSo<9-52DT-qPf9O!g4bv5~dG(0!Z;fi&~{S+wh zb-sTy1k9`9xs6YHhR_-Eyg{F9%?Ee4)397aLwiemki!O+pXyA)AW|oX=yoK^PwU0H z8*gN1$9ifzfQ+Sf3fOxFxZP%8R28Nt$IZ-cQ{$n>eSd_#2l#flkJ&`TlWu-rw$mD- zXGh?r!rp`XWZNX&2(Yh?!M=p^BkrT;8PJ29p4#vmEc`TsgsqM2OE}Ne({mo~yNusg z&+m)1Xq)|@M-8D5t~|Py9!>-luao)$4HOkaLhF=N4aihYHBx5{I_DZxPVg}i=EPm( zM(9`dg5?20QD3iwzlYU$%Tf%vV$P`nN{A+M_=X+3$nK`Po-4$CXV0oo8VUEF!@YO% zdjaNwVg&H`Q1Ac-pb0!QR3~!+?2X(}x%r>@%|v*OxRu-_eiObW(*qU&y_MkPVz+`S zKe82b9379LfBmlu#Tg0NN}Nu2ShYpBP27qEbSKc#lp3qo?;YHP_M8^?3%^8rE`m|< zVJRJ=k^+G<k zejia4z&*1dilTw`86q5pX&lw*a;b(rW2k36mVm+NxA^NP>-t(PL8HNMX79yahWa`~ zq_^8pS5GgL4+qHe1Q7SYzBq-stzgn5SMEjT?Dg1ZP@{;F#Kwy=;oZw;cg%brKJouA z>LlF@EEA3`z6Q(W-c>9Uv~D-P^M2U&dCm)JfFkF`Ex6Qf7upP6pGTKeCm~dWFFC1r zS+oU%HmA?GB3(1?0k?Ps`^YJMgsb(o+vuM0ZRmZdql5KFW4@uky^~3P8u6`zKUUOZ z6xVuH{|`7Qpz|Zp*~$E{%}J(Tu0XH|fzJ>Fa|=E?&;Vzw9*ci1F}APKd%w-@ZL7H# zrzQvoe($=5%J&i-yEnD=UNzJ{`CipU?!~$OKgB0-ipxJyX>;h)a9F7xTv_=X^#nCR z_&m7s>{?H-)L-7_zVXVv=x+lQ_Xk)0z}l<>CvMQ+@1ein1YLNb=qMrL>>3O~=+pQ( zLh0s_k3f+ zSU(I;1nY{%Lv_)0g}uJ^b2t5*pn3B5Y0N#oKMpxWV|3sNr$vS3iZOJl=d372hlr>> z$KP<_wRe%z2b45senwQ#HB4!t_77+^SDuNsF0Xu_u})0Eos zM%otgm35!PcZKg4DxYI4{tImc??F}q=M?pNcy%bLWDi<}Fhayx6yX2~X})-W`A=lT zRGz|_;$AaaC@zBRB2P&gy#S z@b>V{JGG?o;AN8?2S=MO{Pyyh$^7MGP2K92k;}VA&+f~syL&XPV+XrN&g{vnyU6v^ zN3`WV>S*t-y4{C$mry z=^n5hG3^6a*^~qme}pu)Bgb5!Y@1E1w~S8DPPdHZ!*!Y2ysm5a@ow+z-GQEco#v=j zSSkN8)Ht%0mK*UG##@M`{N%v=Nc3*B1GQ>F{|{K38N^edByLFnKx-60i;5*D=Pc&u zr9hn3?pr6OGq)zDnU=f8o_CWow|_Vh>fMv;Ti5}V`0k*y{9D6&i7OR%_AcCU+14}L zn}WU5Ir1*!jSZdKugK<5r#LlL>TTcB8Q0x7qqL;^wxy=tSnAJCpX%*AJU!-Bf-5-2 z$*AJi)%|#5jFXG)#}wm)Sfow&6Vg>nLlUF4`|%6{6v2b*Z8%FW)`r7k4Z?IU7q8-b z`6T65rl>PsKwKR8Fx?ButNy_D`c7^?DHOvAYUM1TqF+s14`NV~1hF#IlDU`eM6bc@ z;be%}^Tko_BTop<fOO{VXI3N`7Fu&d@|A~Y#s8Qu{~-D3&&e-Q72{Rq z;4d*@ax4nl4JmrLi)AxZ5?H7$Dg`$&?8a1Nh5$Ug(I)t0YN{s`9GoAyLr-oqmjCpL zw&vz-S9Q>msK9g%pcrPOJ)Sd#H|@#g4s9=JZX2I|Z}a6n{U@hdr#-pJY`B<2MfR<& zk*yE7gMS$f^+d{?M>IPK|%5)$l0aowH4QCo(5upSV5eOpmi z-4ck^ncU_cXT+hi$Fly+kU^LN#%;t>`~R%I~p1qyus;R#Wt(O>bHsRnA^9h zqkk*o7rt&WrZjKf$SWqGTms_8bP~_gU7Ll6zi`cjnc4jtM~E3nd2M7EJd#+4v_7~M zJ7W{W9J7@?anvjow8)E^lxZc1#~)LIX; z^~rZdhr8Pv$bvXwN=`0}Ouysoh_ra_VEs)&mGS ze&Pw`ub)>w_Z*%<_ciEeGvWgei|=*`u8Y*0l4&$_)HUPryb>eJqV73hZx;!BO9e=aZ!v<2rej z$RQ1EjjXN~zsR`_gUb+4!JBlo!$Hb1H^{``oNvm})rvZ`&XL>!OVEp|=n$#O!3+?TC5P zLy7StL1mpY)Z%NJ=#EJ88nrdhN5sTc=0XSoYv&^vM;~~{EI3Q1I(gEBa@Uu>q_Z zn$Jz^1iDMcPzjWgTO~~EWH=dg&%FY3 z<-l0cmN`p$y2w{&iq4a~o8;i=Pe=$Ee5B+ML&wjcO9tax7Dh>zadb1W_}jK!nTigk z8*Sl~y9s5LCryE_o!R2C9hqsR(l(OoKfbkT+M6A1j&#OtfdUFXb;N> z!J(5@wBdypXx%^LF7R9o=Py_?J?WB1he#$Sq}dos+AhVVr?cv`R=5u3fn@bqt$8ex z-Q5!m^-gEqqx)!;nVj0))D=%|PJ7z-Tr(cYrnYwl^zMkm9#=j3h&tK3J(Zss@3igm z&P{~#373k50)>P#+`B8?e|%fJ>S2O-g2F@=<=E{mNF|&l{uyJ8+7?*12p+6jw>I?Q zJjO2xnP>B(Y*Lap9|O->Kpr_j8}Y+L)#%|w@$2?&T>)i^jvoqmc_wBjI;OHNllcn0 zrG5AENMzR3nBUUoaS}!OLY<>N)b2Ns50>{gZ{F9|G2Ie5(7E;8p42z_Tspv)|AV&V z>HgDZRN=%A2z}H@Tv9O+Vn%*0om0q=gj)6s!OAG5`BTT2Buvm7~C^%1t9g}#faHyJ8H1|`>rshS}ybEC(`ZvwhejxBNN+`{hOz! z-tY#C$CNH6`==6}S7eiOL)nv;(PJjg82Cq=>>T@l$GO>CgLCtz7s9!L4CeYa&I(-L z78a0y!aT)Lx4lreMG}U#z3NR}jhUe9-pK5QkloZ?hzrgGG=}@4xG%M|Bs?(^j;ewn z#VFPIhy^x}ln%KAbf`c25eyUc_7;l3Hm9eR)~eG0_Yq?~xzN5D?2~B;Yu#LGNtRk* z{VA5!RgvJCo~^T$o2IV|?nY@4XqlpEP?s7nmA zdpzmUwn#RJ5(lCE#{Ihb)aZdP5aMmL1?;+f#)Ln&8X*SM7YrUZDF*TmUZH!%!+0iL-Jc1nqNJ;2Zh!gQZlSW@}i+VoXIor9j!ELQGtGv2a=T85C z@+NyU92I|P+pbhL#{XLW2HEu2c%w?OQ`K1Ly!SDzO%LoifdP{}0?-SskS*z3we=F%KlXR3 z#K?k!>&p=tT!|v7RVIutP!{Zss%#y_r=tUDUoDM;XD9vv@!J~}j844Yhmj)ZfxKYuWo znO*#ni9BZdTF9gf9@x~pwJY?y^xnbv4C?iU3WLp&@-x(c_vK-K+>?WK0&9$pMGSsl z6cuNCzoLXVKU%+*nck?Ym+iPL<29$bncQP4e|d41G*UmuBPh44P{Q4c{=p6?{tMP> zl%D%gUd`<&x!r=+Tvd~L-DyDz)};poO~{X62^O_U_6%G-o$0^&-G_Fb%WVlC>MtIf z?$eW9#`0%K6lLwVEEKv2Z!@*;d&Q2aw_QEl(Ld0Zj`Z&@?(N*)A1>qu`Ufe0OLdR2 zzrip9zOJ%;ueZ@kwxRL=ceWv0jcusm1#CldU3Ih%d5v=Wptt^X<|*(`-g;+a(Av1ES!1a)X-o=5ARo8)F#G_o>F`UeQ-e1p&E#qSVJYN zpb6R)6d7j?$|o%m!}f5hOoZ@S)BdV9pP%hOfuPxrkFB=&zq`v{?-q~9ZA9Vq?~_!e zg=Dn+g&Db3eEq!#Z3^PdWpN5VeaYt&G)e_c>XQnUON$RNt+gu+r+Sn-T8%dOebq7x z&Gb;!Rw2d@#uUB1xm`iN!h8u=YN)BfW%$8&t(LY;&Gf89*VDlW)-bkXLn`be}7x^3WVvD{^FO zVmq;B9V^GhcVbL*I3ell6k1VL66=;wWE-0+7CwtcbQWpZNjftT&G$V&e#Pt}*}gdY zibp+n-35=tt>W^9{9SatQC|jpkKpGjUy~$X;Qs+Gu$M{(owwvM!HS@x*c;-KZjI-G zNI0*QcOep_$PsWX*S9J8$E zk*1jIQvhL(mZgS~2&Kt1=u@<5vp?4q)FsJ7i#wWUJQx1rAr1DS964~P{On!NdiE_` z*Za#0w?2!Z-FwZa`o%_huVQ_`UM2GiY!eSb+59Zpud7JjJgo9$DQiL<2q?%$9zPbkTF`9UQ) zN)9Q@A0V^zr7YiG{s=y2b`1i4Q;JW(M!Q(uCN{1gLYr8No`%{>%`}hPYvI%@~L6HQoe;x4d-HkLoP&p`jgBJ~Xt0$s4r8Lgj`^ zv>;BYq!*V&K9D@BA%+7PvNfnO*UzfXlN=ES4*KHyIO1C(miSxePb!6##Xa*{^&Y*q zu}8F&XUM&v4`1BMcNIkZo8*f#eKryI`5UOh4<8)Pp{fm>a8fBc75BvvueI;!XnfPL z*G*Vwt^G$wW4*^-KWX{%kpmNjLt9$J$YC5y4~@5^OU&^$an0_Os&taR@0`45SG(#2 zdF8I7I&z;1V9kc(_5K&lL$Savj@-ffL{&7gsoqrQJ7@6I|IlDzfxojE8XHH z<6myI^e6cXw2!Xfp3RBTd_W;44ki_w`*VAPlq1NHC+ch)rq+y~{nDl{bTvmX?u@?_ z=j1-9QE(Nqado$Dp+be>anlNJ>;(w!#owKtU3_M7_PpGDx*@Pf z>qdk>kB^D`#GFF>f$xH=^4!zaLqj>4TYp8SPk$P1mG%{cAF0eGl}b>ECAxvF*;iQ;0DCW%P@72gc~$~ z9AxDaqJK&`5>4 zMk83_B`rc$>y|95ZIkkixy2XdAhB$~!xi(}T2rQ96cFGDV-(FqrSOAkR2E0cB5&R7 z#7wz{?%hC`!|9(TdG=rv=M^p;Ir6>P{GtC{LfJM?CgB3$eCKm_wZ1*lopfyOAmQ?p zN_0;XM9DZ>8?Sud-<#+o?&}3Tm2}L^W2*(vV5Hx@n?v*>4!u&Iyy@YsZxVx zDM^+ba(FCc4fW(eky*tdGKi}-H+mq2_YS5v4fE_fT z!NkN>qwP~&0ZXha*?iTYFKTa4%{GM7gV!cU3w|41bj{RMEFP~nShSAuOe7QN-P2J# zu`3%Jnd?a(KYCd>+M=v$sE?-H+OH!?yERly*kfIz;qBWI>+%;cE43Y4kx?I`yjpKB z+59D&tzPS+CT*#5u5=cSN!AF+W$L>_00J7$;=~iEI>6qLMNbk10;A;%`+hdO3zmM< z|2ppprbE7S_O}L`LxDdxG{-|-%`WovV23N=v#XOg#$wLE*RPHE-S95bwL{zVF0^S? z--H2^d6KxkiFuwPlg4Mq4=_tMy@#Fyk0YY=X~?$?^i5c_sfK=0eG^-P20|~<)TwXu zI5bEn7=ym)Eg$!Ots&_vcmD9M-|8}h+Y*0YF8%hdyT~KsC)>yn$bv^;6C}W;gDyaw zk8;ETS@|*=^-Hve>6;F>O{q7-I4qSzmh?^3_k_BO5FK6<8O}CT40Co9jP9iN6Ry`9dX*;NXK zfg@lRP~9F?(%1TtCBS`q#j;dWtw9R>rzSK~PX&BF779l;?MjVb5uCJbZgaGT?QOXs zeSe{tNk+OG2bvF(=czHTZBVDQrkf*ONk<{k<&D?df(}<~YdqjhI(@}t6WWOwJyC&i zS=D&~X)e1QYjs|7-)x&*S$^cuK6)kjLqs@Keh+ziML0wb;S9x|B_h|3Jc@Iinrxfk zxpvl|IwYU|YI5xuZ>_@+Lc4woeYc2q*{J^w)wa|nElym3xm@*zaN9Y}Sn<;|YHB=y z%z=^A=d&5>4RxVNLx$YFcXs@#h%VfktE=0s5c`LTyX+u${!`y{mihZvoO-xCX=|CFMF=B?Pr)$@b{;(F1#D@pE?H z!w-|EAAXoVj`KC5i`Mxm+4%hqbgFb}x#)OBr)Deko_!b!x<=^$luMYeN8u~+vS;q8 ztQBbu{hob}^OC%V@OhBSCd}+@Yd)9igJaDdtbES&;H3DE*e{&8vx%NMqnenxQR6k6 zum*%BjT|wu8c9qA7!G_qaDwIiC^T!L)5L!)9JpaDG8BPT@_Bj%>`({J-{#0U9vqMzb54sh_ z|1p|=T6TP1XL7f6h3XuhYu;ecBdD3lZ1Jmd+htuCE$4EZ>#j_PkaS@UxZ~q!H(vYX zcBAcJK8pQwT>K`UwN@9d9*GTgVU0NExo}tIxogIRkBdl8Y@!X%HbF8&QW{XvMubXbLwXE~5nlr!A zG**mhcGWvVZT@g)YoP9N{f|Xww9VZ)=GJm0`&2JL)$!98PI*L-jZ7Zi@UWxR^r0|G)b$hyh-y< zfh4PRUaGwz7ls>IWiP&8g!G)!<+mQ}kg;?!yLuyvy%BP(20RDSHNB%dC}ZY?d#BnS z9+uH@LmQuo!O#=I38IBZU8ojozI&~!6cfejy+exwFutdRVS_2OO zOgYWxt8qSusv*DzMX}Hp=xFw-WXMnbZF;e{8uas)3LKc$TGxgE+fIM^e?@?QvO2|( z=S*K^v86v?V|Fc!FVtHLbiN41bEN2lYka(r&s1-G2K>m*hYc#`sTUd?fs#YB)Ual) zFa#Us>5-2QR4B>OF6iCmqBEBE7Kc3($;I96+i6V6iOp?exu9mZp}#ZKl?q|?{A&Oa zESZ+b_(-^KrXxSUGwVuby}{;oTPz=-t}XQAk7yI}->|RF-~&mQ1y9g_z|6$w+bf?# zn&BUzXL}u$D>w=LUux9M*7)u(ys+<{<-YN@Ae&uoBi78*@B2E0c{Q?s59cxrMw!oRbQmxNH+_FOr7532UV)Bv3Tdm*)Ub}6^*Ri$j50aiozUO&+ zGT#zh)%QNWeJC-|=8}}YXWtfY4#enCF*z(gk2XTbq<$W)N^q0}rAD;t;OAMp53Krm zLKSUIZXV)^!(^E~@qytX{W&=0zb}hQ^q|N#DugTLN|G3naU(1c)XRH68VvKE|{>n0-Ru-XSyrI}RufT;E!(cJ3!7&MaSq2+uGE+9$M zaok0h4r7XxqL;^QaqF~Lh2)EHe+|wUFDIkcnMnD|rw)jZoc<=WO>=#V+$T;}?gc22 zhHedPC%00$N44-jSZ0FkpnUj&a*5J}I2T`wHEYDqV$!N~22p`?f>9+S)lgM{G9R^s zL3~oXB?WfG!@iXHg#Pb8c;e;CUo4g%|DgU88DbkJr^`Phk@9WhWvX*FU_5cIhi--V zBc-6O*BcNSw*(cTwe~7*-6Tu_>N-p2H#OM=>PUd={pnT ze+1+vLZ5B;j6pCjX<*!;fsP!uf#-zwlmG11`kOqyq}#kr{L&Vszme9ZGDq#R7#kwY z;2D3!GfZepJ(6%}J$vRS0RRGx3OACOUX`JtUVegki&$@T7%I(?B1#3XhO(LSxSBKBcAp9t1Wp&qgJQ@9XZCkkuM$aZA@ns@}8D=)V8A zmFLA-;Z@MztibI_;OB2?n*=MBrE;4=&xRWIIjnCL=qU)N2UuUH<`2E^f42P}j;0@f zyzo(ckJh@U`5N?l1oO~xS83PE?;z89#QR`V0rrSOH0cp2B&cY~@diOv zS5hgLga#Uj&W*)%?`KF{RP->%Idz^>VHCAZ7`D!^kexUQ8_Q2xZZ_O(Tbd-#%sjq# zeiBARLh~@m(}?pQ5OM=PWpAm_S+6Z2ZQj$8GMPbF>4}s)swHKGAY>jVi4zvBiHW=} zLP^s&P+a7!jfzS~L+#KUYM!&(Xv1KgbW)2G`Ic|@NcmfaV5jv!!%3BLw?3uP1bg=s zS_ax0HT&yxXB;fu@>Xi9X&e?t&Nj)&Zv3PlJqoh>y5aR zIZq^(w?*3A&L;b$DP&bBlBW@PHs_cvgyo0eZ5cV zu0t&;K~U?IOKR3zwspXfp2a`-zUUr8s4%Urmx%;r?jW=HrxaHiDm(S`iy!R;ZX?{ItQ#`!Kq@?jF{545D<)`G=0 z>xsq>yb3(mMtLseE!ka(WGRsdsZCa)4O0+O>q~0YQb@nb=dA1qD+4`Jep1^R zd*Qf*z-oS~E#~rwH*DVL7{C1L!OW;HYb(b0yfXTf{yFXM^^e<54i5E2Tc$IG%eQ2; zNAx>Ys!8%lZr@>K&KwIJ5&&BMKU?0S!BNG=y`c$L7T(3#vwiN7NZ8`j9GABLP;c zRY*Dx^4hTY=sHuhK>lE%g*0%(kroo&K53ZnKbaWm2rCB-UE1{KJ*itW-$_rH%YQU@ z5gArUy@`B&@T6X9^>s+rg;3<~ z2;K$^gO278zhmla2J)ogYg3=nlPC4$tCN%E0rAizc1Aj$^m*IR*8n2j?0zLqHc&$9 z?Jk@ih}C2)nW#+WF_97jK;l4iLkH0(-X%KXOuW#}YTz=s_A%2NyT=XXcN)k+O}gzj zdh$2@l3(3#C}`V)HIwcW{Md(G4JDDn zG++T?Vsp}@_JKAf#OBubrueLnPV1XYZ}ucdVc*%`(7Ah|e^2O*rZ<}3L|(h&C-2{R zaK{|x_fFzT^~HPUhGSpg6OI0hOwJF39<3YMLh~be&}2QF_%RwOS0Ak!*;TQN5vJmn zN&AX9j|u!r#1s?##tHpo&(E8;6asO$&TchQ>HnQzvB}w6e0|f>$jJm^eA@@y=|WRV8;zBs(Sp_{fGZ2y#*(&RVH+IX z64)A*C1uUV&BaVy@?nA>HQ0I?zB*!pVB1ifr492J#+pM&DGU_G0-k1{F_pkiQ}%W} zaq0i|p%X+<+PBW0pDF)=f~_M_xT!1BG?MW;lf_{BSjN5C8f=FB%pPj8w`3k4A16;< z_`xmY{fL1Xrzq@v4gVMU4^m({8%`{AFGrk~Cafn9=uzR5*$p1U7k|oPu57qR)`Ec5 zjY*rr-cFIXfL>{^CbSz>6$}^#s*!TMl?sgwl+tvVM}|iufg7E)R`?7yYuZ0 zy-{C-ORH6@MSa3yvv+$_na*Zg$mh}58;wSbbpY*lqFtN7V;i)?`l8z5ymULr!ArM< zj$w@KCBtY7n^%e>jN`9fXtb9aSLHnwk5#C=kc^TkM-KU*XAdb|?7c1H^@DX`mp8d7 zsEc_bUbEL<*KHU{r#CgZ9bQMGgX~S`ok4HN)!yrK*sMN-JDMsa{8_Co;q|nItdN<% zM#F66l%R!HZXc*Oi1li!uF?io^k6EmvHxMZBbuA^AFX?Ab5mNE)~6iG)8pyqp6(rg z%`09(_cN}4zreHLzN~)MKdpThIaqlbn#JrqCGuySD@+gAG)i4bX{Id1QcqdbL?z%I zt1b5qrQ?dCp+RHT*bITL_Mkd7yLX1Xv%MWw8d0<(;;4K9rw+36UF7XkOwL9DzFeGACzeauAQ>W@{Fs~uOpMfZ1u3~1c^>yeS4$uwZH&e{A#braSpr~|)>_IAm9kl_H1=%&2R~GD z7-@GA{KHciS%8Sspx}wBTpgtm7u6tC;>^h6_NJbNyJokZYuaiEHW$JJr*@`7Jv-9T zo?OaV@b?aHFK1mi1ELgZkY798zUBaZnKIL-`**(W^yW>4K5NjaZ=YTs&F|{+>s=8? zzrW3IBoCYX&7NQ(Za4dz-Sj%p6z~M;i}w{B9z|d4Vb5%$J040gpt7vZucby;8&Q@B z4voXta~J;8y-*iP*LCD=rt&xA#BD79Xa4bv^hLZ6M>N=?zHspt z6XZpmM(LZsLB^rJH9&a;NN%)@H^*vE6zw7Va}PbFXdtHdJoM0eu(=T-x$-*l8AZNY zE~+BtNYNP~wn_S}YsIG?LU1wtZYQ3vm7h=We3e2~c|I~qoSH(XqT!*3h>6|GpTB_T zyV&zlScmSfv79D>y^z>gBeD-YBtFHTp9F6KD+u4!ep*S`L76IeL}LbekFw*!x2`6x ztK%JFfnL!b>hpR+`aV%f-zSNXIYAQqOBcS?As)E8{OhanMxuc^MXWMt7#>iYmJqGV z)D>73oFTK5IRBM?TP$>3O=;jt`+$F-!|xGrhF8h=n2V%n4i#yO=v^PAO~e}vjL?d{ zxNwgSr^^;tCAU$Hu3^j)7RJfV8JZ`C#u`y_21I*9KkC|S z9*oBaO~bCELkw}l+n_1A zfP0n(?pZqCU2+BBosOr6OX)bbjJ&`|gsMgYHO%Bd6*hz!EJW%0g0w)#i^V_^0RiC~m-7)NRxPy*Z+H}bozb>73g*`sE)7$H@15}V| zS3T|SsKL`sMk zFK!;SjQJngq2OXdzg(x_tsK(_l85T zMt8dyM|1Q927u$#@I2I7E$))c+(U5)+_w6t#qR2uRyL@pZtsYu8ymynMsGOuB3K#L zM^B*GsL#56p*S3V_Hdiag8>|fyIqNd%N?&><0quh99b?3r}F#!xr!3YmAO*^KQUFYVtiXgjx;Iy-AyUQs|_ zL?5^_r9+Aj4ax7ly}mD^_Zc&s+}2mDUpp*mf@ z7vgn^$t9NGK!m@D(Xz70F`zpUY-@?th{8V6$s&@t0J!Av^m1O;M*h#dPW~h@yCHK? zW*1lGHq|!_s4yuh8PqWZ`3oye#jTbYC!h zHe`}?14<)CjU+IWNCLlgzOowZkv3QBn08Pdaya}x z{irYUe60%b`xy52`g*4?XmKYSjoCJ@ztLs0$MEH7@A{@zwbcrgB}4{vX4v9sN|&0N zr2SkrSXQ4{tEFBX;o?lhS9PX^*mub<@AYla4upZijQYBi*C#Q+d2Lzt0(K za!2A$d$Mj&Ke28deAeV|Y4kN23{iJ`Pb%s*_?$Mor`O|j`#oV-p4r$c`U>BdEuYgh z)GMW}m}kYsa@dC>7;1YC>ZHAP8z#R?r#FB6qoem8RMu|0f9ZYtx%BvJUcJFCOV?Os z-mUOLA<8@KFNMRELA==b(Nl_A1HT~HPjW)@QQJU>dNFDgCCD7mHQf0<_a;}2?v0+P zdQj6CZC*EIpSOnFy^ZZbi&nd{_e<_j%;OU=Z${0MKmOB@)luF>ev?HGgY<-pNno0PX7kH(1EHN zRb5=qyoyzORYwtwOD(@t4x~g46K6acW8HY~sO5!-_Jw&tEY>hX#;^f1z~=zF!p*Sd zH>0i*EK+Kd&{DEmxF^sP${{I6@YI#y*{-%oA*z%b9Vspn&5c-Z;jaoer4~4z4aqw0 z5Km@z+LaCaHuL~AnUwBb{ViLIftJbR9r4W>-^fq`h*{fB>F98cci^evaF&9hb)DFi znGO`Uwv6qFbWbG5w?d|HAH@@}Ew@k~u*s$fH0qoNN5WESj8I>S(3ns!HIgM8_oh&! zC#2R>Cp`DVui8CUeHXI-f^Ivj@Hs%sA>v!Ayec;uZ-h&u;#Qfc`r8w5vsAn*%+JBV z<=|eCT`qVFd5Aeteg$psL;UF2KpY_iLZDITFG)Q}B!tGo7)qW9jj6g)DkY#x6^nUA zW260HHh_!F)x~-Q56uhApW^-N=Il#d1?zys0g zECSQZXbDhcl&leC@)Fr#-BWyq)i8O4g*^LywRyrgls z@Xgg|`hTZmaf$I@a!>gl%zqy7JdHZLTOAFV8m-<|XQ{PS9m?rTxtuzbEQNpzBo%U^ z2GTX_fTmSikyc5aJXNCoV!fvh-OO{>kzd?F7M5ljUjnB)eyIF%`3dEJP2968+H3pA z#DFBv73a~fX2^5Dw%OoLs?2q!P(vvxmK?rPQdkP9mmEaSNMa6hCeIl!ukk{^P)EcM zyy%5m@m6oxXy&}8xK)2vdIE_@;}I?X?mF!wYlb+Rcf3DPc6lK^x~u8os2+O0_#*m=)LTTi#jkwG0|nbjdYL z6(kAs{q%IFi;4+8BX++^6Yaej=yr^GiYpM4JOG*WG-`TwN zrcymn1EiQGz6*0t48wk+YD4u*traz~D%JM3St&lFdO}ENjZ`5;DR_eiYsjNtEj+%s z8oP0Q-Ng0h7Ohc~zvWB4QSM$o-% zE!`wS{2yHTGNYRVTF+{Aa(Al5qx>1VSi^2qx6{HtrfB$~B8rU6Az`mj5?&#ENcacgE5hT#uY@-b z?EIqeW$pP}N`e<2w)5Jr{-E^Lub$VoZ!WdBpV#g?S=zVny!Q5cOSj*CUK@=d$nLzh zYg?(S>%4aEY-w)py!P(W+Xle~@<6Ub^%3O&NJ zC=CQx7D7C_$v{H276*1(l#l*?vGSm5<5}hJG#G-X#&!UAMb69S@n5+@&WsQcUXh`4 z3jz{y)iD$SqLQ{pg3k9ZfD*Q-@!=iihMs$k*C0>`ia$PU_}0z&-a4O(<<+ z>Fj~g=H}4@+4PuAc`7+_pv9kyJCL#K_ZOO+&ZYwS(eg6sqj=!S&4^6fUJTbYl;7R7 zudUCyUl%Kmw`vO!N4CjBOoNMe?Ade2;^1I_?F|F17we@xjPc+X>kERJQC?{cFid2{`Tvpu|u6bbEENSzT4ryeMd6X zYif$R$dWr?O>|?$LPk-$@C4&~>gV>BB2lU~Q5Ib_ORTZCUxMRa!rZ>N(W5psib0Gr zTZm^(&Z4@h%%e53Ov+0GG%oB9Jn=$M6pvGWT{k^;lN_z+|5#aN5zrx(%e z7;9@u8<1F`W~?__*r6A*XfDN+MEE3X2Lk(%wGk0LJZC4T`DEAod_CqI#VyA924D!k)sHB#M{cR zB6@O7`ESZzU{Lid$yGOq-z>kS{81&@QT`;*rtT(3=~z;s1*}q|e=fYC(&_Q6xwnL2>aQ9M5yYDd7zMu(Wve?2+Z*?zvO= zZ43T52mJ37_?oa~d2ZnZt{EsfY5dp4!a}DIWH5$zYo91TuWjukGO2XBt7mL#YOF^h zMCr82PhR_sN~?lvpHkh%pnUIMVIF-tgQhN`C&8*#3VY|5&YW006=c+F`+kv@Zq&Tv z7>#A&G>E_+kN?iG|588@U&MdMFi!Irnj?6B75;i9YsfOb-7bybJR9P9{C;}P@Sl+f z|M>Fq=`-E!?dQ&&OC4KWJhm{OI_jZqZuDUf^yhs*ZMaO>kJ>r2 zs8MkcQ4xoPIieKaFT6&0t?(t`PT|+WO~RXmHwb?xd|3DZQ3QE5bNl;@Eg>eeLb}L6T(T%?E>cUJ(%6A**u=XB3c%% z0WUm_>IT;XfB9v?%Y_?|dHi1CJk9|(3a=D?D?B57fvAzBR7dKGmgs~FLYXu`${UDL zSb=CX5i`L_lvokrZYK^TjJk-Mc!(DzK7GV5{8{)536LNOkuWMlMoEmsNrEIv6KO_r zS1WQk}lOuo&o+nq5W8^qFfmq>xAScNJxeAf`PYM4?7RhOHHCZBO$TB%gt|8~hwd6W- zz3`~;3!ILg5Pl~7l)Q}GKweH>LC%vK$t%gL$g9a~$ZN?>}qXypg<# zyqVla-a>9CZzXRdcaS^D+sR$z9ps%r*t?tj9l3|Ro4kj-m%NX>pL~G)J^3K{5V@Cp zn0y3CAor1vk&ly4kWZ3Nk^7M<_8Ib7;a#X!_Brw(`8@do`3Lev@+IU*u`>8}eK7JD{ZeH+dFyfc}U4f&3BpSkI9^ zkw25aKwtSQ`5U=_NdFa4z*$uk6+l^5iE2?J)`|6~7^M>%P{)&k6q-;E+=6;|Hqj2m z2dC%)p0!8xijAUA^os#8D2Bwa7!jjlOpJ>OXueHiv)CfGifv*FIecj`BWA^%m>2FB zJ}o>Td`|dB;WNT#g?~c+_eYW8{}thjVnOT>i()4##daebxmVmI_KE%CfH)}pw>Sh` z;9+q@97UedxVS~!Do%)#$mQD(yW0-b2Hqv^7N>zFxD^|W|3u!PTu%i>w_8u6TXt$3Yyz4$Wm z2Jz+ME5!5Sjp8ejJ^E^7NxoLRNxWHno%nk37R}jHC(`NkuyXsJ$w~gpU|PBkYX+~H zJF>KRN;Ai=>cPWH^ViI)=h#IvxOi;w)ci@!9KY%|A33pffF3ex6J5qVC-F$R$ zdF}`V@Ur&E>Ra{jkvTkCeUx8@L1%7RGa?b3mk5q0PV5Wx2)rtY*xDFbmNsfbjK_2mE(u!mXycwL%n7B#KO^e z^$B*-Y?0c3LTdjO-u@GOz_v&&Js}O)*2)ugrOJCwJ=Y;!iw!4_EzQrLT9`X^^u!VM zr7Us?_>ldqkQ}5!`ma3=~q@EOt>Rp`L62I)` z-#x=Fy4{r#(4DEg=PfOaNY_z*?U3fEBd4C`4_jszC%+0f_ZtBd!+?nJ2{bJ=kdrCIbDP6my z>!5Vy43o_j+3&exUb=F=$Q4Ia%ZsNL&*+bym|vPdbK(s9O*^=7`uH4sTR(Sdae01W z{={6v$mugD7NBI&@7IkiOMl;jCC@%;n7Zl&l_C6>Jyl=UPF*#BjDN{|0$*Q4OT}m@ zhvt{(RAX~jT{XuW*p=5v^pyMYCk5!KrjO&Dk`h!+%$+_xhZS<{@kQS3%~{nM*12VLP9m&0jgB3`3++%X(h+Z<&y(9@ ztZ83^^iV!s(jVC`T6Qyg%c%`#q$^*Fx#67jyTnYxeA(yId_T#j`SeVjS-Y0kv?G-u*`nlo`e&6zl#-puIc)5Fr0i;8@ji;8?kqMwoIXC(R= ziGD_+pONTiB>EYNzO;z)8Hs*IqMwoIXC(R=iGD_+pONTiCHh&3zO=pMvl9KRL_aIh z&r0;O68)^y|ExqmE78wN^s^HEtVBO6(a%csa}xcWL_a6>KPS=8N%V6PeQD##OTrFG&5DwyS(WqF<2c7bN-xiGD$%Uy$e* zB>K`;l`ly2yCnKu68$0R_o0k>-?61R=u_wTWgnB`>T~Q;zfWGG_2=Yw>dW{SuVWX= z?b$RJ-P!aY>kY1~H@LFa;yR=pIlIIJ1b$&dlT8n?rr^rDhO6}DA=Wkg&bpG#@THK= zW(tZcuQ-9%0wp@yIXbF`=9fBk_Nw_M$hjrs^0~#-(fLyl9;fKEU}*_ z+0O;`a~a=0j#mk#AUt!N#VpRHJT4r0L>#6t55Hj&q`cE~F8k62wU zV`UQTo6fH+@cwtbKj!@Ol=t5=KTZFqVTHA@(lF@#-#UNpDd#sX0V|jrxA^#WA9tLp zG#>Zy^WJ~OIM;xcYAfw)#S(Q0nonV2I*SeH>)3E!!&>ugEJm(3FvgIt`(Si;DFc?$ zXZM44cPS1sF6DC1MDWjB_zcuk;9CRMvxl&hH6ukwuz(F>?K%e+FJKw`6CflP86{D@ z=}VSA^V<2QA4^>YE8aY;bbIN+KCEx2v9z7X(xsZ`XEmfRE9~o>Nn0Cy4YE$F-W}K< z^R=~DZFO+_`eQ04;%rt6UmYMHp>=9iYt*V&tW^$cjJv$Q=0<#)176n>Nq2(nRJ~MI zPJCko;9gdnq@bH$C%<+45*t}6SUdGjVKFvWjTIpz+wDr~)q4Q`e-nD*64KVW$Z`f> zmhuiV*8=Y^;y2NXE$NH&^dPPu+iM?kFks<9MzW?{`lgzcet6r=IItvHfgLGLHroAY zst;Lu?S5nm5E_a4lW#I=R4Qi~NjtHU{vVd=UTl;7SfhK8m%UJS8p(VW%j$QNx083V z<9>kUYB>2NmfK&aCuw;ryy2Z%dcwl@TKKqyS6KL%g_m2n-@@#z0RE_jmsz;a!b>gu zxP`kce8j>_EPNPxsVO~#Pc5nb6NnnpgZN-i58#72eGDJ0>3)1Lru*>0mg-H7FfkLP zkHD$57TRSY@w0={tJA$ebpXA`{8r+r#Zua-o@+}w(??0`Om}kMpKjycVVHIkTFb97 zX-RkBuSvICN(J7w0>h{3Sn>DswxI&|Bn|Ii_iaZOdQGPGF%q3Dj*F>AtfR@9^l9@4 z(x=QnnVvTPMEa!p$J0UckEKtTe>i=Hx{sxj=QHVZ+)t*@azBxt;(jz8;C?v0pZh@C z%l$Z|nj}%_SFQZf^c<;Y(zD!;@>846TgnTTBH200dje_RN`6fS)5b{EAlYTyXfUak zc4}O1OxJL)Am#k`A-^VmbI{B=PUeYKfES#4r3m*m;QG)_r{}siBAJ3y``VH@&Wc`n zoRm2ncC<=XdNf#C+Dr{E<>vPuo8PzC{Js~=TsALjZ_ec5$~RuV_$u^=wS;)XoNw{Y zb}43CFsiwwvE_q+b6K zdDC)lCf_DBhxl0XI-$SfrxaIGDQU6{9Lvd9+E~&>n#zxI|JnG6-GzVu__gHV_?6@m zFgjaPBS`kHX(iITp0_w}CGV#_NxJI;QO{X^{{i1Q+m-)?xq%zQ_fFjU{4d&?yT(Vy zN1$?ywsP@LAtSn5nA9BTU5vOItybvEXw-@P`OD}V&8N*)4DJk#e@k4^KV*GgSFvv7 z3u@>=dMCyG7O1`Yx^=db}RqJqasPr5Puq!G!}A zS^DgUYR3deLy`WwN!b^H55@K|Ru9o<)jW|Bp+`^HRZicp5StICW{mMB>X&m`l zZ`e3otZ91jdb}!*`PAsw6sCXFJ)1q6Z`0>fGri_!XK{Wc=1%H8O~SJ>Y}$qZL%$hF5-wJK4MHsK8zGvD`? z`rggARqlvK8Lry0wJDCP?w;ia99c?Md%cigjUv(HD7!j7jFh@`J%x>uepPw(a44PJ zxdB4$544bT-SQ>%HEV9mF+M01UtqZ5uoyPOsJLQf;)i z)>tb`pP52iVk}0}U43>gt2L&eEc^wbO19SMaxF31OdHTa-+Kbe+DYn!auuPpU>S6I zpO->Pcct@^#!uvD)QG+;RvDtd#o9 z{oE3KwU(c61)-9WA;S{WYZ!C=MBCq%^#dBNL)#Vjb3^OB>0DtB1xv6v4Z~$PS9+Dt z&rZT4X?C;*&Xl*O6n`Pn+D4nRwif^*eah!piR9Ja;lC zUL(cQ!Ko|>!9+#C0yi{p^R!Wqpq;$@$8a`34S>DL_zgurygVH+Vox|erh1v{X zpar~!OTEhKI*%tum4h>S$3L?&jwZFlp5`6RU~sDt--4_yRwlI3`++{7ZaN9GI^m4V zu~-#Tl;-I0-1F%g=RTwr*N}PjnEavfHmfx|?Cu8&-68v!01@*K2(VuQaxmO65F>Y&T7if^jUhnP_2|;S9rr)x_C>ja=c&b3 zhR2OMwhDIkBE3m^!|?~+*}6O9R=%DPGyRf1r@9KF?nqk$T)AJ~<{~WEeXEK&w-*n! zPnBNdp=P;ebJfc2Y2q^Dh2Bj!*Ypk(5%)y}4la-%cautLpuUukz zWU^7Typ&Q)YgT%0WP&%?(hOcYsV_hZeVT_CWOmi&g&qrXf!yjTjQ2CHRBZ`z1%Yjq zyGC}+zr#qovutD2R!Q<7Bc~q!4LS5XyQA>sMvqXgI?`BfMoXaD(dh0_p^++@p{QRr z;4$S#KU9u}zC36x)rP)q8oaB}Q)Jd#Ml~#c^V&9oI*UC;E zf-Jc%Rjg12kpG5fB1|DK)NdS`Os+zqg74ly6cUM|hZ-l;gEN?eY|PO(VKbszG1k_h zlya_=rwY1WP6_sA)0o23S>Ra-e3qnnuI8$(IUc=Y@FHX}9>eCt<&fp6`I5gIv2u>h zUi)kmEn!tQidyCjM$rB{*@-*H;q2ZuBb-#huwoa#BQl3cECwv z+t~9spIu5VxI?71vMZInfb7v~$2G9Cd111Ty>cDwm|BF}$F8|f_I@qKJ;83irQ|Qe z&1avK+bOl2T}&%*kFzK6UTRy3Yq1mao@8g|I`;Rj$7yG35BToGb+X^C7ku~Qmaq?a z1ACx0;+8_oCUEOSy>8Cbdl0NTXz^ao*4qqLov+u;F6OOZ--cVw{^9L}AHl6*Pv{O# z=-Y`~%Rc8_)ch!JJ$sFJQ}Z6&eYOXzm(%#ZLe2Ydo7fGwAAUWC8)SFbLE3!?w}BJ- z4nxrqoQ`mOoIRL*xXtXtItm^AxQ98p?-*E*;~wDzzZ2Aa61R(ebOY3^)BN^;b&y)0 z#C-)Er`cWj6z(wo)8IaX>u2ZlGhliacbr|w&w=TA+)4Ilp9Rxbai`d|b`EOKc!bk?HIU);ude18PDF5({K6uv(N=bz*Da|+*I!lz5PcFqs{YkJ{LTo}YFzS<7XR{I&!_YrOnr>zacji2N8a*E+Epzz;t z1MJ59CEWfMZh+GY|2NsqS%4i$7pDO(N;d1{x8xzterrn(a{gO;@_U>Cw;(ydIdBU} z=}0?BS(Gj!r8Dhh2jk*&ak7uI;g%%ZI3KPn+0H3&_a%LtQP7(_!f9~#Cy#QX+sWhs zPHP)r@9rt~WNzWqwkHS=vX65Qr?))`Tks>x4J(oMY!^Kj*x?K=?d+Hv7@GKQldCXL`8P^pNw9aSxatt}_kn zGYx#uG*D+zN)n}k3rz!cE~PYP32rO>)rCAs8+V&FZl}Mx;qP+XF4M+Urj3u9Ha<)b z>IhM3<5JVc9j1-VriYwijpNVLaV@4@71JyI2l@_9tCU7{nMQS+Mx~}v%cyM+qlb1? zwwXq4FkO1cbV<9Z4oOmmt{a~7NC%rnhdVmi}jdh#W9QNIe6(w0r8 zEr(27zQhR#uQ7t?Jk!;tIggp<9N`?xzkr+H!Z|HEY+AICkPMhh>0d(dAc=927vcUAD2R-=-W0OuJ>@jWGV%oCNv}J*5%L3Du zM$?vgrY#3dTjrUzY&31r*_n%y1*R>BOj}l(wyZU6=`d|sY1*>Zw57weWwU9^LerK_ zrY(m#PjOkYoU;>qlD(!mojOC2-NMqDedx^dgwG=78+Do?qi2eI%tf|k`)H?k7b2<6 zNZM**8vmD8rQsYBr7`RYMzELZVU1p=tv{ZkKQ7X@8n>P??muhe)^l+DU3zthk>iiy z#jhBlG(rr~=0(Xr)7q}&2T*x`@*(t{=XBEwe47I;t<CBV73nFrR|og?>w@lC-j&x`7iybpq%m&i#Co(?0)+6FvX4 z@@bN`UQ?S|_O-SlDZK4sjj!)nrnAzYbZt?F4a#PG-A>uEN_p10W9@ZoY@Xcw4iUcW z()S5^H}i2!Hx`u<<8=A=aKU7P4=g96k@JC(r0DQvuq(<1-6Sm8LiV=4r^E(JsCijWM1O zzJvJeYj)#~C6J;5_j=y}bDJz?U7WM+XYmH!$8c;5o;zl(ar z!jVMNYqnz|P$fV6Ib5R7@~fP6DWRs|!;_Er%JE}r{McJnuMn^9{G-*>?B8`bn+p5C z^%b{TPbv4l!P#L|qx)jiy;E}itGy&N^Na$9Mh|qlFFc1x zu{T9vxDMyl^X@xYeIKiDZATQx*VjVO2c`adO{+tFTjdJ5_WfXejZ&G3E+-KgL@lN) zCA2?-cA*SH@t8ixIpc&`%R@;AW9h~-S1#mcKK#(P{CZP#+4FCh6dOgT$$#nU`&T_H zOaEnipQE3Yx3u;)hk^g&d*P$q{UgNNU>HktGN8%jIfI=_Xi|K>wNt?*m|A>!?E$1Zwoe7;6!1+2O%+D?B6-&6 zq!jI!j5zQ5wO&6}ky*g{H!8;|GyRgmH+8a IAAbM;0R!oOJ^%m! literal 0 HcmV?d00001 diff --git a/shared/src/commonMain/composeResources/font/productsans_light.ttf b/shared/src/commonMain/composeResources/font/productsans_light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..33de1c39f2dd872e5e3d55a8b80161bdcbdbbe44 GIT binary patch literal 92564 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

+7M-@pUL<&Vo;R0WMe65Wct)gX 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 literal 0 HcmV?d00001 diff --git a/shared/src/commonMain/composeResources/font/productsans_medium.ttf b/shared/src/commonMain/composeResources/font/productsans_medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..fd818d6f5b2febcbd5e919f146fdb38a5cd5c73e GIT binary patch literal 92312 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 F>3?g0VA}uy literal 0 HcmV?d00001 diff --git a/shared/src/commonMain/composeResources/font/productsans_mediumitalic.ttf b/shared/src/commonMain/composeResources/font/productsans_mediumitalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ec70cd38411657291dc13fec6c7274ce44471f66 GIT binary patch literal 95656 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 dLHeE;D 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 literal 0 HcmV?d00001 diff --git a/shared/src/commonMain/composeResources/font/productsans_regular.ttf b/shared/src/commonMain/composeResources/font/productsans_regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e2c69c3fb94651237de80e67a69d458c55b45649 GIT binary patch literal 109128 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 B-RJ-S literal 0 HcmV?d00001 diff --git a/shared/src/commonMain/composeResources/font/productsans_thin.ttf b/shared/src/commonMain/composeResources/font/productsans_thin.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5e046b53295fdd1501c2d6cf22f1dac2cfd42793 GIT binary patch literal 92836 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 Ru7BM5!iPFv`?Bkoe*ui=y`}&F literal 0 HcmV?d00001 diff --git a/shared/src/commonMain/composeResources/font/productsans_thinitalic.ttf b/shared/src/commonMain/composeResources/font/productsans_thinitalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7b0495518876d68c69e024ae51032a5e4345a04f GIT binary patch literal 96540 zcmcG%d0<=BnKyjS)h^juEX$I$Ug>JFwb_zv%exitv7N+m9LIJR>}(_=B*COKY$0ta zr7oq=GL$lu0otLIVW<))O&OrHg_fa|GHr&JGL)qQl=kf~l(uZe()&C2N{-@$wtsw| z?W=q5x%WBCbDsS?M<$dIQiTsW;RgpN*BpIelqd9J6cXo9|G=Prm2owpfxB=&GBmz! z!=^Q#pCUANl918x(1wk@xBYN`2cG|l5Y>z8HZ&ynoag8vgtGVNcAP(d@F)I9stD0N zL5S?$9Ty$qt2XJcCWOBU&+pxR@Z9rH9AB>_#BdnLh3C#+c#x=w1NGg4=N0GfyLk5% zo~sOm1fzuf-~K(j=6BZYf3F4gjiJwOvB7{>DqIq=R zfgSUAU%o#{h<6wR{Pp?shYu?1IUU|d9|*sH{`_72n;&|MP}4!c{P^I33lBYi^ZZFd zt6K>%?L4@!>)_|#{mI*exPK3ry9vV;<<=fL*TS7+==?MB$lgE+`Nfr=x{;m#&wcTG z3-N-i;-u^$!VxtN=?8GKM+-6x=_H=VD^8M~1Xwxs!yROG@&QrctwnCS@JBMr&ZL`eBS{)Wsg9G$6r6^W9mQ0fs+o|Xb?b)cCrG8w zBK1M`Bbww1{U`Pg{T=rdPCPMkNitCmMc3f&102T-OXO`b&P|aTPElBr1<5LIEvY0| zl5rYA`;o#==v8DBXD3ne?}aykr4)mqpDnz|?I$7HO{VF)C|@E?ARiG>t9V8JhUdVGs;OB14&fSGW^vAfq z1M}RCxj;GR$NTZZZ@D;8Q&1rNZsAXK6~<>Iwe%U{%SYQ9fS)^ys)JL5~+Gljd>SuKK1kr%*-Vkxl#?0_9o(nmY-(I?B zyus*G~>rCD->UPG=WKO%?e9psh#2<8lOjk}pxxwnaBxgExT>^aJ~dC03?(#2f?9N-K&-(8X;+|Mxoj1HN+ z2d#*7jQ%q^mfH$1$k?-!7$?IAI_z|Y_>hJWcO9FpxVyd~rD zjJ?D9!TQ76s)mmEBk;!Hnn*i+y6~qG|ExSO^HVQL$@W3l|CsE+aSr)$;g7V3?7(pj z?ZI^m*$DbrqW#2$ql!FQh|@mwT?;)3eJ1-Zc)*YL<}v1XNF6szA~?sn8WN{}0qxfp z{v>Y%ZXv6gEkR$z^XEwv7S{$*Z+Qy8f^MsYZi~`B&_3#>AvU(cbFzO2-Pe&Jc`a!{ zKVmrdurv1y@lce`vT>69|Mb`ky8yO%Uy_L5dDLV0Q)d1+X`Iu!scorHRRt&nB!nCTmno#L$0H@l64{-gLj$CVe}0W z0^5s+6V@Vr=+|+_uAD?J+Pm9G2Jg91QVH3WpgnY$9-&{P1M{|A>1I^&dk0SEBxFWnYs$Ec*}CPvqChbMB+= zw|O&f1Rt%Fa31ssih~E{(b4TrGrZo|9$0u+g^G9m3LnG^DA$^^1rYA>XnnP{PQax zf91kg7GAmFl>@KredXL&3@=x|TqWU69KruTA4*QkD#oO+xhnsmu=u5)(z*O;`L#H= zm`5e%UJV;SOLU|H7}pa6dwY%h;LyWJbPn)0c1j z%vGPh@>8Gu#L+8`Tz=W5mt1`K;}?DG(1i;Z96Yf9{C($T_wG4&_pY5gw$IOLNq7A42DI$Y{vQJPcsaqQS6oS?<4+36fV zw|iz7Zxb$%6aO~knuWvCJtokr*B{H!t2fL}qs<(D_2l$%j&gl-y)(!Bcs6~4C%KGR%CS;b$X?;u z4H>1Fm*bpTeBW^*LvlyOr*g4Cyx)OAh-K1C-_1aX>^Q+mWk&J6V?ovso^W^~m!4$A zCn%GnOf8iimENu^zOBX+BYW~)!chXuq0iC}kc4^6XcZZCMw4M2fvI3c$5HYiH8qs zs6+h38@=o&79BXot&Iq$hwT^pGLVUOZg78u|`)w#@uY4+}%0~GI_ndy&3 z8FBH`!Y+p}bF8|0@!$Ze5su9(gL9F^=^UdmMp=Tf9ds%Stll9E&apSZC-_^9q8(HG zTyFbZ1ed&Va1oFB9rNsMQge)x1&&d9fOeB^U`kn$(+azKb2_1S`AHAyk)9~o6P3`L zqcswq2ZRCMws&!dupKm)8J|Aav3q77b>}j|d`>R(I*!RnFWAXOF^GX)opy0ADP z%=0@TEHGT)YJ-5w*;&@gSnKH`p=1GbaPx zdHJ0)5|NN`QBZv3OQ=EsNr*4;m{YOxMPmn}6n2N_;vH`Ax0pNk^xHklw}W^LVvq;o z60PLqK}KuS-dxs^+c(4LMS5jEcXT_y$Qy-rfqjT{HiV}`b2&v|D0g(nJjMYj1lmKv z2nzV=?Vw&%I5@XhqBwxbgUc=D_G9kLs)P)slW3X?Ff8Pbj`MRf{M;N$p#;1RKBvH$ z-#yQWi^w2!}W2z#977dHTwFqU!TR1o>Q=XVKSNOP3Uh^4DSqakPqA0 z$Bso%V{R+BO%X6HRxR>vi;z=WA!6mh9aD4A1H6$RVi({uhgiL)t{>n|1^P| zI)I7J$ugqa!EJpMp-3_`4WQL31s3pK0Hii%=OQscTnPKUSH5TpX>Qya+P z4}(IV*)s`oXaYrIe_1<4?4eie4ww`1CZdi3$O4~X69FbI^4%^+ZqH0)r&O6zI<4j* zQ6af^tQXCT&EN*XtAZQ@1|ZP*+=d9$hB&s*h!8=LIFk;HD(Rp=20=r`3z*nBA`DaZ zLBLwVaE`<6@|AF$BUCN4vXe$=J;qTLq&;M?V63cwpkLfEw^LHhz%prdbTZor_`pQt z4|97F6HSxT3WuC=7C0*RacN4yR=^8;Vfp#TAqg3_BuuK=v&H3y43Q!*!!}q`{Jbcl zMtx^OEvo;%#y|?AUs#(WAx+$47K_jdI2v<%T#t0jV0*D4rFD~wwhfTFr z&p5OqXhL}v#?*$PwH3z(++2onkF(a%2Mk!eot$RF*MSH`;?d!O5qSK*MA|w$G-BZ2 zhd?X#`$VC@5LkR$LLKmn`b0V}J{B2+RmR{x9+|<_AUo#p)*w5IoTMvqX~hbuU#wo5 zQ_blQh2_eaq8FBHWOv7C1*~7WLxC29ybGx@{TNjRi!Xp8jTmP8;-XHF zREogF|99bK5Ye0@Gmy@w7vwI-)GscoE6@H!z4HA^5r8p2X1Q|t%xWpJgSIcH>tpr{ zCNQUAawv}Zz5H=TZlDU$oG4R=Vi9A-awTbFxCf#E0*oIJg&BLXgty%yOH0+QES;Rb z9E~tcJq~0Ta};Mq(8~@7L!C%-IdH~-$c$uiUCwCZlOjufGQ#tFVQ2MGSX)q(xaJ`Z z@U}WA3b{oXJbUM%njwdn+?lb#mYQJpFl>Io$kR^JDfvNyL~$FS1LT3}PDk4eJc$#9 z-#VGR0%|xYc^s3AJa07N(IQ_3*CO|6#)|UdGeIna7FPy~Z?O@4I(OmEG|Nw zE(*+vnqqDZKG`E0bu!CYC+~;Ht(sFB;mRegtYpmJDQggW- zXKjs98Qsi2#XoU$$6AOWwJ#4A8(rM8I17i+n{%;-i~Yc@-pQo4*pr)BPe`UP)zs?Q z*mSvPvgrfQhNgmNqPgH%pDK99lLgPZ#0Q?WiGpWM<2O8GjRntWG2~g@Q1A@L zcY2261^3L3hCu@Dx0LoP~DpS2y$B z?!smtyYjFrmp9`s%$g%r8%+`8#;O_POl72Eqaq^TSTQ4?G4T40x`=k8Dx%zokj6&+ zjCMvjLuND)^+s8Q+o+!5W@MR+;=6>>>vEHk(G#k|#Ar@EzBxy)&ILBGPnh{R<<&W| zadz|cF-otU`Sj;MPh7pDx$8DeA4j;#1y>fn>-y|yz<9#mDgD?8!Md~>hY{;gM*2HMwn*Wv(ov{G{2b-dJ>I$kZ)`%z z#q2u)blNvhA_mVSC2*g6$342er!D zc+%LG_^R#+y^E~J|>3Ne^^L{?Vzv`Xx9`r7H zzwUk1`-1l!pV2qs%lV%4z3R&gHX$jj6|NNC@N<5rzr{b{-{*hS|APO$Kvf_f7!F(; ztPVB?M}oV9M}xNnPXtc}-wbI(!O-r|lcCq^NS(8;rEa2bf8B%i$@;bRd+M*OzqS6s z`jhoqJN4R zW6{`1Y*@rHd3S2g4so^Q-Fo@{(8p-lu6 z{fW86C5amoxx`b6*OM0|^C^3(DRm_E`zCeMSkomGnhI*R|i% zezN`T4r51m$85(JJD%)#w&V5An$Eh;xz0;EZ|FSU`Ap}VT@_v7uHmjdU5C1kc3snT zch>`5Kj?b8o9~Wxw{}l;Z|~mUeM|RU-S>At+WkiNpAfzn@7dCGThE^|(acC@HghO* zZRW1bk1{`B)w^ovs)u@wz1iNo`$%6!UsK;?-(261`hMPD)xWL(uKu?Nj00;2ZX38~ z;Df=*!D|Qa9(-)@#la7Twh!GkbYkd<;qdU_@Xq1f@X6Ku>gej$)dyE!w)%yU@W}p= zBO^DDJTUUY$Qz?|qfMi0N3R*ZY4o|#SI4wt!Lj7n{;^BOt{VH|*zvJP#-1E|c8z+? z^qQS(u37Wsnzz^TYv4x!Sz?JzkB^->z|oW zPBcwynbxQQ$)srKWM<<`$=-aq+WpHIID`5T<+pV>ZhWag%s zyJsGnd4A@-S>?O0;&E7eCeD;maJ2yYFrE$wmTb|tN-a5GTrmfFx zb8fqO+s)hV-1go%@pBHJbKN=5%vH_J&0RBhV(#_%y7`;t-`-xa-M_ti`_%UR+ppSw z>-PJ1wC;Fhr*Y@vu9{tkcD=RxqTNrPyZ_vG_SEgUWX}tG?R)p_eHshapUl2`-t>9* z?_0a?=jRWe|MdR${Wlz_IdIW|=MM4*j~u+^;DZOByP)NQ+b(ooxc|c3q3T2aL%R*y#}7UKvGB(_KXwNs9Ea#Wp)W(?%CP!i{EiGOU8*c@8X6*z#)R2x@}_Z2&};Jx zema(+%2$lYj5K}J3AsRSI4 zczN%&IJl|@@gu_2V8GqbC=ST`j*Q1f^cu6QhO;}fc017+R8>lyC#%yDjd6jnzC}!J z4UuFrl`>V~B9%%eO(|2VF_B860r5zwQfLYdiieOERAxLl7(A*z!e6GkG~f^1kK?H7 z3jT7{WkG-N{$TWe{{ENv@9@v!2d9@_dWjUiLuA0sE%Z&SQOBTbRuTQTld;OK8U^V| z^`Jp(Toy{Zsnr~pH8*v0Jv61loeHHgx6D$FH4Q45n}Q)(w~SW1IjdPOJK^n42}*C} zR9CCFJ#6!Jj>iqWp>v&HP+C0owavcvuvO@qi0k>^4YjrSSEbzf-&y=|TS{1!a2e!| zSa(q0KcS@E27j!@8|jVJ(#a{+nna~J;IYW_|6vHkT71!gq?1n4s^(#P{-@LuYNJ;= z<9?Tp`l;F944f;-DrojsxrabfR;*2RVNEHOamm6|AMVZ$H)V&1!+M)MYqN#*zO3G~ z5H>TsM;N|Eq(>x-r>T{ieQ}PBK9zKHY|No#H}2z{;M3#2hW_=(=W_}B($k}^=8)Bq z9E^CHf);0TAmD4Qt1)@PHg9X#nxUWhtB-5A8)}x$r@O56EuPR&lgC!q;tmfrx!kqk zc5iLWS6v%v_cjma_x+6*L|Uu_UP4$)j$N zG-QG*wbdT)@w*d&S|zWpcKL0tMxk1G_<%G2ozyzves^2MQ5(zbm@{u`sN-8k!r|fW zW^0AMGbnV&9Ts2IW~d1|jWxlPE76hv!B?yQ60W03L$swg80d-FArly1UN7>c`k2U* z5=V{~IT2&0q`PpKo+GQkx~BRV)=wKC13=+yFlj66ca8O%=Ag$M8r;!oY2DTr?5Mxm zHMV(g`^3$kKhk1LUi6uNSikpMov4b?!cPi^$?uDGVd<3BWtMnC73xNv!O$mG)FrDp zdu5btqIc6P(RVYcxX)05N*fv&yPK_y;jC$^0X!EHRH1YO-JR80{1(jxy2FYVzC}0f zoZci*CCCQjWlmde$dtXi!AZ=iZ6S0J=# zNq#`C(1iC9C6iw4lm6-v{t^6%V|}x52bo6*Ko7jB^x{}mX+{M_v1y=H8MUt3P+6l> zuUWfBX3`p}jRu`jJ1g}gMT2w~@(5*s7iB!g=2dyyXjGX$Ne6j`PbQ=L(1s2YS}96F zHalgqnJ-FZWtlhz+=~GI7jfPp*-LN&O;I2HMle6k^aeV?kr3#<5Awi@)oDwng4nVZ z{%nO}fmDi&52a(32wYy|b2<22UgUE*qv`h(Yg=6AV0vV>W%()*UzbVvOf}KI`0|x8 zJaTSlY{&deU)`xIU!Z&a%xqoDmfparD>g^d_+UA5N>cG{xqLxc!MX!cZ^m?|WS1Vi z((p~|#oVPm?un&IhNn02@jS-SD2^hYbr`b_j6wusfI_+@-A&Vs1SJv0$gw24R7(8n zDL%ZaDpg0iafje*tE;KdR z?`dgn_q$S|8e1?{qqj9jT+W7WU#PFE-K{g4s?7D(4Jn^A=gPuc^q1&oGx21qh$d^U z%9_paxU<4C&Y7?+;+<)klqgq&5t=Su2|gt{k8(-w8mOBx`TJvErrEFbRZREP54Z3e z2HTX~p-h8)xVLxG!q@Iz8_qYkhQls$Qt3Ykm1QIKIx29Emq=8wz90Rp{4Cm@7N9f;VH?1him_6|0Cz@ zw#3#jt<~j^uIuzW8hYq)XKEi1Mj3)NjkJgQx3xr9H@jv=wuaVqhwRZ#Uu?YH2Ub(^JxSggOgVi6 zExsvwP*v-T+QUQ5Zfm&1-?_yqubyabT;J(+B{KfuT`p5?!$8c_9JDwR{b6rYu$uO; z7u^lMDr{0b9BB@3CM@lPL69LeIP z8I6yfbw(rO;*5T9#)Rfyuc5V=PJI-pYEH>RM6l=<&ds#8(w{8rsR$ZrUU^2JY{p?^edZigWv~YaIY-Q9fxhcgEVK{gPCmAc($r)kZ7Y>t#*(&XR}V{AZf`G;CgNJvYDhUzA9YfvsDP2l6+gFmTzC5 zbVP+(rMwc%BDlRNe^qS#!QS;Z9PEp%J-=_PyOJMCuD*Dxarolf)uFzm%OW&5>iQCn zYF`6)ylLj+{Tybe?y^bkF|v z4IYoNDm8ua(6&1+>RWnRXdkWjq#G1vK02!HADrdnl@u(7>uzOS*a0zR!>K zo;Ox+;$tpmConvH3V6~2Q|N)#k|os)KUxVt5^fMv&~q?oP9fs)8@|$Z{y&}j_)liP zu(SEvPuzYX_tfpzPTaWA=j%N8>e0`AX6Z1-#_G1BZXLtFT9K90tQ^)m$!fR-_38MR zF{1znjDv5bX|E#xNSba}<}bN|UZv#j&i~YhKklibO;)N`18Om-ZzaY6ICQ$ws5vYDczOwxE?}p=irc zZ?$N2{*?(4O;#`cXF9}j`sTXk>$k4DV6t)5peJbn)o&FGU# zJ9KX2+Rk;mO*8)4sw2h?le+QD{2YBhPxl@=H5Pa@&}Wv6R;$)4(70Y~904L`ufQIK z$4Jd8fl2Y}{iAaFNR;OP{aG4#>drbgj*awRS1}p8tIH0}GZznTylJ7Aou|IM(CY&o zuIW9nF4nbYag3dd@}f?hI|t*wN=LS`l2Ch=f+i$PAG-^)EyQ681pX+IsFL>c8bgi=S4<)_3}u&-V}z_PKCqVoQF!$pywJ! z(}ISto*55~vovuOHuR zPYgs`hH8yEZ=}@=Tf*sxG}-jFm{3y_N_$-~ze8)P@J8DFvGGm;e2D-7X9q4-;2o_J zj*+Zn2}BUnksFqbI6I-(P8`reBWb$?Xb#j?4))#J?>WymF`zN)D(OpJQ>$-&AXQhwiJ1tH_ zsISo}^lVDer}M$KZ3D#^)foDGrPycv15A)emJ5rgi(-TdW5G-1bWbF|-^JLQyGD|& z*dIV-Ebe1qa)K%h%97lIC~Qo48D$7Eu?i-QxE*!t<@w*%O$FA-sX00>r}cGQ>(Udb z@*vl^^bB_$oBv;+FBhOrQTAvmmEg@X*+Zr3s9CI=nvO?rlT%Y7|IEYTf0gH7YM_Vc zbpFS*Eq`bJS~`)xm&qTB9SwxrjXE{3z*SsH)r!+mtWWl2>jQO&9k^rpM_XwJy*~eC zx;KA7G<0a;HNZzict|hABBWU@Dr`uvkmTz!6M!@1FHq7G>ZP~n*Od9@Fn81!UOHOT z{j_i;otD|e)fU(j3X)aG;YEn6Ei8&*PN^RL@{NiIZht_*eemI3ZrKfNe-Fie3qqMa zC}S%pvf|2#7hR{Sy9F$&h|>YwokacNQvHf7C0V*$KMZj!T$m4__HQfx;mh0y?hp65 zWr%#C{u+8Sy$AKHi1xT#!Gf|ZEXg<<1VFF4K>l>>5B#U`k~aSi<5|EIa)mm2abXyJ zT8<|opyeH*KUC(sPVi#A_i&Ts9kJi1;=!Sm_1SRD#Z4YQjCZ)g`{4Hnu}3^@vL4i=dIa#?EMpsgzJl2#rd%rs#Ok{c`{L z{r>j48lh`LL(^8bTDGpwmWT!_oqV>tzBgXWan1gaupu4nYjTga)UWA@>`HedmhTR) zidvoKu-Tj$s<86a=0wUC=?dt~y8OvktoACbRHr+9?W1fgz{*>k0^@ZczUj(TnVnfp zFze7RXjBV?ESQS}5vLogUa6A9r^hlzoI`L~kc`yQvWA8Qt46I%D-!5BD_9 z_641d8KHODdw8Hv=v?2>zHdWv^(sSqPjvNsb7Uy#=wv~Aj;Mg66mV1v&mB0D+p~Hs zd}tT+s)ZV|JlSW?wCGGStK$sQ;2Uyxc6D^L#a*|0PfuM}#Ho?18~jK3@3&0%_-xTm zf9q_I;D~qaO!Rr$Cz34^a;*JSyVPWCSTmoD3^v*u*UUA>h8k;2eWWh{KP-C$mbJp2 zwI;GwtHKb?8dM7k2~S1KI-tkuOZnoI$_s~A@^E_SDfO+Q_nRUuef81NRz5Vet2;50 zu&MK#Wz3`Uw>Ja~Zc2@Tp13g7-Jm<f<0Q(QRUHb|M(W=7E|R$?xa!cRO_44=kWO!`Xs-7Xcgnxe8I-q zMV{@3y(}=- zu4zMxqJrN&jO8|W{pxdj0v!>jQc>lf)HMujGGda2+Gx8sJ=5c}N88`9g;I`+mOj3H zym73d$BR?P`I9M&FH*B7@9o&!#lP3Mc0K`Ku*OE`QqiG=L&70L}ko7^fZ zJTu0%?jiz<-U+z27z}4p##|CUu=J1k|I&2N zh^@ZW3)K#rCtyFk3eE#~D=!7bKffCPdSfn}_$O1V%|!Da(m1{8p$;}i@_W;Q@R21B$g zq<4j~O6Lixv;OncZ3#oIlGYU4IU!d@>$y!!x5avta@p$T1{un55c&h)trFI4g+^5= z1KE*|*6Xk+hwa`-P1A;J;XC{f<8f!`L zic`o~R8$~r9liW!inqusgN>d3z8;xAoND)k(msPs4uQ|f8oL91Eg^Nx-@?g~t#tz} zL3PAWe|N~y=(mitIkB#44J2yy9&7#?n$kNf8#BI+@pyqY=$sXh=Bp;-e<^@ zMqi40!D!E_{aGVABF<&4z9dkXQCgfw7T76jPMHuWt^kxGOuKlK+z@H*@o(K%H*#)| zzcc26UWg0S(l*=Wwa2>s&6~Of#Q*84QK;B4F>5Qwky@VN(F}~oX1xr(ZZiV zH#Y+hDdwY@B1)%_b)t6oXhp*Lt3KLtgNO>lLlf5%zZL3f^eDZC^*ye(n6tU9(a|7O zyOMpO%&gg4Inv^8uD5l@+S~j1*40L9DCN|*^;S9~ZXsdPtF4~8T2BHP@2rd`D{F(c zP`uJ)>W&MoAr_;>8bRSb8t0C(ozod(b&WC0QKcrUR4&MCScf4RlPM-0MRh2q^guj{ zF>9qJ}dwv?B4REDPE}k>6 zm!pPI{#T7qE3Nbiu?o}y^~n7ab?Au`E3q0ml#^aB(=M>x@2Deb!~t)?tCL(kRvpGc z9~t|Ncb#v5z7!szFA0C4Kg@Tb-ckDP{5Z`O>n=Qr{@sJRbTJ-HOG*MEZM^-cQ z?cjKtZu>;&Lhjv2e(X7lSX};m`lY3A2Fv_9`VGKpm>pd7>%h%LE-ClxYUtJZpD5{0 zMgHS-l`?<5litfa^AkL(0gkNjyEeh^iXn@RM6zr#nK@CC@xzD*Us!?EPpFx664%{u zFjZJiHc&k^P{gMVl+8K{Z|`oUT!3?VRrPghf5nDji^AO6tMjV`7p<2CWbr1w;k3SyyN#A;RZ~K$6m{x8`8w}5w40UnlHlkm&a5eb>`X%~k(s~u1*g1JDP+sX1Gz%vP3g8i3)#+&iU8V8&RDRWfQ)dP=7~Nvo~V zCB)UduCKw9v{`Y}z;52R8;$lDpXw}~fuFJ;;e@EtG@%V~LKfj4Tc zv8GgCm6_c2R@tk~O(vIhpez3~#n33n%_%08YK=kXPLC(ytT~hcWwkHi4fn=uI)g@| zyj4LfOeRk{5i!f^gvw;x7CygOXV&soYfVGS6&`8zT0=0cEcITCv$4nLO2%ufRvzQx z@J$wbI3>q+2&73UWA@pNYIi--JM8L;RJNkRuC`-F*|?0N4sMvTtDP&cXU+SY}L4T4%LT;P@FdgW_pNrP)86X%L9j3Bb|?FSfqr)G2W=)XpjOZjm@!jZsjjk6 zE=y^RT5nP)vA|fPmcy`TvQkQh5l55~L^6WmOj4k zFjBkeVR~8qN~|5+lHUpbd8c5b?<00QfNu(c##x=5s7!WbD{a}zq$pCPd`>fz>DlA8 zU}R>~i4#yX?M~$+f#vZWwmWFAfr_buLqq9Gx83Av9gD?RMQfFQWwpOK=ZNWtC zmTkyctl@pZ%toYBH9F-!&G_oTKr_E~aQ&J)Y=P?Pu+K8P&T5MXk^WiTGHO-Yd@+a6 zjfM02q)l&)`YdK4;c`X1R*iSO&7BB&Dr&m@4QtXKM?;sdVqh&6kwY!rR{PpMwLzn` z@%8pppGTvzA#)@qu6=R%UcU=zKX;#)S7Cg>LJUeI7-Jn>oT_rqE?vsxgOIsNUsa{Y zu}%EM6?b9jiZ4E5u1`HRBA1D+t0aK)5Ipg8wmOont|nS{fTcBQS7o)L)LbTd@bekT zvdiI%qGcwTAR@DeR!}}QG;Zke`+GJtHcYbfWW*NnR3R#1bB7F8ox9qbO8KmOWsNcV zfBBZx_4TV;_|lmg%itPVJqOHu)K(ke%@$*g-q2WI-)J(KEw+!BLjhXk?G(m(AI2Jl zw}*9hc`~aHvpAvKnstjo&SE^~^Z*0+S#r^fS!^u9{LJX!`Eu9h25)nq+S5E33Xi!I zvhL276 zdZ*%kqQLSW;}!^BEP|%JE%e5izYgK-^YZy$OZ63VInBt?EUQOeMh_uKz>G){wb^h6 znFB-{>&eDqL@WF~6tQB~6(PYQG($Q9d&q;eU~LLa7@?Lk$l=}6WS7oV-c`p>yJH>; z*Ew0`bXGN=Nd~X4nD3jXr+*#v*u6?_n!BjVVyRjS99_hHieAcnO|)g8xkb|zODPv2 z`Cq|(iszZk8bTy_HT0T+c-ZO-F=o}atlA{1p=IUI!klIFqRh`PXwuRzRQqsHo?2{OnL-LW;K+3WeujFT*E6ozO64H0VJ2 z8-T4M#s>_f!j!EL1qX9c6sA&yvxAxh{L;E;Y@#a==$eTA-hcQxj<{OZCRjf1hg)Qy z{O|}v3HJGU$rL>oc!4FwsscTi1)EaEY$YTnux%b}X9q=W*i3#3XcmL3LkPZCCWpqF@cUI}Js&f;>i;L? ztgLp1c)Q!6t=Qb5P&WoFK^HcX6!5*N+sH}oDc}U`xQr8ybVBC5CCb+jtk7b3gyosD zj{MEgNIKM?aE1D|G_!NCe~Tuzrj4&bZea1uz2IsZ3b)S>)s@cqNq^U5qrW>^TRNjV z=wB7&$InFh0m~Bd!&)vs@Vpa*cpLYWF~r#@98buZh=aY?KzE)R58g}Ty{C%rY0BP< z(*HW`y&=5!JH&p2T--s#TCq+f*7v-PcEgIDN7AxaHI*lR+|{E&wx9O?=q9%D)cJ< z-wj7c$?ZirlSSoWa3-|Z{}Jt7QtBtZUF`ZIT#cY|m)cwV5pd^~;P72(_X3X3RLeMn zTWnA!j_{lkq&P&35&MlK+)U#uFYLgMD`PMdi#=;WAm5wSh?x)#Qkox_U!bS9nlR-l zX0fp(HnXS%z=05+NqU9VVQ>U27Qe$7bbnDyZlf>F7+-tW5ehkQa9^#Ax!6;q!*B7W zv`go)jJGf5_tWD+w>!uVbPY81I68O(7r3&d z1kt!*GB&M37Pyp)#geDe0k2v3$9uwDoaG+`A7_gPZnT}-<Svu4WGb`D3DrCm{;Ah%8Ajc}1S_DeKJ zD-G&eQFuL5NzttQOc5Z-TYQOp z8SLT04~zYkD`gWO0VuUeJ*VL4C?Z@Eel(Rb3xm^~DFa~90IyUhJ5Tfx9Ecd`EDnRx z#w{ovjLKR5sJQx6raseU&VmXdolA>eL2t=TXh#m5EK<5FISm)EOabUMd^`jm)59`F z1far|t##@Ntmz8QmPLL<#VO{JA^-@ZNj6ginHQ&0O4%CXs572jp|bD zgt_j)kgAw*8=^Hb?COIWVM{Z!&8b$F#@Dnt*z9ahwXy4FT>mf2<4eTyJp+$4#_0Eb z6Ai3D(Hr~JT9(!4viH`oYnIuEYb5o3cFpibbxY4AXbfkr(P~GYALCsyk4mJE_9A`U zB;lQU1%<;7y5eikCfioR9VXWm;dr0IC&HaoCa!=3eSD<|7pPab8C>A3vQ2_x^DOmq zpJ*>&o;fNc6)pgBln3P5WSf}JB#serZ1BKgk}bBWt>Anrw5jFJ7$f+e$!`<*-YD|D z+2G0=41~&@Ss9j5#px|2tTWXnE;3+ef<@?6MImXaZJKQB*( zgl?_EIt+^sbuisp8PS@uE@#%|!WSrHBie-oU0!$;HLFOtQUV{Nr{zcx>p=PTG{k31 zOQeV$(cRWCZw{=QZL<3f3RA!u9%B&9v3MvGwbg`L zJoJd#6-j%e>%07NxmO`~rUvT0t@SqE7VW9)9NcowQh}2SQ5kOQ0wxXE3 z#65*M9K<{*$v`i@pBa*&vs>c|U;;aMN@u66wq*bX|B!g3F9(yl6~<>p2|`b|~;Cn}+|;i*F*BBW)R9<=U*GE32q%naj4c zRMuGs;r~_Ed9(4&*?665p;BBoOP23JNikQfn1dt6YfAxXM1G2k`K8bga%m-EF3ZqK z2PCn?);F+#9AF?)`jtx7Xu{fhxURq1tE@Y^raXC!QDgy_Hkz(a2 zCG*s^$AqfqFjLoPGJ_3aV^i}`TkEj9d3B_e z^Mftw_N`^9W;4b7pW5UYR421p)djPd&(%ljGqucS?G$a+7SU#H(JipW+0x>4$!a}| zAN>(ls}%M33BJ%NyY<)#vo+#t3tP{g59UU;m|QOZ2)lK?rml6U{w&F2#(_Q>Et##5 z>&P3Uu+~9iBNDF-OFWQj);g=f14+Xfymki6;Z+h$vPtXz>oAOO#CGsyzVC{BlkU)Z z8B7}OY%EbSvo4nV#`s3++rF71-!%7Yc>u}8-krfW(6)qpK^)Vcr7?YlNERwNQFtR$ z3Fi+BmXv18n`1hT!M#=nclgWVxTsu7X3h#!gh4JQ`Y5(VM{k#4G?n<52xthZhVDPD zA(`0DOU2l=WS80u%37>n+t?E6-?B?(SSVPx(k{IP`AcU8vzIPqxl9-pN6g3%B*Z2(*QrYK0;Z`qzp`azh95L%Mv)>E58dv7g&l>(_cKV%~^5 zn7nG^hAX!ug!Z-3<{fL|)o!0&*0Vn{G(QP2GdBZ+p7c4y-G?~$Y3caN1Ufgi6)R*%H(!^#9&u|{_5aCtk5ZEz#N{CVCupK z|KABiN0DUA+KD9$JcEg$ofSHz*bd_jv2S#ILeeS89tno^?d;=WydjNe-`*l`MA}6l zShIjy!5g5wDE@2I)5tMkIdKkB^&J_3m15N5A8Cmzcq}>&55LSMl9gR2SvYm)yTuse z=2EQD!#`e%Hs;4mS7cLq#jN%+j?=8_q(=aG`?GZLhP}y9Aag1A~*v&lXSmNMmC-!Kg5{eK6-m51_(9s$s@+6n|&p zq!_>33^*yadEs(w6ER9L*2rR;2TC4d$phC*~UEK(xKKW}+SBY#Hp&6>-`>e^8V0^!W@}1vMcn5r`kQs2Q|52JQuH_dL3MzDB%b-GlTB*;ZwhoutLV^Wig<=mw#kSbEwydOWQ4hW>`blhDD!LZ`^F)FR z6kB5>Th?hM{$nvH?)#_kNXN*lMLfpprCvq>*YpwXG22bVBOSwX48vn2r1r6~h0iiJ zCLup1wpB0oaSZ?&U+n@2TqP$(r;JCe%#f2E>#;uf2Oli0V zW~iOUxo8Ui!;)~9o{A~zT}ZNY*1~er%WYYCX^*V9tRTe{*>0resN$*J*(|2GdP+If zUWzJ~c4;?1tNhP4y30*hpSD>$|4VoNVc3nV-`ml?7vFXh%Q)otMzLttW_5bd1-acT zZZNT*+F&9|5iHCy?@?fRK4r#Z3cQPqFDVi9@7r%udrX-#`Ar#R&z`@z@8nv3%NBkq zcebr3&zqmVgAty z*@j9Rq3HqZJj3Dm&e6;O>B$Hk{=X# z#$Zu|Rs;pV`L#^7W*00Su8_6Y@DV%L_~8 zvQ?y0Uff&M3LOk@y@e#R8eewMksTZ)=2clKmAvGwuh1#!(+Sm5T^`A>vhNKM;~1jD zE^cKghBBxo)LidT8`Njpd=zMp2MmsQUv-_&Qqqt&m+eKufc)Lnlz!B9B-1*##wpY- zE6}&fwkIvmJ6ng9XlxAs%Sl=jv6v(ps|JmoRSuQ=`*cjQWx0;D=u&g-nDw*C^WV#u z?ud#qZiNi=FDG6a`;|}<@U}aSe3<2xJnvJ z8$xVsO|ZI86kfjX^x?6XsJJ(bd%oDFfjbWw6cvwh;uu%=*aX(s$(J$=V65-Yq;WifG{B>?e1>pRmo-2#O0z`_1e+T*k8}dmi z<1m;ta+5WS{4=?htE9>rY!5I)ZAqKoS)n|+G~z2XaB5W6pp0euNVB3NsK22W{&LQszVhH|nH zlv2`5N=sY~Tvk!cV<|>9SMEvM)2r+?`i=K0zGeH!y=mi29ew7bx2Iul;APRZ;#!@! zmP*I*t)&tV>m4#>X+89ewL1JiTS8;p4?qi2%xU~aiE8x@{JTiZ3=3`@E~f)&UT9Rd z8D6=#gC2XBF$+}OkuIj)AQu}0mI)cfZxlDOMa~YMKfsnX(NDz1XeGyVpcGHL@QYG= zh>I<^_m%SYB)X#s8RKJVY-|hrm5x=|e zRQzsVd6VcO_D=KScbBl-c588`KXYx^avF1Lpo9=RE0^XKp=KmbRAcwsN?lVQaBI28 zPIqFMJ$5_VV0#L!gnc`Y&O%IVFQ8l0EK*a_7fh{esXR$b7HVz_Px&{Mw|Q}KQy_CK z25t1!d`-E6ldWIg9Vp?J{8E%#47dM}xA%aLt2)!h@3~VoGn(E<)B8v>8ckL2vgB^L z$lX}j!uH^XYzNa~NJv5;4N@Rq2#{Z< z6bYJNf);)~*;=baN4bpXQ(=M~Guo%fdQ~8#x{vhS`kYFfp z;uLMJk{S90g{egkO8WtR2+SAMg@9Z1<5KA4C^s~?^jXqajY6O#-q(-D)gJ;(xCmcKJ**g4{g8Fs0h&7 z+F_wzrr+o8KbNENn<&TX<1al&{m^JhwI0cRQBsR`F6lX2!;pW;=W2E0`~m+ffojml zZd$8~_NRH{L0jP&M+VusW!6F4dI)W$BO`|fW#=5wDF`jqFwoL?*6ZS~A_OsWw4H4R zMVyVNg6AG4#papCi{K}OU*81JZs&m6)2^XhO2DbWiGt#WVgW-%NZpX$u+F$J8X0`V z_*98u_>gjEM<2X-?>*=}RsDVl??PBU;4vuHlHe!kBaYEJG9q)dLUA+nBFi( z=7wGC$lB`TE8JVE?-lyNzn{%7(nOPd)P)%S+?-nAc+l7c~YxSW9WTcnBOv9)HG^Toy|B#50 z1mZmBBQzG6VtHAtCTB$%u5>CFQ;4)vEt(|j9o-@sm&}M!)EO zg1U@xrHJDy>w*lNoojJ7VtQ-VY0$3FXS!C<4kI+! zW?^-{4MQos(a;IWmX3i3S6LUh;tsr#T_jdje0khLcG;q??x-UiB;g1|84~?zIZ8Spxk@NbQUtZE+~_gs>AUxcguzvwU)yJh z;t>4ucV}jE z&)=sQuLkp@{QWNi541VBxCT7e23*wEbwK$$u)sjGv@Y;>tR3u#e6Z8vqczp|bfe=m z|@g2mU@>msWWmO$(3 z8&U*lU41CkvUi~_3&Ty@DfumLramBD8(fvHa%Dr?VwY^O#cz2z4-jTkr=Bn@$WC$y z!Ah$o8K|SGg+t+3xb6~iZ>vkl)F%CFgIvoi6-tF?S@-5y{lbdZ3dUAtJe3;it4Y2`XD+i)dX3WDVpvN}EG{3O}Vyx_Vk@liLiRdWwk`q29*S zKgmmJ(`mC-TT->85imS%(Fa1n%76=_hMQ9@Ouwxdss-mJK} zAfPK2-l=%!D7hp+BB4~$SRfI|8?Fu?{N0I3551pHEp=7xqaB5gBq!$n z$x*3WimDd$eJq%7X`lC_yMbDl1HcM#G-HLl6u6Ie`o6#$TG2x2ya_(R)MuRe1j8r( zo#MQu%7QO3^<7?~XA}KCsGd#m$JhXMFI|(Jy@Edm_4{bjv(@UNb|zLAV#1iFw`9h=ZED#r``TFbp%>{qlX)`fnxDA>?O zp{_@yy2#oI(a)q*@7M0;{aqZxFV^Z>&%F_KF|UefHycFwRin0qq(KloO_&_Q>XXT9 z^{s~p#7f4ptaI>D!ANeDYOMLFH2Hwjvjg`{@$9e$bwkqUBQb@GjcG`cq^{i&*gQt( zT3pfVl76l4R1U*9)3bwGko?{toE`XRT$^Fz9oD*W<)dg>WsW(~)cpnPZ$v4IF&bbP zqXn#PQ;rLnxJ(7kG3vHOlf;C@SQF8=0@ydyF1j4*C{TqOtWBgCFQx)E+j)>kjk_o8 zsi0LdFF|)09k$BO4^6uLa!;d8iMHJZe+-JHQ{tkd_5v4oaM_9>P)TSO4cbKq6os`T zvT#agJ)r5bUIAaHsRxJRMN>!2YJCO7okl$<`e08W-PKV6WNTRvo<_|mUEk1#$v(;W zG9qxmmsbZqY0Fy-_KPN;51l$U(sX5!w%5dH7-o2>lcs(iX0(mM#k=G@CEKCaIh0 zxB2w#T8}wi(#CK7;GDLv*Ny;9ZO0e$IdXsC$&nRmwNItW_dOn{t{9lms(rxA2g!>* zWFKLEU7e5t&1#*-k62Ef+ZKaAm7B$zb)#KO#1i5@7zT@wGNBfMn$H0YiaK{S#-5T- zIek*dX;O+qg@B>xh8m$BfGS(t-t83wPiw)b0F>XcBnVX@_{qtxha8jvHE#qpj*Z6E z{gs@#lFO+{#8cTcQrWagO;&ENtX!#vjVi5Ki~s?S`WRBlL5QAZ@~k8^LS91bY@-4H zd@f-v*tN0-*A(ijD1a{raknHom|L586)s{e*tQ#NQIA22*QF7;VwA4TU8$U1{hEMp z$(tJAL*E;y2lRrLSv)UW+cyepoBUW<+q~7z_rOlWTbEtif2Xk_Y;E5y@`RU#bt;?R zXAb3|Et}7@oVfA59JXu3uVL;ee^VhAh_l1SU3;m=ot~^whQHcE0pubNgQDvBx0xKp1xla2nA&~VKzpZ z#o9noNJJD?2Ew{525T$sY7^WldI96IrE(t8|6=S^8Te~|GDkX0e1wGHL*+8cLh2cQPVWla z+B8aDCif)t4zIBU*$3|iE)w@Ji!DK}K>-p8ri}(4ehCAq9xZ}$Ja;EQNDj#4@Z0cH zWH_XfaEpjGlY#T>zDlVo?yDn1Ll` z89=(SJ!~-g&0e=6peRM{VWUC%gTL6K(^)#4>7;aJXOLCAock|^MRFAVtz!hG&)tUz z=_|1(Ab`F8v_| zvSVn#555P2K<*{1&lp{w-062PnT&@QyarZgz8#M>TZ+kR$E86%zd=2t%(k3D3g+UYo)HTa|kw(WB-w%WGE{yxm=9p2c~UmXPh zhtECA`l|&MF4N{8VyuAEMNh$WlyG`@-M_JVIl$3y_PI6=Ci&&-1y=y>0&BD2PqPJE zfBZoptZJ1g@xZ77lZwBn+t9@l&n*b&2+HRrdax)dJz(uH69xD`^{GYv1bhxA3{yEkXr7IVsbrrSl)5O@km|=)AnY<-frrusV(>I@eaFG1+^>P|T5aM4?_ZnIk zVoy+4s{T&24L#xq*ykh0);7GGeV(pAj}^phpKL8TIFuFdQa>j2HB)c9I0hHd&-ugk&!K~- z;|j-Ecy<{dQh_?fF=YZjQiaeIY2*@l^|mhZrpyg7{;sooMXYb(oiyJO>dMvMDb@v> zJwDIZKgXHldC<~d#W`d-yyOZfIuJ}Er)e?xMR^_L0&{Ai_D}I=o07awdG^mr@?K^2 zTM9C#5R}KE5FHxEqs6&Fomg8f^aLMx(9>6zd=8r$o-fuv$9Vh^5}r8%z9E_Hf;DLFUAg%&y62mm&$HFIZ1x6tp2xl@Yv>jbaH-R)pdKz{=!sT zTYX9Kd$MQW;GuW%&vnlp8JfIeVLbvJMy*p9otiA{UDY`-f9+Ih*G#T`G$c>owSU#y zj*O$70=|qv_J4yfbGf)HuaY+6;q&6lU`+~or$$}SbvX0ADqRpq<2bZ*Pv?&ad-@ce zc_z>^nmC6z@LTEk1|b)3*rqz+-NMfcwyff~$fzA#7EB%< zFWbCA&^<5Nyf9|0-{c?Qndo<|$1IkS*>(LuNeGYHG>J;C2U-Y|ZKwj6ejBNUGm6$T zG36C?3kuU50g`Jl;?5KjBpWmD)<86*Ac2GSWWW*{-dY&kWs+;-;cHEc7srP>9gal7 zTm37~yAZaVm@fHuY)&dVlQTuXJ(~3dhw@Q1xw_5QRSuip&@lX7x*?C5xl|?OuV81P?&pXn6s&A@Td2T!B@i~~=-A;Mhc4iS z3mz?iu0Y6#^4{4!mO!VIIKqL_R4hH);mvIHl)F0m(__j>U%AV@W<0k#;n-kYHR$RL zxl}5BPb5^1*^+7d8iTjf+nEg*eVv|kZ!G25$>xSTw~aiExhYT->$Xl1+tHCW7-X?@Z@y9?K#p2g! z`HtR1q9@tqO!*;sQ3(=jUCOCp5He~Yg$sP>3{u!{vj-h=xSZc>Zf9iY`90;%zRb&8 z9pq%S9QPJ3i1|gn0US}|?n*q)X+k)t3JSv7>CQkCKdXWQ9?8qO z^<{5z$;JPS9GeY$UWljo_P1buBo(^e2WnLVeDLs1f3taYOQn)_3b(}XpNf7 zyC$Oi4L!4m${LG?{z2t8(bpRLD|lRu=U@ymknleSJ{WRbBBzTSISYi0Eo!afVi7zvh){ zZc)j+y6Ix{K$}ZNKiLzXM*@QCxh@i(;2%3XOx{yHOukC!e4dl?7@(b$E~VXo<_g?K z{)qjTW+Ac^q7|p7;&gUtjFp0BQ9~BH7?p}+UCSfB+zRsX11u6;39*J^n}eQ(nJGVD z3U^q-!<(|>yWN5AqoIjnaQo^p#e8xtavCE2)|UR#x?yE}D4O1qTa|OUvr{SZMwK(t z6Qr!NkhN}ebq<6k*9MSBFcmCCtv#{)aDc=j9YLU%hockagogEz`3h40Pp$X$K2n=J z|1J!p7qnzT>I(RHD=IuF8i{=bR)NSADrMkABDzVm+NH4`$?vPr&qijiRPcKgR}UdF zvqS6DYXbgAfhR~{!IC=#s{h=kReRu>>TOSCwHl2c8)Pjniyh|~*Qh}qtRRD>5KCVR zq^B}YOC0^MtTi<|S=AClzo{*|Vn>gdg(WP;(Ua!*dGeFe&ar5qd)>Z53TarRAbPBG z^|={_dDBUG6X&VGRTqm(qgo8rcvDRo)Zm*_n`sH2Ec`+qcpoj$HQwUNu1u*HGi!lD zE1EV2`et*;BPC`uTQy(mTG8X~junRZfY#!(!o>|^#VN55O<}I+SfN!qM}@ZsD|Y3g zPM9e+PgpcI0q0!Wc$k1H;SSNY&rd0KZ%-CuGnb4aV$UC&IW#d@+%iZ}Ng}1~@IB0q zkrx7TdB={)@`2eN*ip0PLmP9|Sl9ISuJm-?lb+d~O-^P#7^g0*y}QvS7EObonhMf? z$c1nsh|36dWNE;1j7T zMW?8EUg+PGn|I0N=}0#(v&RbF{;mC9n=N6qhbQ+NFWKDgcbu*Mx8ALb_b05~YxnjH zUzANQpXvin|bn>Zj6;Km+g}&r91R&MPhB2sg6uKlDB8 z%RZUBGt$M&_H1k49NDa&J}@588xvYXWX-7!=19co$d9G)4kpxhNOP83)@3{Z>tHs$Fc zQ#nIw=#t4M|LKN9o6NIkWPI~PvCESQTSo``_gbA1r-HZm6Sl&tZoAnOZ@0M1+l{7B zF+4M|cgNNbtP8H*FqdAr@!+wWZgK^TfZ#TkHttQ0&c}oE^Xug4|}g5%X5bjPc3+n6l89Q@;nK_yR>HU6|_(&*W~j}b&?weo#ZQF z@d$O%GYva8aHpXrpcd&+L>53*gq|5%&l+qV**WOFIBQUw`Ij+Dv{b2A8|*=;Qo5xS zTUTDp{}}enGqkI6-S)vY3YW^@_ry4k+7;U>yj&|00B>PN>Kl5 zMjJ#-Y9#`$ZH)9N9Enn3_N2*}SUnJSq(_paNf{sCt=MgBOOI{LZrRVv$YnmbDf)Ax z!8i{*zZa^%@34i-G3%jS+hy<=FGgqj!b4+PxBjPr9mUN9fxWYOuYvrcqkAZrneOS} zpV!&7nH=aDngRYw;6(*+25(o{HS&tZU7@0(AAMnaKV4@;M)mVTicq@)X@MH;-71)r ziTse#V6i*e)Iqb?r@l!3q&4N$dsTX5`ZT%pzbZyxh2CYe={*X)-&_4E83}Y~6@InK zW~+Xr`h5&7_yN@uKSXh}$?ZdRy{V|f2W_yn%5W|w&>vvV4s%!FTrBDMd2Sdw+gYql zH+--CqecrXd=^d{smRit8@n5w9PIV9Qv&C%ZzJ`U8dfkyQeU4;o)nhuluW0%L}S1L z9H}qX{`3O5roFyK^}1?%dW5arcI*a$**sl^(t=(0wiUOnZdj-Jjp31ZZJ^5+F>zE@ zgB0F`5j@4c7HnavGf0VJbETv+cx9|#&;ZqYy^raGqN6q>F>*w8LF9Xhis_dH?NBFk z83cP1dC=3EanA9)wk7CUx=y_2VtuR~-S-Z8;E?bl}>4BHe+)5Mk zGD97uic*=sx^&B)$;)QD#!uY7bMuuUUwC1|%Io$|YRL%&@=PRW76vz;=|3?tncseD zeC`7$Cr3-paxuT{b>kteLZKSDbVGV@h|;^5s~h`YFZ$c0Z(phx7WGh0*=bO+1NQ9y zFF41CMLu3jj51C2P`#2?N-__<9s$8KerML8g7~+)FYb|3LrQ!vTZA zW%pL}J(Vn9$!6`q8rQRsFS{5$!*~dbyF$k0T3UF7d+UixS&P1cz)@DP!!W}~{TX?z z{|m{fo{q@auHN6AuD*GvI_ymJsmyAX!mRG{=uCR8g1;nv z4T9~D#~H6}v zDc#g7z-C$E%ZhOcoIA+hfN{}MJF;4);c<#nF9OLPwoxzw!5gcLoStlo!m5Aw{POjY z)8xaaBd`CZ=eFBm2$=(26mFw*P31p{kn%}9ot99lZ(|mZu`7rcHy#KQaC&Jd5mxO4UZJ~8|wX7V#9+T12Zn$jy6$W z0G&p!HWP)NUo?xx2MF|3(t!95VIE=1P;1a5IZP#O@E9U_^2NxQoZQ$&s;?tj60iPA zJ`@>}SFh-T7+L*0Nf0&ch}HLy!}97a)w|^YO~0G|$jNSUKwfcYC<6gD5l41r2}34KfJjnZU`%@4em<|e_I z!A4@Ya1}M;3Dk?2LW>lhh&GmUlz~zVjb_Hmi>z`7c?_$enT@&y*2v4w7MeI_Z4S?C zFI-}8Mf;bv>4~&UajjJJ3@lTz^h}EkjzTymiER$wOkOw>xuUw~3S@g`4FZOYv?i9= z{)GByo(ZgBI6p~qiO-j{;h`Oj@1Pwtr)#@XW;Mt(6$31&!XOIXoGhqQM9PNDE-21_ z<)6trgYCw)4y!lsPQ5$!W;rRRF3yc=ltE=%BsIY&s|xZz)J_yR_Z0to{$n(Sxg#V~5|q)*La{ZwJS&xp*)# zzPmpn-SqOw>Aihxuin;84jyc{zcIP?*oxwop|GrWL;2qM?18~^+o+vT;;n5hyp?eX z1#q3j?N3F1?X3nVcfIj+C1Z<< zI{nafMNvpBl0#?hV9ao&%e)&~=~(*GHSx)uSI#FQDW2alm6+_y+9-LDqum3JuCbJ% zqy3kE(AqUK)3$KN#p(vHlr2boQM7SNEl>HS$h-xuf!Gn&p_?ODkir$huQO-6q@K{d zLU`Y~ZL@NvENWyy%F!Gc=Fh9kVgVITO6k|=qYJ7WRZZJDzDeyO(aDPk z|9r*gl+SJlC~v@-8VG-Tl-xse02jB9MzO)4o%5Ggr-J~RuHHuHiP<%I_)4)mKNlXg z@J7yL^jGA*id=3)OoOpDVVE&#&Kg^i7xq0Sv+>`-oK;Kp#+->XG=)uDy*b#98(SmD z`mkF zmBBcJykk+1wc>#Vy6IdXg6C7a49i3-2wm~Iaggev;vK2WXJ@Y4H`Z3YNkLAK#LWJo znbUc9@vU2?j<4?=z3lzl(;FsB=C_q5i#tw@gcR_yUa`0_-CK0^4(E4Vy=udIE}OJR zdT6);FjHQXtI;+Zryd`5a`5*yz~!5>B`d;^*ks5U4R>k!kIC2+Ig@JLi-j`XII)k` zVf}?%ZGxqeX-F5S`wDrGnxO~o*G(UO`^NM5vlyuhi&~}9RsRXQ>-{UQ-BWr6rxyMk zTs-*_=FtKP7TPMAj+y@{bc;jS2@KX!MblM*+B-%Tt)e}Et~Q9&kb!EzP<1OO^Yn+> z6dJweY~(TWk*$esXnsb-^2-xn!hkZC}tF8%%Z`UFA!<^@@nf5iQ@C+cp|? zXD1THxxwH?{7sQy?9!3p6@h`-p1vcS^TDw_Lxn>Z&jmtprOwjs@LSqG+c~o<+l9kU zX7xq69hcCxi+|)Z&~;cqAxTQVZMKRvfZUaBG&G==g;}JGsG#Zq?+})cQl&s<3TDR+ z$7^?Fn>XUlj70u8_lz$aG#A3vzeb+uKqm2W{GURPIlBUuzSQ6K#bB@;w?l0kNZK=_ zI_ncgbS>5W{G&z>jG;Z?|Fmt_qHQ&KRk^4TROXVrI!98<>OW?~OkIx0dE`+($h%O+ zh_fx^Rin1e!BXmIRTio>hw?@sbINX!S-MGF4M(Ae$8@!Knfz{>GEEN7W&;sVb=(~x z|GK97d{W=p5zxd7qhZ2VOAp_6+r#AU-)`8-Fvb}^BBPU4Xoo~6MtOeMjs`KFUu1lSoqpAr23!VAhB=B z(`V1Z1@9N~U*R7$^PN3QePKYa?_*dFm`kLBw_-Z2Ts%;TFSTid)hw6IqlGCU4n>&t zPdIbkah=z*CgUE+1@+!8-=uQP*^_je0_l##%8)C%GlTpC&XG#oQxM%at=BinKChv_5 z+T_)Pu{nArx5dfkz11~PVBWKrJOKHP(#{`v#4M7haXwU%wbMe(q9v>hN~g-!m_=G< z3EmUN^hLWIvF+ju%{uAM--o_Vm7W;>)XQN_Fi)3 zTx7QTy<(fw7q;omijZ8sa+-`*?Xdd&I6D?MD(G4y=MY`NUxd0$RPHlrj9MgmZx{o1 zgph@iJC)^BM~3%b4`CQyOukLtJ`|oyze`3uxvgaT-s)S(6??15s$M;foauJ@Zv0?O z@NVjdXVl6y3SNbXUOAxE1xu75Q8A86bpVz;^@@UuxU+g+XLv3WBkN+3t$+FxdEuF7 zs&wvN};HWe9$fR<6je*$|QIFeKlajm(iZFx%$~;J5z)n~jC7qb+!i zxQ@iSo@bxWG<`1G)iA>VeQS7L%tsHty{G;;_+13=zZd(8AX`KC74432ZXF`>1e2C2 zpTmk&G$sTW1#AXw4G%l845}@O6GfzN?^L{R{@UqiBr4rT=7x^XBsqC z|E9u5x(}!{YIR6-^n*dsm zZG-lLz6<=GzN=ZTM&n`aZIP?#8`%$)tA%&b_c0!S_AY5$*w|3P7JEDz`#Zc_)Z^&P z6_{Q{xf-u$BP7X8jq)Vo0T8=K^D=^!e&qf@F>Z6D$5MZsJ+6FDJoWg{Dx){yBwff| z+z}p3yT}uPpZ#G4GH-UkBU#*&f!9f~_sks43}=D2yvb;-$Sq8sRxTQAlOxL0^tjL@ zPuJ@wFT%uDovOEDE?RE`vVAnPWwE8-XB9iSh?M<5C2G^9j#5blV@!t{;f2 z{R;OxP|J8ldM57~>obne00*e2@9?JF z>Ji=R&~={EK)!nfZSvp*4!K#=Q8C#oCX7jDG!h@ z$SiTcwIe$o|JBw=_`_fMVMjjhumn<$dlHJT$mOk3=M>~9;P{q(G zp|yJCIYgr~z*dhKjV>X!GW!AOhe}y0_+{1vb7;O9YZG%C1H+A_HD)f>%cHlhYC>{b zDeoVQuO7*?B_sS2B-d?qD7QNtuEJDwe0$-yqKtAavmHaDLT%Jq!Ey|Q#R$daq$rY2 z_Ga|kfwl5S&?U1$VTk~BOs#fe(S%fs&Zf0O?ZLE{4HhSdq$U}L0g8F!knZ&CNT>R5 z%D)h5(;c7pcZ5%@O0Db-q*fmsp51N^8P=0$*?Lj&G6l7|jr8fKru_Yz@`Hyr_N*D! z_Yc$kieV+O{dpVKPkrB}QVsA=u@A4Se-6$@KmUK4(+F)mdCn)cWqm_icq-q?VEbk# zAfOWYEFM|K0`Gf2j`1FqjmCFE)bog|ow1^3c@HNa=68j25$sV+?!%!^JQvj^!0^Y0 z)`2I*o@o>Wd3oZ~ewa^2x`7O@nFbRIlm2id{2`%@*Cjq7 zv+3DT`hsdtuq?GR zGy?;|x0=?MSmqC`3_CY)Q_vra)+mbb#;j4TWdxiCigO5Alb$s~LK(eJz-ed$nnW}B zFyY!E$Ne4i*l34pkmHuvp+4z(gi3MiAT2)vJCrD`*deG0c89nuEBKZ%nSN1DsLBmz zU7RkV%g4kAOOe-;i%k(n^$z~K``=8<;U9S?KQ5Hx6x1b+XL27aCzO)GjZ|rf*o0H| zw$oKmAuS688U7N?y@8%3GztkFNFgBuQSpEpJw;?7(Rm!~NcMQ)G36tlN_rFu=M`V4zN_(_6LI!A6?oK;jh4sk;`rC=SB2G=dT9l{(KI^GY< zPG0qJEE2m*`R>Ko5u!;}U$|NM^wU&coFreYmI9O}TChL3DWVtcjV6nd;z|-D@FeeQ z4j@E1s!VIZ{y-jc8hb)1!)TT>$}k`Zv57Jm4Yt;Ins~+fjAmu%)z^2LyGN|gfLt_` z5?`r4>c24vT7+*4=|X+XW6Ba@;3hmVf*zN_{}B5~_u^ebS?+$+$;z684B!L_pV7+} zZ!6Z%HIyD0z5ulLsCNoTMuHtz+s=C!o(F0jT{5mM7$@8mgl;;j8Xae#rRdse9#z8S zNsOE&E3k*CKZ{>11{SUredO!P>L6JWp!A7nWbtL5;s2;SYZjl`$FJd@gl@DQ?`c;F z?@>`*2lXc?7GR$oXw%XBWQH03i9xwZ!xEU8*3$Q1gtFfe%c|OivMhk8#P$LMxu{-t zPh(l^Lw`AUKM+MGEGph`?mcG@V7K&iAU7!+N3@AdBW^Rs3)ZMS{rJI>Fc4Q4#25}zaD9O9{Xc?)}vd_#B;?`*x`g`7b3P(y#TJHYoCrP6jmh3ufaIec(QIB37xbGhOQqEXK( z#?*RmXQ5=|P=m|iF}2$t)bqdWh;>-oIvmp@)gL(5%>`6) znIcqh1l;9lVr6g88S8QBU5M=PbdN;*Wlz9a@G5nRfV(^HYm*@`#N3`w10w_5p6p(X zG4(2f9SDe9E|X2A<#lQ~XYjfm0ZSziuwkfetmkx#0D3|W_Ihl0Y{wH$=v1{u@2F3- z@E^hxEX;JCkB8&OJu&x#@9K;^_{P}vM-`XfnoQpF&xye4N8a)g7a#biu>3dg_sPlC zBwKw-`UzA&LUysA=Mgz5{ER}=My1emZ{zo%&0+ZZ4RGbrz(8+>i&Qij7)b`~PLI_B z{po<+ix#&p+U1MDDWuvyI$I(x9YZc*785E}Qw32|-l8(5r`2pi9l^erSW)mLW-b|y z52QRQx!U?M<;RdCVp2JIaR52(;`vd3X>KU!O!bF~+eX8#uEBfB9m?uHqrZPcu5e&B zZw(|Ylb5k1Mo(R2Px#G&%-W0diIFZx7u?P}C$sMT0dx^-jmBji2R)W3J*L7Hk1q>1 zt87YXb#Nsd>QhPs00tLR?X?9^hui~e5bZw#DiMu_QNfqdi;&o`51v?JT3RT?K&#Yd zl-g*W+VxxHt zdAM}ZiljPOs?=xGyR!4Ml??6C3jLn5mOFeI8bOw)fy;f7v z$`>8NV#1oumsKM?{%3wnsw?X^cX62C{M80UPFXCEe z5;|tcjP2vSkVr87odn`HnnU$LI3m!M8dT-ihn`9zmZGdIs*^+OGY=Ia8#i1@_8=zC z|LLnDWBG`x`XMLzuP?c~$Ktv`d^GK<{){{s(1eOHYxVQyNFjI=&I1{7EL2RA)G=2} z8P}$!IUXb@VnGJOd}(QH^(Qt7W3cIrl02#;KZ5sUCx0UisitE~-&a7tlkmv1SzPdZ zwR6D$1)tQ|Wn8j?0io`%4fM}!F^m}@5t7(nl?7{;B?qUbPNCnM$@9wF!*SB5EVp%y zZR*}C`!h-WkK%2y$#P6xy$a!=u2gT}N&cRIW2#He%apXwPFeM5a*_;KBZWXf6D&k6 z;ID|1LQc|qI3K5bz}8lQULH+?u}z$8I=(Qg!L3dp1So3nRNm>iLwRSsJijKV_O?%z8V`{ma7w%hRYON}YX9q%8gx>NN{PN`{qEjoY085i&59Q=NkU=bH zR~(T9-J`B-F`3asqLoObsIhWjT}aDZ(G=;1DR`7K&LwhNDY{Cq<>>)wC0O#9Hps4` z4qT9_X~;Irt1vmpjzN`a_ZFv9RHf-JPDcVT_Y4&SK-nI?MoFB?mmlgOqjKxc$eBR( z0SdNH-t34Mys4E%zaxQ&O|U$;6!eEmA{(CD z5ZSOxxy#hPEXoW&U}97RKnFY6!{SC{|2&$`ytMbO2~+ELH_g*h<Y+~ z;YPz9#p*4LC!PB#E`K7g1C;}52!ULNik&exG(3!;Iz+p&|Cz>4$}$fjo|r0j_I#e(5DfEI>Q>2k_%4_PBQGN%Ji z(nb?q@o?Ceh`U2UmoAdXMxzByTf*<|44A+l|A_hdJ2}c}a9a0|8cykn9yULXQmG<3 zGtN~b{{m4jPX%_e7^gaMgsni7hM1(%|IXS~R+bF`!@Upq?7hBG{fXLpiAsl~#lO(F67jVjtdNG+W}9D&sNDK{1-(aC}jeWaj@9%LLoYZ_;p5|yO0j3a^X41X0A2eZB`BWA4_b36QASG5z4yFY%96f7lT9T(zI10H|qDgN&O-{L055?*-vk zKgLBzt=9HRE(Ej!t4~%*^;c3UtIvua(@k8mUm>ifzTRsx9gXaxnqm>fRaA2}?-R0{ zqL+-UyLNXzF?~^K`gpIkblt|$%G%D}g}2XbITAZ-$PL*LPR4%w>`9-wJK{*-H{adU zKXTyusj-7AFv+m>Rm=uj0$WTm4=Ni0AM3w68^br4pE!mGAr$9Ne=762YCrPS1yf}v6f16dXIH{uiES))cLd2#+;kM!@}Tm8 zC?hneNCX6YQDjg#8cu(8N~(LwH7Zv~)MU4t@H6VzJv_YI5wNZ-7FSwtS$o4J0|S@b zuvWTGU%jhX+;#P|czr@FMQ&$>@q*tK1_lcH&TO{xbB*O_`A}*1RV!9pwYwx;$&FGe z4Dxd0kREj|KMtS>QE+6I=4D1g@OfdDrC6NqKf;m{oGV)ozRQk;I0OMXc@P|vb<1> z<3#}_ggUf9+G~&zAaL3)*pbe#*JZaU40gRL84YB$9_>gXI~F#*=?kx-cfOF zGMTVcJioAX!Le9jNrXF;9w&gK%8^iqAJRfN-qz7R5G@QuES{7ln7;6!+^Tg%+}gG2 za4hVLgaT0}YxO03oo(9Aush{5f;%_TE5)HjbQeaZl*@EhGMU!VU>UwILtpy-h1WOT)0O>Eec+rrHcB-wyzN9bRt!mnBX)6*~-8Grzw;x zg+gmD>YMkgplO*noixh0jBeF)l~B3rf}L}i*Cz_4L{Hr1`sznb`jn?MVF{%@$r=5H zJIkG0RZOP~1?`u7SLn=Bz1~d7oXoY%*#s<5;tHN*)ZD+S(%-*qmtRG1DFxHK0670Z zW@CMo7_TVzQ@4%__Ijct6SBChZ4tB0>AqAOG`qV3W@|X(=`0pIQ-yX{_` z{k|Wd5-}v2q7w*BGWP{IL{UGH(^*xgA3sac01&If3WV5zwMP=76PSd-l(%akG^4CPK z(hN#kej0JfY*17kQ=3D(0Ps{7DK6OOX;%hII%p43xj8!#vIp^J#ud$U#p7L>s0nVk zCQryjuSQSk!h>_G!JBY-d-R%4w>{=D^rhWhZTcRsE8#W#IPUW%65c@cVXe(;F?nrT zt*ygs!S%efFA{x1SOWE}_ORyoQ`1&S`s@%FV#X6&ovXwe(3 zex<37Jgvj~3Uz?vgL#PCzk{83scgeQ!<4F}GC=eTZ!(3)vWXGw_Srx*snXjG3Y*>K zUCJT8>U)zZAMftVrFE%r+RJu&h28G-hI~zJ9HE}hL#4{lkl0RkN^2bq8MQUA0~(@b zTH34ZBsRfx84fLF@!wN+gcBBbI%r9GERlgibfCSXEglY}3;vE!q?}Eyb1oT7;&MkE zTB9%J4tHwXI(>=0L`3HbMgyUU54&?3Cg3Js=7a6a{7}$u^VG(Axfzw&^2K}+8?DSn z_ogL_xELsxt)IVJe%;arIiVH#d-Z?JPkxs2r$#!;(eVp(z_j*C<5xSgD^)PlfUrcJ zCPobGW=Ts{d(t7xM9GuVn?Cwgmn+_rC>0Xx&3^sNlBPXk4rRQ(Q)b;2zAtGDg><^O zsy&x%9-nSR$(SF?_FBWQI6n2`0$LeoOItuR61k)>$x9kL>juh=Ind=c2ep^Foi=l% z&FaEY#A6G)+6yUcVoMuM7pyZAvT4*eS%O|yuDzqz&vq+=J8Yr*RgyZoG>$V{ul8E| zmszitV%P>h+|~oY8}9qvH|}22g8Rrmx3Bscs>iR(MLig&S$O=KhPjzuD;LuA=CHxk z=e2$sZCxMY{7Q?1YHsZ~l+o?K+<0(pIiM|R>{@Bxn8-Sl0dwoVf$$BZv~S?Lq*srK z+e8~O81xxDF}u4{BW)AW0No~hakfpEJLopyHL-1?X)Ot~(u*XWwXt4yE!DKsr81~l zQUPapm9Q^B+=FDaq<}lnN-YCNx8rT1)9cxd&?AA;Zn zGX{mRf{|aa?l)RlM1w}ns6n-ik_CnuIrv6f%eBgDVQ8rvTN=N64#tpjQ zE|Mh%w`?tAe&qdV_ZYVYnPkEm{aD|4C1}za*%bBJyVq2@yZh`hQi;X-07hc(Ynmxx zy3iL)SnG_Pw;wRpyqa0$8)tF(j{bz%cwYO=wvGlYL$7Y4F=lM88R`3AU-S{G?WbV@4Wv)m_W7)g=N*Yg%VX1Os97GDrxUs$&Toxu z?RK+qlJYPg|12P)DnW9 zwXEmH^-VE*=xJOqR3Y&d9fR692s!V>t-fh$(XqzA33q9e5B`2S9`!LHUtD66ZQu!K z@2WZF^JO|H(!eyDY6be2{T<_xhwqHro9J?CyHlBPtP&18IfS?yjZQic&LsmO(tOJ( zP}419gDkM-Qv1)cE27ED{2U|aon%dhJasFHz2iL{;YQ1EeGSxEAYI=aam(+o{;Qn0 zUw?a@$4gxl*ERnv`bF(09onQJWNtUX3?1hy4qqh>XjtWDsh4 zPOxaD=9{e4^|m%nqR_zx%eB->#?gM- z{W@{hr>lV`k&(1|Pjf<2GTlgOmDQNCz1yOKb7YNPT+GZ&^&0Ij=~ z=HUg**-ZLMVTsc%rbrL_;xempLSZoa9wk zyxx%6RY3@tXnhg$<53R{hZtL1=M#-HCvg-3|61T3_bbg>e@xA|g5aakNOv{nLF^)% z5b`}SUO-(oB>EE=g=}TbgF}#iUcwwutP7LJ@2XEiFkI3!bivEEh-QR3a4t)-6%Ey|9L+XwJb@Z9E0xg-V;}S-0m~0OW@AEZ=2`z0OUg zQCxrUv&DUs-8%`62b$yOGL3c8Q&stg8XX<77 zjU*?Q?WAQNV`WX9aB5qoEMmI%^1xT(w$oDZv=-_mJ;-V!<<^PY0czh;7Hg1VL7$Pv z+vxSXD%ylT-esJDXL@vLz0ajrSHMWSyY()=BV_jw?D2Y+9$lb$UvIy*V{#1m%#Z>>2aO>bS%`zr31SP{kZPJ@pX z@cN5E7DxuyrC`fRy{;P(N-VDcXgC+5JR~ffA4`Zur2>~^nuYUtF>ivFuQf1L5jN!; zeFFW!Rb$(rUS<*8LmcG0TW(z(A78w6%bvI9)<^bD3rs zQmGX?bJ=OD{AgloXQwY8v)f~NpRbs7IFd#3qmw7eaPQs~iNplF&eoMfYF+it>hY!zo8ysFk&#vRXL|}4itfQ|# zv0}cjZ+H6d+Sjj%IU~sxdwYB5 zr;`y!XEnC+;Dn!Sfk$LGJkaTMb`FHY{VC@*nfm450g$BB-696nI`Cbr!ebx$kfo5)p8H!v1IQ!c@iIDtUVZO>WAh5ZXCwi zQlSt&%Z>GN3p@B~7R`l-l5pSR)x-#&y_Woh-wMZic=gx6&gk}CvR>{f8O5JnHvZ@{ zBDicFPZoV&e=f&8cJ3*77^*+3qPr7=o`)qs)MFdS`cbvkYG}7>)oZ`JRz`!hj0UYy zvjbJ4Jba~j(NF zev+X<;(a`gb?3E+!*;?W6DX9t%4t%$DyA@U$1vg+J!P0nUjy%@;UQ(xqQc{~a{)8J z(pFMvro_sFBZ;BT4yfy@|54gD5<;q;Nbe408^{bH>eIfzXTsmVF;@~4bdJN-Sohlb zT<40cJG*voHa(q1kSqEt=C5S)fabCoFGwDbrQa0bzx%i_CL)d{{Kf>IQUNQdGf;to zPx61lFbgXJDj8~ip%Dzsv7g>DSIKx?M+u}L2A1-J@MLd76)^1|?p)m)fU9?;q!>s{ z7yQ;(8JsQ?()e`o-sx0oJmYjGpa!7fE3xsNMpt?yreC?v(=(BvNJ1g}9rfg*c3ULv z)Kizhj_$!gbhszjCU5g43%=~8!4T~WLejWzqSw+tUc4uq34BNR9C;z&IVjxA;2U|1 zcrV8s{#LwKaP5RoSkeH@AX`kJcY3&a)ec|f5JV1-G|1q)6aQM2d#;4FM}kq zPP~^xE`LD0R{&F-=6j*fE8*Xo6z}!iUii=A-v$8D9OL#QLHR!JBp2W|aq|G~K7qT< z+(GUT?zs(%$M&DvcQUYP{^*Io=7Wdu(Mmi!4;b@9D8;QjIe+*Nu04>4viPqLaZS_Q zdi*zyyXFd{*QaZ*Z+cZvCY$XWm|nkrdO)m|R+^@jO0_h*$} zGW(7l>AvLH(UT{-F+~eUPb_pF7iRMW#ud{K=6&vO_?ZQ+#WMf+xTy}2ta?Ob1AP(v&I12t8^U!Qpl=uG&V&3tUc}7!F|m6tVkp z?2Ae4EuG+GY4o@oG}D9n^T;<k29 z8~0)EQ{17h{Irg$45+%+}-F6Wo*Bce#JzzQ_FsX6y&tQ{2CDpT=Ci$lc8S znEN63BP^odbN|V`6{mzl+@+Y=!v)TLoOnF$vnB3>?8Zh0=a}7AP31I z_z-`ATuKg;Bj8TI<^G){g#0u4DES!qI1p~`2146CFSMtBf-^hQHmw@T;5Ar|cWyGgf$vK{b2S3lt zcsZ{?W;_+LjJEL_F`BnUCeht5tU&pWK zH}JFkMt&2&ncu?C@mu+A{C0i^zmwm^??%MzMbMti^B42``2G9>kRT552l+#Mg};

gb_tHajU_w`I*5;g7Mbc%4*@9hu*^cc}OB7fx&U)$cVE`;VQR-v{=4QnRmitDM|7k5?=A3ztdM zGk;PwC04i~RyZY8xWF!&sak2xLhV*HB{pM0xGJZFH!iS?cDmjW?ScBeX1ewQ&4Jo2 z>v&HtCtmZ~nR*HB!TP;?=HmH9`9b_rt~`0@@csqmA$C!%6x)ADZ2wB3{fC4BTPe2m zkT_tg>TlFm>i0tJ`I2}o>n=U8xUg{a@chyJhxRGg%>^)-o+;M}1&%fpC=`VPg_3feP~a%L$fk}SkS!cNpjt2Xi0rRi&Nrt zg>Ws2b5zPJHw!O2$u9EEbY@P{nb{)D%qd}Jwun7CCH82G(4$lAqS&%{=;#5(Df+v8 zOJjf9Pc{Cf+9J;2DRBn3*4wGQyy1R(!~GTYd*u$H$5*n8W=DZ$Cl1e_I4Im7tKYM?6R!fnv2t&|!#J^m+Bv zN%8xYSn}*6-TEVkC=U_-*wXN@X8ngFYR(QUU6`n6-1>!4YrE&ss7P0~n z7qS8o7qS8o7qS8o7qSzq-a>X#yb7$MkQG=(p+~I0N36d`tRGNDl=urhV*Nd0{XJs+ zJ!1XhA}aKV_4kPN_lWiPi1qh~_4kPN_lWi9#QJk${o?jg$cgpm#QJk${W-DzoLGNO z?0-(IKPT3o6YI~3_2$I>(7hz=f(Q-V*Pos{(@M4L9D+Z)?X0oFNpPvn^K`5_W%D>ceb&471<^vRYBPYAK?t6?MCm zuJQp^RaRZ3Dyl@w=1cp*QkSiWRPn`MkXBKpd}vgO5AN@OX6Css&wXsiB!PWK_s-mz zGv}N+bI#nk=iZs8+qb{l)9?25yFLAGPruvK@AmY&J^gM^zsJ+>@$`EGyd0J)VA#r{Cl0_jvj}p1!|Tb@zDs{hofmr@z%-Z|$rAzsl-kUo&BanTw5m+yOwtLyZRfPGIwoo%G}l4XPrXqB-7jH@b&gNe7${6 zti64vB)~cRy?qXUZ=b{8+vg@KHOTwDSvge$mb^*?HK`qd=eJc#-2Jj?tPCYI7VuPHMDflz8;;=-eI7B{Iy^ zrGCeHi(Yoo2^S5!=$J)wY&xZmXf97p)<1XT_vgxHI=VVKcu`e1pz^vSI=6iHG&6#c z7fzfU)#aQ~>vJmL(ZhSo{;rdFN=o=snm9J%&rohDzMZ2zUKjCih%x_b$g|aP(kJ2* zq))~tNxu-kOg;83khjJ#2#ZTK;x+i^sCL`-!J%l`bFYmIN9&j?zZ7j8J~Da=ABk$| ztvCA)86VSK&x_*Sj|{GkR_uRdH|I=U;o<*iqT1(ZAJm3lI1#PqbR_B?e)+j!+Qceo zPbcveiLT3)C$nU}qBVGay7BI8!-F$~hvqmwkyr3`$Sd<>ye?<(G70uA7nkPw_=i5e z=;C<4kKeaAj{m1-g|)a;Gvwpny0~t?i|ZGI70mToC8c+o{;EsKsTRDhCDQH)->EdEsGj7;2*A0BnzUdUM;pf) zj>txq65dX|hggKqRbxd+$#=UFx+VOwF?!RNsiD!S4qZ;*i*nvY=bDiH2yp|g=!&1i z|2sq&#P_-jJ=kaIAx5%>O#WRJIfKZyk#S&gv;sd8UufBRAijrmyPbE=074_tU^L38 zQ7S#dNZN*%^#AZwx8s`}#2dX9ec1$O2hhw@cvgSN`+e>Rj|Gp4(r?due$y zvf&+F{D`G*vh*HHudwuPOE0(dpru)t0sMfaZ?yC-OE0zb!7ABdZ0Q~F3#C}Q zNJ1rkh!_g-He#^H4-$hp-bxJCcndKYUa}S z3xL+YC$kwh*Rfs2v{OCT!tMz7lGhgZbKVm7a9&`T?jv<82YWxneZ)7%8!V@UY@316 z{=YZlhd%=Jr{HRe-o@|RiY~O9PHko++M6F2V~tn?(V_Tpi}%F`E#4a+u=tVqF^l)a zLl*CjAGLT#`~a8M#YD%{y_XJ zxrgFsI1g~BO-C)~h~-Fk26#^(%hP^44F=P~NL3@zrcak%IyQ0&{=-~Cvf-X@*KN_SUFyE*zTZIo?+jxL zmec{i8V&bwmwC}_?g!kP-^TXzy~RYdly^wd(nfT!5pAxc_G0o>dO7+&M=hy~(d(rC z)aSUG%88>J!Lgij$W0?i@~&wm+It&s zao&zTh}TB(#t+3kE0zBPxep+t%afM~7m0n4w7&TtEzA5SCnm??@*-{JH+ez-`RUW- zW7uKKJ@uBb~9y?@21?&o}nV(?-AGh90EnvOmi&%Q9Pb zb+6DO7W%oKLrPwoET^&u$&t=oT@H$|RmjR|`9z{`p`LQC4^K`?f$q#=jUh9G{JKyx zqjS9^^>thG?W#{N+Wp{KaW%8POH+p*Z->`T!}46|j+5}<(t(R4|N5sY#{|bip8spf z*|$Ls`SvkZkI`r62^ToVot*66@(yE}jqi3>)(Cn=N3kTSO`u%!4I78^l&lI6P%4NVx|2#)J$*{rd)e$R;wD-coW`$F^PR|sqfwWwk#dV zD8W@(w=%~`)t!^lfFsFCs8>q~);JPRj11a-VWlbFim@4g;?mXAXm732Ic+})2ca!V zj_SbEHGOt2qMGKQDE%$r%C-+o_zUv2UX+x-3cV+oq@CnWsaFX`mnwoT?(;%O*{*C} z+W48`gd6dfTvel6h_yUxz*-E4=QX95hB-4nCKQm7#$WC<@F06j@R=Q2T`wi>L7MN_ z6>YHlCw0#i+m_WFqg*3j8EZn>rHboHPJwdyuA))&3okWqOX3CWseVjkWM%4ke{Tt~ zTFc>EL8@S6NU)^&HH?{lqV4a<`vDJM!rNKob4l~PEBS>n04%}dbPg#axx!!Z{n=@_ z?x2F()F4NjZo0GQZ0K6pgxxU7i$VKCpMo;S{&epRI-tlF)Q@mH=x>`A+ zjTXxW&oh5Qb2*=xWX?`LGeVAYoZq3ms>T}Z1a};-Mbr^CgD2%%hb^5oIw?G|&)^eE zAd|LL>!n0W3G8fA>VfnaF*Od8m1f7XI@#G}la1*mtyBBP88yxbVk%75>YzcA6f-u# zO9H-SlDy_|8W}w#1#YAhU;b&{@eBqx3-K*T+G2D<8+{!31M0HVFr$fNT!~$k zF{SbxA6|MsUmE%mt+;^BtH%_NO}?p8`fUuUpQn6GEp;`D&y;%z(*fCt!7Ja@FiboL zi|c^HxIbrFbO}g}(#E$xyAi>p6>2C>>ZfzBEM1mWnU|T>w|N3};52nd;rc6g@31@3 zSG--h;$M){6<4$haB~SB{G2q8gOZCXgU{5REGtK~KtXk_6(*T9Jo!1Ta&$tiJXPSY zP`Z^Ee2)IhrOL0Erz*f?`yQ$QsNRYSP@mysr3){$DJ{fRO6qtd7UkREuIaPXox#_q z2lCKqOxH3kx?x6lE$K{+yISiDWTmmKP-Ca2K&{+JTcpQsj7(wT`BL7O{K$Z}#LdSz zQ#Knf#tif-#YZNody_cIWOBir3r`M*J@r9X5uDvKGrC>9<)MBN-RWzE z+UYoVS18X<+#D-F8jrHUoHd1V7w09NExocUr&pDAf?idYT#uZq#8vy%l$6 z@-!iy^fZ&b?=ZXfcRs~-=-yNAk7SG}8l_wlitjNqZ65Pq&5)}+-%{f5lR;mEH%rm# z`a8Hdcid;k!eVYhk4P$62zcV1$PBnjjiFf_!X5Aj*+Qp0&o{l#QFT0sYL*-Erx_l- zC68a$*XMnCNezQJUx_0HQAk(#6PTH$vTaW4Ohr9`x?U;wRR2x$B`VTf@+?Tw&+=6LD$ivxB-qV+Ra`pz+(3P%{F)4Pmus|8CTvd= zXBaQ^Zo09uugicHi8QR^LOdlJnyXN74+cU{R{rjife%k!natvg=|<7wTFNb?to+Q_ zifr(u8N6&#Ux4KLGz%}tXk+k!dZ-> z1xSC#v!=h}%ve@hFZK0*ar6_j=mAzm)w4eA2Euk$OEs_pP8Qq3n#Z}UQfeY>C$E`R zsjLNLjaDn6hF$RHN1ItIw}2H>3kjQ9HP^=4uSJAMSjcYAXp%wqxF7tn6IF`rg|JTA8{Qe76(YSZ~)3zB>quSqHq1HBfgFv@a+-gW8UI z{F^Xg1zdE1wUe;Y_Saj+D&}smvlln(_BIe!vD;oR>-zc#x3KG8KdY!V64tQ$-n~#} zDHxRRC){ppz}nf3?*S-pCfvnpz%9sYD`AM$VcTf;cEUP#=-UBDI|+BwvWHoNxr@-r zI;;Ws7$n@o&V9SVx`)tfdqwuMdT=k4_Yv;3-Tdwc>kw2QBRl|(1FSAQNZ3LAIJgfH z23h(11el&A>|qu1Q{Z}?uKJ)xcTrysC(?H>r;(a+d@a2@MWKSVMg5$=wD&fbBY ztg1YVOn$-sYJKdj_Di(yW5WIHwssC_{0m_dyBPi}9R3?&AFDAxMcTh2>|?jW|Bdcr zFTe%S5_SVz7;%ZD1F;+u7Z;`v7bT2!)?Tyy6TiZU??(SzzW*57*JxY3r zb)5IJd)s3`9*7S_{p{j)FxtpYZjV#z5UW84+0pF@V4h?>XFof;Jw@r$tn(by4sYOj zmX(`>?DKYn^ikGq4q|P8Xm+^9?6BYLko}Yi>&*_=m<8@K3;d2*p!TAaCdvZmn+0lL zN?FWeLO1=j1bvb<-e=ah!L0FqdT=@VCu>|~)_AX3<2_tyM^RbhQnSWBv&KfVL-xcb z@W<+eCbKU6^SX8wkVQRY7PZ7IYMEJ7Y!-E+SyZ!GREt^EI+6VJavz&QmIohHA0Ci3cQGRLgtL9>=QW-WJ`wP^3m zh0#2-mhEOOE6rMNHEUU5*0R#9kySnVl-r$OSyNW5hK#H9_3`+G>{+7W^P;!B-EMJe>gdbYqv@>gj zOTPi;bMU*=Z%LKdMLFW1j!u`f1Lzy<`}r2Tef|wQdj4nW^C)h(nhG%Cpv0YpD z=>iTyZh#CtrTKO8<$Fze*_@<>-7qKcE+ts>#x#I?xBq1+8-L@DJD~k{*}|=QoK0^e zf70P=ZoGb3ECIJc`PJI^Bit9&UnJTUe2)R%w?OOfQu45LN~7sDTQL#1Qk)zPm$M9(U5E3ju>MZdQ)`9%kLaHHi}Tgf9dM`S3N6F{~3Fqsxj9|*F5J5PRC*P zI(?N*7jcz9xKe3?%X2Adj<-`~FqQbS)&od+Y@Z3v8Q_}KQ{qc~nUUyaz#I6=@Qwb3e2Y_eD(kKCmmV3Y%teMf ze^(6vn$LuEwK&H?1}tA2d{fNsGF1yNbk9i_Bz>LEb?HDmHI{iGUs5W)&l8t?fo(jK z)#Gn)|9>Ih-fH|14Lq?m@PyIEUAUE}xf{sS6J3LqDQ@Cure-UUO+2|SvC<{fXyA}H zu>p^T_FZ%To3WH<+*O37cGkXItMJY(q;xIm+wfPYbQ#`^RU9{4xx5#vfmnzK&;9R) zydA3z$6DZ8DVxtzU8(c|&yX!NvBc9*1jZZSNPE%gC%?4zUes?n$4$_6Kf$;?sdU!A zez<)d7g_#dJIf2Z(w=A~n-z@6YJ<{kv`FVhjy0T{kWeEsX@e`}FSGM%&Nq|aNE-z^ w7mgRuBGpxnB&>0x4c}cYbm*D5#wdHv{_AHy`EOl+_JcQmtoZYPTK|v#4@}!Nw*UYD literal 0 HcmV?d00001 diff --git a/shared/src/commonMain/composeResources/values/strings.xml b/shared/src/commonMain/composeResources/values/strings.xml new file mode 100644 index 000000000..3e6f4f768 --- /dev/null +++ b/shared/src/commonMain/composeResources/values/strings.xml @@ -0,0 +1,9 @@ + + CloudStream + None + Ok + Cancel + Navigate up + Lorem ipsum + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.kt new file mode 100644 index 000000000..e7d8900e1 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.kt @@ -0,0 +1,202 @@ +package com.lagradost.cloudstream4 + +import androidx.compose.runtime.Composable +import com.lagradost.cloudstream3.AllLanguagesName +import com.lagradost.cloudstream3.DubStatus +import com.lagradost.cloudstream3.SearchQuality +import com.lagradost.cloudstream3.TvType +import com.mihon.common.preference.PreferenceStore +import com.mihon.common.preference.getEnumSet + +@Composable +expect fun rememberAppSettings(): AppSettings + +/** + * App settings for every setting we have, that way we can just inject AppSettings into a viewmodel, + * or similar. + * + * We use an internal constructor to force the user to construct it using the plantform specific + * initalizer + * */ +class AppSettings internal constructor( + preferences: PreferenceStore, + // Todo add database key-value interface here, possibly using the same PreferenceStore interface +) { + val general = GeneralPreferences(preferences) + val player = PlayerPreferences(preferences) + val provider = ProviderPreferences(preferences) + val ui = UIPreferences(preferences) + val security = SecurityPreferences(preferences) + val updates = UpdatePreferences(preferences) + val backup = BackupPreferences(preferences) + val plugins = PluginPreferences(preferences) +} + +class PluginPreferences(preferences: PreferenceStore) { + val autoUpdate = preferences.getBoolean("auto_update_plugins", true) + val autoDownload = preferences.getInt("auto_download_plugins_key2", 0) +} + +class BackupPreferences(preferences: PreferenceStore) { + val frequency = preferences.getInt("automatic_backup_key", 0) + val path = preferences.getString("backup_path_key") + val visualPath = preferences.getString("backup_dir_key") +} +class UpdatePreferences(preferences: PreferenceStore) { + val apkInstaller = preferences.getInt("apk_installer_key",1) + val showAppUpdates = preferences.getBoolean("auto_update", true) +} + +class SecurityPreferences(preferences: PreferenceStore) { + val biometrics = preferences.getBoolean( + "biometric_key", false + ) + val skipAccountSelection = preferences.getBoolean( + "skip_startup_account_select_key", false + ) +} + +class UIPreferences(preferences: PreferenceStore) { + val primaryColor = preferences.getString( + "primary_color_key", "Normal" + ) + val theme = preferences.getString( + "app_theme_key", "AmoledLight" + ) + val layout = preferences.getInt( + "app_layout_key", -1 + ) + val bottomTitle = preferences.getBoolean( + "bottom_title_key", true + ) + val advancedSearch = preferences.getBoolean( + "advanced_search", true + ) + val searchSuggestions = preferences.getBoolean( + "search_suggestions_enabled", true + ) + val kitsuPostersEnabled = preferences.getBoolean( + "show_kitsu_posters_key", true + ) + val trailersEnabled = preferences.getBoolean( + "show_trailers_key", true + ) + val castEnabled = preferences.getBoolean( + "show_cast_in_details_key", true + ) + val fillersEnabled = preferences.getBoolean( + "show_fillers_key", false + ) + val showMetadataOverlay = preferences.getBoolean( + "show_player_metadata_key", true + ) + val overscanDp = preferences.getInt( + "overscan_key", 0 + ) + val posterSize = preferences.getInt( + "poster_size_key", 0 + ) + val posterShowHd = preferences.getBoolean( + "show_hd_key", true + ) + val posterShowDub = preferences.getBoolean( + "show_dub_key", true + ) + val posterShowSub = preferences.getBoolean( + "show_sub_key", true + ) + val posterShowRating = preferences.getBoolean( + "show_rating_key", true + ) + val posterShowTitle = preferences.getBoolean( + "show_title_key", true + ) + val posterShowEpisode = preferences.getBoolean( + "show_episode_text_key", true + ) + val showClock = preferences.getBoolean( + "tv_layout_clock_key", false + ) + + val randomButtonEnabled = preferences.getBoolean( + "random_button_key", false + ) + + val confirmExit = preferences.getInt( + "confirm_exit_key", -1 + ) + + /** This had to be refactored to use enumSet because the old system is prone to bugs */ + val filterQuality = preferences.getEnumSet( + "pref_filter_search_quality_key2", emptySet() + ) +} + +class ProviderPreferences(preferences: PreferenceStore) { + companion object { + private val defaultPreferredMedia = + TvType.entries.filter { it != TvType.NSFW }.map { it.ordinal.toString() }.toSet() + private val defaultDub = DubStatus.entries.map { it.name }.toSet() + } + + val preferredMedia = preferences.getStringSet( + "prefer_media_type_key_2", defaultPreferredMedia + ) + + val extensionLanguages = preferences.getStringSet( + "provider_lang_key", setOf(AllLanguagesName) + ) + + val displayDubSub = preferences.getStringSet( + "display_sub_key", defaultDub + ) +} + +class PlayerPreferences(preferences: PreferenceStore) { + val episodeSync = preferences.getBoolean("episode_sync_enabled_key", true) + val defaultPlayer = preferences.getString("player_default_key", "") + val limitPlayerTitle = preferences.getInt("prefer_limit_title_key", 0) + val hidePlayerControlNames = preferences.getBoolean("hide_player_control_names_key", false) + val showName = preferences.getBoolean("show_name", true) + val showResolution = preferences.getBoolean("show_resolution", true) + val showMediaInfo = preferences.getBoolean("show_media_info", false) + val pipEnabled = preferences.getBoolean("pip_enabled_key", true) + val resizeEnabled = preferences.getBoolean("player_resize_enabled_key", true) + val speedEnabled = preferences.getBoolean("playback_speed_enabled_key", false) + val tiktokEnabled = preferences.getBoolean("speedup_key", false) + val autoPlayEnabled = preferences.getBoolean("autoplay_next_key", true) + val skipOpEnabled = preferences.getBoolean("enable_skip_op_from_database", true) + val autoRotateEnabled = preferences.getBoolean("auto_rotate_video_key", true) + val rotateButtonEnabled = preferences.getBoolean("rotate_video_key", false) + val previewBarEnabled = preferences.getBoolean("preview_seekbar_key", true) + val softwareDecoding = preferences.getInt("software_decoding_key2", -1) + val extraBrightnessEnabled = preferences.getBoolean("extra_brightness_key", false) + val swipeHorizontalEnabled = preferences.getBoolean("swipe_enabled_key", true) + val swipeVerticalEnabled = preferences.getBoolean("swipe_vertical_enabled_key", true) + val doubleTapToSeekEnabled = preferences.getBoolean("double_tap_enabled_key", false) + val doubleTapToPauseEnabled = preferences.getBoolean("double_tap_pause_enabled_key", false) + val doubleTapTime = preferences.getInt("double_tap_seek_time_key2", 10) + val bufferDiskMB = preferences.getInt("video_buffer_disk_key", 0) + val bufferRamMB = preferences.getInt("video_buffer_size_key", 0) + val bufferTimeSec = preferences.getInt("video_buffer_length_key", 0) + val tvSeekOnTime = preferences.getInt("android_tv_interface_on_seek_key", 10) + val tvSeekOffTime = preferences.getInt("android_tv_interface_off_seek_key", 10) +} + +class GeneralPreferences(preferences: PreferenceStore) { + val locale = preferences.getString("app_locale", "") + val bananas = preferences.getInt("benene_count", 0) + val parallelDownloads = preferences.getInt("download_parallel_key", 3) + val concurrentConnections = preferences.getInt("download_concurrent_key", 3) + val batterOptimization = preferences.getBoolean("battery_optimisation_key", false) + + /** Please note that it used R.array.dns_pref before, this was a bug and caused this setting to be language sensitive **/ + val dns = preferences.getInt("dns_key", 0) + + /** Very weird setKey based usage, just having this setting might not do it */ + val jsdelivrProxy = preferences.getBoolean("jsdelivr_proxy_key", false) + + /** This should honesty be refactored to a single setting */ + val downloadPath = preferences.getString("download_path_key", "") + val downloadPathVisual = preferences.getString("download_path_key_visual", "") +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt new file mode 100644 index 000000000..d39bffa66 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt @@ -0,0 +1,24 @@ +package com.lagradost.cloudstream4.compose + +import androidx.compose.material3.ButtonColors +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors + +object Colors { + val blackButton + @Composable @ReadOnlyComposable get() = ButtonColors( + containerColor = colors.surfaceVariant, + contentColor = colors.onBackground, + disabledContainerColor = colors.surface.copy(alpha = 0.9f), + disabledContentColor = colors.onBackground.copy(alpha = 0.9f) + ) + + val whiteButton + @Composable @ReadOnlyComposable get() = ButtonColors( + containerColor = colors.onBackground, + contentColor = colors.surfaceVariant, + disabledContainerColor = colors.onBackground.copy(alpha = 0.9f), + disabledContentColor = colors.surface.copy(alpha = 0.9f) + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt new file mode 100644 index 000000000..9dbed5f42 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt @@ -0,0 +1,491 @@ +package com.lagradost.cloudstream4.compose + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.toMutableStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component1 +import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component2 +import androidx.compose.ui.focus.focusProperties +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.DialogProperties +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.check +import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors +import com.mihon.material.padding +import org.jetbrains.compose.resources.painterResource + +@Composable +fun ActionDialog( + title: String, + text: String, + icon: Painter? = null, + + confirmText: String, + dismissText: String, + + dismiss: () -> Unit, + confirm: () -> Unit +) { + AlertDialog( + containerColor = colors.background, + onDismissRequest = dismiss, + title = { + if (icon == null) { + Text(text = title) + } else { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon(painter = icon, modifier = Modifier.size(36.dp), contentDescription = null) + Spacer(modifier = Modifier.size(MaterialTheme.padding.medium)) + Text(text = title) + } + } + }, + text = { Text(text = text) }, + confirmButton = { + WhiteButton(text = confirmText, onClick = confirm) + }, + dismissButton = { + BlackButton(text = dismissText, onClick = dismiss) + } + ) +} + +@Composable +fun WhiteButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + BaseButton( + text = text, + onClick = onClick, + buttonColors = Colors.whiteButton, + modifier = modifier + ) +} + +@Composable +fun BlackButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + BaseButton( + text = text, + onClick = onClick, + buttonColors = Colors.blackButton, + modifier = modifier + ) +} + +@Composable +fun BaseButton( + text: String, onClick: () -> Unit, buttonColors: ButtonColors, + modifier: Modifier = Modifier, +) { + var hasFocus by remember { mutableStateOf(false) } + Button( + onClick = onClick, + colors = buttonColors, + modifier = modifier.onFocusChanged { newFocus -> + hasFocus = newFocus.hasFocus + }, + border = if (hasFocus) BorderStroke(1.dp, colors.onBackground) else null + ) { + AnimatedVisibility(hasFocus) { + Icon( + painter = painterResource(Res.drawable.check), + modifier = Modifier.padding(end = 5.dp), + contentDescription = null + ) + } + Text(text = text) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SingleSelectDialog( + modifier: Modifier = Modifier, + shape: Shape = AlertDialogDefaults.shape, + containerColor: Color = colors.background, + iconContentColor: Color = AlertDialogDefaults.iconContentColor, + tonalElevation: Dp = AlertDialogDefaults.TonalElevation, + icon: @Composable (() -> Unit)? = null, + title: String? = null, + properties: DialogProperties = DialogProperties(), + /* visual */ + entries: List, + /* Selected keys used in entries */ + selectedIndex: Int, + confirmText: String? = null, + dismissText: String? = null, + dismiss: () -> Unit, + confirm: (Int) -> Unit +) { + SingleSelectDialog( + modifier = modifier, + shape = shape, + containerColor = containerColor, + iconContentColor = iconContentColor, + tonalElevation = tonalElevation, + icon = icon, + title = title, + properties = properties, + entries = entries.mapIndexed { index, string -> index to string }.associate { it }, + selectedKey = selectedIndex, + confirmText = confirmText, + dismissText = dismissText, + dismiss = dismiss, + confirm = confirm, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SingleSelectDialog( + modifier: Modifier = Modifier, + shape: Shape = AlertDialogDefaults.shape, + containerColor: Color = colors.background, + iconContentColor: Color = AlertDialogDefaults.iconContentColor, + tonalElevation: Dp = AlertDialogDefaults.TonalElevation, + icon: @Composable (() -> Unit)? = null, + title: String? = null, + properties: DialogProperties = DialogProperties(), + /* key - visual */ + entries: Map, + /* Selected keys used in entries */ + selectedKey: T, + confirmText: String? = null, + dismissText: String? = null, + dismiss: () -> Unit, + confirm: (T) -> Unit, + iconProvider: (@Composable (key: T, value: String) -> Unit)? = null +) { + var selected by remember { mutableStateOf(selectedKey) } + val selectedPainter = painterResource(Res.drawable.check) + val (dismissFocus, confirmFocus) = remember { FocusRequester.createRefs() } + + AlertDialog( + properties = properties, + tonalElevation = tonalElevation, + modifier = modifier, + icon = icon, + shape = shape, + iconContentColor = iconContentColor, + containerColor = containerColor, + onDismissRequest = dismiss, + title = title?.let { { Text(text = it) } }, + text = { + LazyColumn { + entries.forEach { (key, value) -> + item(key = key) { + val isSelected = selected == key + SingleSelectionItem( + isSelected = isSelected, + key = key, + text = value, + iconProvider = iconProvider, + selectedPainter = selectedPainter, + modifier = Modifier.focusProperties { + start = dismissFocus + end = confirmFocus + }) { + if (confirmText == null) { + confirm(key) + } else { + selected = key + } + } + } + } + } + }, + confirmButton = { + if (confirmText != null) { + WhiteButton( + text = confirmText, + onClick = { confirm(selected) }, + modifier = Modifier.focusRequester(confirmFocus) + ) + } + }, + dismissButton = { + if (dismissText != null) { + BlackButton( + text = dismissText, + onClick = dismiss, + modifier = Modifier.focusRequester(dismissFocus) + ) + } + } + ) +} + +/* +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SinglePairSelectDialog( + modifier: Modifier = Modifier, + shape: Shape = AlertDialogDefaults.shape, + containerColor: Color = colors.background, + iconContentColor: Color = AlertDialogDefaults.iconContentColor, + tonalElevation: Dp = AlertDialogDefaults.TonalElevation, + icon: @Composable (() -> Unit)? = null, + title: String? = null, + properties: DialogProperties = DialogProperties(), + /* key - visual */ + entries: Map, String>, + /* Selected keys used in entries */ + selectedKey: T, + confirmText: String? = null, + dismissText: String? = null, + dismiss: () -> Unit, + confirm: (T) -> Unit, + iconProvider: (@Composable (key: T, value: String) -> Unit)? = null +) { + val selected = remember { mutableStateOf(selectedKey) } + + AlertDialog( + properties = properties, + tonalElevation = tonalElevation, + modifier = modifier, + icon = icon, + shape = shape, + iconContentColor = iconContentColor, + containerColor = containerColor, + onDismissRequest = dismiss, + title = title?.let { { Text(text = it) } }, + text = { + LazyColumn { + entries.forEach { (keyPair, value) -> + item(key = keyPair) { + val (a, b) = keyPair + val isSelected = selected.value == a || selected.value == b + + val painter = if (a == b) { + R.drawable.ic_baseline_check_24_listview + } else if(selected.value == a) { + R.drawable.ic_baseline_arrow_downward_24 + } else { + R.drawable.ic_baseline_arrow_upward_24 + } + + val nextKey = if (selected.value != a) a else b + SingleSelectionItem( + isSelected = isSelected, + key = nextKey, + text = value, + iconProvider = iconProvider, + selectIcon = painter + ) { + if (confirmText == null) { + confirm(nextKey) + } else { + selected.value = nextKey + } + } + } + } + } + }, + confirmButton = { + if (confirmText != null) { + Button( + onClick = { + confirm(selected.value) + }, + colors = Colors.whiteButton + ) { Text(text = confirmText) } + } + }, + dismissButton = { + if (dismissText != null) { + Button( + onClick = dismiss, colors = Colors.blackButton + ) { Text(text = dismissText) } + } + } + ) +}*/ + +@Composable +fun SingleSelectionItem( + isSelected: Boolean, + key: T, + text: String, + modifier: Modifier = Modifier, + iconProvider: (@Composable (key: T, value: String) -> Unit)? = null, + selectedPainter: Painter, + onClick: () -> Unit, +) { + val interactionSource = remember { MutableInteractionSource() } + Row( + modifier = modifier + .fillMaxWidth() + .combinedClickable( + interactionSource = interactionSource, + indication = null, + onClick = onClick, + onLongClick = { + } + ) + .rounded() + .ripple(interactionSource), + verticalAlignment = Alignment.CenterVertically + ) { + if (isSelected) { + Icon( + painter = selectedPainter, + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = colors.onBackground + ) + iconProvider?.invoke(key, text) + Text( + text = text, + style = MaterialTheme.typography.bodyLarge, + color = colors.onBackground, + modifier = Modifier.padding(15.dp) + ) + } else { + Spacer(modifier = Modifier.size(24.dp)) + iconProvider?.invoke(key, text) + Text( + text = text, + style = MaterialTheme.typography.bodyLarge, + color = colors.onSurfaceVariant, + modifier = Modifier.padding(15.dp) + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MultiSelectDialog( + modifier: Modifier = Modifier, + shape: Shape = AlertDialogDefaults.shape, + containerColor: Color = colors.background, + iconContentColor: Color = AlertDialogDefaults.iconContentColor, + tonalElevation: Dp = AlertDialogDefaults.TonalElevation, + icon: @Composable (() -> Unit)? = null, + title: String? = null, + properties: DialogProperties = DialogProperties(), + /* key - visual */ + entries: Map, + /* Selected keys used in entries */ + selectedKeys: Set, + confirmText: String? = null, + dismissText: String? = null, + dismiss: () -> Unit, + confirm: (Set) -> Unit, + iconProvider: (@Composable (key: T, value: String) -> Unit)? = null +) { + val selected = remember { + entries.keys + .filter { selectedKeys.contains(it) } + .toMutableStateList() + } + + val selectedPainter = painterResource(Res.drawable.check) + val (dismissFocus, confirmFocus) = remember { FocusRequester.createRefs() } + + AlertDialog( + properties = properties, + tonalElevation = tonalElevation, + modifier = modifier, + icon = icon, + shape = shape, + iconContentColor = iconContentColor, + containerColor = containerColor, + onDismissRequest = { + if (confirmText == null) { + confirm(selected.toSet()) + } else { + dismiss() + } + }, + title = title?.let { { Text(text = it) } }, + text = { + LazyColumn { + entries.forEach { (key, value) -> + item(key = key) { + val isSelected = selected.contains(key) + SingleSelectionItem( + isSelected = isSelected, + key = key, + text = value, + iconProvider = iconProvider, + selectedPainter = selectedPainter, + modifier = Modifier.focusProperties { + start = dismissFocus + end = confirmFocus + } + ) { + if (isSelected) { + selected.remove(key) + } else { + selected.add(key) + } + } + } + } + } + }, + confirmButton = { + if (confirmText != null) { + WhiteButton( + onClick = { + confirm(selected.toSet()) + }, + text = confirmText, + modifier = Modifier.focusRequester(confirmFocus) + ) + } + }, + dismissButton = { + if (dismissText != null) { + BlackButton( + onClick = dismiss, + text = dismissText, + modifier = Modifier.focusRequester(dismissFocus) + ) + } + } + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Modifiers.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Modifiers.kt new file mode 100644 index 000000000..d2bc48b46 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Modifiers.kt @@ -0,0 +1,63 @@ +package com.lagradost.cloudstream4.compose + +import androidx.compose.foundation.border +import androidx.compose.foundation.indication +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ripple +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors + +@Composable +fun Modifier.ripple( + interactionSource: MutableInteractionSource, + bounded: Boolean = true, +): Modifier = indication( + interactionSource = interactionSource, + indication = ripple(bounded = bounded, color = colors.onBackground), +) + +// no dimens.xml allowed in compose :/ +fun RoundedShape() = RoundedCornerShape(10.dp) + +@Composable +fun Modifier.rounded(): Modifier = + clip(RoundedShape()) + + +@Composable +fun Modifier.circle(): Modifier = + clip(CircleShape) + +@Composable +fun Modifier.focusOutline(shape: Shape = RoundedShape()): Modifier { + var hasFocus by remember { mutableStateOf(false) } + + return this.onFocusChanged { newFocus -> + hasFocus = newFocus.isFocused + }.whiteOutline(hasFocus = hasFocus, shape = shape) +} + +@Composable +fun Modifier.whiteOutline(hasFocus: Boolean, shape: Shape = RoundedShape()): Modifier { + return if (hasFocus) { + this.border( + width = 1.dp, + color = MaterialTheme.colorScheme.onBackground, + shape = shape + ) + } else { + this + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Screen.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Screen.kt new file mode 100644 index 000000000..a99b623e8 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Screen.kt @@ -0,0 +1,8 @@ +package com.lagradost.cloudstream4.compose +import androidx.compose.runtime.Composable + +// https://github.com/adrielcafe/voyager +interface Screen { + @Composable + fun Content() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Viewmodel.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Viewmodel.kt new file mode 100644 index 000000000..8255b4260 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Viewmodel.kt @@ -0,0 +1,145 @@ +package com.lagradost.cloudstream4.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext +import kotlin.time.Duration.Companion.milliseconds + +/** + * Use with the DefaultStateContainer + * + * `X : ViewModel(), StateContainer by DefaultStateContainer(XState)` + */ +interface StateContainer { + val state: StateFlow + fun updateState(reducer: State.() -> State) +} + +/** + * Use with the DefaultEffectContainer + * + * `X : ViewModel(), EffectContainer by DefaultEffectContainer()` + */ +interface EffectContainer { + val effect: Flow + suspend fun postEffect(builder: () -> Effect) +} + +/** + * Use an interface on the viewmodel directly + * + * `X : ViewModel(), ActionHandler` + */ +interface ActionHandler { + fun onAction(action: Action) +} + +/** + * The default implementation of a state container to allow by delegate. + * + * All state should be updated with updateState + copy to allow atomic operations + * */ +class DefaultStateContainer(initialState: State) : StateContainer { + private val _state = MutableStateFlow(initialState) + override val state: StateFlow = _state.asStateFlow() + + /** Do not do expensive operations in updateState, as this can get rerun twice or more if we have concurrent writers */ + override fun updateState(reducer: State.() -> State) { + _state.update(reducer) + } +} + +/** + * The default implementation of an effect container to allow by delegate + * */ +class DefaultEffectContainer : EffectContainer { + private val _effect = Channel() // To ensure events are sent + override val effect: Flow = _effect.receiveAsFlow() + + override suspend fun postEffect(builder: () -> Effect) { + _effect.send(builder()) + } +} + +/** + * Observe the viewmodel EffectContainer in the UI on the Main thread for e.g. toasts + * */ +@Composable +fun ObserveEffect(flow: Flow, onEvent: (T) -> Unit) { + val lifecycleOwner = LocalLifecycleOwner.current + LaunchedEffect(flow, lifecycleOwner.lifecycle) { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + withContext(Dispatchers.Main.immediate) { // To ensure events are sent + flow.collect(onEvent) + } + } + } +} + +/** Single active job, replacing the old job by cancellation if launched again */ +data class SingleActiveQuery( + val dispatcher: CoroutineDispatcher, + private var job: Job? = null, + private val mutex: Mutex = Mutex(), +) { + suspend fun launch(block: suspend CoroutineScope.() -> Unit) { + val currentScope = CoroutineScope(currentCoroutineContext()) + val obj: suspend CoroutineScope.() -> Unit = { + try { + withContext(dispatcher) { + block() + } + } catch (_: Throwable) { + //logError(t) + } + } + mutex.withLock { + job?.cancel() + job?.join() + job = currentScope.launch(block = obj) + } + } +} + +/** Debounce Query to handle e.g. user search without spamming the endpoint */ +data class DebounceQuery( + private val pipe: MutableSharedFlow = MutableSharedFlow( + extraBufferCapacity = 64, + replay = 64 + ) +) { + @OptIn(FlowPreview::class) + suspend fun launch(collector: FlowCollector) { + pipe.debounce(200L.milliseconds).distinctUntilChanged() + .flowOn(Dispatchers.Default) + .collect(collector) + } + + suspend fun emit(query: String) { + this.pipe.emit(query) + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.kt new file mode 100644 index 000000000..72da2f789 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.kt @@ -0,0 +1,17 @@ +package com.lagradost.cloudstream4.theme + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.graphics.Color + +internal expect object DeviceTheme { + @Composable + @ReadOnlyComposable + fun resolveDynamicTheme(): CloudStreamColorScheme + @Composable + @ReadOnlyComposable + fun resolveDynamicPrimaryColor(): Color + @Composable + @ReadOnlyComposable + fun resolveDynamicSecondaryColor(): Color +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Font.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Font.kt new file mode 100644 index 000000000..c81519e6d --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Font.kt @@ -0,0 +1,71 @@ +package com.lagradost.cloudstream4.theme + +import androidx.compose.material3.Typography +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.em +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.productsans_black +import com.lagradost.cloudstream4.generated.resources.productsans_blackitalic +import com.lagradost.cloudstream4.generated.resources.productsans_bold +import com.lagradost.cloudstream4.generated.resources.productsans_bolditalic +import com.lagradost.cloudstream4.generated.resources.productsans_italic +import com.lagradost.cloudstream4.generated.resources.productsans_light +import com.lagradost.cloudstream4.generated.resources.productsans_lightitalic +import com.lagradost.cloudstream4.generated.resources.productsans_medium +import com.lagradost.cloudstream4.generated.resources.productsans_mediumitalic +import com.lagradost.cloudstream4.generated.resources.productsans_regular +import com.lagradost.cloudstream4.generated.resources.productsans_thin +import com.lagradost.cloudstream4.generated.resources.productsans_thinitalic +import org.jetbrains.compose.resources.Font + +object AppFont { + val googleSans @Composable get() = FontFamily( + Font(Res.font.productsans_thin, weight = FontWeight.W100, style = FontStyle.Normal), + Font(Res.font.productsans_thinitalic, weight = FontWeight.W100, style = FontStyle.Italic), + + Font(Res.font.productsans_light, weight = FontWeight.W300, style = FontStyle.Normal), + Font(Res.font.productsans_lightitalic, weight = FontWeight.W300, style = FontStyle.Italic), + + Font(Res.font.productsans_regular, weight = FontWeight.W400, style = FontStyle.Normal), + Font(Res.font.productsans_italic, weight = FontWeight.W400, style = FontStyle.Italic), + + Font(Res.font.productsans_medium, weight = FontWeight.W500, style = FontStyle.Normal), + Font(Res.font.productsans_mediumitalic, weight = FontWeight.W500, style = FontStyle.Italic), + + Font(Res.font.productsans_bold, weight = FontWeight.W700, style = FontStyle.Normal), + Font(Res.font.productsans_bolditalic, weight = FontWeight.W700, style = FontStyle.Italic), + + Font(Res.font.productsans_black, weight = FontWeight.W900, style = FontStyle.Normal), + Font(Res.font.productsans_blackitalic, weight = FontWeight.W900, style = FontStyle.Italic)) + + private val defaultTypography = androidx.compose.material3.Typography() + val typography @Composable get() = + googleSans.let { fontFamily -> + val lineHeight = 1.3.em + Typography( + displayLarge = defaultTypography.displayLarge.copy(fontFamily = fontFamily, lineHeight = lineHeight), + displayMedium = defaultTypography.displayMedium.copy(fontFamily = fontFamily, lineHeight = lineHeight), + displaySmall = defaultTypography.displaySmall.copy(fontFamily = fontFamily, lineHeight = lineHeight), + + headlineLarge = defaultTypography.headlineLarge.copy(fontFamily = fontFamily, lineHeight = lineHeight), + headlineMedium = defaultTypography.headlineMedium.copy(fontFamily = fontFamily, lineHeight = lineHeight), + headlineSmall = defaultTypography.headlineSmall.copy(fontFamily = fontFamily, lineHeight = lineHeight), + + titleLarge = defaultTypography.titleLarge.copy(fontFamily = fontFamily, lineHeight = lineHeight), + titleMedium = defaultTypography.titleMedium.copy(fontFamily = fontFamily, lineHeight = lineHeight), + titleSmall = defaultTypography.titleSmall.copy(fontFamily = fontFamily, lineHeight = lineHeight), + + bodyLarge = defaultTypography.bodyLarge.copy(fontFamily = fontFamily, lineHeight = lineHeight), + bodyMedium = defaultTypography.bodyMedium.copy(fontFamily = fontFamily, lineHeight = lineHeight), + bodySmall = defaultTypography.bodySmall.copy(fontFamily = fontFamily, lineHeight = lineHeight), + + labelLarge = defaultTypography.labelLarge.copy(fontFamily = fontFamily, lineHeight = lineHeight), + labelMedium = defaultTypography.labelMedium.copy(fontFamily = fontFamily, lineHeight = lineHeight), + labelSmall = defaultTypography.labelSmall.copy(fontFamily = fontFamily, lineHeight = lineHeight) + ) + } +} + diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Palette.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Palette.kt new file mode 100644 index 000000000..c6332808f --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Palette.kt @@ -0,0 +1,84 @@ +package com.lagradost.cloudstream4.theme + +import androidx.compose.ui.graphics.Color + +enum class CloudStreamPrimaryColor(val color: Color) { + NORMAL(Color(0xFF3D50FA)), + BLUE(Color(0xFF5664B7)), + PURPLE(Color(0xFF6200EA)), + GREEN(Color(0xFF00BFA5)), + GREEN_APPLE(Color(0xFF48E484)), + RED(Color(0xFFD50000)), + BANANA(Color(0xFFE4D448)), + PARTY(Color(0xFFEA596E)), + PINK(Color(0xFFFF1493)), + CARNATION_PINK(Color(0xFFBD5DA5)), + MAROON(Color(0xFF451010)), + DARK_GREEN(Color(0xFF004500)), + NAVY_BLUE(Color(0xFF000080)), + GREY(Color(0xFF515151)), + WHITE(Color(0xFFFFFFFF)), + BROWN(Color(0xFF622C00)), + ORANGE(Color(0xFFCE8500)), + DANDELION_YELLOW(Color(0xFFF5BB00)), + COOL_BLUE(Color(0xFF408CAC)), + LAVENDER(Color(0xFF6F55AF)), + DYNAMIC(Color(0xFF3D50FA)), + DYNAMIC_TWO(Color(0xFF3D50FA)), +} + + +internal object CloudStreamPalette { + // Default dark (AppTheme / Black) + val Primary = Color(0xFF3D50FA) + val PrimaryDark = Color(0xFF3700B3) + val Ongoing = Color(0xFFF53B66) + + val DarkPrimaryGrayBg = Color(0xFF2B2C30) + val DarkBlackBg = Color(0xFF111111) + val DarkIconGrayBg = Color(0xFF1C1C20) + val DarkBoxItemBg = Color(0xFF161616) + val DarkText = Color(0xFFE9EAEE) + val DarkGrayText = Color(0xFF9BA0A4) + val DarkIcon = Color(0xFF9BA0A6) + + // Amoled + val AmoledBlack = Color(0xFF000000) + val AmoledNearBlack = Color(0xFF121212) + + // Light + val LightPrimaryGrayBg = Color(0xFFF1F1F1) + val LightBlackBg = Color(0xFFFFFFFF) + val LightIconGrayBg = Color(0xFFEEEEEE) + val LightBoxItemBg = Color(0xFFEEEEEE) + val LightText = Color(0xFF202125) + val LightGrayText = Color(0xFF5F6267) + val LightIcon = Color(0xFF5F6267) + + // Dracula + val DraculaPrimaryGrayBg = Color(0xFF414450) + val DraculaBlackBg = Color(0xFF282A36) + val DraculaIconGrayBg = Color(0xFF44475A) + val DraculaBoxItemBg = Color(0xFF373844) + val DraculaText = Color(0xFFF8F8F2) + val DraculaGrayText = Color(0xFF6272A4) + val DraculaIcon = Color(0xFF6272A4) + + // Lavender Dreams + val LavenderPrimaryGrayBg = Color(0xFFF7EEFC) + val LavenderBlackBg = Color(0xFFFDF0FB) + val LavenderIconGrayBg = Color(0xFFB794F6) + val LavenderBoxItemBg = Color(0xFFF8F5FF) + val LavenderText = Color(0xFF2D1B47) + val LavenderGrayText = Color(0xFF9AB3FF) + val LavenderIcon = Color(0xFF7C3AED) + + // Silent Blue + val SilentBluePrimaryGrayBg = Color(0xFF282F49) + val SilentBlueBlackBg = Color(0xFF151A30) + val SilentBlueIconGrayBg = Color(0xFF3A446A) + val SilentBlueBoxItemBg = Color(0xFF3A446A) + val SilentBlueText = Color(0xFFE0E1F3) + val SilentBlueGrayText = Color(0xFF7B83B0) + val SilentBlueIcon = Color(0xFF7B83B0) +} diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Scheme.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Scheme.kt new file mode 100644 index 000000000..d51c75d08 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Scheme.kt @@ -0,0 +1,162 @@ +package com.lagradost.cloudstream4.theme + +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.Color + +/** + * Maps to the XML custom attrs declared in attrs.xml: + * TODO: Remove this comment when we migrate fully + * and attrs.xml will no longer be used at all. + * + * | XML ?attr | Here | + * |--------------------------|--------------------| + * | primaryBlackBackground | [background] | + * | primaryGrayBackground | [surfaceVariant] | + * | iconGrayBackground | [surface] | + * | boxItemBackground | [surfaceContainer] | + * | textColor | [onBackground] | + * | grayTextColor | [onSurfaceVariant] | + * | iconColor | [icon] | + * | colorPrimary | [primary] | + * | colorOngoing | [ongoing] | + * + * All fields are [MutableState] so Compose recomposes automatically + * if the scheme is swapped at runtime (e.g. user changes theme without restart). + */ +@Stable +class CloudStreamColorScheme( + background: Color, + surfaceVariant: Color, + surface: Color, + surfaceContainer: Color, + onBackground: Color, + onSurfaceVariant: Color, + icon: Color, + primary: Color, + ongoing: Color, + isLight: Boolean, +) { + /** primaryBlackBackground */ + var background by mutableStateOf(background) + + /** primaryGrayBackground */ + var surfaceVariant by mutableStateOf(surfaceVariant) + + /** iconGrayBackground */ + var surface by mutableStateOf(surface) + + /** boxItemBackground */ + var surfaceContainer by mutableStateOf(surfaceContainer) + + /** textColor */ + var onBackground by mutableStateOf(onBackground) + + /** grayTextColor */ + var onSurfaceVariant by mutableStateOf(onSurfaceVariant) + + /** iconColor */ + var icon by mutableStateOf(icon) + + /** colorPrimary */ + var primary by mutableStateOf(primary) + + /** colorOngoing */ + var ongoing by mutableStateOf(ongoing) + var isLight by mutableStateOf(isLight) + + fun copy( + background: Color = this.background, + surfaceVariant: Color = this.surfaceVariant, + surface: Color = this.surface, + surfaceContainer: Color = this.surfaceContainer, + onBackground: Color = this.onBackground, + onSurfaceVariant: Color = this.onSurfaceVariant, + icon: Color = this.icon, + primary: Color = this.primary, + ongoing: Color = this.ongoing, + isLight: Boolean = this.isLight, + ) = CloudStreamColorScheme( + background, surfaceVariant, surface, surfaceContainer, + onBackground, onSurfaceVariant, icon, primary, ongoing, isLight, + ) +} + +internal fun darkScheme() = CloudStreamColorScheme( + background = CloudStreamPalette.DarkBlackBg, + surfaceVariant = CloudStreamPalette.DarkPrimaryGrayBg, + surface = CloudStreamPalette.DarkIconGrayBg, + surfaceContainer = CloudStreamPalette.DarkBoxItemBg, + onBackground = CloudStreamPalette.DarkText, + onSurfaceVariant = CloudStreamPalette.DarkGrayText, + icon = CloudStreamPalette.DarkIcon, + primary = CloudStreamPalette.Primary, + ongoing = CloudStreamPalette.Ongoing, + isLight = false, +) + + +internal fun amoledScheme() = darkScheme().copy( + background = CloudStreamPalette.AmoledBlack, + surface = CloudStreamPalette.AmoledBlack, + surfaceVariant = CloudStreamPalette.AmoledBlack, + surfaceContainer = CloudStreamPalette.AmoledBlack, +) + +internal fun amoledLightScheme() = amoledScheme().copy( + surfaceVariant = CloudStreamPalette.AmoledNearBlack, +) + +internal fun lightScheme() = CloudStreamColorScheme( + background = CloudStreamPalette.LightBlackBg, + surfaceVariant = CloudStreamPalette.LightPrimaryGrayBg, + surface = CloudStreamPalette.LightIconGrayBg, + surfaceContainer = CloudStreamPalette.LightBoxItemBg, + onBackground = CloudStreamPalette.LightText, + onSurfaceVariant = CloudStreamPalette.LightGrayText, + icon = CloudStreamPalette.LightIcon, + primary = CloudStreamPalette.Primary, + ongoing = CloudStreamPalette.Ongoing, + isLight = true, +) + +internal fun draculaScheme() = CloudStreamColorScheme( + background = CloudStreamPalette.DraculaBlackBg, + surfaceVariant = CloudStreamPalette.DraculaPrimaryGrayBg, + surface = CloudStreamPalette.DraculaIconGrayBg, + surfaceContainer = CloudStreamPalette.DraculaBoxItemBg, + onBackground = CloudStreamPalette.DraculaText, + onSurfaceVariant = CloudStreamPalette.DraculaGrayText, + icon = CloudStreamPalette.DraculaIcon, + primary = CloudStreamPalette.Primary, + ongoing = CloudStreamPalette.Ongoing, + isLight = false, +) + +internal fun lavenderScheme() = CloudStreamColorScheme( + background = CloudStreamPalette.LavenderBlackBg, + surfaceVariant = CloudStreamPalette.LavenderPrimaryGrayBg, + surface = CloudStreamPalette.LavenderIconGrayBg, + surfaceContainer = CloudStreamPalette.LavenderBoxItemBg, + onBackground = CloudStreamPalette.LavenderText, + onSurfaceVariant = CloudStreamPalette.LavenderGrayText, + icon = CloudStreamPalette.LavenderIcon, + primary = CloudStreamPalette.Primary, + ongoing = CloudStreamPalette.Ongoing, + isLight = true, +) + +internal fun silentBlueScheme() = CloudStreamColorScheme( + background = CloudStreamPalette.SilentBlueBlackBg, + surfaceVariant = CloudStreamPalette.SilentBluePrimaryGrayBg, + surface = CloudStreamPalette.SilentBlueIconGrayBg, + surfaceContainer = CloudStreamPalette.SilentBlueBoxItemBg, + onBackground = CloudStreamPalette.SilentBlueText, + onSurfaceVariant = CloudStreamPalette.SilentBlueGrayText, + icon = CloudStreamPalette.SilentBlueIcon, + primary = CloudStreamPalette.Primary, + ongoing = CloudStreamPalette.Ongoing, + isLight = false, +) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Theme.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Theme.kt new file mode 100644 index 000000000..ab1ea1e95 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Theme.kt @@ -0,0 +1,132 @@ +package com.lagradost.cloudstream4.theme + +import androidx.compose.animation.core.InfiniteTransition +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.remember +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color + +enum class CloudStreamThemeMode { + /** "Black" standard dark, #111111 backgrounds */ + Dark, + + /** "Amoled" / "AmoledLight" pure black (#000000) */ + Amoled, + + /** "AmoledLight" pure black (#000000) */ + AmoledLight, + + /** "Light" white/gray backgrounds, dark text */ + Light, + + /** "Dracula" */ + Dracula, + + /** "Lavender" */ + Lavender, + + /** "SilentBlue" */ + SilentBlue, + + /** "System" resolved on each platform via [isSystemInDarkTheme] */ + FollowSystem, + + /** + * Uses platform dynamic color system, Material You on Android 12+, + * falls back to [Dark] on unsupported platforms. + */ + Dynamic, +} + +@Composable +fun modeToTheme(mode : CloudStreamThemeMode, primaryColor: CloudStreamPrimaryColor) : CloudStreamColorScheme { + val dynamicTheme = DeviceTheme.resolveDynamicTheme() + val dynamicPrimary = DeviceTheme.resolveDynamicPrimaryColor() + val dynamicSecondary = DeviceTheme.resolveDynamicSecondaryColor() + val systemDark = isSystemInDarkTheme() + val color = remember(mode, primaryColor, systemDark, dynamicTheme, dynamicPrimary, dynamicSecondary) { + val base = when (mode) { + CloudStreamThemeMode.Dark -> darkScheme() + CloudStreamThemeMode.Amoled -> amoledScheme() + CloudStreamThemeMode.AmoledLight -> amoledLightScheme() + CloudStreamThemeMode.Light -> lightScheme() + CloudStreamThemeMode.Dracula -> draculaScheme() + CloudStreamThemeMode.Lavender -> lavenderScheme() + CloudStreamThemeMode.SilentBlue -> silentBlueScheme() + CloudStreamThemeMode.FollowSystem -> if (systemDark) darkScheme() else lightScheme() + CloudStreamThemeMode.Dynamic -> dynamicTheme + } + when { + mode == CloudStreamThemeMode.Dynamic -> base + primaryColor == CloudStreamPrimaryColor.DYNAMIC -> base.copy(primary = dynamicPrimary) + primaryColor == CloudStreamPrimaryColor.DYNAMIC_TWO -> base.copy(primary = dynamicSecondary) + else -> base.copy(primary = primaryColor.color) + } + } + return color +} + +private fun CloudStreamColorScheme.toMaterial3ColorScheme() = if (isLight) { + lightColorScheme( + primary = primary, + background = background, + surface = surface, + surfaceVariant = surfaceVariant, + surfaceContainer = surfaceContainer, + onBackground = onBackground, + onSurface = onBackground, + onSurfaceVariant = onSurfaceVariant, + onPrimary = Color.White, + ) +} else { + darkColorScheme( + primary = primary, + background = background, + surface = surface, + surfaceVariant = surfaceVariant, + surfaceContainer = surfaceContainer, + onBackground = onBackground, + onSurface = onBackground, + onSurfaceVariant = onSurfaceVariant, + onPrimary = Color.White, + ) +} + +val LocalSharedInfiniteTransition = staticCompositionLocalOf { throw NotImplementedError() } +val LocalCloudStreamColors = staticCompositionLocalOf { darkScheme() } + +object CloudStreamTheme { + val colors: CloudStreamColorScheme @Composable @ReadOnlyComposable get() = LocalCloudStreamColors.current + /** Global synchronized animation, so many items in e.g. a LazyList can animate at the same time, + * even if they appeared at different times */ + val transition: InfiniteTransition @Composable @ReadOnlyComposable get() = LocalSharedInfiniteTransition.current +} + +@Composable +fun CloudStreamPreviewTheme(content: @Composable () -> Unit) { + CloudStreamTheme(content = content) +} + +@Composable +fun CloudStreamTheme( + mode: CloudStreamThemeMode = CloudStreamThemeMode.FollowSystem, + primaryColor: CloudStreamPrimaryColor = CloudStreamPrimaryColor.NORMAL, + content: @Composable () -> Unit, +) { + val csColors = modeToTheme(mode, primaryColor) + val globalTransition = rememberInfiniteTransition(label = "GlobalSharedTransition") + CompositionLocalProvider(LocalCloudStreamColors provides csColors, LocalSharedInfiniteTransition provides globalTransition) { + MaterialTheme( + colorScheme = csColors.toMaterial3ColorScheme(), + content = content, + typography = AppFont.typography + ) + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/mihon/LICENSE b/shared/src/commonMain/kotlin/com/mihon/LICENSE new file mode 100644 index 000000000..d9a10c0d8 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/shared/src/commonMain/kotlin/com/mihon/common/preference/InMemoryPreferenceStore.kt b/shared/src/commonMain/kotlin/com/mihon/common/preference/InMemoryPreferenceStore.kt new file mode 100644 index 000000000..5c604e540 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/common/preference/InMemoryPreferenceStore.kt @@ -0,0 +1,125 @@ +package com.mihon.common.preference + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.stateIn + +/** + * Local-copy implementation of PreferenceStore mostly for test and preview purposes + */ +class InMemoryPreferenceStore( + initialPreferences: Sequence> = sequenceOf(), +) : PreferenceStore { + + private val preferences: Map> = + initialPreferences.toList().associateBy { it.key() } + + override fun getString(key: String, defaultValue: String): PreferenceData { + val default = InMemoryPreference(key, null, defaultValue) + val data: String? = preferences[key]?.get() as? String + return if (data == null) default else InMemoryPreference(key, data, defaultValue) + } + + override fun getLong(key: String, defaultValue: Long): PreferenceData { + val default = InMemoryPreference(key, null, defaultValue) + val data: Long? = preferences[key]?.get() as? Long + return if (data == null) default else InMemoryPreference(key, data, defaultValue) + } + + override fun getInt(key: String, defaultValue: Int): PreferenceData { + val default = InMemoryPreference(key, null, defaultValue) + val data: Int? = preferences[key]?.get() as? Int + return if (data == null) default else InMemoryPreference(key, data, defaultValue) + } + + override fun getFloat(key: String, defaultValue: Float): PreferenceData { + val default = InMemoryPreference(key, null, defaultValue) + val data: Float? = preferences[key]?.get() as? Float + return if (data == null) default else InMemoryPreference(key, data, defaultValue) + } + + override fun getBoolean(key: String, defaultValue: Boolean): PreferenceData { + val default = InMemoryPreference(key, null, defaultValue) + val data: Boolean? = preferences[key]?.get() as? Boolean + return if (data == null) default else InMemoryPreference(key, data, defaultValue) + } + + override fun getStringSet(key: String, defaultValue: Set): PreferenceData> { + val default = InMemoryPreference(key, null, defaultValue) + + @Suppress("UNCHECKED_CAST") + val data: Set? = preferences[key]?.get() as? Set + return if (data == null) default else InMemoryPreference(key, data, defaultValue) + } + + @Suppress("UNCHECKED_CAST") + override fun getObjectFromString( + key: String, + defaultValue: T, + serializer: (T) -> String, + deserializer: (String) -> T, + ): PreferenceData { + val default = InMemoryPreference(key, null, defaultValue) + val data: T? = preferences[key]?.get() as? T + return if (data == null) default else InMemoryPreference(key, data, defaultValue) + } + + @Suppress("UNCHECKED_CAST") + override fun getObjectFromInt( + key: String, + defaultValue: T, + serializer: (T) -> Int, + deserializer: (Int) -> T, + ): PreferenceData { + val default = InMemoryPreference(key, null, defaultValue) + val data: T? = preferences[key]?.get() as? T + return if (data == null) default else InMemoryPreference(key, data, defaultValue) + } + + @Suppress("UNCHECKED_CAST") + override fun getObjectSetFromStringSet( + key: String, + defaultValue: Set, + serializer: (T) -> String, + deserializer: (String) -> T?, + ): PreferenceData> { + val default = InMemoryPreference(key, null, defaultValue) + val data: Set? = preferences[key]?.get() as? Set + return if (data == null) default else InMemoryPreference(key, data, defaultValue) + } + + override fun getAll(): Map { + return preferences + } + + class InMemoryPreference( + private val key: String, + private var data: T?, + private val defaultValue: T, + ) : PreferenceData { + override fun key(): String = key + + override fun get(): T = data ?: defaultValue() + + override fun isSet(): Boolean = data != null + + override fun delete() { + data = null + } + + override fun defaultValue(): T = defaultValue + + override fun changes(): Flow = flow { data } + + override fun stateIn(scope: CoroutineScope): StateFlow { + return changes().stateIn(scope, SharingStarted.Eagerly, get()) + } + + override fun set(value: T) { + data = value + } + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/common/preference/PreferenceData.kt b/shared/src/commonMain/kotlin/com/mihon/common/preference/PreferenceData.kt new file mode 100644 index 000000000..16aeb41bc --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/common/preference/PreferenceData.kt @@ -0,0 +1,71 @@ +package com.mihon.common.preference + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow + +interface PreferenceData { + + fun key(): String + + fun get(): T + + fun set(value: T) + + fun isSet(): Boolean + + fun delete() + + fun defaultValue(): T + + fun changes(): Flow + + fun stateIn(scope: CoroutineScope): StateFlow + + companion object { + /** + * A preference that should not be exposed in places like backups without user consent. + */ + fun isPrivate(key: String): Boolean { + return key.startsWith(PRIVATE_PREFIX) + } + fun privateKey(key: String): String { + return "$PRIVATE_PREFIX$key" + } + + /** + * A preference used for internal app state that isn't really a user preference + * and therefore should not be in places like backups. + */ + fun isAppState(key: String): Boolean { + return key.startsWith(APP_STATE_PREFIX) + } + fun appStateKey(key: String): String { + return "$APP_STATE_PREFIX$key" + } + + private const val APP_STATE_PREFIX = "__APP_STATE_" + private const val PRIVATE_PREFIX = "__PRIVATE_" + } +} + +inline fun PreferenceData.getAndSet(crossinline block: (T) -> R) = set( + block(get()), +) + +operator fun PreferenceData>.plusAssign(item: T) { + set(get() + item) +} + +operator fun PreferenceData>.plusAssign(items: Iterable) { + set(get() + items) +} + +operator fun PreferenceData>.minusAssign(item: T) { + set(get() - item) +} + +fun PreferenceData.toggle(): Boolean { + set(!get()) + return get() +} diff --git a/shared/src/commonMain/kotlin/com/mihon/common/preference/PreferenceStore.kt b/shared/src/commonMain/kotlin/com/mihon/common/preference/PreferenceStore.kt new file mode 100644 index 000000000..6075fb45c --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/common/preference/PreferenceStore.kt @@ -0,0 +1,87 @@ +package com.mihon.common.preference + +interface PreferenceStore { + + fun getString(key: String, defaultValue: String = ""): PreferenceData + + fun getLong(key: String, defaultValue: Long = 0): PreferenceData + + fun getInt(key: String, defaultValue: Int = 0): PreferenceData + + fun getFloat(key: String, defaultValue: Float = 0f): PreferenceData + + fun getBoolean(key: String, defaultValue: Boolean = false): PreferenceData + + fun getStringSet(key: String, defaultValue: Set = emptySet()): PreferenceData> + + fun getObjectFromString( + key: String, + defaultValue: T, + serializer: (T) -> String, + deserializer: (String) -> T, + ): PreferenceData + + fun getObjectFromInt( + key: String, + defaultValue: T, + serializer: (T) -> Int, + deserializer: (Int) -> T, + ): PreferenceData + + fun getObjectSetFromStringSet( + key: String, + defaultValue: Set, + serializer: (T) -> String, + deserializer: (String) -> T?, + ): PreferenceData> + + fun getAll(): Map +} + +fun PreferenceStore.getLongArray( + key: String, + defaultValue: List, +): PreferenceData> { + return getObjectFromString( + key = key, + defaultValue = defaultValue, + serializer = { it.joinToString(",") }, + deserializer = { it.split(",").mapNotNull { l -> l.toLongOrNull() } }, + ) +} + +inline fun > PreferenceStore.getEnum( + key: String, + defaultValue: T, +): PreferenceData { + return getObjectFromString( + key = key, + defaultValue = defaultValue, + serializer = { it.name }, + deserializer = { + try { + enumValueOf(it) + } catch (e: IllegalArgumentException) { + defaultValue + } + }, + ) +} + +inline fun > PreferenceStore.getEnumSet( + key: String, + defaultValue: Set, +): PreferenceData> { + return getObjectSetFromStringSet( + key = key, + defaultValue = defaultValue, + serializer = { it.name }, + deserializer = { + try { + enumValueOf(it) + } catch (_: IllegalArgumentException) { + null + } + }, + ) +} diff --git a/shared/src/commonMain/kotlin/com/mihon/material/AppBar.kt b/shared/src/commonMain/kotlin/com/mihon/material/AppBar.kt new file mode 100644 index 000000000..52fe5af75 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/material/AppBar.kt @@ -0,0 +1,403 @@ +package com.mihon.material + +import androidx.compose.foundation.basicMarquee +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.RowScope +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.surfaceColorAtElevation +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.action_bar_up_description +import com.lagradost.cloudstream4.generated.resources.arrow_back +import com.lagradost.cloudstream4.generated.resources.cancel +import com.lagradost.cloudstream4.generated.resources.close +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource + +@Composable +fun AppBar( + title: String?, + + modifier: Modifier = Modifier, + backgroundColor: Color? = null, + // Text + subtitle: String? = null, + // Up button + navigateUp: (() -> Unit)? = null, + navigationIcon: Painter? = null, + // Menu + actions: @Composable RowScope.() -> Unit = {}, + // Action mode + actionModeCounter: Int = 0, + onCancelActionMode: () -> Unit = {}, + actionModeActions: @Composable RowScope.() -> Unit = {}, + + scrollBehavior: TopAppBarScrollBehavior? = null, +) { + val isActionMode by remember(actionModeCounter) { + derivedStateOf { actionModeCounter > 0 } + } + + AppBar( + modifier = modifier, + backgroundColor = backgroundColor, + titleContent = { + if (isActionMode) { + AppBarTitle(actionModeCounter.toString()) + } else { + AppBarTitle(title, subtitle = subtitle) + } + }, + navigateUp = navigateUp, + navigationIcon = navigationIcon, + actions = { + if (isActionMode) { + actionModeActions() + } else { + actions() + } + }, + isActionMode = isActionMode, + onCancelActionMode = onCancelActionMode, + scrollBehavior = scrollBehavior, + ) +} + +@Composable +fun AppBar( + // Title + titleContent: @Composable () -> Unit, + + modifier: Modifier = Modifier, + backgroundColor: Color? = null, + // Up button + navigateUp: (() -> Unit)? = null, + navigationIcon: Painter? = null, + // Menu + actions: @Composable RowScope.() -> Unit = {}, + // Action mode + isActionMode: Boolean = false, + onCancelActionMode: () -> Unit = {}, + + scrollBehavior: TopAppBarScrollBehavior? = null, +) { + Column( + modifier = modifier, + ) { + TopAppBar( + navigationIcon = { + if (isActionMode) { + IconButton(onClick = onCancelActionMode) { + Icon( + painter = painterResource(Res.drawable.close), + contentDescription = stringResource(Res.string.cancel), + ) + } + } else { + navigateUp?.let { + IconButton(onClick = it) { + UpIcon(navigationIcon = navigationIcon) + } + } + } + }, + title = titleContent, + actions = actions, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = backgroundColor ?: MaterialTheme.colorScheme.surfaceColorAtElevation( + elevation = if (isActionMode) 3.dp else 0.dp, + ), + ), + scrollBehavior = scrollBehavior, + ) + } +} + +@Composable +fun AppBarTitle( + title: String?, + modifier: Modifier = Modifier, + subtitle: String? = null, +) { + Column(modifier = modifier) { + title?.let { + Text( + text = it, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + subtitle?.let { + Text( + text = it, + style = MaterialTheme.typography.bodyMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.basicMarquee( + repeatDelayMillis = 2_000, + ), + ) + } + } +} + +/* +@Composable +fun AppBarActions( + actions: List, +) { + var showMenu by remember { mutableStateOf(false) } + + actions.filterIsInstance().map { + TooltipBox( + positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above), + tooltip = { + PlainTooltip { + Text(it.title) + } + }, + state = rememberTooltipState(), + focusable = false, + ) { + IconButton( + onClick = it.onClick, + enabled = it.enabled, + ) { + Icon( + imageVector = it.icon, + tint = it.iconTint ?: LocalContentColor.current, + contentDescription = it.title, + ) + } + } + } + + val overflowActions = actions.filterIsInstance() + if (overflowActions.isNotEmpty()) { + TooltipBox( + positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above), + tooltip = { + PlainTooltip { + Text(stringResource(MR.strings.action_menu_overflow_description)) + } + }, + state = rememberTooltipState(), + focusable = false, + ) { + IconButton( + onClick = { showMenu = !showMenu }, + ) { + Icon( + MaterialSymbols.Rounded.MoreVert, + contentDescription = stringResource(MR.strings.action_menu_overflow_description), + ) + } + } + + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false }, + ) { + overflowActions.map { + DropdownMenuItem( + onClick = { + it.onClick() + showMenu = false + }, + text = { Text(it.title, fontWeight = FontWeight.Normal) }, + ) + } + } + } +}*/ + +/* +/** + * @param searchEnabled Set to false if you don't want to show search action. + * @param searchQuery If null, use normal toolbar. + * @param placeholderText If null, [MR.strings.action_search_hint] is used. + */ +@Composable +fun SearchToolbar( + searchQuery: String?, + onChangeSearchQuery: (String?) -> Unit, + modifier: Modifier = Modifier, + titleContent: @Composable () -> Unit = {}, + navigateUp: (() -> Unit)? = null, + searchEnabled: Boolean = true, + placeholderText: String? = null, + onSearch: (String) -> Unit = {}, + onClickCloseSearch: () -> Unit = { onChangeSearchQuery(null) }, + actions: @Composable RowScope.() -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null, + visualTransformation: VisualTransformation = VisualTransformation.None, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, +) { + val focusRequester = remember { FocusRequester() } + + AppBar( + modifier = modifier, + titleContent = { + if (searchQuery == null) return@AppBar titleContent() + + val keyboardController = LocalSoftwareKeyboardController.current + val focusManager = LocalFocusManager.current + + val searchAndClearFocus: () -> Unit = f@{ + if (searchQuery.isBlank()) return@f + onSearch(searchQuery) + focusManager.clearFocus() + keyboardController?.hide() + focusManager.moveFocus(FocusDirection.Next) + } + + BasicTextField( + value = searchQuery, + onValueChange = onChangeSearchQuery, + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester) + .runOnEnterKeyPressed(action = searchAndClearFocus) + .showSoftKeyboard(remember { searchQuery.isEmpty() }) + .clearFocusOnSoftKeyboardHide(), + textStyle = MaterialTheme.typography.titleMedium.copy( + color = MaterialTheme.colorScheme.onBackground, + fontWeight = FontWeight.Normal, + fontSize = 18.sp, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), + keyboardActions = KeyboardActions(onSearch = { searchAndClearFocus() }), + singleLine = true, + cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground), + visualTransformation = visualTransformation, + interactionSource = interactionSource, + decorationBox = { innerTextField -> + TextFieldDefaults.DecorationBox( + value = searchQuery, + innerTextField = innerTextField, + enabled = true, + singleLine = true, + visualTransformation = visualTransformation, + interactionSource = interactionSource, + placeholder = { + Text( + modifier = Modifier.secondaryItemAlpha(), + text = (placeholderText ?: stringResource(MR.strings.action_search_hint)), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleMedium.copy( + fontSize = 18.sp, + fontWeight = FontWeight.Normal, + ), + ) + }, + container = {}, + ) + }, + ) + }, + navigateUp = if (searchQuery == null) navigateUp else onClickCloseSearch, + actions = { + key("search") { + val onClick = { onChangeSearchQuery("") } + + if (!searchEnabled) { + // Don't show search action + } else if (searchQuery == null) { + TooltipBox( + positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above), + tooltip = { + PlainTooltip { + Text(stringResource(MR.strings.action_search)) + } + }, + state = rememberTooltipState(), + focusable = false, + ) { + IconButton( + onClick = onClick, + ) { + Icon( + MaterialSymbols.Rounded.Search, + contentDescription = stringResource(MR.strings.action_search), + ) + } + } + } else if (searchQuery.isNotEmpty()) { + TooltipBox( + positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above), + tooltip = { + PlainTooltip { + Text(stringResource(MR.strings.action_reset)) + } + }, + state = rememberTooltipState(), + focusable = false, + ) { + IconButton( + onClick = { + onClick() + focusRequester.requestFocus() + }, + ) { + Icon( + MaterialSymbols.Rounded.Close, + contentDescription = stringResource(MR.strings.action_reset), + ) + } + } + } + } + + key("actions") { actions() } + }, + isActionMode = false, + scrollBehavior = scrollBehavior, + ) +}*/ + +@Composable +fun UpIcon( + modifier: Modifier = Modifier, + navigationIcon: Painter? = null, +) { + val icon = navigationIcon + ?: painterResource(Res.drawable.arrow_back) + Icon( + painter = icon, + contentDescription = stringResource(Res.string.action_bar_up_description), + modifier = modifier, + ) +} +/* +sealed interface AppBar { + sealed interface AppBarAction + + data class Action( + val title: String, + val icon: ImageVector, + val iconTint: Color? = null, + val onClick: () -> Unit, + val enabled: Boolean = true, + ) : AppBarAction + + data class OverflowAction( + val title: String, + val onClick: () -> Unit, + ) : AppBarAction +} +*/ \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/mihon/material/Constants.kt b/shared/src/commonMain/kotlin/com/mihon/material/Constants.kt new file mode 100644 index 000000000..2b38b4485 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/material/Constants.kt @@ -0,0 +1,26 @@ +package com.mihon.material + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.material3.MaterialTheme +import androidx.compose.ui.unit.dp + +val topSmallPaddingValues = PaddingValues(top = MaterialTheme.padding.small) + +const val DISABLED_ALPHA = .38f +const val SECONDARY_ALPHA = .78f + +class Padding { + + val extraLarge = 32.dp + + val large = 24.dp + + val medium = 16.dp + + val small = 8.dp + + val extraSmall = 4.dp +} + +val MaterialTheme.padding: Padding + get() = Padding() diff --git a/shared/src/commonMain/kotlin/com/mihon/material/LabeledCheckbox.kt b/shared/src/commonMain/kotlin/com/mihon/material/LabeledCheckbox.kt new file mode 100644 index 000000000..123bb34b7 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/material/LabeledCheckbox.kt @@ -0,0 +1,50 @@ +package com.mihon.material + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.material3.Checkbox +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp + +@Composable +fun LabeledCheckbox( + label: String, + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, +) { + Row( + modifier = modifier + .clip(MaterialTheme.shapes.small) + .fillMaxWidth() + .heightIn(min = 48.dp) + .clickable( + role = Role.Checkbox, + onClick = { + if (enabled) { + onCheckedChange(!checked) + } + }, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), + ) { + Checkbox( + checked = checked, + onCheckedChange = null, + enabled = enabled, + ) + + Text(text = label) + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/material/Pill.kt b/shared/src/commonMain/kotlin/com/mihon/material/Pill.kt new file mode 100644 index 000000000..44d6c2931 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/material/Pill.kt @@ -0,0 +1,61 @@ +package com.mihon.material + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp + +@Composable +fun Pill( + text: String, + modifier: Modifier = Modifier, + color: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + contentColor: Color = MaterialTheme.colorScheme.onSurface, + style: TextStyle = LocalTextStyle.current, +) { + Surface( + modifier = modifier + .padding(start = 4.dp), + shape = MaterialTheme.shapes.extraLarge, + color = color, + contentColor = contentColor, + ) { + Box( + modifier = Modifier + .padding(6.dp, 1.dp), + contentAlignment = Alignment.Center, + ) { + Text( + text = text, + maxLines = 1, + style = style, + ) + } + } +} + +@Composable +fun Pill( + text: String, + modifier: Modifier = Modifier, + color: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + contentColor: Color = MaterialTheme.colorScheme.onSurface, + fontSize: TextUnit = LocalTextStyle.current.fontSize, +) { + Pill( + text = text, + modifier = modifier, + color = color, + contentColor = contentColor, + style = LocalTextStyle.current.merge(fontSize = fontSize), + ) +} diff --git a/shared/src/commonMain/kotlin/com/mihon/material/Scaffold.kt b/shared/src/commonMain/kotlin/com/mihon/material/Scaffold.kt new file mode 100644 index 000000000..65c11f193 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/material/Scaffold.kt @@ -0,0 +1,85 @@ +package com.mihon.material + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.material3.FabPosition +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.ScaffoldDefaults +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.contentColorFor +import androidx.compose.material3.rememberTopAppBarState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp + +@Composable +fun Scaffold( + modifier: Modifier = Modifier, + topBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior( + rememberTopAppBarState(), + ), + topBar: @Composable (TopAppBarScrollBehavior) -> Unit = {}, + bottomBar: @Composable () -> Unit = {}, + snackbarHost: @Composable () -> Unit = {}, + floatingActionButton: @Composable () -> Unit = {}, + floatingActionButtonPosition: FabPosition = FabPosition.End, + containerColor: Color = MaterialTheme.colorScheme.background, + contentColor: Color = contentColorFor(containerColor), + contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, + content: @Composable (PaddingValues) -> Unit, +) { + var fabHeight by remember { mutableIntStateOf(0) } + val density = LocalDensity.current + + Scaffold( + modifier = modifier.nestedScroll(topBarScrollBehavior.nestedScrollConnection), + topBar = { topBar(topBarScrollBehavior) }, + bottomBar = bottomBar, + snackbarHost = snackbarHost, + floatingActionButton = { + Box(Modifier.onSizeChanged { fabHeight = it.height }) { + floatingActionButton() + } + }, + floatingActionButtonPosition = floatingActionButtonPosition, + containerColor = containerColor, + contentColor = contentColor, + contentWindowInsets = contentWindowInsets, + ) { contentPadding -> + val paddingValues = remember(contentPadding) { + FabAwarePaddingValues(contentPadding) { + if (fabHeight == 0) 0.dp else with(density) { fabHeight.toDp() } + FabSpacing + } + } + content(paddingValues) + } +} + +private class FabAwarePaddingValues( + private val base: PaddingValues, + private val extraBottom: () -> Dp, +) : PaddingValues { + override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp = + base.calculateLeftPadding(layoutDirection) + + override fun calculateTopPadding(): Dp = base.calculateTopPadding() + + override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp = + base.calculateRightPadding(layoutDirection) + + override fun calculateBottomPadding(): Dp = base.calculateBottomPadding() + extraBottom() +} +private val FabSpacing = 16.dp diff --git a/shared/src/commonMain/kotlin/com/mihon/material/SettingsItems.kt b/shared/src/commonMain/kotlin/com/mihon/material/SettingsItems.kt new file mode 100644 index 000000000..ab640e685 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/material/SettingsItems.kt @@ -0,0 +1,474 @@ +package com.mihon.material + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.FlowRowScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyGridScope +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.material3.Checkbox +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream4.compose.whiteOutline +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.arrow_downward +import com.lagradost.cloudstream4.generated.resources.arrow_upward +import com.lagradost.cloudstream4.generated.resources.preview +import com.lagradost.cloudstream4.theme.CloudStreamPreviewTheme +import com.mihon.common.preference.PreferenceData +import com.mihon.common.preference.toggle +import com.mihon.presentation.secondaryItemAlpha +import com.mihon.presentation.settings.collectAsState +import com.mihon.presentation.settings.widget.PrefsHorizontalPadding +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource + +object SettingsItemsPaddings { + val Horizontal = 24.dp + val Vertical = 10.dp +} + +@Composable +fun HeadingItem(labelRes: StringResource) { + HeadingItem(stringResource(labelRes)) +} + +@Composable +fun HeadingItem(text: String) { + Text( + text = text, + style = MaterialTheme.typography.header, + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), + ) +} + +@Composable +fun IconItem(label: String, icon: ImageVector, onClick: () -> Unit) { + BaseSettingsItem( + label = label, + widget = { + Icon( + imageVector = icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + ) + }, + onClick = onClick, + ) +} + +@Composable +fun SortItem(label: String, sortDescending: Boolean?, onClick: () -> Unit) { + val arrowIcon = when (sortDescending) { + true -> painterResource(Res.drawable.arrow_downward) + false -> painterResource(Res.drawable.arrow_upward) + null -> null + } + + BaseSortItem( + label = label, + icon = arrowIcon, + onClick = onClick, + ) +} + +@Composable +fun BaseSortItem(label: String, icon: Painter?, onClick: () -> Unit) { + BaseSettingsItem( + label = label, + widget = { + if (icon != null) { + Icon( + painter = icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + ) + } else { + Spacer(modifier = Modifier.size(24.dp)) + } + }, + onClick = onClick, + ) +} + +@Composable +fun CheckboxItem(label: String, pref: PreferenceData) { + val checked by pref.collectAsState() + CheckboxItem( + label = label, + checked = checked, + onClick = { pref.toggle() }, + ) +} + +@Composable +fun CheckboxItem(label: String, checked: Boolean, onClick: () -> Unit) { + BaseSettingsItem( + label = label, + widget = { + Checkbox( + checked = checked, + onCheckedChange = null, + ) + }, + onClick = onClick, + ) +} + +@Composable +fun RadioItem(label: String, selected: Boolean, onClick: () -> Unit) { + BaseSettingsItem( + label = label, + widget = { + RadioButton( + selected = selected, + onClick = null, + ) + }, + onClick = onClick, + ) +} + +@Composable +fun SliderItem( + value: Int, + valueRange: IntProgression, + label: String, + onChange: (Int) -> Unit, + steps: Int = with(valueRange) { (last - first) - 1 }, + valueString: String = value.toString(), + labelStyle: TextStyle = MaterialTheme.typography.bodyMedium, + pillColor: Color = MaterialTheme.colorScheme.surfaceContainerHigh, +) { + BaseSliderItem( + value = value, + valueRange = valueRange, + steps = steps, + title = label, + valueString = valueString, + onChange = onChange, + titleStyle = labelStyle, + pillColor = pillColor, + modifier = Modifier.padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), + ) +} + +@Composable +fun BaseSliderItem( + value: Int, + valueRange: IntProgression, + title: String, + onChange: (Int) -> Unit, + modifier: Modifier = Modifier, + subtitle: String? = null, + steps: Int = with(valueRange) { (last - first) - 1 }, + valueString: String = value.toString(), + titleStyle: TextStyle = MaterialTheme.typography.titleLarge, + subtitleStyle: TextStyle = MaterialTheme.typography.bodySmall, + pillColor: Color = MaterialTheme.colorScheme.surfaceContainerHigh, + icon: Painter? = null, +) { + var hasFocus by remember { mutableStateOf(false) } + val haptic = LocalHapticFeedback.current + Column( + modifier = Modifier.whiteOutline(hasFocus) + .fillMaxWidth() + .then(modifier), + verticalArrangement = Arrangement.spacedBy(2.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), + ) { + if (icon != null) { + Box( + modifier = Modifier.padding(end = PrefsHorizontalPadding) + ) { + Icon( + painter = icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.onBackground, + ) + } + } + Column(modifier = Modifier.weight(1f)) { + Text( + text = title, + style = titleStyle, + ) + if (subtitle != null) { + Text( + text = subtitle, + style = subtitleStyle, + modifier = Modifier.secondaryItemAlpha(), + ) + } + } + Pill( + text = valueString, + style = MaterialTheme.typography.bodyMedium, + color = pillColor, + ) + } + Slider( + modifier = Modifier.onFocusChanged { newFocus -> + hasFocus = newFocus.hasFocus + }, + value = value, + onValueChange = f@{ + if (it == value) return@f + onChange(it) + haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove) + }, + valueRange = valueRange, + steps = steps, + ) + } +} + +@Composable +@PreviewLightDark +fun SliderItemPreview() { + CloudStreamPreviewTheme { + var value by remember { mutableIntStateOf(0) } + Surface { + BaseSliderItem( + value = value, + valueRange = 0..10, + title = "Item per row", + valueString = if (value == 0) "Auto" else value.toString(), + onChange = { value = it }, + modifier = Modifier.padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), + icon = painterResource(Res.drawable.preview) + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SelectItem( + label: String, + options: Array, + selectedIndex: Int, + onSelect: (Int) -> Unit, +) { + var expanded by remember { mutableStateOf(false) } + + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = !expanded }, + ) { + OutlinedTextField( + modifier = Modifier + .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable) + .fillMaxWidth() + .padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), + label = { Text(text = label) }, + value = options[selectedIndex].toString(), + onValueChange = {}, + readOnly = true, + singleLine = true, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon( + expanded = expanded, + ) + }, + colors = ExposedDropdownMenuDefaults.textFieldColors(), + ) + + ExposedDropdownMenu( + modifier = Modifier.exposedDropdownSize(), + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + options.forEachIndexed { index, text -> + DropdownMenuItem( + text = { Text(text.toString()) }, + onClick = { + onSelect(index) + expanded = false + }, + contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, + ) + } + } + } +} + +/* +@Composable +fun TriStateItem( + label: String, + state: TriState, + enabled: Boolean = true, + onClick: ((TriState) -> Unit)?, +) { + Row( + modifier = Modifier + .clickable( + enabled = enabled && onClick != null, + onClick = { + when (state) { + TriState.DISABLED -> onClick?.invoke(TriState.ENABLED_IS) + TriState.ENABLED_IS -> onClick?.invoke(TriState.ENABLED_NOT) + TriState.ENABLED_NOT -> onClick?.invoke(TriState.DISABLED) + } + }, + ) + .fillMaxWidth() + .padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.large), + ) { + val stateAlpha = if (enabled && onClick != null) 1f else DISABLED_ALPHA + + Icon( + imageVector = when (state) { + TriState.DISABLED -> MaterialSymbols.Rounded.CheckBoxOutlineBlank + TriState.ENABLED_IS -> MaterialSymbols.RoundedFilled.CheckBox + TriState.ENABLED_NOT -> MaterialSymbols.RoundedFilled.DisabledByDefault + }, + contentDescription = null, + tint = if (!enabled || state == TriState.DISABLED) { + MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha) + } else { + when (onClick) { + null -> MaterialTheme.colorScheme.onSurface.copy(alpha = DISABLED_ALPHA) + else -> MaterialTheme.colorScheme.primary + } + }, + ) + Text( + text = label, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = stateAlpha), + style = MaterialTheme.typography.bodyMedium, + ) + } +}*/ + +@Composable +fun TextItem(label: String, value: String, onChange: (String) -> Unit) { + OutlinedTextField( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = 4.dp), + label = { Text(text = label) }, + value = value, + onValueChange = onChange, + singleLine = true, + ) +} + +@Composable +fun SettingsChipRow(labelRes: StringResource, content: @Composable FlowRowScope.() -> Unit) { + Column { + HeadingItem(labelRes) + FlowRow( + modifier = Modifier.padding( + start = SettingsItemsPaddings.Horizontal, + top = 0.dp, + end = SettingsItemsPaddings.Horizontal, + bottom = SettingsItemsPaddings.Vertical, + ), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), + content = content, + ) + } +} + +@Composable +fun SettingsIconGrid(labelRes: StringResource, content: LazyGridScope.() -> Unit) { + Column { + HeadingItem(labelRes) + LazyVerticalGrid( + columns = GridCells.Adaptive(128.dp), + modifier = Modifier.padding( + start = SettingsItemsPaddings.Horizontal, + end = SettingsItemsPaddings.Horizontal, + bottom = SettingsItemsPaddings.Vertical, + ), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), + content = content, + ) + } +} + +@Composable +private fun BaseSettingsItem( + label: String, + widget: @Composable RowScope.() -> Unit, + onClick: () -> Unit, +) { + Row( + modifier = Modifier + .clickable(onClick = onClick) + .fillMaxWidth() + .padding( + horizontal = SettingsItemsPaddings.Horizontal, + vertical = SettingsItemsPaddings.Vertical, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + widget(this) + Text( + text = label, + style = MaterialTheme.typography.bodyMedium, + ) + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/material/Slider.kt b/shared/src/commonMain/kotlin/com/mihon/material/Slider.kt new file mode 100644 index 000000000..105c38ab8 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/material/Slider.kt @@ -0,0 +1,58 @@ +package com.mihon.material + +import androidx.annotation.IntRange +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.height +import androidx.compose.material3.Slider +import androidx.compose.material3.SliderColors +import androidx.compose.material3.SliderDefaults +import androidx.compose.material3.SliderState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import kotlin.math.roundToInt + +@Composable +fun Slider( + value: Int, + onValueChange: (Int) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + valueRange: IntProgression = 0..1, + @IntRange(from = 0) steps: Int = with(valueRange) { (last - first) - 1 }, + onValueChangeFinished: (() -> Unit)? = null, + colors: SliderColors = SliderDefaults.colors(), + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + thumb: @Composable (SliderState) -> Unit = { + SliderDefaults.Thumb( + interactionSource = interactionSource, + colors = colors, + enabled = enabled, + thumbSize = DpSize(4.0.dp, 34.0.dp) + ) + }, + track: @Composable (SliderState) -> Unit = { sliderState -> + SliderDefaults.Track( + modifier = Modifier.height(10.dp), + colors = colors, + enabled = enabled, + sliderState = sliderState + ) + }, +) { + Slider( + value = value.toFloat(), + onValueChange = { onValueChange(it.roundToInt()) }, + modifier = modifier, + enabled = enabled, + valueRange = with(valueRange) { first.toFloat()..last.toFloat() }, + steps = steps, + onValueChangeFinished = onValueChangeFinished, + colors = colors, + interactionSource = interactionSource, + thumb = thumb, + track = track, + ) +} diff --git a/shared/src/commonMain/kotlin/com/mihon/material/Typography.kt b/shared/src/commonMain/kotlin/com/mihon/material/Typography.kt new file mode 100644 index 000000000..789f81539 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/material/Typography.kt @@ -0,0 +1,14 @@ +package com.mihon.material + +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Typography +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight + +val Typography.header: TextStyle + @Composable + get() = bodyMedium.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontWeight = FontWeight.SemiBold, + ) diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/Modifier.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/Modifier.kt new file mode 100644 index 000000000..7a60342e9 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/Modifier.kt @@ -0,0 +1,120 @@ +package com.mihon.presentation + +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onPreviewKeyEvent +import androidx.compose.ui.input.key.type +import com.mihon.material.SECONDARY_ALPHA + +@Composable +fun Modifier.selectedBackground(isSelected: Boolean): Modifier { + if (!isSelected) return this + val alpha = if (isSystemInDarkTheme()) 0.16f else 0.22f + val color = MaterialTheme.colorScheme.secondary.copy(alpha = alpha) + return this.drawBehind { drawRect(color) } +} + +fun Modifier.secondaryItemAlpha(): Modifier = this.alpha(SECONDARY_ALPHA) + +fun Modifier.clickableNoIndication( + onLongClick: (() -> Unit)? = null, + onClick: () -> Unit, +) = this.combinedClickable( + interactionSource = null, + indication = null, + onLongClick = onLongClick, + onClick = onClick, +) + +/** + * For TextField, the provided [action] will be invoked when + * physical enter key is pressed. + * + * Naturally, the TextField should be set to single line only. + */ +fun Modifier.runOnEnterKeyPressed(action: () -> Unit): Modifier = this.onPreviewKeyEvent { + when (it.key) { + Key.Enter, Key.NumPadEnter -> { + // Physical keyboards generate two event types: + // - KeyDown when the key is pressed + // - KeyUp when the key is released + if (it.type == KeyEventType.KeyDown) { + action() + true + } else { + false + } + } + + else -> false + } +} + +/** + * For TextField on AppBar, this modifier will request focus + * to the element the first time it's composed. + */ +@Composable +fun Modifier.showSoftKeyboard(show: Boolean): Modifier { + if (!show) return this + val focusRequester = remember { FocusRequester() } + var openKeyboard by rememberSaveable { mutableStateOf(show) } + LaunchedEffect(focusRequester) { + if (openKeyboard) { + focusRequester.requestFocus() + openKeyboard = false + } + } + return this.focusRequester(focusRequester) +} + +/* +/** + * For TextField, this modifier will clear focus when soft + * keyboard is hidden. + */ +@Composable +fun Modifier.clearFocusOnSoftKeyboardHide( + onFocusCleared: (() -> Unit)? = null, +): Modifier { + var isFocused by remember { mutableStateOf(false) } + var keyboardShowedSinceFocused by remember { mutableStateOf(false) } + if (isFocused) { + val imeVisible = WindowInsets.isImeVisible + val focusManager = LocalFocusManager.current + LaunchedEffect(imeVisible) { + if (imeVisible) { + keyboardShowedSinceFocused = true + } else if (keyboardShowedSinceFocused) { + focusManager.clearFocus() + onFocusCleared?.invoke() + } + } + } + + return this.onFocusChanged { + if (isFocused != it.isFocused) { + if (isFocused) { + keyboardShowedSinceFocused = false + } + isFocused = it.isFocused + } + } +} +*/ \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/Navigation.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/Navigation.kt new file mode 100644 index 000000000..5cdb8aada --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/Navigation.kt @@ -0,0 +1,9 @@ +package com.mihon.presentation + +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.staticCompositionLocalOf + +/** + * For invoking back press to the parent activity + */ +val LocalBackPress: ProvidableCompositionLocal<(() -> Unit)?> = staticCompositionLocalOf { null } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/Preference.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/Preference.kt new file mode 100644 index 000000000..11ad8b984 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/Preference.kt @@ -0,0 +1,198 @@ +package com.mihon.presentation.settings + +import androidx.annotation.IntRange +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.painter.Painter +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.none +import com.mihon.common.preference.PreferenceData +import org.jetbrains.compose.resources.stringResource + +sealed class Preference { + abstract val title: String + abstract val enabled: Boolean + + sealed class PreferenceItem : Preference() { + abstract val subtitle: String? + abstract val icon: Painter? + abstract val onValueChanged: suspend (value: T) -> R + + /** + * A basic [PreferenceItem] that only displays texts. + */ + data class TextPreference( + override val title: String, + override val subtitle: String? = null, + override val enabled: Boolean = true, + val widget: @Composable (() -> Unit)? = null, + val onClick: (() -> Unit)? = null, + override val icon: Painter? = null + ) : PreferenceItem() { + override val onValueChanged: suspend (value: String) -> Unit = {} + } + + /** + * A [PreferenceItem] that provides a two-state toggleable option. + */ + data class SwitchPreference( + val preference: PreferenceData, + override val title: String, + override val subtitle: String? = null, + override val enabled: Boolean = true, + override val onValueChanged: suspend (value: Boolean) -> Boolean = { true }, + override val icon: Painter? = null + ) : PreferenceItem() + + /** + * A [PreferenceItem] that provides a slider to select an integer number. + */ + data class SliderPreference( + val value: Int, + override val title: String, + override val subtitle: String? = null, + val valueString: String? = null, + val valueRange: IntProgression = 0..1, + @IntRange(from = 0) val steps: Int = with(valueRange) { (last - first) - 1 }, + override val enabled: Boolean = true, + override val onValueChanged: suspend (value: Int) -> Unit = {}, + override val icon: Painter? = null + ) : PreferenceItem() + + /** + * A [PreferenceItem] that displays a list of entries as a dialog. + */ + @Suppress("UNCHECKED_CAST") + data class ListPreference( + val preference: PreferenceData, + val entries: Map, + override val title: String, + override val subtitle: String? = "%s", + val subtitleProvider: @Composable (value: T, entries: Map) -> String? = + { v, e -> subtitle?.format(e[v]) }, + override val icon: Painter? = null, + override val enabled: Boolean = true, + override val onValueChanged: suspend (value: T) -> Boolean = { true }, + val iconProvider: (@Composable (key: T, value: String) -> Unit)? = null, + ) : PreferenceItem() { + internal fun internalSet(value: Any) = preference.set(value as T) + internal suspend fun internalOnValueChanged(value: Any) = onValueChanged(value as T) + + @Composable + internal fun internalSubtitleProvider(value: Any?, entries: Map) = + subtitleProvider(value as T, entries as Map) + @Composable + internal fun InternalIconProvider(key: Any?, value: String) = + iconProvider?.invoke(key as T, value) + } + + /** + * [ListPreference] but with no connection to a [PreferenceData] + */ + data class BasicListPreference( + val value: String, + val entries: Map, + override val title: String, + override val subtitle: String? = "%s", + val subtitleProvider: @Composable (value: String, entries: Map) -> String? = + { v, e -> subtitle?.format(e[v]) }, + override val icon: Painter? = null, + override val enabled: Boolean = true, + override val onValueChanged: suspend (value: String) -> Unit = {}, + ) : PreferenceItem() + + /** + * A [PreferenceItem] that displays a list of entries as a dialog. + * Multiple entries can be selected at the same time. + */ + @Suppress("UNCHECKED_CAST") + data class MultiSelectListPreference( + val preference: PreferenceData>, + val entries: Map, + override val title: String, + override val subtitle: String? = "%s", + val subtitleProvider: @Composable (value: Set, entries: Map) -> String? = + { v, e -> + val combined = remember(v, e) { + v.mapNotNull { e[it] } + .joinToString() + .takeUnless { it.isBlank() } + } + ?: stringResource(Res.string.none) + subtitle?.format(combined) + }, + override val icon: Painter? = null, + override val enabled: Boolean = true, + override val onValueChanged: suspend (value: Set) -> Boolean = { true }, + ) : PreferenceItem, Boolean>() { + internal fun internalSet(value: Set) = preference.set(value as Set) + internal suspend fun internalOnValueChanged(value: Set) = onValueChanged(value as Set) + + @Composable + internal fun internalSubtitleProvider(value: Set, entries: Map) = + subtitleProvider(value as Set, entries as Map) + } + + /** + * A [PreferenceItem] that shows a EditText in the dialog. + */ + data class EditTextPreference( + val preference: PreferenceData, + override val title: String, + override val subtitle: String? = "%s", + override val enabled: Boolean = true, + override val onValueChanged: suspend (value: String) -> Boolean = { true }, + ) : PreferenceItem() { + override val icon: Painter? = null + } + + /** + * A [PreferenceItem] for individual tracker. + */ + /*data class TrackerPreference( + val tracker: Tracker, + val login: () -> Unit, + val logout: () -> Unit, + ) : PreferenceItem() { + override val title: String = "" + override val enabled: Boolean = true + override val subtitle: String? = null + override val icon: ImageVector? = null + override val onValueChanged: suspend (value: String) -> Unit = {} + }*/ + + data class InfoPreference( + override val title: String, + ) : PreferenceItem() { + override val enabled: Boolean = true + override val subtitle: String? = null + override val icon: Painter? = null + override val onValueChanged: suspend (value: String) -> Unit = {} + } + + data class CustomPreference( + override val title: String, + val content: @Composable () -> Unit, + ) : PreferenceItem() { + override val enabled: Boolean = true + override val subtitle: String? = null + override val icon: Painter? = null + override val onValueChanged: suspend (value: Unit) -> Unit = {} + } + } + + data class PreferenceGroup( + override val title: String, + override val enabled: Boolean = true, + + val preferenceItems: List>, + ) : Preference() +} + +@Composable +fun PreferenceData.collectAsState(): State { + val flow = remember(this) { changes() } + return flow.collectAsState(initial = get()) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceItem.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceItem.kt new file mode 100644 index 000000000..151f3cb69 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceItem.kt @@ -0,0 +1,188 @@ +package com.mihon.presentation.settings + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.structuralEqualityPolicy +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.mihon.presentation.settings.widget.EditTextPreferenceWidget +import com.mihon.presentation.settings.widget.InfoWidget +import com.mihon.presentation.settings.widget.ListPreferenceWidget +import com.mihon.presentation.settings.widget.MultiSelectListPreferenceWidget +import com.mihon.presentation.settings.widget.PrefsHorizontalPadding +import com.mihon.presentation.settings.widget.PrefsVerticalPadding +import com.mihon.presentation.settings.widget.SwitchPreferenceWidget +import com.mihon.presentation.settings.widget.TextPreferenceWidget +import com.mihon.presentation.settings.widget.TitleFontSize +import kotlinx.coroutines.launch +import com.mihon.material.BaseSliderItem + +val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false } +val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) { 56.dp } + +@Composable +fun StatusWrapper( + item: Preference.PreferenceItem<*, *>, + highlightKey: String?, + content: @Composable () -> Unit, +) { + val enabled = item.enabled + val highlighted = item.title == highlightKey + AnimatedVisibility( + visible = enabled, + enter = expandVertically() + fadeIn(), + exit = shrinkVertically() + fadeOut(), + content = { + CompositionLocalProvider( + LocalPreferenceHighlighted provides highlighted, + content = content, + ) + }, + ) +} + +@Composable +internal fun PreferenceItem( + item: Preference.PreferenceItem<*, *>, + highlightKey: String?, +) { + val scope = rememberCoroutineScope() + StatusWrapper( + item = item, + highlightKey = highlightKey, + ) { + when (item) { + is Preference.PreferenceItem.SwitchPreference -> { + val value by item.preference.collectAsState() + SwitchPreferenceWidget( + title = item.title, + subtitle = item.subtitle, + icon = item.icon, + checked = value, + onCheckedChanged = { newValue -> + scope.launch { + if (item.onValueChanged(newValue)) { + item.preference.set(newValue) + } + } + }, + ) + } + is Preference.PreferenceItem.SliderPreference -> { + BaseSliderItem( + value = item.value, + valueRange = item.valueRange, + steps = item.steps, + title = item.title, + subtitle = item.subtitle, + valueString = item.valueString.takeUnless { it.isNullOrEmpty() } ?: item.value.toString(), + onChange = { + scope.launch { + item.onValueChanged(it) + } + }, + titleStyle = MaterialTheme.typography.titleLarge.copy(fontSize = TitleFontSize), + modifier = Modifier.padding( + horizontal = PrefsHorizontalPadding, + vertical = PrefsVerticalPadding, + ), + icon = item.icon + ) + } + is Preference.PreferenceItem.ListPreference<*> -> { + val value by item.preference.collectAsState() + ListPreferenceWidget( + value = value, + title = item.title, + subtitle = item.internalSubtitleProvider(value, item.entries), + icon = item.icon, + entries = item.entries, + iconProvider = item::InternalIconProvider, + onValueChange = { newValue -> + scope.launch { + if (item.internalOnValueChanged(newValue!!)) { + item.internalSet(newValue) + } + } + }, + ) + } + is Preference.PreferenceItem.BasicListPreference -> { + ListPreferenceWidget( + value = item.value, + title = item.title, + subtitle = item.subtitleProvider(item.value, item.entries), + icon = item.icon, + entries = item.entries, + onValueChange = { scope.launch { item.onValueChanged(it) } }, + ) + } + is Preference.PreferenceItem.MultiSelectListPreference<*> -> { + val values by item.preference.collectAsState() + MultiSelectListPreferenceWidget( + values = values, + title = item.title, + subtitle = item.internalSubtitleProvider(values, item.entries), + icon = item.icon, + entries = item.entries, + onValuesChange = { newValues -> + scope.launch { + if (item.internalOnValueChanged(newValues)) { + item.internalSet(newValues) + } + } + }, + ) + } + is Preference.PreferenceItem.TextPreference -> { + TextPreferenceWidget( + title = item.title, + subtitle = item.subtitle, + icon = item.icon, + widget = item.widget, + onPreferenceClick = item.onClick, + ) + } + is Preference.PreferenceItem.EditTextPreference -> { + val values by item.preference.collectAsState() + EditTextPreferenceWidget( + title = item.title, + subtitle = item.subtitle, + icon = item.icon, + value = values, + onConfirm = { + val accepted = item.onValueChanged(it) + if (accepted) item.preference.set(it) + accepted + }, + ) + } + /*is Preference.PreferenceItem.TrackerPreference -> { + val isLoggedIn by item.tracker.let { tracker -> + tracker.isLoggedInFlow.collectAsState(tracker.isLoggedIn) + } + TrackingPreferenceWidget( + tracker = item.tracker, + isLoggedIn = isLoggedIn, + onClick = { if (isLoggedIn) item.logout() else item.login() }, + ) + }*/ + is Preference.PreferenceItem.InfoPreference -> { + InfoWidget(text = item.title) + } + is Preference.PreferenceItem.CustomPreference -> { + item.content() + } + } + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScaffold.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScaffold.kt new file mode 100644 index 000000000..4e79407c5 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScaffold.kt @@ -0,0 +1,46 @@ +package com.mihon.presentation.settings + +import androidx.compose.foundation.layout.RowScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component1 +import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component2 +import androidx.compose.ui.focus.focusProperties +import androidx.compose.ui.focus.focusRequester +import com.mihon.material.AppBar +import com.mihon.material.Scaffold + +@Composable +fun PreferenceScaffold( + title: String, + actions: @Composable RowScope.() -> Unit = {}, + onBackPressed: (() -> Unit)? = null, + itemsProvider: @Composable () -> List, +) { + val (top, bottom) = remember { FocusRequester.createRefs() } + + Scaffold( + topBar = { + AppBar( + modifier = Modifier.focusRequester(top).focusProperties { + down = bottom + }, + title = title, + navigateUp = onBackPressed, + actions = actions, + scrollBehavior = it, + ) + }, + content = { contentPadding -> + PreferenceScreen( + modifier = Modifier.focusRequester(bottom).focusProperties { + up = top + }, + items = itemsProvider(), + contentPadding = contentPadding, + ) + }, + ) +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScreen.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScreen.kt new file mode 100644 index 000000000..0e99aa9a3 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScreen.kt @@ -0,0 +1,96 @@ +package com.mihon.presentation.settings + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastForEachIndexed +import com.mihon.presentation.settings.widget.PreferenceGroupHeader +import kotlinx.coroutines.delay +import kotlin.time.Duration.Companion.seconds + +/** + * Preference Screen composable which contains a list of [Preference] items + * @param items [Preference] items which should be displayed on the preference screen. An item can be a single [PreferenceItem] or a group ([Preference.PreferenceGroup]) + * @param modifier [Modifier] to be applied to the preferenceScreen layout + */ +@Composable +fun PreferenceScreen( + items: List, + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(0.dp), +) { + val state = rememberLazyListState() + val highlightKey = SearchableSettings.highlightKey + if (highlightKey != null) { + LaunchedEffect(Unit) { + val i = items.findHighlightedIndex(highlightKey) + if (i >= 0) { + delay(0.5.seconds) + state.animateScrollToItem(i) + } + SearchableSettings.highlightKey = null + } + } + + LazyColumn( + modifier = modifier, + state = state, + contentPadding = contentPadding, + ) { + items.fastForEachIndexed { i, preference -> + when (preference) { + // Create Preference Group + is Preference.PreferenceGroup -> { + if (!preference.enabled) return@fastForEachIndexed + + item { + Column { + PreferenceGroupHeader(title = preference.title) + } + } + items(preference.preferenceItems) { item -> + PreferenceItem( + item = item, + highlightKey = highlightKey, + ) + } + item { + if (i < items.lastIndex) { + Spacer(modifier = Modifier.height(12.dp)) + } + } + } + + // Create Preference Item + is Preference.PreferenceItem<*, *> -> item { + PreferenceItem( + item = preference, + highlightKey = highlightKey, + ) + } + } + } + } +} + +private fun List.findHighlightedIndex(highlightKey: String): Int { + return flatMap { + if (it is Preference.PreferenceGroup) { + buildList { + add(null) // Header + addAll(it.preferenceItems.map { groupItem -> groupItem.title }) + add(null) // Spacer + } + } else { + listOf(it.title) + } + }.indexOfFirst { it == highlightKey } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/SearchableSettings.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/SearchableSettings.kt new file mode 100644 index 000000000..c114f0dd0 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/SearchableSettings.kt @@ -0,0 +1,39 @@ +package com.mihon.presentation.settings + +import androidx.compose.foundation.layout.RowScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import com.lagradost.cloudstream4.compose.Screen +import com.mihon.presentation.LocalBackPress + +interface SearchableSettings : Screen { + @Composable + @ReadOnlyComposable + fun getTitleRes(): String + + @Composable + fun getPreferences(): List + + @Composable + fun RowScope.AppBarAction() { + } + + @Composable + override fun Content() { + val handleBack = LocalBackPress.current + PreferenceScaffold( + title = getTitleRes(), + onBackPressed = if (handleBack != null) handleBack::invoke else null, + actions = { AppBarAction() }, + itemsProvider = { getPreferences() }, + ) + } + + companion object { + // HACK: for the background blipping thingy. + // The title of the target PreferenceItem + // Set before showing the destination screen and reset after + // See BasePreferenceWidget.highlightBackground + var highlightKey: String? = null + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/BasePreferenceWidget.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/BasePreferenceWidget.kt new file mode 100644 index 000000000..6537ea9a9 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/BasePreferenceWidget.kt @@ -0,0 +1,127 @@ +package com.mihon.presentation.settings.widget + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.StartOffset +import androidx.compose.animation.core.StartOffsetType +import androidx.compose.animation.core.repeatable +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.sizeIn +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.lagradost.cloudstream4.compose.focusOutline +import com.mihon.presentation.settings.LocalPreferenceHighlighted +import com.mihon.presentation.settings.LocalPreferenceMinHeight +import kotlinx.coroutines.delay +import kotlin.time.Duration.Companion.seconds + +@Composable +internal fun BasePreferenceWidget( + modifier: Modifier = Modifier, + title: String? = null, + subcomponent: @Composable (ColumnScope.() -> Unit)? = null, + icon: @Composable (() -> Unit)? = null, + onClick: (() -> Unit)? = null, + widget: @Composable (() -> Unit)? = null, +) { + val highlighted = LocalPreferenceHighlighted.current + val minHeight = LocalPreferenceMinHeight.current + Row( + modifier = modifier + .highlightBackground(highlighted) + .focusOutline() + .sizeIn(minHeight = minHeight) + .clickable(enabled = onClick != null, onClick = { onClick?.invoke() }) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + if (icon != null) { + Box( + modifier = Modifier.padding(start = PrefsHorizontalPadding, end = 8.dp), + content = { icon() }, + ) + } + Column( + modifier = Modifier + .weight(1f) + .padding(vertical = PrefsVerticalPadding), + ) { + if (!title.isNullOrBlank()) { + Text( + modifier = Modifier.padding(horizontal = PrefsHorizontalPadding), + text = title, + overflow = TextOverflow.Ellipsis, + maxLines = 2, + style = MaterialTheme.typography.titleLarge, + fontSize = TitleFontSize, + ) + } + subcomponent?.invoke(this) + } + if (widget != null) { + Box( + modifier = Modifier.padding(end = PrefsHorizontalPadding), + content = { widget() }, + ) + } + } +} + +@Composable +internal fun Modifier.highlightBackground(highlighted: Boolean): Modifier { + var highlightFlag by remember { mutableStateOf(false) } + LaunchedEffect(Unit) { + if (highlighted) { + highlightFlag = true + delay(3.seconds) + highlightFlag = false + } + } + val highlight by animateColorAsState( + targetValue = if (highlightFlag) { + MaterialTheme.colorScheme.surfaceTint.copy(alpha = .12f) + } else { + Color.Transparent + }, + animationSpec = if (highlightFlag) { + repeatable( + iterations = 5, + animation = tween(durationMillis = 200), + repeatMode = RepeatMode.Reverse, + initialStartOffset = StartOffset( + offsetMillis = 600, + offsetType = StartOffsetType.Delay, + ), + ) + } else { + tween(200) + }, + label = "highlight", + ) + return this.background(color = highlight) +} + +internal val TrailingWidgetBuffer = 16.dp +internal val PrefsHorizontalPadding = 16.dp +internal val PrefsVerticalPadding = 16.dp +internal val TitleFontSize = 16.sp diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/EditTextPreferenceWidget.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/EditTextPreferenceWidget.kt new file mode 100644 index 000000000..137318928 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/EditTextPreferenceWidget.kt @@ -0,0 +1,97 @@ +package com.mihon.presentation.settings.widget + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.window.DialogProperties +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.cancel +import com.lagradost.cloudstream4.generated.resources.error +import com.lagradost.cloudstream4.generated.resources.ok +import kotlinx.coroutines.launch +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource + +@Composable +fun EditTextPreferenceWidget( + title: String, + subtitle: String?, + icon: Painter?, + value: String, + onConfirm: suspend (String) -> Boolean, +) { + var isDialogShown by remember { mutableStateOf(false) } + + TextPreferenceWidget( + title = title, + subtitle = subtitle?.format(value), + icon = icon, + onPreferenceClick = { isDialogShown = true }, + ) + + if (isDialogShown) { + val scope = rememberCoroutineScope() + val onDismissRequest = { isDialogShown = false } + var textFieldValue by rememberSaveable(stateSaver = TextFieldValue.Saver) { + mutableStateOf(TextFieldValue(value)) + } + AlertDialog( + onDismissRequest = onDismissRequest, + title = { Text(text = title) }, + text = { + OutlinedTextField( + value = textFieldValue, + onValueChange = { textFieldValue = it }, + trailingIcon = { + if (textFieldValue.text.isBlank()) { + Icon(painter = painterResource(Res.drawable.error), contentDescription = null) + } else { + IconButton(onClick = { textFieldValue = TextFieldValue("") }) { + Icon(painter = painterResource(Res.drawable.cancel), contentDescription = null) + } + } + }, + isError = textFieldValue.text.isBlank(), + singleLine = true, + modifier = Modifier.fillMaxWidth(), + ) + }, + properties = DialogProperties( + usePlatformDefaultWidth = true, + ), + confirmButton = { + TextButton( + enabled = textFieldValue.text != value && textFieldValue.text.isNotBlank(), + onClick = { + scope.launch { + if (onConfirm(textFieldValue.text)) { + onDismissRequest() + } + } + }, + ) { + Text(text = stringResource(Res.string.ok)) + } + }, + dismissButton = { + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(Res.string.cancel)) + } + }, + ) + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/InfoWidget.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/InfoWidget.kt new file mode 100644 index 000000000..3382ebda5 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/InfoWidget.kt @@ -0,0 +1,53 @@ +package com.mihon.presentation.settings.widget + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewLightDark +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.info +import com.lagradost.cloudstream4.generated.resources.lorem_content +import com.lagradost.cloudstream4.generated.resources.lorem_title +import com.lagradost.cloudstream4.theme.CloudStreamPreviewTheme +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource +import com.mihon.presentation.secondaryItemAlpha +import com.mihon.material.padding + +@Composable +internal fun InfoWidget(text: String) { + Column( + modifier = Modifier + .padding( + horizontal = PrefsHorizontalPadding, + vertical = MaterialTheme.padding.medium, + ) + .secondaryItemAlpha(), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium), + ) { + Icon( + painter = painterResource(Res.drawable.info), + contentDescription = null, + ) + Text( + text = text, + style = MaterialTheme.typography.bodySmall, + ) + } +} + +@PreviewLightDark +@Composable +private fun InfoWidgetPreview() { + CloudStreamPreviewTheme { + Surface { + InfoWidget(text = stringResource(Res.string.lorem_content)) + } + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/ListPreferenceWidget.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/ListPreferenceWidget.kt new file mode 100644 index 000000000..6a573e7e8 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/ListPreferenceWidget.kt @@ -0,0 +1,110 @@ +package com.mihon.presentation.settings.widget + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.painter.Painter +import com.lagradost.cloudstream4.compose.SingleSelectDialog +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.cancel +import com.lagradost.cloudstream4.generated.resources.ok +import org.jetbrains.compose.resources.stringResource + +@Composable +fun ListPreferenceWidget( + value: T, + title: String, + subtitle: String?, + icon: Painter?, + entries: Map, + onValueChange: (T) -> Unit, + iconProvider: (@Composable (key: T, value: String) -> Unit)? = null +) { + var isDialogShown by remember { mutableStateOf(false) } + + TextPreferenceWidget( + title = title, + subtitle = subtitle, + icon = icon, + onPreferenceClick = { isDialogShown = true }, + ) + + if (isDialogShown) { + SingleSelectDialog( + dismiss = { isDialogShown = false }, + title = title, + entries = entries, + selectedKey = value, + confirm = { key -> + onValueChange(key) + isDialogShown = false + }, + confirmText = stringResource(Res.string.ok), + dismissText = stringResource(Res.string.cancel), + iconProvider = iconProvider + ) + /*AlertDialog( + onDismissRequest = { isDialogShown = false }, + title = { Text(text = title) }, + text = { + Box { + val state = rememberLazyListState() + LazyColumn(state = state) { + entries.forEach { current -> + val isSelected = value == current.key + item { + DialogRow( + label = current.value, + isSelected = isSelected, + onSelected = { + onValueChange(current.key!!) + isDialogShown = false + }, + ) + } + } + } + if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) + } + }, + confirmButton = { + TextButton(onClick = { isDialogShown = false }) { + Text(text = stringResource(Res.string.cancel)) + } + }, + )*/ + } +} +/* +@Composable +private fun DialogRow( + label: String, + isSelected: Boolean, + onSelected: () -> Unit, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clip(MaterialTheme.shapes.small) + .selectable( + selected = isSelected, + onClick = { if (!isSelected) onSelected() }, + ) + .fillMaxWidth() + .minimumInteractiveComponentSize(), + ) { + RadioButton( + selected = isSelected, + onClick = null, + ) + Text( + text = label, + style = MaterialTheme.typography.bodyLarge.merge(), + modifier = Modifier.padding(start = 24.dp), + ) + } +} +*/ \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/MultiSelectListPreferenceWidget.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/MultiSelectListPreferenceWidget.kt new file mode 100644 index 000000000..c0a2d735f --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/MultiSelectListPreferenceWidget.kt @@ -0,0 +1,97 @@ +package com.mihon.presentation.settings.widget + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.window.DialogProperties +import com.lagradost.cloudstream4.compose.MultiSelectDialog +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.cancel +import com.lagradost.cloudstream4.generated.resources.ok +import org.jetbrains.compose.resources.stringResource + +@Composable +fun MultiSelectListPreferenceWidget( + values: Set, + title: String, + subtitle: String?, + icon: Painter?, + entries: Map, + onValuesChange: (Set) -> Unit, +) { + var isDialogShown by remember { mutableStateOf(false) } + + TextPreferenceWidget( + title = title, + subtitle = subtitle, + icon = icon, + onPreferenceClick = { isDialogShown = true }, + ) + + if (isDialogShown) { + MultiSelectDialog( + title = title, + entries = entries, + properties = DialogProperties( + usePlatformDefaultWidth = true, + ), + confirmText = stringResource(Res.string.ok), + confirm = { selection -> + onValuesChange(selection) + isDialogShown = false + }, + dismissText = stringResource(Res.string.cancel), + dismiss = { + isDialogShown = false + }, + selectedKeys = values, + ) + + /* + AlertDialog( + onDismissRequest = { isDialogShown = false }, + title = { Text(text = title) }, + text = { + LazyColumn { + entries.forEach { current -> + item { + val isSelected = selected.contains(current.key) + LabeledCheckbox( + label = current.value, + checked = isSelected, + onCheckedChange = { + if (it) { + selected.add(current.key) + } else { + selected.remove(current.key) + } + }, + ) + } + } + } + }, + properties = DialogProperties( + usePlatformDefaultWidth = true, + ), + confirmButton = { + TextButton( + onClick = { + onValuesChange(selected.toMutableSet()) + isDialogShown = false + }, + ) { + Text(text = stringResource(Res.string.ok)) + } + }, + dismissButton = { + TextButton(onClick = { isDialogShown = false }) { + Text(text = stringResource(Res.string.cancel)) + } + }, + )*/ + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/PreferenceGroupHeader.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/PreferenceGroupHeader.kt new file mode 100644 index 000000000..b7d1197a9 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/PreferenceGroupHeader.kt @@ -0,0 +1,28 @@ +package com.mihon.presentation.settings.widget + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun PreferenceGroupHeader(title: String) { + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 8.dp, top = 14.dp), + ) { + Text( + text = title, + color = MaterialTheme.colorScheme.onBackground, + modifier = Modifier.padding(horizontal = PrefsHorizontalPadding), + style = MaterialTheme.typography.bodyMedium, + ) + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/SwitchPreferenceWidget.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/SwitchPreferenceWidget.kt new file mode 100644 index 000000000..2744cdcdc --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/SwitchPreferenceWidget.kt @@ -0,0 +1,73 @@ +package com.mihon.presentation.settings.widget + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.tooling.preview.PreviewLightDark +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.preview +import com.lagradost.cloudstream4.theme.CloudStreamPreviewTheme +import org.jetbrains.compose.resources.painterResource + +@Composable +fun SwitchPreferenceWidget( + modifier: Modifier = Modifier, + title: String, + subtitle: String? = null, + icon: Painter? = null, + checked: Boolean = false, + onCheckedChanged: (Boolean) -> Unit, +) { + TextPreferenceWidget( + modifier = modifier, + title = title, + subtitle = subtitle, + icon = icon, + widget = { + Switch( + checked = checked, + onCheckedChange = null, + modifier = Modifier.padding(start = TrailingWidgetBuffer), + ) + }, + onPreferenceClick = { onCheckedChanged(!checked) }, + ) +} + +@PreviewLightDark +@Composable +private fun SwitchPreferenceWidgetPreview() { + CloudStreamPreviewTheme { + Surface { + Column { + SwitchPreferenceWidget( + title = "Text preference with icon", + subtitle = "Text preference summary", + icon = painterResource(Res.drawable.preview) , + checked = true, + onCheckedChanged = {}, + ) + SwitchPreferenceWidget( + title = "Text preference", + subtitle = "Text preference summary", + checked = false, + onCheckedChanged = {}, + ) + SwitchPreferenceWidget( + title = "Text preference no summary", + checked = false, + onCheckedChanged = {}, + ) + SwitchPreferenceWidget( + title = "Another text preference no summary", + checked = false, + onCheckedChanged = {}, + ) + } + } + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/TextPreferenceWidget.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/TextPreferenceWidget.kt new file mode 100644 index 000000000..91c19cd6a --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/TextPreferenceWidget.kt @@ -0,0 +1,86 @@ +package com.mihon.presentation.settings.widget + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.preview +import com.lagradost.cloudstream4.theme.CloudStreamPreviewTheme +import org.jetbrains.compose.resources.painterResource +import com.mihon.presentation.secondaryItemAlpha + +@Composable +fun TextPreferenceWidget( + modifier: Modifier = Modifier, + title: String? = null, + subtitle: String? = null, + icon: Painter? = null, + iconTint: Color = MaterialTheme.colorScheme.onBackground, + widget: @Composable (() -> Unit)? = null, + onPreferenceClick: (() -> Unit)? = null, +) { + BasePreferenceWidget( + modifier = modifier, + title = title, + subcomponent = if (!subtitle.isNullOrBlank()) { + { + Text( + text = subtitle, + modifier = Modifier + .padding(horizontal = PrefsHorizontalPadding) + .secondaryItemAlpha(), + style = MaterialTheme.typography.bodyMedium, + maxLines = 10, + ) + } + } else { + null + }, + icon = if (icon != null) { + { + Icon( + painter = icon, + modifier = Modifier.size(24.dp), + tint = iconTint, + contentDescription = null, + ) + } + } else { + null + }, + onClick = onPreferenceClick, + widget = widget, + ) +} + +@PreviewLightDark +@Composable +private fun TextPreferenceWidgetPreview() { + CloudStreamPreviewTheme { + Surface { + Column { + TextPreferenceWidget( + title = "Text preference with icon", + subtitle = "Text preference summary", + icon = painterResource(Res.drawable.preview), + onPreferenceClick = {}, + ) + TextPreferenceWidget( + title = "Text preference", + subtitle = "Text preference summary", + onPreferenceClick = {}, + ) + } + } + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/readme.md b/shared/src/commonMain/kotlin/com/mihon/readme.md new file mode 100644 index 000000000..2984058d0 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/readme.md @@ -0,0 +1,4 @@ +Mihon (tachiyomi) has a great codebase for working with compose. +Because they do not provide these features as a lib, we are forced to fork each file we want instead. + +https://github.com/mihonapp/mihon \ No newline at end of file diff --git a/shared/src/jvmMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.jvm.kt b/shared/src/jvmMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.jvm.kt new file mode 100644 index 000000000..cca2d70d7 --- /dev/null +++ b/shared/src/jvmMain/kotlin/com/lagradost/cloudstream4/DevicePreferenceStore.jvm.kt @@ -0,0 +1,16 @@ +package com.lagradost.cloudstream4 + +import androidx.compose.runtime.Composable +import com.mihon.common.preference.InMemoryPreferenceStore + +/** As this is mostly for testing so far, we can assume we have a shared global for settings */ +internal val settings = AppSettings() + +@Composable +actual fun rememberAppSettings(): AppSettings { + return settings +} + +fun AppSettings() : AppSettings { + return AppSettings(preferences = InMemoryPreferenceStore()) +} diff --git a/shared/src/jvmMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.jvm.kt b/shared/src/jvmMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.jvm.kt new file mode 100644 index 000000000..dc9f4f1ae --- /dev/null +++ b/shared/src/jvmMain/kotlin/com/lagradost/cloudstream4/theme/DeviceTheme.jvm.kt @@ -0,0 +1,19 @@ +package com.lagradost.cloudstream4.theme + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.graphics.Color + +internal actual object DeviceTheme { + @Composable + @ReadOnlyComposable + actual fun resolveDynamicTheme(): CloudStreamColorScheme = darkScheme() + + @Composable + @ReadOnlyComposable + actual fun resolveDynamicPrimaryColor(): Color = CloudStreamPrimaryColor.NORMAL.color + + @Composable + @ReadOnlyComposable + actual fun resolveDynamicSecondaryColor(): Color = CloudStreamPrimaryColor.NORMAL.color +} \ No newline at end of file diff --git a/shared/src/readme.md b/shared/src/readme.md new file mode 100644 index 000000000..8b37a84a0 --- /dev/null +++ b/shared/src/readme.md @@ -0,0 +1,11 @@ +https://kotlinlang.org/docs/multiplatform/compose-multiplatform-create-first-app.html#examine-the-project-structure + +`jvmMain` contains source files for the desktop target, which uses Kotlin/JVM. + +`androidMain` contains Android source files and targets Kotlin/JVM. + +`iosMain` contains Kotlin code for iOS and targets Kotlin/Native. + +`jsMain` contains JavaScript-specific Kotlin code and targets Kotlin/JS. + +`wasmJsMain` contains Wasm-specific Kotlin code and targets Kotlin/Wasm. \ No newline at end of file From 519c1df279206364c56835378bd9ffc129c7ca96 Mon Sep 17 00:00:00 2001 From: Osten <11805592+LagradOst@users.noreply.github.com> Date: Tue, 8 Sep 2026 19:01:00 +0200 Subject: [PATCH 21/23] Logcat fixes (#3161) --- .../ui/settings/SettingsUIScreen.kt | 21 +- .../ui/settings/logcat/LogcatDialog.kt | 205 ++++++++++-------- .../lagradost/cloudstream4/compose/Colors.kt | 4 +- .../lagradost/cloudstream4/compose/Dialog.kt | 4 +- 4 files changed, 136 insertions(+), 98 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt index 3e67ed15e..2fce9c582 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt @@ -20,6 +20,7 @@ import com.lagradost.cloudstream3.CommonActivity.activity import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchQuality +import com.lagradost.cloudstream3.mvvm.safe import com.lagradost.cloudstream3.ui.clear import com.lagradost.cloudstream3.ui.home.HomeChildItemAdapter import com.lagradost.cloudstream3.ui.home.ParentItemAdapter @@ -104,8 +105,10 @@ object SettingsUIScreen : SearchableSettings { }, onValueChanged = { newValue -> settings.ui.primaryColor.set(newValue) // We need to set before we recreate - activity?.recreate() - return@ListPreference true + safe { + activity?.recreate() + } + return@ListPreference false }), Preference.PreferenceItem.ListPreference( preference = settings.ui.theme, @@ -127,8 +130,10 @@ object SettingsUIScreen : SearchableSettings { }, onValueChanged = { newValue -> settings.ui.theme.set(newValue) // We need to set before we recreate - activity?.recreate() - return@ListPreference true + safe { + activity?.recreate() + } + return@ListPreference false }), Preference.PreferenceItem.ListPreference( preference = settings.ui.layout, @@ -139,9 +144,11 @@ object SettingsUIScreen : SearchableSettings { ).toMap().toPersistentMap(), onValueChanged = { newValue -> settings.ui.layout.set(newValue) // We need to set before we recreate - activity?.updateTv() - activity?.recreate() - return@ListPreference true + safe { + activity?.updateTv() + activity?.recreate() + } + return@ListPreference false }), ) ), diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt index a157a0cda..73dc7ea58 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt @@ -15,14 +15,22 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component1 +import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component2 +import androidx.compose.ui.focus.focusProperties +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -35,7 +43,8 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper import com.lagradost.cloudstream3.utils.downloader.VideoDownloadManager import com.lagradost.cloudstream3.utils.txt -import com.lagradost.cloudstream4.compose.Colors +import com.lagradost.cloudstream4.compose.BlackButton +import com.lagradost.cloudstream4.compose.WhiteButton import com.lagradost.cloudstream4.compose.circle import com.lagradost.cloudstream4.compose.ripple import com.lagradost.cloudstream4.compose.rounded @@ -54,10 +63,12 @@ import java.util.Locale @Composable fun LogcatDialog(dismiss: () -> Unit) { - val list = remember { mutableStateOf(persistentListOf()) } + var isLoading by remember { mutableStateOf(true) } LaunchedEffect(dismiss) { try { + isLoading = true + // https://developer.android.com/studio/command-line/logcat val process = Runtime.getRuntime().exec("logcat --binary -d") val items = arrayListOf() @@ -71,8 +82,11 @@ fun LogcatDialog(dismiss: () -> Unit) { list.value = items.toPersistentList() } catch (e: Exception) { logError(e) // kinda ironic + } finally { + isLoading = false } } + val (dismissFocus, confirmFocus) = remember { FocusRequester.createRefs() } val context = LocalContext.current val scope = rememberCoroutineScope() @@ -83,98 +97,115 @@ fun LogcatDialog(dismiss: () -> Unit) { Text(text = stringResource(R.string.log_cat)) }, text = { - LazyColumn { + if (isLoading) { + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.onBackground, + trackColor = MaterialTheme.colorScheme.surfaceVariant, + ) + } + LazyColumn( + modifier = Modifier.focusProperties { + start = dismissFocus + end = confirmFocus + } + ) { items(items = list.value) { item -> - LogcatItem(item) + LogcatItem(item, modifier = Modifier.focusProperties { + start = dismissFocus + end = confirmFocus + }) } } }, confirmButton = { - Button( - onClick = { - scope.launch { - withContext(Dispatchers.IO) { - val date = SimpleDateFormat("yyyy_MM_dd_HH_mm", Locale.getDefault()).format(Date(currentTimeMillis())) - var fileStream: OutputStream? - try { - fileStream = VideoDownloadManager.setupStream( - context, - "logcat_${date}", - null, - "txt", - false - ).openNew() - fileStream.bufferedWriter() - .use { writer -> - list.value.forEach { - writer.write(it.toString()) - writer.write("\n\n") - } + WhiteButton( + text = stringResource(R.string.sort_save), + modifier = Modifier.focusRequester(confirmFocus) + ) { + scope.launch { + withContext(Dispatchers.IO) { + val date = SimpleDateFormat("yyyy_MM_dd_HH_mm", Locale.getDefault()).format( + Date(currentTimeMillis()) + ) + var fileStream: OutputStream? + try { + fileStream = VideoDownloadManager.setupStream( + context, + "logcat_${date}", + null, + "txt", + false + ).openNew() + fileStream.bufferedWriter() + .use { writer -> + list.value.forEach { + writer.write(it.toString()) + writer.write("\n\n") } - dismiss() - } catch (t: Throwable) { - logError(t) - showToast(t.message) - } - /*try { - val date = SimpleDateFormat( - "yyyy_MM_dd_HH_mm", - Locale.getDefault() - ).format( - Date(System.currentTimeMillis()) - ) - - val file = FileHelper.logcat.createFile(context, "logcat_${date}") - ?: throw ErrorLoadingException("Unable to create file") - val stream = file.openOutputStream(append = false) - ?: throw ErrorLoadingException("Unable to create stream") - - stream.bufferedWriter() - .use { writer -> - list.value.forEach { - writer.write(it.toString()) - writer.write("\n\n") - } - } - dismiss() - showToast( - txt( - R.string.logcat_success, - file.absolutePath ?: file.uri.toString() - ), - Toast.LENGTH_LONG - ) - } catch (t: Throwable) { - logError(t) - showToast(t.message) - }*/ + } + dismiss() + } catch (t: Throwable) { + logError(t) + showToast(t.message) } + /*try { + val date = SimpleDateFormat( + "yyyy_MM_dd_HH_mm", + Locale.getDefault() + ).format( + Date(System.currentTimeMillis()) + ) + + val file = FileHelper.logcat.createFile(context, "logcat_${date}") + ?: throw ErrorLoadingException("Unable to create file") + val stream = file.openOutputStream(append = false) + ?: throw ErrorLoadingException("Unable to create stream") + + stream.bufferedWriter() + .use { writer -> + list.value.forEach { + writer.write(it.toString()) + writer.write("\n\n") + } + } + dismiss() + showToast( + txt( + R.string.logcat_success, + file.absolutePath ?: file.uri.toString() + ), + Toast.LENGTH_LONG + ) + } catch (t: Throwable) { + logError(t) + showToast(t.message) + }*/ } - }, colors = Colors.whiteButton - ) { Text(text = stringResource(R.string.sort_save)) } - Button( - onClick = { - clipboardHelper( - txt("Logcat"), - list.value.joinToString(separator = "\n\n") { it.toString() } - ) - }, colors = Colors.whiteButton - ) { Text(text = stringResource(R.string.sort_copy)) } - Button( - onClick = { - try { - Runtime.getRuntime().exec("logcat -c") - } catch (t: Throwable) { - logError(t) - } - dismiss() - }, colors = Colors.whiteButton - ) { Text(text = stringResource(R.string.sort_clear)) } + } + } + + WhiteButton(text = stringResource(R.string.sort_copy)) { + clipboardHelper( + txt("Logcat"), + list.value.joinToString(separator = "\n\n") { it.toString() } + ) + } + WhiteButton(text = stringResource(R.string.sort_clear)) { + try { + Runtime.getRuntime().exec("logcat -c") + } catch (t: Throwable) { + logError(t) + } + dismiss() + } }, dismissButton = { - Button( - onClick = dismiss, colors = Colors.blackButton - ) { Text(text = stringResource(R.string.sort_close)) } + BlackButton( + text = stringResource(R.string.sort_close), + onClick = dismiss, + modifier = Modifier.focusRequester(dismissFocus) + ) }, properties = DialogProperties(usePlatformDefaultWidth = false) ) @@ -182,7 +213,7 @@ fun LogcatDialog(dismiss: () -> Unit) { @Composable -fun LogcatItem(item: LogcatItem) { +fun LogcatItem(item: LogcatItem, modifier: Modifier = Modifier) { val interactionSource = remember { MutableInteractionSource() } val color = when (item.level) { @@ -227,7 +258,7 @@ fun LogcatItem(item: LogcatItem) { ) } Row( - modifier = Modifier + modifier = modifier .height(IntrinsicSize.Min) .fillMaxWidth() .clickable( diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt index d39bffa66..2e009e4e7 100644 --- a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.ReadOnlyComposable import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors object Colors { - val blackButton + internal val blackButton @Composable @ReadOnlyComposable get() = ButtonColors( containerColor = colors.surfaceVariant, contentColor = colors.onBackground, @@ -14,7 +14,7 @@ object Colors { disabledContentColor = colors.onBackground.copy(alpha = 0.9f) ) - val whiteButton + internal val whiteButton @Composable @ReadOnlyComposable get() = ButtonColors( containerColor = colors.onBackground, contentColor = colors.surfaceVariant, diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt index 9dbed5f42..9a6ab32c8 100644 --- a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt @@ -87,8 +87,8 @@ fun ActionDialog( @Composable fun WhiteButton( text: String, - onClick: () -> Unit, modifier: Modifier = Modifier, + onClick: () -> Unit, ) { BaseButton( text = text, @@ -101,8 +101,8 @@ fun WhiteButton( @Composable fun BlackButton( text: String, - onClick: () -> Unit, modifier: Modifier = Modifier, + onClick: () -> Unit, ) { BaseButton( text = text, From fe981345bdad180338e6cee75d59e77a568b96ee Mon Sep 17 00:00:00 2001 From: Osten <11805592+LagradOst@users.noreply.github.com> Date: Tue, 8 Sep 2026 19:10:00 +0200 Subject: [PATCH 22/23] Minor import fix --- .../jvmMain/kotlin/com/lagradost/cloudstream4/Main.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/desktopApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/Main.kt b/desktopApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/Main.kt index f40cc9f8b..0e1066df1 100644 --- a/desktopApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/Main.kt +++ b/desktopApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/Main.kt @@ -2,7 +2,6 @@ package com.lagradost.cloudstream4 import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.material3.Button import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.getValue @@ -11,7 +10,8 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.window.Window import androidx.compose.ui.window.application -import com.lagradost.cloudstream4.compose.Colors +import com.lagradost.cloudstream4.compose.BlackButton +import com.lagradost.cloudstream4.compose.WhiteButton import com.lagradost.cloudstream4.generated.resources.Res import com.lagradost.cloudstream4.generated.resources.app_name import com.lagradost.cloudstream4.generated.resources.default_icon @@ -37,11 +37,9 @@ fun main() = application { Column { Text("Hello, World!") Row { - Button(onClick = {}, colors = Colors.whiteButton) { - Text("Hello in White") + WhiteButton("Hello in White") { } - Button(onClick = {}, colors = Colors.blackButton) { - Text("Hello in Black") + BlackButton("Hello in Black") { } } var checked by remember { mutableStateOf(false) } From 6b7a81abcedb985a9e538a9d13a82ea410ce090e Mon Sep 17 00:00:00 2001 From: Osten <11805592+LagradOst@users.noreply.github.com> Date: Sat, 12 Sep 2026 01:03:36 +0200 Subject: [PATCH 23/23] Added search to settings (#3167) --- .../ui/account/AccountSelectActivity.kt | 9 +- .../ui/settings/SettingsAccountScreen.kt | 7 +- .../ui/settings/SettingsFragment2.kt | 8 + .../ui/settings/SettingsFragmentScreen.kt | 430 ++++++++++++++++++ .../ui/settings/SettingsGeneralScreen.kt | 55 ++- .../ui/settings/SettingsProviders2.kt | 1 + .../ui/settings/SettingsProvidersScreen.kt | 7 +- .../ui/settings/SettingsUIScreen.kt | 14 +- .../ui/settings/SettingsUpdatesScreen.kt | 9 +- .../ui/settings/logcat/LogcatDialog.kt | 25 +- .../cloudstream3/utils/BaseComposeFragment.kt | 4 +- .../cloudstream3/utils/DataStoreHelper.kt | 7 + .../main/res/drawable/audio_capture_24px.xml | 10 + app/src/main/res/drawable/build_24px.xml | 10 + app/src/main/res/drawable/close_24px.xml | 10 + .../main/res/drawable/description_24px.xml | 10 + app/src/main/res/drawable/encrypted_24px.xml | 10 + .../main/res/drawable/format_paint_24px.xml | 10 + .../res/drawable/keyboard_arrow_left_24px.xml | 11 + .../main/res/navigation/mobile_navigation.xml | 2 +- .../kotlin/com/lagradost/cloudstream4/Main.kt | 43 +- shared/build.gradle.kts | 1 + .../cloudstream4/compose/BackHandler.kt | 42 -- .../composeResources/values/strings.xml | 1 + .../lagradost/cloudstream4/compose/Colors.kt | 18 +- .../lagradost/cloudstream4/compose/Dialog.kt | 39 +- .../cloudstream4/compose/Modifiers.kt | 24 +- .../com/lagradost/cloudstream4/theme/Theme.kt | 19 +- .../kotlin/com/mihon/material/AppBar.kt | 3 + .../settings/PreferenceScaffold.kt | 3 + .../com/mihon/presentation/settings/Search.kt | 158 +++++++ .../settings/widget/BasePreferenceWidget.kt | 2 +- 32 files changed, 850 insertions(+), 152 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment2.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragmentScreen.kt create mode 100644 app/src/main/res/drawable/audio_capture_24px.xml create mode 100644 app/src/main/res/drawable/build_24px.xml create mode 100644 app/src/main/res/drawable/close_24px.xml create mode 100644 app/src/main/res/drawable/description_24px.xml create mode 100644 app/src/main/res/drawable/encrypted_24px.xml create mode 100644 app/src/main/res/drawable/format_paint_24px.xml create mode 100644 app/src/main/res/drawable/keyboard_arrow_left_24px.xml delete mode 100644 shared/src/androidMain/kotlin/com/lagradost/cloudstream4/compose/BackHandler.kt create mode 100644 shared/src/commonMain/kotlin/com/mihon/presentation/settings/Search.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt index ad323c7d1..3990bcc78 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt @@ -54,10 +54,15 @@ class AccountSelectActivity : FragmentActivity(), BiometricCallback { false ) + val isFromMainActivity = intent.getBooleanExtra( + "isFromMainActivity", + false + ) + // Sometimes we start this activity when we have already logged in // For example when using cloudstreamsearch:// // In those cases we want to just go to the main activity instantly - if (hasLoggedIn && !isEditingFromMainActivity) { + if (hasLoggedIn && !isEditingFromMainActivity && !isFromMainActivity) { navigateToMainActivity() return } @@ -98,7 +103,7 @@ class AccountSelectActivity : FragmentActivity(), BiometricCallback { // Don't show account selection if there is only // one account that exists - if (!isEditingFromMainActivity && skipStartup) { + if (!isFromMainActivity && !isEditingFromMainActivity && skipStartup) { val currentAccount = accounts.firstOrNull { it.keyIndex == selectedKeyIndex } if (currentAccount?.lockPin != null) { CommonActivity.init(this) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccountScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccountScreen.kt index 47dac104d..97890bbfa 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccountScreen.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccountScreen.kt @@ -85,7 +85,12 @@ object SettingsAccountScreen : SearchableSettings, BiometricAuthenticator.Biomet val activity = LocalActivity.current val context = LocalContext.current val hasSecurity = remember(context) { - deviceHasPasswordPinLock(context) + try { + deviceHasPasswordPinLock(context) + } catch (_ : Throwable) { + // e.g preview + false + } } return persistentListOf( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment2.kt new file mode 100644 index 000000000..24867b898 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment2.kt @@ -0,0 +1,8 @@ +package com.lagradost.cloudstream3.ui.settings + +import com.lagradost.cloudstream3.utils.BaseComposeFragment +import com.lagradost.cloudstream4.compose.Screen + + +/** Empty glue code to connect the old navigation graph to Compose */ +class SettingsFragment2 : BaseComposeFragment(), Screen by SettingsFragmentScreen \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragmentScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragmentScreen.kt new file mode 100644 index 000000000..066db8be8 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragmentScreen.kt @@ -0,0 +1,430 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.annotation.SuppressLint +import android.os.Bundle +import androidx.activity.compose.BackHandler +import androidx.compose.animation.Crossfade +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.foundation.text.input.rememberTextFieldState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ProvideTextStyle +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SearchBarDefaults +import androidx.compose.material3.SearchBarState +import androidx.compose.material3.SearchBarValue +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.rememberSearchBarState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage +import com.lagradost.cloudstream3.BuildConfig +import com.lagradost.cloudstream3.CommonActivity.activity +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.utils.DataStoreHelper +import com.lagradost.cloudstream3.utils.DataStoreHelper.profileImages +import com.lagradost.cloudstream3.utils.GitInfo.currentCommitHash +import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper +import com.lagradost.cloudstream3.utils.UIHelper.navigate +import com.lagradost.cloudstream3.utils.txt +import com.lagradost.cloudstream4.compose.Screen +import com.lagradost.cloudstream4.compose.TV +import com.lagradost.cloudstream4.compose.circle +import com.lagradost.cloudstream4.compose.focusOutline +import com.lagradost.cloudstream4.compose.isLayout +import com.lagradost.cloudstream4.theme.CloudStreamPreviewTheme +import com.mihon.material.padding +import com.mihon.presentation.settings.Preference +import com.mihon.presentation.settings.SearchableSettings +import com.mihon.presentation.settings.SettingSearchResults +import com.mihon.presentation.settings.SettingsData +import com.mihon.presentation.settings.widget.TextPreferenceWidget +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.launch +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import java.util.TimeZone + +object SettingsFragmentScreen : Screen { + val screens = persistentListOf( + SettingsNavigation( + title = R.string.category_general, + navigation = R.id.action_navigation_global_to_navigation_settings_general, + screen = SettingsGeneralScreen, + icon = R.drawable.build_24px, + ), + SettingsNavigation( + title = R.string.category_player, + navigation = R.id.action_navigation_global_to_navigation_settings_player, + screen = SettingsPlayerScreen, + icon = R.drawable.play_arrow_24px, + ), + /*SettingsNavigation( + title = R.string.category_providers, + navigation = R.id.action_navigation_global_to_navigation_settings_providers, + screen = SettingsProvidersScreen, + icon = R.drawable.build_24px, + ),*/ + SettingsNavigation( + title = R.string.category_ui, + navigation = R.id.action_navigation_global_to_navigation_settings_ui, + screen = SettingsUIScreen, + icon = R.drawable.format_paint_24px, + ), + SettingsNavigation( + title = R.string.category_updates, + navigation = R.id.action_navigation_global_to_navigation_settings_updates, + screen = SettingsUpdatesScreen, + icon = R.drawable.mobile_arrow_down_24px, + ), + SettingsNavigation( + title = R.string.category_account, + navigation = R.id.action_navigation_global_to_navigation_settings_account, + screen = SettingsAccountScreen, + icon = R.drawable.encrypted_24px, + ), + SettingsNavigation( + title = R.string.pref_category_extensions, + navigation = R.id.action_navigation_global_to_navigation_settings_extensions, + screen = null, + icon = R.drawable.extension_24px, + subtitle = R.string.add_repository + ), + ) + + data class SettingsNavigation( + val title: Int, + val navigation: Int, + val screen: SearchableSettings?, + val icon: Int, + val subtitle: Int? = null, + ) + + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") + override fun Content() { + val textFieldState = rememberTextFieldState() + val searchBarState = rememberSearchBarState() + + val screen = screens.mapNotNull { item -> + val contents = item.screen?.getPreferences() ?: return@mapNotNull null + SettingsData( + title = stringResource(item.title), + navigation = item.navigation, + contents = contents + ) + }.toPersistentList() + + val outerListState = rememberScrollState() + + val parentFirstScrollConnection = remember { + object : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + val delta = available.y + return if (delta < 0 && outerListState.canScrollForward) { + val consumed = outerListState.dispatchRawDelta(-delta) + Offset(0f, -consumed) + } else { + Offset.Zero + } + } + } + } + + Scaffold { _ -> + Column(modifier = Modifier.verticalScroll(outerListState)) { + Spacer(modifier = Modifier.height(MaterialTheme.padding.small)) + + val default = DataStoreHelper.getDefaultAccount( + LocalContext.current + ) + val flow by DataStoreHelper.selectedAccountNumberFlow.collectAsState() + val account = remember(flow) { + DataStoreHelper.getCurrentAccount() ?: default + } + + Row( + modifier = Modifier.fillMaxSize().focusOutline().clickable { + activity.navigate( + R.id.accountSelectActivity, + Bundle().apply { putBoolean("isFromMainActivity", true) } + ) + }.padding( + vertical = MaterialTheme.padding.large, + horizontal = MaterialTheme.padding.medium + ) + ) { + val image = + account.customImage ?: profileImages.getOrNull(account.defaultImageIndex) + ?: profileImages.first() + + Box( + modifier = Modifier + .size(50.dp) + .border( + 2.dp, + MaterialTheme.colorScheme.onBackground.copy(alpha = 0.2f), + CircleShape + ) + .circle(), + ) { + AsyncImage( + contentScale = ContentScale.Crop, + model = image, + modifier = Modifier.fillMaxSize(), + contentDescription = null, + ) + } + Spacer(modifier = Modifier.width(MaterialTheme.padding.medium)) + Column { + Text( + text = account.name, + style = MaterialTheme.typography.titleLarge, + ) + Text( + text = stringResource(R.string.title_settings), + style = MaterialTheme.typography.bodyMedium, + ) + } + } + + Spacer(modifier = Modifier.height(MaterialTheme.padding.small)) + SettingsSearch(searchBarState = searchBarState, textFieldState = textFieldState) + Spacer(modifier = Modifier.height(MaterialTheme.padding.small)) + + SettingSearchResults( + nestedScrollConnection = parentFirstScrollConnection, + searchKey = textFieldState.text.toString(), + items = screen, + onItemClick = { item -> + SearchableSettings.highlightKey = item.highlightKey + activity?.navigate(item.navigation) + }, empty = { + Column( + modifier = Modifier + .nestedScroll(parentFirstScrollConnection) + ) { + screens.forEach { settingsTab -> + SettingsTab(settingsTab) + } + BuildStamp() + } + }) + + } + } + } + + @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) + @Composable + fun SettingsSearch(searchBarState: SearchBarState, textFieldState: TextFieldState) { + val scope = rememberCoroutineScope() + val inputField = + @Composable { + SearchBarDefaults.InputField( + textFieldState = textFieldState, + searchBarState = searchBarState, + modifier = Modifier.onFocusChanged { newFocus -> + if (newFocus.hasFocus) { + scope.launch { + searchBarState.animateToExpanded() + } + } + }, + onSearch = { scope.launch { searchBarState.animateToCollapsed() } }, + placeholder = { + Text(modifier = Modifier.clearAndSetSemantics {}, text = stringResource(R.string.search_hint)) + }, + leadingIcon = { + Crossfade( + targetState = searchBarState.targetValue, + label = "leftsearch", + ) { value -> + when (value) { + SearchBarValue.Expanded -> { + IconButton(onClick = { + textFieldState.edit { replace(0, length, "") } + scope.launch { + searchBarState.animateToCollapsed() + } + }) { + Icon( + painter = painterResource(R.drawable.keyboard_arrow_left_24px), + tint = MaterialTheme.colorScheme.onBackground, + contentDescription = null + ) + } + } + + SearchBarValue.Collapsed -> { + IconButton(onClick = { + scope.launch { + searchBarState.animateToExpanded() + } + }) { + Icon( + painter = painterResource(R.drawable.search_icon), + tint = MaterialTheme.colorScheme.onBackground, + contentDescription = null + ) + } + } + } + } + + /*SampleLeadingIcon(searchBarState, scope)*/ + }, + trailingIcon = { + Crossfade( + targetState = searchBarState.targetValue, + label = "rightsearch", + ) { value -> + when (value) { + SearchBarValue.Expanded -> { + IconButton(onClick = { + textFieldState.edit { replace(0, length, "") } + }) { + Icon( + painter = painterResource(R.drawable.close_24px), + tint = MaterialTheme.colorScheme.onBackground, + contentDescription = null + ) + } + } + + SearchBarValue.Collapsed -> { + } + } + } + }, + ) + } + + Surface( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp - 12.dp * searchBarState.progress) + .focusOutline(enabled = isLayout(TV), CircleShape) + .onGloballyPositioned { searchBarState.collapsedCoords = it }, + shape = SearchBarDefaults.inputFieldShape, + color = MaterialTheme.colorScheme.surfaceVariant, + tonalElevation = SearchBarDefaults.TonalElevation, + shadowElevation = SearchBarDefaults.ShadowElevation, + content = inputField, + ) + + BackHandler(enabled = searchBarState.targetValue == SearchBarValue.Expanded) { + textFieldState.edit { replace(0, length, "") } + scope.launch { + searchBarState.animateToCollapsed() + } + } + } + + @Composable + fun SettingsTab(settingsTab: SettingsNavigation) { + val pref = settingsTab.screen?.getPreferences() ?: emptyList() + val groups = pref.filterIsInstance() + .filter { it.enabled } + TextPreferenceWidget( + title = stringResource(settingsTab.title), + icon = painterResource(settingsTab.icon), + subtitle = settingsTab.subtitle?.let { stringResource(it) } + ?: groups.joinToString { it.title }) { + // Clear it if we have already set it but navigated back instantly + SearchableSettings.highlightKey = null + activity?.navigate(settingsTab.navigation) + } + } + + @Composable + fun BuildStamp() { + val (commitHash, buildTimestamp) = remember { + val commitHash = activity?.currentCommitHash() ?: "" + val buildTimestamp = SimpleDateFormat.getDateTimeInstance( + DateFormat.LONG, DateFormat.MEDIUM, + Locale.getDefault() + ).apply { + timeZone = TimeZone.getTimeZone("UTC") + }.format(Date(BuildConfig.BUILD_DATE)).replace("UTC", "") + commitHash to buildTimestamp + } + + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .height(50.dp) + .focusOutline() + .clickable { + clipboardHelper( + txt(R.string.extension_version), + "${BuildConfig.VERSION_NAME} $commitHash $buildTimestamp" + ) + }, + ) { + ProvideTextStyle(MaterialTheme.typography.bodyMedium) { + Text(text = BuildConfig.VERSION_NAME) + if (commitHash != "") { + Text("•") + Text(text = commitHash) + } + Text("•") + Text(text = buildTimestamp) + } + } + } +} + + +@PreviewLightDark +@Composable +fun Preview() { + CloudStreamPreviewTheme { + SettingsFragmentScreen.Content() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneralScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneralScreen.kt index 7ce49e8e9..b2f20b017 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneralScreen.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneralScreen.kt @@ -15,17 +15,23 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark +import com.lagradost.cloudstream3.APIHolder +import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.CloudStreamApp import com.lagradost.cloudstream3.CommonActivity.activity import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.UnsafeSSL import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.insecureApp import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.network.initClient +import com.lagradost.cloudstream3.ui.settings.SettingsProvidersScreen.toStringRes import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.isAppRestricted import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.showRequestIgnoreBatteryOptDialog +import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTagToLanguageName +import com.lagradost.cloudstream3.utils.SubtitleHelper.getNameNextToFlagEmoji import com.lagradost.cloudstream4.AppSettings import com.lagradost.cloudstream4.compose.ActionDialog import com.lagradost.cloudstream4.compose.PHONE @@ -104,18 +110,43 @@ object SettingsGeneralScreen : SearchableSettings { ) } + val default = AllLanguagesName to stringResource(R.string.all_languages_preference) + val languages = APIHolder.apis.withLock { + APIHolder.apis.map { api -> api.lang }.distinct() + }.sortedBy { fromTagToLanguageName(it) ?: it } + return persistentListOf( - Preference.PreferenceItem.BasicListPreference( - value = locale, - entries = appLanguages.associate { (name, code) -> (code to (name to code).nameNextToFlagEmoji()) }, - title = stringResource(R.string.app_language), - icon = painterResource(R.drawable.language_korean_latin_24px), - onValueChanged = { value -> - settings.general.locale.set(value) - activity?.recreate() - }, - subtitleProvider = { v, e -> e[v] ?: getCurrentLocale(LocalContext.current) } - ), + Preference.PreferenceGroup(title = stringResource(R.string.extension_language), preferenceItems = persistentListOf( + Preference.PreferenceItem.BasicListPreference( + value = locale, + entries = appLanguages.associate { (name, code) -> (code to (name to code).nameNextToFlagEmoji()) }, + title = stringResource(R.string.app_language), + icon = painterResource(R.drawable.language_korean_latin_24px), + onValueChanged = { value -> + settings.general.locale.set(value) + activity?.recreate() + }, + subtitleProvider = { v, e -> e[v] ?: getCurrentLocale(LocalContext.current) } + ), + Preference.PreferenceItem.MultiSelectListPreference( + title = stringResource(R.string.provider_lang_settings), + icon = painterResource(R.drawable.plugin_lang), + entries = mapOf(default) + languages.associateWith { lang -> + (getNameNextToFlagEmoji( + lang + ) ?: lang) + }, + preference = settings.provider.extensionLanguages + ), + Preference.PreferenceItem.MultiSelectListPreference( + title = stringResource(R.string.preferred_media_settings), + icon = painterResource(R.drawable.movie_edit_24px), + preference = settings.provider.preferredMedia, + entries = TvType.entries.associate { + it.ordinal.toString() to stringResource(it.toStringRes()) + }), + )), + Preference.PreferenceGroup( title = stringResource(R.string.title_downloads), preferenceItems = persistentListOf( @@ -233,7 +264,7 @@ object SettingsGeneralScreen : SearchableSettings { Preference.PreferenceItem.TextPreference( title = stringResource(R.string.cs3wiki), subtitle = "https://cloudstream.miraheze.org/", - icon = painterResource(R.drawable.baseline_description_24), + icon = painterResource(R.drawable.description_24px), onClick = { CloudStreamApp.openBrowser("https://cloudstream.miraheze.org/") } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders2.kt index adb44a94d..a0faeca93 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProviders2.kt @@ -3,4 +3,5 @@ import com.lagradost.cloudstream3.utils.BaseComposeFragment import com.mihon.presentation.settings.SearchableSettings /** Empty glue code to connect the old navigation graph to Compose */ +@Deprecated("This setting was merged into other places") class SettingsProviders2 : BaseComposeFragment(), SearchableSettings by SettingsProvidersScreen \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProvidersScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProvidersScreen.kt index 48c305a32..4186994c7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProvidersScreen.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsProvidersScreen.kt @@ -17,6 +17,7 @@ import com.mihon.presentation.settings.Preference import com.mihon.presentation.settings.SearchableSettings import kotlinx.collections.immutable.persistentListOf +@Deprecated("This setting was merged into other places") object SettingsProvidersScreen : SearchableSettings { @Composable override fun getTitleRes(): String = stringResource(R.string.category_providers) @@ -42,7 +43,6 @@ object SettingsProvidersScreen : SearchableSettings { TvType.Video -> R.string.video_singular } - // TODO Move this into general and layout? @Composable override fun getPreferences(): List { val settings = rememberAppSettings() @@ -68,9 +68,10 @@ object SettingsProvidersScreen : SearchableSettings { preference = settings.provider.preferredMedia, entries = TvType.entries.associate { it.ordinal.toString() to stringResource(it.toStringRes()) - }), Preference.PreferenceItem.MultiSelectListPreference( + }), + Preference.PreferenceItem.MultiSelectListPreference( title = stringResource(R.string.display_subbed_dubbed_settings), - icon = painterResource(R.drawable.ic_outline_voice_over_off_24), + icon = painterResource(R.drawable.audio_capture_24px), preference = settings.provider.displayDubSub, entries = mapOf( DubStatus.None.name to stringResource(R.string.none), diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt index 2fce9c582..5c56cb515 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUIScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -17,6 +18,7 @@ import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.lagradost.cloudstream3.CommonActivity.activity +import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchQuality @@ -31,7 +33,6 @@ import com.lagradost.cloudstream4.compose.TV import com.lagradost.cloudstream4.compose.isLayout import com.lagradost.cloudstream4.rememberAppSettings import com.lagradost.cloudstream4.theme.CloudStreamPrimaryColor -import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors import com.lagradost.cloudstream4.theme.modeToTheme import com.lagradost.cloudstream4.theme.perfToColor import com.lagradost.cloudstream4.theme.perfToMode @@ -71,7 +72,7 @@ object SettingsUIScreen : SearchableSettings { modifier = Modifier .padding(start = 15.dp) .size(20.dp) - .border(width = 1.5.dp, shape = CircleShape, color = colors.onBackground) + .border(width = 1.5.dp, shape = CircleShape, color = MaterialTheme.colorScheme.onBackground) .background(color, CircleShape) ) } @@ -241,6 +242,15 @@ object SettingsUIScreen : SearchableSettings { title = stringResource(R.string.pref_filter_search_quality), icon = painterResource(R.drawable.filter_alt_24px), entries = SearchQuality.entries.associateWith { stringResource(it.toStringRes()) }), + Preference.PreferenceItem.MultiSelectListPreference( + title = stringResource(R.string.display_subbed_dubbed_settings), + icon = painterResource(R.drawable.audio_capture_24px), + preference = settings.provider.displayDubSub, + entries = mapOf( + DubStatus.None.name to stringResource(R.string.none), + DubStatus.Dubbed.name to stringResource(R.string.app_dubbed_text), + DubStatus.Subbed.name to stringResource(R.string.app_subbed_text), + )), Preference.PreferenceItem.SwitchPreference( preference = settings.ui.bottomTitle, title = stringResource(R.string.bottom_title_settings), diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdatesScreen.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdatesScreen.kt index dc03174ff..3c90e8e36 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdatesScreen.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdatesScreen.kt @@ -178,7 +178,7 @@ object SettingsUpdatesScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( title = stringResource(R.string.automatic_plugin_download), - subtitle = "%s\n"+stringResource(R.string.automatic_plugin_download_summary), + subtitle = "%s\n" + stringResource(R.string.automatic_plugin_download_summary), icon = painterResource(R.drawable.extention_renew2), entries = AutoDownloadMode.entries.map { it.value }.sorted() .zip(stringArrayResource((R.array.auto_download_plugin))).toMap(), @@ -196,6 +196,13 @@ object SettingsUpdatesScreen : SearchableSettings { } } ), + Preference.PreferenceItem.TextPreference( + title = stringResource(R.string.test_extensions), + subtitle = stringResource(R.string.test_extensions_summary), + icon = painterResource(R.drawable.baseline_network_ping_24), + onClick = { + activity?.navigate(R.id.navigation_test_providers) + }) ) ), Preference.PreferenceGroup( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt index 73dc7ea58..7d5ce2313 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/logcat/LogcatDialog.kt @@ -46,9 +46,7 @@ import com.lagradost.cloudstream3.utils.txt import com.lagradost.cloudstream4.compose.BlackButton import com.lagradost.cloudstream4.compose.WhiteButton import com.lagradost.cloudstream4.compose.circle -import com.lagradost.cloudstream4.compose.ripple import com.lagradost.cloudstream4.compose.rounded -import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.Dispatchers @@ -91,7 +89,7 @@ fun LogcatDialog(dismiss: () -> Unit) { val context = LocalContext.current val scope = rememberCoroutineScope() AlertDialog( - containerColor = colors.background, + containerColor = MaterialTheme.colorScheme.background, onDismissRequest = dismiss, title = { Text(text = stringResource(R.string.log_cat)) @@ -214,8 +212,6 @@ fun LogcatDialog(dismiss: () -> Unit) { @Composable fun LogcatItem(item: LogcatItem, modifier: Modifier = Modifier) { - val interactionSource = remember { MutableInteractionSource() } - val color = when (item.level) { LogcatLevel.Fatal -> Color.Magenta LogcatLevel.Error -> Color.Red @@ -233,9 +229,9 @@ fun LogcatItem(item: LogcatItem, modifier: Modifier = Modifier) { modifier = Modifier .padding(2.dp) .rounded() - .background(colors.onBackground) + .background(MaterialTheme.colorScheme.onBackground) .padding(4.dp), - color = colors.surfaceVariant + color = MaterialTheme.colorScheme.surfaceVariant ) } Text( @@ -243,32 +239,29 @@ fun LogcatItem(item: LogcatItem, modifier: Modifier = Modifier) { modifier = Modifier .padding(2.dp) .rounded() - .background(colors.surfaceVariant) + .background(MaterialTheme.colorScheme.surfaceVariant) .padding(4.dp), - color = colors.onBackground + color = MaterialTheme.colorScheme.onBackground ) Text( item.tag, modifier = Modifier .padding(2.dp) .rounded() - .background(colors.surfaceVariant) + .background(MaterialTheme.colorScheme.surfaceVariant) .padding(4.dp), - color = colors.onBackground + color = MaterialTheme.colorScheme.onBackground ) } Row( modifier = modifier .height(IntrinsicSize.Min) .fillMaxWidth() + .rounded() .clickable( - interactionSource = interactionSource, - indication = null, onClick = { clipboardHelper(txt("Logcat"), item.toString()) }) - .rounded() - .ripple(interactionSource) .padding(5.dp) ) { Box( @@ -281,7 +274,7 @@ fun LogcatItem(item: LogcatItem, modifier: Modifier = Modifier) { Spacer(modifier = Modifier.width(5.dp)) Text( item.message, - color = colors.onBackground, + color = MaterialTheme.colorScheme.onBackground, fontSize = 14.sp, lineHeight = 15.sp, ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BaseComposeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BaseComposeFragment.kt index 3c3f55adb..91a75d0ab 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/BaseComposeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BaseComposeFragment.kt @@ -22,6 +22,7 @@ abstract class BaseComposeFragment : Fragment(), Screen { ): View = createComposeView(inflater, container, savedInstanceState) override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) this.view?.let { view -> fixSystemBarsPadding( view, @@ -29,15 +30,14 @@ abstract class BaseComposeFragment : Fragment(), Screen { padBottom = isLandscape() ) } - super.onConfigurationChanged(newConfig) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) fixSystemBarsPadding( view, padLeft = isLayout(TV or EMULATOR), padBottom = isLandscape() ) - super.onViewCreated(view, savedInstanceState) } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt index 340266f6c..eabac20d3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -33,6 +33,8 @@ import com.lagradost.cloudstream3.ui.result.VideoWatchState import com.lagradost.cloudstream3.utils.AppContextUtils.filterProviderByPreferredMedia import com.lagradost.cloudstream3.utils.downloader.DownloadObjects import com.lagradost.cloudstream3.utils.serializers.WriteOnlySerializer +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KeepGeneratedSerializer import kotlinx.serialization.SerialName @@ -180,6 +182,10 @@ object DataStoreHelper { var selectedKeyIndex by PreferenceDelegate("$TAG/account_key_index", 0) val currentAccount: String get() = selectedKeyIndex.toString() + private val _selectedAccountNumberFlow = MutableStateFlow(0) + /** What account instance we are on, this number changes whenever anything about local accounts changes */ + val selectedAccountNumberFlow : StateFlow = _selectedAccountNumberFlow + /** * Get or set the current account homepage. * Setting this does not automatically reload the homepage. @@ -207,6 +213,7 @@ object DataStoreHelper { // This is not a new account, and the homepage has changed, reload it MainActivity.reloadHomeEvent(true) } + _selectedAccountNumberFlow.value += 1 } fun getDefaultAccount(context: Context): Account { diff --git a/app/src/main/res/drawable/audio_capture_24px.xml b/app/src/main/res/drawable/audio_capture_24px.xml new file mode 100644 index 000000000..a9ed40e02 --- /dev/null +++ b/app/src/main/res/drawable/audio_capture_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/build_24px.xml b/app/src/main/res/drawable/build_24px.xml new file mode 100644 index 000000000..0616691da --- /dev/null +++ b/app/src/main/res/drawable/build_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/close_24px.xml b/app/src/main/res/drawable/close_24px.xml new file mode 100644 index 000000000..77be1d01e --- /dev/null +++ b/app/src/main/res/drawable/close_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/description_24px.xml b/app/src/main/res/drawable/description_24px.xml new file mode 100644 index 000000000..f55b9ad85 --- /dev/null +++ b/app/src/main/res/drawable/description_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/encrypted_24px.xml b/app/src/main/res/drawable/encrypted_24px.xml new file mode 100644 index 000000000..66231650a --- /dev/null +++ b/app/src/main/res/drawable/encrypted_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/format_paint_24px.xml b/app/src/main/res/drawable/format_paint_24px.xml new file mode 100644 index 000000000..b27cf246e --- /dev/null +++ b/app/src/main/res/drawable/format_paint_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/keyboard_arrow_left_24px.xml b/app/src/main/res/drawable/keyboard_arrow_left_24px.xml new file mode 100644 index 000000000..b4b89a922 --- /dev/null +++ b/app/src/main/res/drawable/keyboard_arrow_left_24px.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 970850882..6a1ea6912 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -356,7 +356,7 @@ Unit) { - // Safely update the current `onBack` lambda when a new one is provided - val currentOnBack by rememberUpdatedState(onBack) - // Remember in Composition a back callback that calls the `onBack` lambda - val backCallback = remember { - object : OnBackPressedCallback(enabled) { - override fun handleOnBackPressed() { - currentOnBack() - } - } - } - // On every successful composition, update the callback with the `enabled` value - SideEffect { - backCallback.isEnabled = enabled - } - val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) { - "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner" - }.onBackPressedDispatcher - val lifecycleOwner = LocalLifecycleOwner.current - DisposableEffect(lifecycleOwner, backDispatcher) { - // Add callback to the backDispatcher - backDispatcher.addCallback(lifecycleOwner, backCallback) - // When the effect leaves the Composition, remove the callback - onDispose { - backCallback.remove() - } - } -} \ No newline at end of file diff --git a/shared/src/commonMain/composeResources/values/strings.xml b/shared/src/commonMain/composeResources/values/strings.xml index 3e6f4f768..fbd98f649 100644 --- a/shared/src/commonMain/composeResources/values/strings.xml +++ b/shared/src/commonMain/composeResources/values/strings.xml @@ -1,6 +1,7 @@ CloudStream None + No results found Ok Cancel Navigate up diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt index 2e009e4e7..6a363909f 100644 --- a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Colors.kt @@ -1,24 +1,24 @@ package com.lagradost.cloudstream4.compose import androidx.compose.material3.ButtonColors +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable -import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors object Colors { internal val blackButton @Composable @ReadOnlyComposable get() = ButtonColors( - containerColor = colors.surfaceVariant, - contentColor = colors.onBackground, - disabledContainerColor = colors.surface.copy(alpha = 0.9f), - disabledContentColor = colors.onBackground.copy(alpha = 0.9f) + containerColor = MaterialTheme.colorScheme.surfaceVariant, + contentColor = MaterialTheme.colorScheme.onBackground, + disabledContainerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.9f), + disabledContentColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.9f) ) internal val whiteButton @Composable @ReadOnlyComposable get() = ButtonColors( - containerColor = colors.onBackground, - contentColor = colors.surfaceVariant, - disabledContainerColor = colors.onBackground.copy(alpha = 0.9f), - disabledContentColor = colors.surface.copy(alpha = 0.9f) + containerColor = MaterialTheme.colorScheme.onBackground, + contentColor = MaterialTheme.colorScheme.surfaceVariant, + disabledContainerColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.9f), + disabledContentColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.9f) ) } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt index 9a6ab32c8..05948e457 100644 --- a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Dialog.kt @@ -2,15 +2,12 @@ package com.lagradost.cloudstream4.compose import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.Image -import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults @@ -19,7 +16,6 @@ import androidx.compose.material3.ButtonColors import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -36,7 +32,6 @@ import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.Dp @@ -44,7 +39,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties import com.lagradost.cloudstream4.generated.resources.Res import com.lagradost.cloudstream4.generated.resources.check -import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors import com.mihon.material.padding import org.jetbrains.compose.resources.painterResource @@ -61,7 +55,7 @@ fun ActionDialog( confirm: () -> Unit ) { AlertDialog( - containerColor = colors.background, + containerColor = MaterialTheme.colorScheme.background, onDismissRequest = dismiss, title = { if (icon == null) { @@ -118,13 +112,14 @@ fun BaseButton( modifier: Modifier = Modifier, ) { var hasFocus by remember { mutableStateOf(false) } + val canHaveFocus = LocalFocusOutlineDefault.current Button( onClick = onClick, colors = buttonColors, modifier = modifier.onFocusChanged { newFocus -> - hasFocus = newFocus.hasFocus + hasFocus = canHaveFocus && newFocus.hasFocus }, - border = if (hasFocus) BorderStroke(1.dp, colors.onBackground) else null + border = if (hasFocus) BorderStroke(1.dp, MaterialTheme.colorScheme.onBackground) else null ) { AnimatedVisibility(hasFocus) { Icon( @@ -142,7 +137,7 @@ fun BaseButton( fun SingleSelectDialog( modifier: Modifier = Modifier, shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = colors.background, + containerColor: Color = MaterialTheme.colorScheme.background, iconContentColor: Color = AlertDialogDefaults.iconContentColor, tonalElevation: Dp = AlertDialogDefaults.TonalElevation, icon: @Composable (() -> Unit)? = null, @@ -180,7 +175,7 @@ fun SingleSelectDialog( fun SingleSelectDialog( modifier: Modifier = Modifier, shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = colors.background, + containerColor: Color = MaterialTheme.colorScheme.background, iconContentColor: Color = AlertDialogDefaults.iconContentColor, tonalElevation: Dp = AlertDialogDefaults.TonalElevation, icon: @Composable (() -> Unit)? = null, @@ -353,19 +348,13 @@ fun SingleSelectionItem( selectedPainter: Painter, onClick: () -> Unit, ) { - val interactionSource = remember { MutableInteractionSource() } Row( modifier = modifier .fillMaxWidth() - .combinedClickable( - interactionSource = interactionSource, - indication = null, - onClick = onClick, - onLongClick = { - } - ) .rounded() - .ripple(interactionSource), + .clickable( + onClick = onClick, + ), verticalAlignment = Alignment.CenterVertically ) { if (isSelected) { @@ -373,13 +362,13 @@ fun SingleSelectionItem( painter = selectedPainter, contentDescription = null, modifier = Modifier.size(24.dp), - tint = colors.onBackground + tint = MaterialTheme.colorScheme.onBackground ) iconProvider?.invoke(key, text) Text( text = text, style = MaterialTheme.typography.bodyLarge, - color = colors.onBackground, + color = MaterialTheme.colorScheme.onBackground, modifier = Modifier.padding(15.dp) ) } else { @@ -388,7 +377,7 @@ fun SingleSelectionItem( Text( text = text, style = MaterialTheme.typography.bodyLarge, - color = colors.onSurfaceVariant, + color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(15.dp) ) } @@ -400,7 +389,7 @@ fun SingleSelectionItem( fun MultiSelectDialog( modifier: Modifier = Modifier, shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = colors.background, + containerColor: Color = MaterialTheme.colorScheme.background, iconContentColor: Color = AlertDialogDefaults.iconContentColor, tonalElevation: Dp = AlertDialogDefaults.TonalElevation, icon: @Composable (() -> Unit)? = null, diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Modifiers.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Modifiers.kt index d2bc48b46..ee910dd36 100644 --- a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Modifiers.kt +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/compose/Modifiers.kt @@ -1,32 +1,31 @@ package com.lagradost.cloudstream4.compose import androidx.compose.foundation.border -import androidx.compose.foundation.indication -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ripple import androidx.compose.runtime.Composable +import androidx.compose.runtime.ProvidableCompositionLocal import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.dp -import com.lagradost.cloudstream4.theme.CloudStreamTheme.colors +/* @Composable fun Modifier.ripple( interactionSource: MutableInteractionSource, bounded: Boolean = true, ): Modifier = indication( interactionSource = interactionSource, - indication = ripple(bounded = bounded, color = colors.onBackground), -) + indication = ripple(bounded = bounded, color = MaterialTheme.colorScheme.onBackground), +)*/ // no dimens.xml allowed in compose :/ fun RoundedShape() = RoundedCornerShape(10.dp) @@ -40,8 +39,19 @@ fun Modifier.rounded(): Modifier = fun Modifier.circle(): Modifier = clip(CircleShape) +/** + * If we should have outlines on items when focused by default, this should be false for desktop + * as we do not use a dpad for that + * */ +val LocalFocusOutlineDefault: ProvidableCompositionLocal = staticCompositionLocalOf { true } + @Composable -fun Modifier.focusOutline(shape: Shape = RoundedShape()): Modifier { +fun Modifier.focusOutline( + enabled : Boolean = LocalFocusOutlineDefault.current, + shape: Shape = RoundedShape() +): Modifier { + if(!enabled) return this + var hasFocus by remember { mutableStateOf(false) } return this.onFocusChanged { newFocus -> diff --git a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Theme.kt b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Theme.kt index ab1ea1e95..196d8d6a1 100644 --- a/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Theme.kt +++ b/shared/src/commonMain/kotlin/com/lagradost/cloudstream4/theme/Theme.kt @@ -99,15 +99,15 @@ private fun CloudStreamColorScheme.toMaterial3ColorScheme() = if (isLight) { ) } -val LocalSharedInfiniteTransition = staticCompositionLocalOf { throw NotImplementedError() } -val LocalCloudStreamColors = staticCompositionLocalOf { darkScheme() } +internal val LocalSharedInfiniteTransition = staticCompositionLocalOf { throw NotImplementedError() } -object CloudStreamTheme { - val colors: CloudStreamColorScheme @Composable @ReadOnlyComposable get() = LocalCloudStreamColors.current - /** Global synchronized animation, so many items in e.g. a LazyList can animate at the same time, - * even if they appeared at different times */ - val transition: InfiniteTransition @Composable @ReadOnlyComposable get() = LocalSharedInfiniteTransition.current -} +/** + * Global synchronized animation, so many items in e.g. a LazyList can animate at the same time, + * even if they appeared at different times + * */ +@Composable +@ReadOnlyComposable +fun MaterialTheme.infiniteSharedTransition() = LocalSharedInfiniteTransition.current @Composable fun CloudStreamPreviewTheme(content: @Composable () -> Unit) { @@ -122,7 +122,8 @@ fun CloudStreamTheme( ) { val csColors = modeToTheme(mode, primaryColor) val globalTransition = rememberInfiniteTransition(label = "GlobalSharedTransition") - CompositionLocalProvider(LocalCloudStreamColors provides csColors, LocalSharedInfiniteTransition provides globalTransition) { + // We do not provide csColors as a global, because people should use MaterialTheme directly instead + CompositionLocalProvider(LocalSharedInfiniteTransition provides globalTransition) { MaterialTheme( colorScheme = csColors.toMaterial3ColorScheme(), content = content, diff --git a/shared/src/commonMain/kotlin/com/mihon/material/AppBar.kt b/shared/src/commonMain/kotlin/com/mihon/material/AppBar.kt index 52fe5af75..e54441595 100644 --- a/shared/src/commonMain/kotlin/com/mihon/material/AppBar.kt +++ b/shared/src/commonMain/kotlin/com/mihon/material/AppBar.kt @@ -3,6 +3,7 @@ package com.mihon.material import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -99,6 +100,8 @@ fun AppBar( modifier = modifier, ) { TopAppBar( + // TODO @WindowInsets remove this when we have converted everything to compose + windowInsets = WindowInsets(0, 0, 0, 0), navigationIcon = { if (isActionMode) { IconButton(onClick = onCancelActionMode) { diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScaffold.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScaffold.kt index 4e79407c5..4f97d9f2b 100644 --- a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScaffold.kt +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/PreferenceScaffold.kt @@ -1,6 +1,7 @@ package com.mihon.presentation.settings import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -22,6 +23,8 @@ fun PreferenceScaffold( val (top, bottom) = remember { FocusRequester.createRefs() } Scaffold( + // TODO @WindowInsets remove this when we have converted everything to compose + contentWindowInsets = WindowInsets(0, 0, 0, 0), topBar = { AppBar( modifier = Modifier.focusRequester(top).focusProperties { diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/Search.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/Search.kt new file mode 100644 index 000000000..d221fc801 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/Search.kt @@ -0,0 +1,158 @@ +package com.mihon.presentation.settings + +import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.retain.retain +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream4.generated.resources.Res +import com.lagradost.cloudstream4.generated.resources.no_results_found +import com.mihon.presentation.secondaryItemAlpha +import com.mihon.presentation.settings.widget.TextPreferenceWidget +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.toPersistentList +import org.jetbrains.compose.resources.stringResource + +private fun getLocalizedBreadcrumb(path: String, node: String?, isLtr: Boolean): String { + return if (node == null) { + path + } else { + if (isLtr) { + // This locale reads left to right. + "$path > $node" + } else { + // This locale reads right to left. + "$node < $path" + } + } +} + +data class SettingsData( + val title: String, + val contents: List, + val navigation: T, +) + +data class SearchResultItem( + val title: String, + val breadcrumbs: String, + val highlightKey: String, + val navigation: T, +) + +@Composable +fun SettingSearchResults( + searchKey: String, + onItemClick: (SearchResultItem) -> Unit, + items: ImmutableList>, + nestedScrollConnection: NestedScrollConnection, + empty: @Composable () -> Unit +) { + val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr + + // We want to retain the results as backpress should not re-calc it + var result by retain>?>> { + mutableStateOf(null) + } + + LaunchedEffect(searchKey) { + if (searchKey.isEmpty()) { + result = null + return@LaunchedEffect + } + result = items.asSequence() + .flatMap { settingsData -> + settingsData.contents.asSequence() + // Only search from enabled prefs and one with valid title + .filter { it.enabled && it.title.isNotBlank() } + // Flatten items contained inside *enabled* PreferenceGroup + .flatMap { p -> + when (p) { + is Preference.PreferenceGroup -> { + if (p.enabled) { + p.preferenceItems.asSequence() + .filter { it.enabled && it.title.isNotBlank() } + .map { p.title to it } + } else { + emptySequence() + } + } + + is Preference.PreferenceItem<*, *> -> sequenceOf(null to p) + } + } + // Don't show info preference + .filterNot { it.second is Preference.PreferenceItem.InfoPreference } + // Filter by search query + .filter { (_, p) -> + val inTitle = p.title.contains(searchKey, true) + val inSummary = p.subtitle?.contains(searchKey, true) ?: false + inTitle || inSummary // TODO filter out %s ? This is a bug-ish in mihon + } + // Map result data + .map { (categoryTitle, p) -> + SearchResultItem( + navigation = settingsData.navigation, + title = p.title, + breadcrumbs = getLocalizedBreadcrumb( + path = settingsData.title, + node = categoryTitle, + isLtr = isLtr, + ), + highlightKey = p.title, + ) + } + } + .take(10) // Just take top 10 result for quicker result + .toPersistentList() + } + + Crossfade( + targetState = result, + label = "results", + ) { + when { + it == null -> empty() + + it.isEmpty() -> { + Text( + text = stringResource(Res.string.no_results_found), + modifier = Modifier.secondaryItemAlpha().fillMaxWidth().padding(20.dp), + textAlign = TextAlign.Center + ) + } + + else -> { + Column( + modifier = Modifier + .nestedScroll(nestedScrollConnection) + ) { + it.forEach { item -> + TextPreferenceWidget( + title = item.title, + subtitle = item.breadcrumbs, + onPreferenceClick = { + onItemClick(item) + } + ) + } + } + } + } + } +} diff --git a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/BasePreferenceWidget.kt b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/BasePreferenceWidget.kt index 6537ea9a9..e103a1440 100644 --- a/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/BasePreferenceWidget.kt +++ b/shared/src/commonMain/kotlin/com/mihon/presentation/settings/widget/BasePreferenceWidget.kt @@ -99,7 +99,7 @@ internal fun Modifier.highlightBackground(highlighted: Boolean): Modifier { } val highlight by animateColorAsState( targetValue = if (highlightFlag) { - MaterialTheme.colorScheme.surfaceTint.copy(alpha = .12f) + MaterialTheme.colorScheme.onBackground.copy(alpha = .12f) } else { Color.Transparent },