From fd1620f3d7f9d0fca58123a4eefae79a06f985db Mon Sep 17 00:00:00 2001
From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com>
Date: Fri, 13 Oct 2023 22:34:26 +0000
Subject: [PATCH 01/44] Fix unresponsive settings (#688)
* Lower targetSdk to get all installed packages
* Update sdk version
* Let's not be too radical
* Many fixes
* Revert targetSdk
* Make account homepage persistent
* Update mobile_navigation.xml
* Update SettingsFragment.kt
---
.../ui/settings/SettingsFragment.kt | 16 +--
.../main/res/navigation/mobile_navigation.xml | 103 +++++++++---------
2 files changed, 59 insertions(+), 60 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
index 4895b0d2..a4d19eba 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
@@ -187,13 +187,13 @@ class SettingsFragment : Fragment() {
}
binding?.apply {
listOf(
- settingsGeneral to R.id.action_navigation_settings_to_navigation_settings_general,
- settingsPlayer to R.id.action_navigation_settings_to_navigation_settings_player,
- settingsCredits to R.id.action_navigation_settings_to_navigation_settings_account,
- settingsUi to R.id.action_navigation_settings_to_navigation_settings_ui,
- settingsProviders to R.id.action_navigation_settings_to_navigation_settings_providers,
- settingsUpdates to R.id.action_navigation_settings_to_navigation_settings_updates,
- settingsExtensions to R.id.action_navigation_settings_to_navigation_settings_extensions,
+ settingsGeneral to R.id.action_navigation_global_to_navigation_settings_general,
+ settingsPlayer to R.id.action_navigation_global_to_navigation_settings_player,
+ settingsCredits to R.id.action_navigation_global_to_navigation_settings_account,
+ settingsUi to R.id.action_navigation_global_to_navigation_settings_ui,
+ settingsProviders to R.id.action_navigation_global_to_navigation_settings_providers,
+ settingsUpdates to R.id.action_navigation_global_to_navigation_settings_updates,
+ settingsExtensions to R.id.action_navigation_global_to_navigation_settings_extensions,
).forEach { (view, navigationId) ->
view.apply {
setOnClickListener {
@@ -212,4 +212,4 @@ class SettingsFragment : Fragment() {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index 6d0a94fb..d0df339b 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -331,57 +331,56 @@
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim"
- tools:layout="@layout/main_settings">
-
-
-
-
-
-
-
-
+ tools:layout="@layout/main_settings" />
+
+
+
+
+
+
+
-
\ No newline at end of file
+
From b7322ffb192d3f500e3d3070854fdcdd2b40f0e0 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Fri, 13 Oct 2023 16:44:51 -0600
Subject: [PATCH 02/44] Add clear search query icons (#687)
---
.../cloudstream3/ui/library/LibraryFragment.kt | 9 +++++++++
.../ui/quicksearch/QuickSearchFragment.kt | 13 ++++++++++---
.../cloudstream3/ui/search/SearchFragment.kt | 13 ++++++++++---
app/src/main/res/layout/fragment_library.xml | 2 ++
app/src/main/res/layout/fragment_library_tv.xml | 1 +
app/src/main/res/layout/fragment_search.xml | 1 +
app/src/main/res/layout/fragment_search_tv.xml | 1 +
app/src/main/res/layout/quick_search.xml | 2 ++
8 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
index 85f0aedd..c3986dca 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
@@ -8,12 +8,14 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
+import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.FOCUS_AFTER_DESCENDANTS
import android.view.ViewGroup.FOCUS_BLOCK_DESCENDANTS
import android.view.animation.AlphaAnimation
+import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
@@ -137,6 +139,13 @@ class LibraryFragment : Fragment() {
tag = "tv_no_focus_tag"
}
+ // Set the color for the search exit icon to the correct theme text color
+ val searchExitIcon = binding?.mainSearch?.findViewById(androidx.appcompat.R.id.search_close_btn)
+ val searchExitIconColor = TypedValue()
+
+ activity?.theme?.resolveAttribute(android.R.attr.textColor, searchExitIconColor, true)
+ searchExitIcon?.setColorFilter(searchExitIconColor.data)
+
binding?.mainSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
libraryViewModel.sort(ListSorting.Query, query)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt
index 53c7c2fa..5b300c06 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt
@@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
+import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -215,10 +216,16 @@ class QuickSearchFragment : Fragment() {
binding?.quickSearch?.findViewById(androidx.appcompat.R.id.search_close_btn)
//val searchMagIcon =
- // binding.quickSearch.findViewById(androidx.appcompat.R.id.search_mag_icon)
+ // binding?.quickSearch?.findViewById(androidx.appcompat.R.id.search_mag_icon)
- //searchMagIcon?.scaleX = 0.65f
- //searchMagIcon?.scaleY = 0.65f
+ // searchMagIcon?.scaleX = 0.65f
+ // searchMagIcon?.scaleY = 0.65f
+
+ // Set the color for the search exit icon to the correct theme text color
+ val searchExitIconColor = TypedValue()
+
+ activity?.theme?.resolveAttribute(android.R.attr.textColor, searchExitIconColor, true)
+ searchExitIcon?.setColorFilter(searchExitIconColor.data)
binding?.quickSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
index ce92d723..65dfd679 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
@@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.search
import android.content.DialogInterface
import android.content.res.Configuration
import android.os.Bundle
+import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -231,9 +232,15 @@ class SearchFragment : Fragment() {
val searchExitIcon =
binding?.mainSearch?.findViewById(androidx.appcompat.R.id.search_close_btn)
// val searchMagIcon =
- // main_search.findViewById(androidx.appcompat.R.id.search_mag_icon)
- //searchMagIcon.scaleX = 0.65f
- //searchMagIcon.scaleY = 0.65f
+ // binding?.mainSearch?.findViewById(androidx.appcompat.R.id.search_mag_icon)
+ // searchMagIcon.scaleX = 0.65f
+ // searchMagIcon.scaleY = 0.65f
+
+ // Set the color for the search exit icon to the correct theme text color
+ val searchExitIconColor = TypedValue()
+
+ activity?.theme?.resolveAttribute(android.R.attr.textColor, searchExitIconColor, true)
+ searchExitIcon?.setColorFilter(searchExitIconColor.data)
selectedApis = DataStoreHelper.searchPreferenceProviders.toMutableSet()
diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml
index 879ddbd9..446c2a81 100644
--- a/app/src/main/res/layout/fragment_library.xml
+++ b/app/src/main/res/layout/fragment_library.xml
@@ -68,6 +68,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
+ android:layout_marginEnd="25dp"
android:iconifiedByDefault="false"
android:imeOptions="actionSearch"
@@ -81,6 +82,7 @@
app:queryBackground="@color/transparent"
app:queryHint="@string/search_hint"
app:searchIcon="@drawable/search_icon"
+ app:closeIcon="@drawable/ic_baseline_close_24"
tools:ignore="RtlSymmetry">
diff --git a/app/src/main/res/layout/fragment_library_tv.xml b/app/src/main/res/layout/fragment_library_tv.xml
index ec6565ee..22b9feb1 100644
--- a/app/src/main/res/layout/fragment_library_tv.xml
+++ b/app/src/main/res/layout/fragment_library_tv.xml
@@ -102,6 +102,7 @@
app:queryBackground="@color/transparent"
app:queryHint="@string/search_hint"
app:searchIcon="@drawable/search_icon"
+ app:closeIcon="@drawable/ic_baseline_close_24"
tools:ignore="RtlSymmetry">
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
index c5a43b6d..7dc2b9d2 100644
--- a/app/src/main/res/layout/fragment_search.xml
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -49,6 +49,7 @@
app:queryBackground="@color/transparent"
app:queryHint="@string/search_hint"
app:searchIcon="@drawable/search_icon"
+ app:closeIcon="@drawable/ic_baseline_close_24"
tools:ignore="RtlSymmetry">
diff --git a/app/src/main/res/layout/fragment_search_tv.xml b/app/src/main/res/layout/fragment_search_tv.xml
index e34b0ac3..8b352c1f 100644
--- a/app/src/main/res/layout/fragment_search_tv.xml
+++ b/app/src/main/res/layout/fragment_search_tv.xml
@@ -50,6 +50,7 @@
app:queryBackground="@color/transparent"
app:queryHint="@string/search_hint"
app:searchIcon="@drawable/search_icon"
+ app:closeIcon="@drawable/ic_baseline_close_24"
tools:ignore="RtlSymmetry">
diff --git a/app/src/main/res/layout/quick_search.xml b/app/src/main/res/layout/quick_search.xml
index f34591b0..12d94aaa 100644
--- a/app/src/main/res/layout/quick_search.xml
+++ b/app/src/main/res/layout/quick_search.xml
@@ -49,6 +49,8 @@
app:queryBackground="@color/transparent"
app:searchIcon="@drawable/search_icon"
+ app:closeIcon="@drawable/ic_baseline_close_24"
+
android:paddingStart="-10dp"
android:iconifiedByDefault="false"
app:queryHint="@string/search_hint"
From 7e9d1ded7fb74f97038948e5e81ee10d58356c84 Mon Sep 17 00:00:00 2001
From: KingLucius
Date: Sat, 14 Oct 2023 01:54:34 +0300
Subject: [PATCH 03/44] Larger top poster in TV loading layout (#685)
---
.../main/res/layout/fragment_result_tv.xml | 131 +++++++++---------
1 file changed, 67 insertions(+), 64 deletions(-)
diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml
index feaf6fbc..b53639ba 100644
--- a/app/src/main/res/layout/fragment_result_tv.xml
+++ b/app/src/main/res/layout/fragment_result_tv.xml
@@ -124,17 +124,16 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:textColor="?attr/textColor" />
-
-
@@ -155,7 +153,6 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
+ android:orientation="vertical"
+ android:layout_marginTop="175dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
From 3cb2196e62a2cd435d1d76172d8a9113dff5d573 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Fri, 13 Oct 2023 17:02:12 -0600
Subject: [PATCH 04/44] Add favorites (#682)
* Add favorites
---
.../syncproviders/providers/LocalList.kt | 45 ++++++++++++----
.../ui/result/ResultFragmentPhone.kt | 27 ++++++++++
.../ui/result/ResultFragmentTv.kt | 42 ++++++++++++++-
.../ui/result/ResultViewModel2.kt | 47 +++++++++++++++++
.../cloudstream3/utils/DataStoreHelper.kt | 51 +++++++++++++++++++
.../main/res/layout/fragment_result_swipe.xml | 21 +++++++-
.../main/res/layout/fragment_result_tv.xml | 15 +++++-
app/src/main/res/values/strings.xml | 5 ++
8 files changed, 238 insertions(+), 15 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
index e6ca9711..71bb2633 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
@@ -8,7 +8,9 @@ import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
@@ -69,24 +71,47 @@ class LocalList : SyncAPI {
}?.distinctBy { it.first } ?: return null
val list = ioWork {
- watchStatusIds.groupBy {
- it.second.stringRes
- }.mapValues { group ->
+ val isTv = isTvSettings()
+
+ val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate {
+ // None is not something to display
+ it.stringRes to emptyList()
+ } + mapOf(
+ R.string.favorites_list_name to emptyList()
+ ) + if (!isTv) {
+ mapOf(
+ R.string.subscription_list_name to emptyList(),
+ )
+ } else {
+ emptyMap()
+ }
+
+ val watchStatusMap = watchStatusIds.groupBy { it.second.stringRes }.mapValues { group ->
group.value.mapNotNull {
getBookmarkedData(it.first)?.toLibraryItem(it.first.toString())
}
- } + mapOf(R.string.subscription_list_name to getAllSubscriptions().mapNotNull {
+ }
+
+ val favoritesMap = mapOf(R.string.favorites_list_name to getAllFavorites().mapNotNull {
it.toLibraryItem()
})
+
+ // Don't show subscriptions or favorites on TV
+ val result = if (isTv) {
+ baseMap + watchStatusMap + favoritesMap
+ } else {
+ val subscriptionsMap = mapOf(R.string.subscription_list_name to getAllSubscriptions().mapNotNull {
+ it.toLibraryItem()
+ })
+
+ baseMap + watchStatusMap + subscriptionsMap + favoritesMap
+ }
+
+ result
}
- val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate {
- // None is not something to display
- it.stringRes to emptyList()
- } + mapOf(R.string.subscription_list_name to emptyList())
-
return SyncAPI.LibraryMetadata(
- (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) },
+ list.map { SyncAPI.LibraryList(txt(it.key), it.value) },
setOf(
ListSorting.AlphabeticalA,
ListSorting.AlphabeticalZ,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
index e5f16dd5..a0d82062 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
@@ -445,6 +445,20 @@ open class ResultFragmentPhone : FullScreenPlayer() {
?: txt(R.string.no_data).asStringNull(context) ?: ""
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
}
+ resultFavorite.setOnClickListener {
+ val isFavorite =
+ viewModel.toggleFavoriteStatus() ?: return@setOnClickListener
+
+ val message = if (isFavorite) {
+ R.string.favorite_added
+ } else {
+ R.string.favorite_removed
+ }
+
+ val name = (viewModel.page.value as? Resource.Success)?.value?.title
+ ?: txt(R.string.no_data).asStringNull(context) ?: ""
+ CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
+ }
mediaRouteButton.apply {
val chromecastSupport = api?.hasChromecastSupport == true
alpha = if (chromecastSupport) 1f else 0.3f
@@ -564,6 +578,19 @@ open class ResultFragmentPhone : FullScreenPlayer() {
binding?.resultSubscribe?.setImageResource(drawable)
}
+ observeNullable(viewModel.favoriteStatus) { isFavorite ->
+ binding?.resultFavorite?.isVisible = isFavorite != null
+ if (isFavorite == null) return@observeNullable
+
+ val drawable = if (isFavorite) {
+ R.drawable.ic_baseline_favorite_24
+ } else {
+ R.drawable.ic_baseline_favorite_border_24
+ }
+
+ binding?.resultFavorite?.setImageResource(drawable)
+ }
+
observe(viewModel.trailers) { trailers ->
setTrailers(trailers.flatMap { it.mirros }) // I dont care about subtitles yet!
}
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 5e4869cc..13734b67 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
@@ -8,6 +8,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.DecelerateInterpolator
+import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone
import androidx.core.view.isVisible
@@ -17,6 +18,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
+import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
@@ -265,6 +267,7 @@ class ResultFragmentTv : Fragment() {
resultEpisodesShow.onFocusChangeListener = rightListener
resultDescription.onFocusChangeListener = leftListener
resultBookmarkButton.onFocusChangeListener = leftListener
+ resultFavoriteButton.onFocusChangeListener = leftListener
resultEpisodesShow.setOnClickListener {
// toggle, to make it more touch accessable just in case someone thinks that a
// tv layout is better but is using a touch device
@@ -283,7 +286,8 @@ class ResultFragmentTv : Fragment() {
resultPlaySeries,
resultResumeSeries,
resultPlayTrailer,
- resultBookmarkButton
+ resultBookmarkButton,
+ resultFavoriteButton
)
for (requestView in views) {
if (!requestView.isVisible) continue
@@ -424,6 +428,7 @@ class ResultFragmentTv : Fragment() {
val aboveCast = listOf(
binding?.resultEpisodesShow,
binding?.resultBookmarkButton,
+ binding?.resultFavoriteButton,
).firstOrNull {
it?.isVisible == true
}
@@ -532,6 +537,41 @@ class ResultFragmentTv : Fragment() {
}
}
+ observeNullable(viewModel.favoriteStatus) { isFavorite ->
+ binding?.resultFavoriteButton?.apply {
+ isVisible = isFavorite != null
+ if (isFavorite == null) return@observeNullable
+
+ val drawable = if (isFavorite) {
+ R.drawable.ic_baseline_favorite_24
+ } else {
+ R.drawable.ic_baseline_favorite_border_24
+ }
+
+ val text = if (isFavorite) {
+ R.string.action_remove_from_favorites
+ } else {
+ R.string.action_add_to_favorites
+ }
+
+ setIconResource(drawable)
+ setText(text)
+ setOnClickListener {
+ val isFavorite = viewModel.toggleFavoriteStatus() ?: return@setOnClickListener
+
+ val message = if (isFavorite) {
+ R.string.favorite_added
+ } else {
+ R.string.favorite_removed
+ }
+
+ val name = (viewModel.page.value as? Resource.Success)?.value?.title
+ ?: txt(R.string.no_data).asStringNull(context) ?: ""
+ CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
+ }
+ }
+ }
+
observeNullable(viewModel.movie) { data ->
binding?.apply {
resultPlayMovie.isVisible = data is Resource.Success
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 6acf476a..e5ed7b92 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -51,11 +51,14 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getFavoritesData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultEpisode
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+import com.lagradost.cloudstream3.utils.DataStoreHelper.removeFavoritesData
import com.lagradost.cloudstream3.utils.DataStoreHelper.setDub
+import com.lagradost.cloudstream3.utils.DataStoreHelper.setFavoritesData
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultEpisode
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
import com.lagradost.cloudstream3.utils.UIHelper.navigate
@@ -425,6 +428,9 @@ class ResultViewModel2 : ViewModel() {
private val _subscribeStatus: MutableLiveData = MutableLiveData(null)
val subscribeStatus: LiveData = _subscribeStatus
+ private val _favoriteStatus: MutableLiveData = MutableLiveData(null)
+ val favoriteStatus: LiveData = _favoriteStatus
+
companion object {
const val TAG = "RVM2"
//private const val EPISODE_RANGE_SIZE = 20
@@ -868,6 +874,40 @@ class ResultViewModel2 : ViewModel() {
return !isSubscribed
}
+ /**
+ * @return true if added to favorites, false if not. Null if not possible to favorite.
+ **/
+ fun toggleFavoriteStatus(): Boolean? {
+ val isFavorite = _favoriteStatus.value ?: return null
+ val response = currentResponse ?: return null
+
+ val currentId = response.getId()
+
+ if (isFavorite) {
+ removeFavoritesData(currentId)
+ } else {
+ val current = getFavoritesData(currentId)
+
+ setFavoritesData(
+ currentId,
+ DataStoreHelper.FavoritesData(
+ currentId,
+ current?.favoritesTime ?: unixTimeMS,
+ unixTimeMS,
+ response.name,
+ response.url,
+ response.apiName,
+ response.type,
+ response.posterUrl,
+ response.year
+ )
+ )
+ }
+
+ _favoriteStatus.postValue(!isFavorite)
+ return !isFavorite
+ }
+
private fun startChromecast(
activity: Activity?,
result: ResultEpisode,
@@ -1750,6 +1790,12 @@ class ResultViewModel2 : ViewModel() {
}
}
+ private fun postFavorites(loadResponse: LoadResponse) {
+ val id = loadResponse.getId()
+ val isFavorite = getFavoritesData(id) != null
+ _favoriteStatus.postValue(isFavorite)
+ }
+
private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) {
if (range == null || indexer == null) {
return
@@ -1887,6 +1933,7 @@ class ResultViewModel2 : ViewModel() {
currentResponse = loadResponse
postPage(loadResponse, apiRepository)
postSubscription(loadResponse)
+ postFavorites(loadResponse)
if (updateEpisodes)
postEpisodes(loadResponse, updateFillers)
}
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 952422a4..4b4157d6 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
@@ -42,6 +42,7 @@ const val VIDEO_WATCH_STATE = "video_watch_state"
const val RESULT_WATCH_STATE = "result_watch_state"
const val RESULT_WATCH_STATE_DATA = "result_watch_state_data"
const val RESULT_SUBSCRIBED_STATE_DATA = "result_subscribed_state_data"
+const val RESULT_FAVORITES_STATE_DATA = "result_favorites_state_data"
const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes
const val RESULT_RESUME_WATCHING_OLD = "result_resume_watching"
const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated"
@@ -406,6 +407,33 @@ object DataStoreHelper {
}
}
+ data class FavoritesData(
+ @JsonProperty("id") override var id: Int?,
+ @JsonProperty("favoritesTime") val favoritesTime: Long,
+ @JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
+ @JsonProperty("name") override val name: String,
+ @JsonProperty("url") override val url: String,
+ @JsonProperty("apiName") override val apiName: String,
+ @JsonProperty("type") override var type: TvType? = null,
+ @JsonProperty("posterUrl") override var posterUrl: String?,
+ @JsonProperty("year") val year: Int?,
+ @JsonProperty("quality") override var quality: SearchQuality? = null,
+ @JsonProperty("posterHeaders") override var posterHeaders: Map? = null,
+ ) : SearchResponse {
+ fun toLibraryItem(): SyncAPI.LibraryItem? {
+ return SyncAPI.LibraryItem(
+ name,
+ url,
+ id?.toString() ?: return null,
+ null,
+ null,
+ null,
+ latestUpdatedTime,
+ apiName, type, posterUrl, posterHeaders, quality, this.id
+ )
+ }
+ }
+
data class ResumeWatchingResult(
@JsonProperty("name") override val name: String,
@JsonProperty("url") override val url: String,
@@ -579,6 +607,29 @@ object DataStoreHelper {
return getKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString())
}
+ fun getAllFavorites(): List {
+ return getKeys("$currentAccount/$RESULT_FAVORITES_STATE_DATA")?.mapNotNull {
+ getKey(it)
+ } ?: emptyList()
+ }
+
+ fun removeFavoritesData(id: Int?) {
+ if (id == null) return
+ AccountManager.localListApi.requireLibraryRefresh = true
+ removeKey("$currentAccount/$RESULT_FAVORITES_STATE_DATA", id.toString())
+ }
+
+ fun setFavoritesData(id: Int?, data: FavoritesData) {
+ if (id == null) return
+ setKey("$currentAccount/$RESULT_FAVORITES_STATE_DATA", id.toString(), data)
+ AccountManager.localListApi.requireLibraryRefresh = true
+ }
+
+ fun getFavoritesData(id: Int?): FavoritesData? {
+ if (id == null) return null
+ return getKey("$currentAccount/$RESULT_FAVORITES_STATE_DATA", id.toString())
+ }
+
fun setViewPos(id: Int?, pos: Long, dur: Long) {
if (id == null) return
if (dur < 30_000) return // too short
diff --git a/app/src/main/res/layout/fragment_result_swipe.xml b/app/src/main/res/layout/fragment_result_swipe.xml
index 4e8e3c14..eb2653d0 100644
--- a/app/src/main/res/layout/fragment_result_swipe.xml
+++ b/app/src/main/res/layout/fragment_result_swipe.xml
@@ -74,7 +74,7 @@
android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_description"
android:nextFocusLeft="@id/result_add_sync"
- android:nextFocusRight="@id/result_share"
+ android:nextFocusRight="@id/result_favorite"
tools:visibility="visible"
@@ -89,10 +89,27 @@
android:layout_gravity="end|center_vertical"
app:tint="?attr/textColor" />
+
+
+
+
tv_no_focus_tag
You have already voted
+ Favorites
+ %s added to favorites
+ %s removed from favorites
+ Add to favorites
+ Remove from favorites
From 8ed7418fe41d314342ae5cf9bb50c78d46b8b7b1 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Sat, 14 Oct 2023 10:30:45 -0600
Subject: [PATCH 05/44] Enable sorting by updated date in LocalList
---
.../cloudstream3/syncproviders/providers/LocalList.kt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
index 71bb2633..0c01c0d5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
@@ -80,7 +80,7 @@ class LocalList : SyncAPI {
R.string.favorites_list_name to emptyList()
) + if (!isTv) {
mapOf(
- R.string.subscription_list_name to emptyList(),
+ R.string.subscription_list_name to emptyList()
)
} else {
emptyMap()
@@ -115,8 +115,8 @@ class LocalList : SyncAPI {
setOf(
ListSorting.AlphabeticalA,
ListSorting.AlphabeticalZ,
-// ListSorting.UpdatedNew,
-// ListSorting.UpdatedOld,
+ ListSorting.UpdatedNew,
+ ListSorting.UpdatedOld,
// ListSorting.RatingHigh,
// ListSorting.RatingLow,
)
From a7a6f2282a539082b9e864ca36848c0a74e47601 Mon Sep 17 00:00:00 2001
From: IndusAryan <125901294+IndusAryan@users.noreply.github.com>
Date: Wed, 18 Oct 2023 00:43:29 +0530
Subject: [PATCH 06/44] Update build.gradle.kts (#694)
---
app/build.gradle.kts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index b0798e44..cea5684e 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -232,7 +232,7 @@ dependencies {
// Networking
// implementation("com.squareup.okhttp3:okhttp:4.9.2")
// implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1")
- implementation("com.github.Blatzar:NiceHttp:0.4.3")
+ implementation("com.github.Blatzar:NiceHttp:0.4.4") // http library
// To fix SSL fuckery on android 9
implementation("org.conscrypt:conscrypt-android:2.5.2")
// Util to skip the URI file fuckery 🙏
From eb58cb1184389ddc0eff0de339fcea62939877dd Mon Sep 17 00:00:00 2001
From: IndusAryan <125901294+IndusAryan@users.noreply.github.com>
Date: Mon, 23 Oct 2023 21:41:05 +0530
Subject: [PATCH 07/44] fix crash when navigation graph is null (#706)
---
app/build.gradle.kts | 12 ++++++------
.../com/lagradost/cloudstream3/utils/UIHelper.kt | 7 ++++---
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index cea5684e..73c53292 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -51,7 +51,7 @@ android {
}
// https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading
- compileSdk = 33 // android 14 is fucked
+ compileSdk = 34 // android 14 is fucked
buildToolsVersion = "34.0.0"
defaultConfig {
@@ -157,7 +157,7 @@ dependencies {
implementation("androidx.test.ext:junit-ktx:1.1.5")
testImplementation("org.json:json:20230618")
- implementation("androidx.core:core-ktx:1.10.1") // need 34 for higher
+ implementation("androidx.core:core-ktx:1.12.0") // need 34 for higher
implementation("androidx.appcompat:appcompat:1.6.1") // need target 32 for 1.5.0
// dont change this to 1.6.0 it looks ugly af
@@ -165,10 +165,10 @@ dependencies {
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
// need 34 for higher
- implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
- implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
- implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
- implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
+ implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
+ implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
+ implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
+ implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
index 9b40e70e..d5357e0c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
@@ -178,9 +178,10 @@ object UIHelper {
fun Activity?.navigate(@IdRes navigation: Int, arguments: Bundle? = null) {
try {
if (this is FragmentActivity) {
- (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.navController?.navigate(
- navigation, arguments
- )
+ val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?
+ navHostFragment?.navController?.let {
+ it.navigate(navigation, arguments)
+ }
}
} catch (t: Throwable) {
logError(t)
From 138dea88c4404aafb8026574c89e69c8e7387ca9 Mon Sep 17 00:00:00 2001
From: KingLucius
Date: Mon, 23 Oct 2023 19:16:48 +0300
Subject: [PATCH 08/44] Poster cropped at 20% from Top (#693)
---
.../ui/result/ResultFragmentTv.kt | 6 +-
.../utils/PercentageCropImageView.kt | 94 +++++++++++++++++++
.../main/res/layout/fragment_result_tv.xml | 7 +-
3 files changed, 102 insertions(+), 5 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/PercentageCropImageView.kt
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 13734b67..9ccc7c01 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
@@ -247,7 +247,7 @@ class ResultFragmentTv : Fragment() {
binding?.apply {
//episodesShadow.rotationX = 180.0f//if(episodesShadow.isRtl()) 180.0f else 0.0f
-
+
val leftListener: View.OnFocusChangeListener =
View.OnFocusChangeListener { _, hasFocus ->
if (!hasFocus) return@OnFocusChangeListener
@@ -804,12 +804,14 @@ class ResultFragmentTv : Fragment() {
R.drawable.profile_bg_red,
R.drawable.profile_bg_teal
).random()
+ //Change poster crop area to 20% from Top
+ backgroundPoster.cropYCenterOffsetPct = 0.20F
+
backgroundPoster.setImage(
d.posterBackgroundImage ?: UiImage.Drawable(error),
radius = 0,
errorImageDrawable = error
)
-
resultComingSoon.isVisible = d.comingSoon
resultDataHolder.isGone = d.comingSoon
UIHelper.populateChips(resultTag, d.tags)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/PercentageCropImageView.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/PercentageCropImageView.kt
new file mode 100644
index 00000000..1e572fb7
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/PercentageCropImageView.kt
@@ -0,0 +1,94 @@
+package com.lagradost.cloudstream3.utils
+//Reference: https://stackoverflow.com/a/29055283
+import android.content.Context
+import android.graphics.Matrix
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+
+class PercentageCropImageView : androidx.appcompat.widget.AppCompatImageView {
+ private var mCropYCenterOffsetPct: Float? = null
+ private var mCropXCenterOffsetPct: Float? = null
+ constructor(context: Context?) : super(context!!)
+ constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs)
+ constructor(
+ context: Context?, attrs: AttributeSet?,
+ defStyle: Int
+ ) : super(context!!, attrs, defStyle)
+
+ var cropYCenterOffsetPct: Float
+ get() = mCropYCenterOffsetPct!!
+ set(cropYCenterOffsetPct) {
+ require(cropYCenterOffsetPct <= 1.0) { "Value too large: Must be <= 1.0" }
+ mCropYCenterOffsetPct = cropYCenterOffsetPct
+ }
+ var cropXCenterOffsetPct: Float
+ get() = mCropXCenterOffsetPct!!
+ set(cropXCenterOffsetPct) {
+ require(cropXCenterOffsetPct <= 1.0) { "Value too large: Must be <= 1.0" }
+ mCropXCenterOffsetPct = cropXCenterOffsetPct
+ }
+
+ private fun myConfigureBounds() {
+ if (this.scaleType == ScaleType.MATRIX) {
+
+ val d = this.drawable
+ if (d != null) {
+ val dWidth = d.intrinsicWidth
+ val dHeight = d.intrinsicHeight
+ val m = Matrix()
+ val vWidth = width - this.paddingLeft - this.paddingRight
+ val vHeight = height - this.paddingTop - this.paddingBottom
+ val scale: Float
+ var dx = 0f
+ var dy = 0f
+ if (dWidth * vHeight > vWidth * dHeight) {
+ val cropXCenterOffsetPct =
+ if (mCropXCenterOffsetPct != null) mCropXCenterOffsetPct!!.toFloat() else 0.5f
+ scale = vHeight.toFloat() / dHeight.toFloat()
+ dx = (vWidth - dWidth * scale) * cropXCenterOffsetPct
+ } else {
+ val cropYCenterOffsetPct =
+ if (mCropYCenterOffsetPct != null) mCropYCenterOffsetPct!!.toFloat() else 0f
+ scale = vWidth.toFloat() / dWidth.toFloat()
+ dy = (vHeight - dHeight * scale) * cropYCenterOffsetPct
+ }
+ m.setScale(scale, scale)
+ m.postTranslate((dx + 0.5f).toInt().toFloat(), (dy + 0.5f).toInt().toFloat())
+ this.imageMatrix = m
+ }
+ }
+ }
+
+ // These 3 methods call configureBounds in ImageView.java class, which
+ // adjusts the matrix in a call to center_crop (android's built-in
+ // scaling and centering crop method). We also want to trigger
+ // in the same place, but using our own matrix, which is then set
+ // directly at line 588 of ImageView.java and then copied over
+ // as the draw matrix at line 942 of ImageView.java
+ override fun setFrame(l: Int, t: Int, r: Int, b: Int): Boolean {
+ val changed = super.setFrame(l, t, r, b)
+ myConfigureBounds()
+ return changed
+ }
+
+ override fun setImageDrawable(d: Drawable?) {
+ super.setImageDrawable(d)
+ myConfigureBounds()
+ }
+
+ override fun setImageResource(resId: Int) {
+ super.setImageResource(resId)
+ myConfigureBounds()
+ }
+ // In case you can change the ScaleType in code you have to call redraw()
+ //fullsizeImageView.setScaleType(ScaleType.FIT_CENTER);
+ //fullsizeImageView.redraw();
+ fun redraw() {
+ val d = this.drawable
+ if (d != null) {
+ // Force toggle to recalculate our bounds
+ setImageDrawable(null)
+ setImageDrawable(d)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml
index 64550457..75215183 100644
--- a/app/src/main/res/layout/fragment_result_tv.xml
+++ b/app/src/main/res/layout/fragment_result_tv.xml
@@ -130,14 +130,15 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_height="250dp"
android:visibility="visible">
-
+ android:scaleType="matrix"
+ tools:src="@drawable/profile_bg_dark_blue" >
+
Date: Mon, 23 Oct 2023 10:21:32 -0600
Subject: [PATCH 09/44] Library/LocalList: enable subscriptions on emulator
layout (#702)
---
.../cloudstream3/syncproviders/providers/LocalList.kt | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
index 0c01c0d5..99723e90 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
@@ -8,7 +8,7 @@ import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
import com.lagradost.cloudstream3.ui.result.txt
-import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
@@ -71,14 +71,14 @@ class LocalList : SyncAPI {
}?.distinctBy { it.first } ?: return null
val list = ioWork {
- val isTv = isTvSettings()
+ val isTrueTv = isTrueTvSettings()
val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate {
// None is not something to display
it.stringRes to emptyList()
} + mapOf(
R.string.favorites_list_name to emptyList()
- ) + if (!isTv) {
+ ) + if (!isTrueTv) {
mapOf(
R.string.subscription_list_name to emptyList()
)
@@ -96,8 +96,8 @@ class LocalList : SyncAPI {
it.toLibraryItem()
})
- // Don't show subscriptions or favorites on TV
- val result = if (isTv) {
+ // Don't show subscriptions on TV
+ val result = if (isTrueTv) {
baseMap + watchStatusMap + favoritesMap
} else {
val subscriptionsMap = mapOf(R.string.subscription_list_name to getAllSubscriptions().mapNotNull {
From 2a4468eb44fe16c90f3312dbefc06d77010d49e8 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Mon, 23 Oct 2023 10:33:44 -0600
Subject: [PATCH 10/44] Add more info on homepage to emulator layout (#698)
* Add more info on homepage to emulator layout
* Support for continue watching and bookmarks
It does some manual changes to avoid having to duplicate the entire layout for minor changes
---
.../cloudstream3/ExampleInstrumentedTest.kt | 6 ++-
.../ui/home/HomeParentItemAdapter.kt | 17 ++++---
.../ui/home/HomeParentItemAdapterPreview.kt | 47 +++++++++++++++++--
.../main/res/layout/fragment_home_head_tv.xml | 24 +++++++++-
.../res/layout/homepage_parent_emulator.xml | 35 ++++++++++++++
5 files changed, 114 insertions(+), 15 deletions(-)
create mode 100644 app/src/main/res/layout/homepage_parent_emulator.xml
diff --git a/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt
index a84b2457..faacdf50 100644
--- a/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt
@@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.databinding.FragmentSearchBinding
import com.lagradost.cloudstream3.databinding.FragmentSearchTvBinding
import com.lagradost.cloudstream3.databinding.HomeResultGridBinding
import com.lagradost.cloudstream3.databinding.HomepageParentBinding
+import com.lagradost.cloudstream3.databinding.HomepageParentEmulatorBinding
import com.lagradost.cloudstream3.databinding.HomepageParentTvBinding
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutBinding
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutTvBinding
@@ -119,8 +120,9 @@ class ExampleInstrumentedTest {
// testAllLayouts(activity, R.layout.home_scroll_view, R.layout.home_scroll_view_tv)
// testAllLayouts(activity, R.layout.home_scroll_view, R.layout.home_scroll_view_tv)
- testAllLayouts(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent)
- testAllLayouts(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent)
+ testAllLayouts(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent_emulator, R.layout.homepage_parent)
+ testAllLayouts(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent_emulator, R.layout.homepage_parent)
+ testAllLayouts(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent_emulator, R.layout.homepage_parent)
testAllLayouts(activity, R.layout.fragment_library_tv, R.layout.fragment_library)
testAllLayouts(activity, R.layout.fragment_library_tv, R.layout.fragment_library)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt
index 163a60a1..443278a9 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt
@@ -15,7 +15,8 @@ import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
-import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable
class LoadClickCallback(
@@ -34,11 +35,13 @@ open class ParentItemAdapter(
) : RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
- val root = LayoutInflater.from(parent.context).inflate(
- if (isTvSettings()) R.layout.homepage_parent_tv else R.layout.homepage_parent,
- parent,
- false
- )
+ val layoutResId = when {
+ isTrueTvSettings() -> R.layout.homepage_parent_tv
+ parent.context.isEmulatorSettings() -> R.layout.homepage_parent_emulator
+ else -> R.layout.homepage_parent
+ }
+
+ val root = LayoutInflater.from(parent.context).inflate(layoutResId, parent, false)
val binding = HomepageParentBinding.bind(root)
@@ -234,7 +237,7 @@ open class ParentItemAdapter(
})
//(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged()
- if (!isTvSettings()) {
+ if (!isTrueTvSettings()) {
title.setOnClickListener {
moreInfoClickCallback.invoke(expand)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
index d7956f39..b7e52b88 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
@@ -35,6 +35,7 @@ import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
@@ -81,6 +82,28 @@ class HomeParentItemAdapterPreview(
parent,
false
) else FragmentHomeHeadBinding.inflate(inflater, parent, false)
+
+ if (binding is FragmentHomeHeadTvBinding && parent.context.isEmulatorSettings()) {
+ binding.homeBookmarkParentItemMoreInfo.isVisible = true
+
+ val marginInDp = 50
+ val density = binding.horizontalScrollChips.context.resources.displayMetrics.density
+ val marginInPixels = (marginInDp * density).toInt()
+
+ val params = binding.horizontalScrollChips.layoutParams as ViewGroup.MarginLayoutParams
+ params.marginEnd = marginInPixels
+ binding.horizontalScrollChips.layoutParams = params
+ binding.homeWatchParentItemTitle.setCompoundDrawablesWithIntrinsicBounds(
+ null,
+ null,
+ ContextCompat.getDrawable(
+ parent.context,
+ R.drawable.ic_baseline_arrow_forward_24
+ ),
+ null
+ )
+ }
+
HeaderViewHolder(
binding,
viewModel,
@@ -553,12 +576,19 @@ class HomeParentItemAdapterPreview(
resumeHolder.isVisible = resumeWatching.isNotEmpty()
resumeAdapter.updateList(resumeWatching)
- if (binding is FragmentHomeHeadBinding) {
- binding.homeWatchParentItemTitle.setOnClickListener {
+ if (
+ binding is FragmentHomeHeadBinding ||
+ binding is FragmentHomeHeadTvBinding &&
+ binding.root.context.isEmulatorSettings()
+ ) {
+ val title = (binding as? FragmentHomeHeadBinding)?.homeWatchParentItemTitle
+ ?: (binding as? FragmentHomeHeadTvBinding)?.homeWatchParentItemTitle
+
+ title?.setOnClickListener {
viewModel.popup(
HomeViewModel.ExpandableHomepageList(
HomePageList(
- binding.homeWatchParentItemTitle.text.toString(),
+ title.text.toString(),
resumeWatching,
false
), 1, false
@@ -576,8 +606,15 @@ class HomeParentItemAdapterPreview(
bookmarkHolder.isVisible = visible
bookmarkAdapter.updateList(list)
- if (binding is FragmentHomeHeadBinding) {
- binding.homeBookmarkParentItemTitle.setOnClickListener {
+ if (
+ binding is FragmentHomeHeadBinding ||
+ binding is FragmentHomeHeadTvBinding &&
+ binding.root.context.isEmulatorSettings()
+ ) {
+ val title = (binding as? FragmentHomeHeadBinding)?.homeBookmarkParentItemTitle
+ ?: (binding as? FragmentHomeHeadTvBinding)?.homeBookmarkParentItemTitle
+
+ title?.setOnClickListener {
val items = toggleList.map { it.first }.filter { it.isChecked }
if (items.isEmpty()) return@setOnClickListener // we don't want to show an empty dialog
val textSum = items
diff --git a/app/src/main/res/layout/fragment_home_head_tv.xml b/app/src/main/res/layout/fragment_home_head_tv.xml
index 05cb3a41..6db7536f 100644
--- a/app/src/main/res/layout/fragment_home_head_tv.xml
+++ b/app/src/main/res/layout/fragment_home_head_tv.xml
@@ -232,7 +232,9 @@
android:layout_marginStart="@dimen/navbar_width"
android:layout_marginEnd="0dp"
android:padding="12dp"
- android:text="@string/continue_watching" />
+ android:text="@string/continue_watching"
+ android:background="?android:attr/selectableItemBackground"
+ app:drawableTint="?attr/white" />
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 48053164dc9737fefeb66a4d6dadc5fcc6410f3a Mon Sep 17 00:00:00 2001
From: KingLucius
Date: Mon, 23 Oct 2023 19:38:53 +0300
Subject: [PATCH 11/44] Old APIs focus fixes (#692)
* Movie button focus fix in TV layout
* Cast item focusable in old API
* Media description focus fix in old API
* Switch account button focus fix in old APi
---
.../com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt | 1 +
app/src/main/res/layout/cast_item.xml | 2 +-
app/src/main/res/layout/fragment_home_tv.xml | 1 +
app/src/main/res/layout/fragment_result_tv.xml | 1 +
4 files changed, 4 insertions(+), 1 deletion(-)
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 9ccc7c01..feb8ca04 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
@@ -575,6 +575,7 @@ class ResultFragmentTv : Fragment() {
observeNullable(viewModel.movie) { data ->
binding?.apply {
resultPlayMovie.isVisible = data is Resource.Success
+ resultPlaySeries.isVisible = data == null
seriesHolder.isVisible = data == null
resultEpisodesShow.isVisible = data == null
diff --git a/app/src/main/res/layout/cast_item.xml b/app/src/main/res/layout/cast_item.xml
index 368fa770..f164384b 100644
--- a/app/src/main/res/layout/cast_item.xml
+++ b/app/src/main/res/layout/cast_item.xml
@@ -9,7 +9,7 @@
android:layout_margin="5dp"
android:foreground="@drawable/outline_drawable"
app:cardBackgroundColor="@color/transparent"
-
+ android:focusable="true"
app:cardCornerRadius="@dimen/rounded_image_radius"
app:cardElevation="0dp">
diff --git a/app/src/main/res/layout/fragment_home_tv.xml b/app/src/main/res/layout/fragment_home_tv.xml
index e44457d9..86ef884e 100644
--- a/app/src/main/res/layout/fragment_home_tv.xml
+++ b/app/src/main/res/layout/fragment_home_tv.xml
@@ -140,6 +140,7 @@
android:layout_gravity="end"
android:background="@drawable/player_button_tv_attr_no_bg"
android:contentDescription="@string/account"
+ android:focusable="true"
android:nextFocusLeft="@id/home_preview_search_button"
android:nextFocusRight="@id/home_switch_account"
android:nextFocusDown="@id/home_change_api"
diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml
index 75215183..51add4cb 100644
--- a/app/src/main/res/layout/fragment_result_tv.xml
+++ b/app/src/main/res/layout/fragment_result_tv.xml
@@ -419,6 +419,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:fadingEdgeLength="30dp"
android:foreground="@drawable/outline_drawable"
android:maxLines="7"
+ android:focusable="true"
android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_bookmark_button"
android:padding="5dp"
From 504258bf152d773e3f09e0152778f7168f5648a8 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Mon, 23 Oct 2023 10:42:17 -0600
Subject: [PATCH 12/44] Show confirm exit dialog on emulator layout (#704)
---
app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index 4e0d93c9..a4d1c7b4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -662,7 +662,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
val isAtHome =
navController?.currentDestination?.matchDestination(R.id.navigation_home) == true
- if (isAtHome && isTrueTvSettings()) {
+ if (isAtHome && isTvSettings()) {
showConfirmExitDialog()
} else {
super.onBackPressed()
From e4ba8520076b84d7b9d219852b2aabc8e92cf034 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Wed, 25 Oct 2023 08:43:29 -0600
Subject: [PATCH 13/44] Use bottom dialogs for synopsis (#709)
* Use bottom dialogs for synopsis
* Add bottomTextDialog
---
.../ui/result/ResultFragmentPhone.kt | 32 ++++++++----------
.../utils/SingleSelectionHelper.kt | 22 +++++++++++++
.../main/res/layout/bottom_text_dialog.xml | 33 +++++++++++++++++++
3 files changed, 68 insertions(+), 19 deletions(-)
create mode 100644 app/src/main/res/layout/bottom_text_dialog.xml
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
index a0d82062..ed9fd270 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
@@ -17,7 +17,6 @@ import android.view.animation.DecelerateInterpolator
import android.widget.AbsListView
import android.widget.ArrayAdapter
import android.widget.Toast
-import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
@@ -66,6 +65,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
+import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogText
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
@@ -681,14 +681,13 @@ open class ResultFragmentPhone : FullScreenPlayer() {
resultPoster.setImage(d.posterImage)
resultPosterBackground.setImage(d.posterBackgroundImage)
resultDescription.setTextHtml(d.plotText)
- resultDescription.setOnClickListener { view ->
- // todo bottom view?
- view.context?.let { ctx ->
- val builder: AlertDialog.Builder =
- AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
- builder.setMessage(d.plotText.asString(ctx).html())
- .setTitle(d.plotHeaderText.asString(ctx))
- .show()
+ resultDescription.setOnClickListener {
+ activity?.let { activity ->
+ activity.showBottomDialogText(
+ d.titleText.asString(activity),
+ d.plotText.asString(activity).html(),
+ {}
+ )
}
}
@@ -879,16 +878,11 @@ open class ResultFragmentPhone : FullScreenPlayer() {
setRecommendations(recommendations, null)
}
observe(viewModel.episodeSynopsis) { description ->
- // TODO bottom dialog
- view.context?.let { ctx ->
- val builder: AlertDialog.Builder =
- AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
- builder.setMessage(description.html())
- .setTitle(R.string.synopsis)
- .setOnDismissListener {
- viewModel.releaseEpisodeSynopsis()
- }
- .show()
+ activity?.let { activity ->
+ activity.showBottomDialogText(
+ activity.getString(R.string.synopsis),
+ description.html()
+ ) { viewModel.releaseEpisodeSynopsis() }
}
}
context?.let { ctx ->
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
index 8285b8ab..5d54ffe5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
@@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.utils
import android.app.Activity
import android.app.Dialog
+import android.text.Spanned
import android.view.LayoutInflater
import android.view.View
import android.widget.AbsListView
@@ -19,6 +20,7 @@ import androidx.core.view.marginRight
import androidx.core.view.marginTop
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.databinding.BottomTextDialogBinding
import com.lagradost.cloudstream3.databinding.BottomSelectionDialogBinding
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
@@ -363,4 +365,24 @@ object SingleSelectionHelper {
dismissCallback
)
}
+
+ fun Activity.showBottomDialogText(
+ title: String,
+ text: Spanned,
+ dismissCallback: () -> Unit
+ ) {
+ val binding = BottomTextDialogBinding.inflate(layoutInflater)
+ val dialog = BottomSheetDialog(this)
+
+ dialog.setContentView(binding.root)
+
+ binding.dialogTitle.text = title
+ binding.dialogText.text = text
+
+ dialog.setOnDismissListener {
+ dismissCallback.invoke()
+ }
+
+ dialog.show()
+ }
}
diff --git a/app/src/main/res/layout/bottom_text_dialog.xml b/app/src/main/res/layout/bottom_text_dialog.xml
new file mode 100644
index 00000000..01b4834d
--- /dev/null
+++ b/app/src/main/res/layout/bottom_text_dialog.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
From 968bd59188df2138ff9b8aae1b8022191b40de5d Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Wed, 25 Oct 2023 18:09:21 -0600
Subject: [PATCH 14/44] Warn of potential duplicates when adding to library
(#691)
---
.../com/lagradost/cloudstream3/MainAPI.kt | 12 +
.../lagradost/cloudstream3/MainActivity.kt | 2 +-
.../syncproviders/providers/SimklApi.kt | 2 +-
.../ui/home/HomeParentItemAdapterPreview.kt | 32 +-
.../ui/result/ResultFragmentPhone.kt | 48 +-
.../ui/result/ResultFragmentTv.kt | 22 +-
.../ui/result/ResultViewModel2.kt | 419 ++++++++++++++----
.../cloudstream3/utils/DataStoreHelper.kt | 85 ++--
app/src/main/res/values/strings.xml | 25 +-
9 files changed, 481 insertions(+), 166 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
index 5b674c4c..bfe86224 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
@@ -1246,6 +1246,18 @@ interface LoadResponse {
return this.syncData[aniListIdPrefix]
}
+ fun LoadResponse.getImdbId(): String? {
+ return normalSafeApiCall {
+ SimklApi.readIdFromString(this.syncData[simklIdPrefix])?.get(SimklApi.Companion.SyncServices.Imdb)
+ }
+ }
+
+ fun LoadResponse.getTMDbId(): String? {
+ return normalSafeApiCall {
+ SimklApi.readIdFromString(this.syncData[simklIdPrefix])?.get(SimklApi.Companion.SyncServices.Tmdb)
+ }
+ }
+
fun LoadResponse.addMalId(id: Int?) {
this.syncData[malIdPrefix] = (id ?: return).toString()
this.addSimklId(SimklApi.Companion.SyncServices.Mal, id.toString())
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index a4d1c7b4..a41028bd 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -1306,7 +1306,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
this@MainActivity.getString(R.string.action_add_to_bookmarks),
showApply = false,
{}) {
- viewModel.updateWatchStatus(WatchType.values()[it])
+ viewModel.updateWatchStatus(WatchType.values()[it], this@MainActivity)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
index cd1df562..bd7979f5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
@@ -203,7 +203,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
}
/** Read the id string to get all other ids */
- private fun readIdFromString(idString: String?): Map {
+ fun readIdFromString(idString: String?): Map {
return tryParseJson(idString) ?: return emptyMap()
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
index b7e52b88..5f194f1f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
@@ -378,21 +378,25 @@ class HomeParentItemAdapterPreview(
showApply = false,
{}) {
val newValue = WatchType.values()[it]
- homePreviewBookmark.setCompoundDrawablesWithIntrinsicBounds(
- null,
- ContextCompat.getDrawable(
- homePreviewBookmark.context,
- newValue.iconRes
- ),
- null,
- null
- )
- homePreviewBookmark.setText(newValue.stringRes)
- ResultViewModel2.updateWatchStatus(
- item,
- newValue
- )
+ ResultViewModel2().updateWatchStatus(
+ newValue,
+ fab.context,
+ item
+ ) { statusChanged: Boolean ->
+ if (!statusChanged) return@updateWatchStatus
+
+ homePreviewBookmark.setCompoundDrawablesWithIntrinsicBounds(
+ null,
+ ContextCompat.getDrawable(
+ homePreviewBookmark.context,
+ newValue.iconRes
+ ),
+ null,
+ null
+ )
+ homePreviewBookmark.setText(newValue.stringRes)
+ }
}
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
index ed9fd270..7bcce764 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
@@ -430,34 +430,36 @@ open class ResultFragmentPhone : FullScreenPlayer() {
}
})
resultSubscribe.setOnClickListener {
- val isSubscribed =
- viewModel.toggleSubscriptionStatus() ?: return@setOnClickListener
+ viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? ->
+ if (newStatus == null) return@toggleSubscriptionStatus
- val message = if (isSubscribed) {
- // Kinda icky to have this here, but it works.
- SubscriptionWorkManager.enqueuePeriodicWork(context)
- R.string.subscription_new
- } else {
- R.string.subscription_deleted
+ val message = if (newStatus) {
+ // Kinda icky to have this here, but it works.
+ SubscriptionWorkManager.enqueuePeriodicWork(context)
+ R.string.subscription_new
+ } else {
+ R.string.subscription_deleted
+ }
+
+ val name = (viewModel.page.value as? Resource.Success)?.value?.title
+ ?: txt(R.string.no_data).asStringNull(context) ?: ""
+ CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
}
-
- val name = (viewModel.page.value as? Resource.Success)?.value?.title
- ?: txt(R.string.no_data).asStringNull(context) ?: ""
- CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
}
resultFavorite.setOnClickListener {
- val isFavorite =
- viewModel.toggleFavoriteStatus() ?: return@setOnClickListener
+ viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
+ if (newStatus == null) return@toggleFavoriteStatus
- val message = if (isFavorite) {
- R.string.favorite_added
- } else {
- R.string.favorite_removed
+ val message = if (newStatus) {
+ R.string.favorite_added
+ } else {
+ R.string.favorite_removed
+ }
+
+ val name = (viewModel.page.value as? Resource.Success)?.value?.title
+ ?: txt(R.string.no_data).asStringNull(context) ?: ""
+ CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
}
-
- val name = (viewModel.page.value as? Resource.Success)?.value?.title
- ?: txt(R.string.no_data).asStringNull(context) ?: ""
- CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
}
mediaRouteButton.apply {
val chromecastSupport = api?.hasChromecastSupport == true
@@ -960,7 +962,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
fab.context.getString(R.string.action_add_to_bookmarks),
showApply = false,
{}) {
- viewModel.updateWatchStatus(WatchType.values()[it])
+ viewModel.updateWatchStatus(WatchType.values()[it], context)
}
}
}
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 feb8ca04..c13854e0 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
@@ -531,7 +531,7 @@ class ResultFragmentTv : Fragment() {
view.context.getString(R.string.action_add_to_bookmarks),
showApply = false,
{}) {
- viewModel.updateWatchStatus(WatchType.values()[it])
+ viewModel.updateWatchStatus(WatchType.values()[it], context)
}
}
}
@@ -557,17 +557,19 @@ class ResultFragmentTv : Fragment() {
setIconResource(drawable)
setText(text)
setOnClickListener {
- val isFavorite = viewModel.toggleFavoriteStatus() ?: return@setOnClickListener
+ viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
+ if (newStatus == null) return@toggleFavoriteStatus
- val message = if (isFavorite) {
- R.string.favorite_added
- } else {
- R.string.favorite_removed
+ val message = if (newStatus) {
+ R.string.favorite_added
+ } else {
+ R.string.favorite_removed
+ }
+
+ val name = (viewModel.page.value as? Resource.Success)?.value?.title
+ ?: txt(R.string.no_data).asStringNull(context) ?: ""
+ CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
}
-
- val name = (viewModel.page.value as? Resource.Success)?.value?.title
- ?: txt(R.string.no_data).asStringNull(context) ?: ""
- CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
}
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index e5ed7b92..1631b706 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -7,6 +7,8 @@ import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Toast
+import androidx.annotation.MainThread
+import androidx.appcompat.app.AlertDialog
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import androidx.lifecycle.LiveData
@@ -31,6 +33,7 @@ import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
+import com.lagradost.cloudstream3.syncproviders.providers.SimklApi
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
@@ -45,22 +48,37 @@ import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.AppUtils.isAppInstalled
import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast
+import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
import com.lagradost.cloudstream3.utils.CastHelper.startCast
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
+import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteBookmarkedData
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllBookmarkedData
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub
import com.lagradost.cloudstream3.utils.DataStoreHelper.getFavoritesData
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getLastWatched
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultEpisode
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getSubscribedData
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getVideoWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.DataStoreHelper.removeFavoritesData
+import com.lagradost.cloudstream3.utils.DataStoreHelper.removeSubscribedData
+import com.lagradost.cloudstream3.utils.DataStoreHelper.setBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.setDub
import com.lagradost.cloudstream3.utils.DataStoreHelper.setFavoritesData
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultEpisode
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
+import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
+import com.lagradost.cloudstream3.utils.DataStoreHelper.setSubscribedData
+import com.lagradost.cloudstream3.utils.DataStoreHelper.setVideoWatchState
+import com.lagradost.cloudstream3.utils.DataStoreHelper.updateSubscribedData
import com.lagradost.cloudstream3.utils.UIHelper.navigate
import kotlinx.coroutines.*
import java.io.File
@@ -113,6 +131,18 @@ data class ResultData(
val plotHeaderText: UiText,
)
+data class CheckDuplicateData(
+ val name: String,
+ val year: Int?,
+ val syncData: Map?
+)
+
+enum class LibraryListType {
+ BOOKMARKS,
+ FAVORITES,
+ SUBSCRIPTIONS
+}
+
fun txt(status: DubStatus?): UiText? {
return txt(
when (status) {
@@ -441,33 +471,6 @@ class ResultViewModel2 : ViewModel() {
return this?.firstOrNull { it.season == season }
}
- fun updateWatchStatus(currentResponse: LoadResponse, status: WatchType) {
- val currentId = currentResponse.getId()
-
- val currentWatchType = getResultWatchState(currentId)
-
- DataStoreHelper.setResultWatchState(currentId, status.internalId)
- val current = DataStoreHelper.getBookmarkedData(currentId)
- val currentTime = System.currentTimeMillis()
- DataStoreHelper.setBookmarkedData(
- currentId,
- DataStoreHelper.BookmarkedData(
- currentId,
- current?.bookmarkedTime ?: currentTime,
- currentTime,
- currentResponse.name,
- currentResponse.url,
- currentResponse.apiName,
- currentResponse.type,
- currentResponse.posterUrl,
- currentResponse.year
- )
- )
- if (currentWatchType != status) {
- MainActivity.bookmarksUpdatedEvent(true)
- }
- }
-
private fun filterName(name: String?): String? {
if (name == null) return null
Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let {
@@ -822,9 +825,77 @@ class ResultViewModel2 : ViewModel() {
val selectPopup: LiveData = _selectPopup
- fun updateWatchStatus(status: WatchType) {
- updateWatchStatus(currentResponse ?: return, status)
- _watchStatus.postValue(status)
+ fun updateWatchStatus(
+ status: WatchType,
+ context: Context?,
+ loadResponse: LoadResponse? = null,
+ statusChangedCallback: ((statusChanged: Boolean) -> Unit)? = null
+ ) {
+ val response = loadResponse ?: currentResponse ?: return
+
+ val currentId = response.getId()
+
+ val currentStatus = getResultWatchState(currentId)
+
+ // If the current status is "NONE" and the new status is not "NONE",
+ // fetch the bookmarked data to check for duplicates, otherwise set this
+ // to an empty list, so that we don't show the duplicate warning dialog,
+ // but we still want to update the current bookmark and refresh the data anyway.
+ val bookmarkedData = if (currentStatus == WatchType.NONE && status != WatchType.NONE) {
+ getAllBookmarkedData()
+ } else emptyList()
+
+ checkAndWarnDuplicates(
+ context,
+ LibraryListType.BOOKMARKS,
+ CheckDuplicateData(
+ name = response.name,
+ year = response.year,
+ syncData = response.syncData,
+ ),
+ bookmarkedData
+ ) { shouldContinue: Boolean, duplicateIds: List ->
+ if (!shouldContinue) return@checkAndWarnDuplicates
+
+ if (duplicateIds.isNotEmpty()) {
+ duplicateIds.forEach { duplicateId ->
+ deleteBookmarkedData(duplicateId)
+ }
+ }
+
+ setResultWatchState(currentId, status.internalId)
+
+ // We don't need to store if WatchType.NONE.
+ // The key is removed in setResultWatchState, we don't want to
+ // re-add it again here if it was just removed.
+ if (status != WatchType.NONE) {
+ val current = getBookmarkedData(currentId)
+
+ setBookmarkedData(
+ currentId,
+ DataStoreHelper.BookmarkedData(
+ current?.bookmarkedTime ?: unixTimeMS,
+ currentId,
+ unixTimeMS,
+ response.name,
+ response.url,
+ response.apiName,
+ response.type,
+ response.posterUrl,
+ response.year,
+ response.syncData
+ )
+ )
+ }
+
+ if (currentStatus != status) {
+ MainActivity.bookmarksUpdatedEvent(true)
+ }
+
+ _watchStatus.postValue(status)
+
+ statusChangedCallback?.invoke(true)
+ }
}
private fun startChromecast(
@@ -839,73 +910,255 @@ class ResultViewModel2 : ViewModel() {
}
/**
- * @return true if the new status is Subscribed, false if not. Null if not possible to subscribe.
- **/
- fun toggleSubscriptionStatus(): Boolean? {
- val isSubscribed = _subscribeStatus.value ?: return null
- val response = currentResponse ?: return null
- if (response !is EpisodeResponse) return null
+ * Toggles the subscription status of an item.
+ *
+ * @param context The context to use for operations.
+ * @param statusChangedCallback A callback that is invoked when the subscription status changes.
+ * It provides the new subscription status (true if subscribed, false if unsubscribed, null if action was canceled).
+ */
+ fun toggleSubscriptionStatus(
+ context: Context?,
+ statusChangedCallback: ((newStatus: Boolean?) -> Unit)? = null
+ ) {
+ val isSubscribed = _subscribeStatus.value ?: return
+ val response = currentResponse ?: return
+ if (response !is EpisodeResponse) return
val currentId = response.getId()
if (isSubscribed) {
- DataStoreHelper.removeSubscribedData(currentId)
+ removeSubscribedData(currentId)
+ statusChangedCallback?.invoke(false)
+ _subscribeStatus.postValue(false)
} else {
- val current = DataStoreHelper.getSubscribedData(currentId)
+ checkAndWarnDuplicates(
+ context,
+ LibraryListType.SUBSCRIPTIONS,
+ CheckDuplicateData(
+ name = response.name,
+ year = response.year,
+ syncData = response.syncData,
+ ),
+ getAllSubscriptions(),
+ ) { shouldContinue: Boolean, duplicateIds: List ->
+ if (!shouldContinue) {
+ statusChangedCallback?.invoke(null)
+ return@checkAndWarnDuplicates
+ }
- DataStoreHelper.setSubscribedData(
- currentId,
- DataStoreHelper.SubscribedData(
+ if (duplicateIds.isNotEmpty()) {
+ duplicateIds.forEach { duplicateId ->
+ removeSubscribedData(duplicateId)
+ }
+ }
+
+ val current = getSubscribedData(currentId)
+
+ setSubscribedData(
currentId,
- current?.bookmarkedTime ?: unixTimeMS,
- unixTimeMS,
- response.getLatestEpisodes(),
- response.name,
- response.url,
- response.apiName,
- response.type,
- response.posterUrl,
- response.year
+ DataStoreHelper.SubscribedData(
+ current?.subscribedTime ?: unixTimeMS,
+ response.getLatestEpisodes(),
+ currentId,
+ unixTimeMS,
+ response.name,
+ response.url,
+ response.apiName,
+ response.type,
+ response.posterUrl,
+ response.year,
+ response.syncData
+ )
)
- )
- }
- _subscribeStatus.postValue(!isSubscribed)
- return !isSubscribed
+ _subscribeStatus.postValue(true)
+
+ statusChangedCallback?.invoke(true)
+ }
+ }
}
/**
- * @return true if added to favorites, false if not. Null if not possible to favorite.
- **/
- fun toggleFavoriteStatus(): Boolean? {
- val isFavorite = _favoriteStatus.value ?: return null
- val response = currentResponse ?: return null
+ * Toggles the favorite status of an item.
+ *
+ * @param context The context to use.
+ * @param statusChangedCallback A callback that is invoked when the favorite status changes.
+ * It provides the new favorite status (true if added to favorites, false if removed, null if action was canceled).
+ */
+ fun toggleFavoriteStatus(
+ context: Context?,
+ statusChangedCallback: ((newStatus: Boolean?) -> Unit)? = null
+ ) {
+ val isFavorite = _favoriteStatus.value ?: return
+ val response = currentResponse ?: return
val currentId = response.getId()
if (isFavorite) {
removeFavoritesData(currentId)
+ statusChangedCallback?.invoke(false)
+ _favoriteStatus.postValue(false)
} else {
- val current = getFavoritesData(currentId)
+ checkAndWarnDuplicates(
+ context,
+ LibraryListType.FAVORITES,
+ CheckDuplicateData(
+ name = response.name,
+ year = response.year,
+ syncData = response.syncData,
+ ),
+ getAllFavorites(),
+ ) { shouldContinue: Boolean, duplicateIds: List ->
+ if (!shouldContinue) {
+ statusChangedCallback?.invoke(null)
+ return@checkAndWarnDuplicates
+ }
- setFavoritesData(
- currentId,
- DataStoreHelper.FavoritesData(
+ if (duplicateIds.isNotEmpty()) {
+ duplicateIds.forEach { duplicateId ->
+ removeFavoritesData(duplicateId)
+ }
+ }
+
+ val current = getFavoritesData(currentId)
+
+ setFavoritesData(
currentId,
- current?.favoritesTime ?: unixTimeMS,
- unixTimeMS,
- response.name,
- response.url,
- response.apiName,
- response.type,
- response.posterUrl,
- response.year
+ DataStoreHelper.FavoritesData(
+ current?.favoritesTime ?: unixTimeMS,
+ currentId,
+ unixTimeMS,
+ response.name,
+ response.url,
+ response.apiName,
+ response.type,
+ response.posterUrl,
+ response.year,
+ response.syncData
+ )
)
- )
+
+ _favoriteStatus.postValue(true)
+
+ statusChangedCallback?.invoke(true)
+ }
+ }
+ }
+
+ @MainThread
+ private fun checkAndWarnDuplicates(
+ context: Context?,
+ listType: LibraryListType,
+ checkDuplicateData: CheckDuplicateData,
+ data: List,
+ checkDuplicatesCallback: (shouldContinue: Boolean, duplicateIds: List) -> Unit
+ ) {
+ val whitespaceRegex = "\\s+".toRegex()
+ fun normalizeString(input: String): String {
+ /**
+ * Trim the input string and replace consecutive spaces with a single space.
+ * This covers some edge-cases where the title does not match exactly across providers,
+ * and one provider has the title with an extra whitespace. This is minor enough that
+ * it should still match in this case.
+ */
+ return input.trim().replace(whitespaceRegex, " ")
}
- _favoriteStatus.postValue(!isFavorite)
- return !isFavorite
+ val syncData = checkDuplicateData.syncData
+
+ val imdbId = getImdbIdFromSyncData(syncData)
+ val tmdbId = getTMDbIdFromSyncData(syncData)
+ val malId = syncData?.get(AccountManager.malApi.idPrefix)
+ val aniListId = syncData?.get(AccountManager.aniListApi.idPrefix)
+ val normalizedName = normalizeString(checkDuplicateData.name)
+ val year = checkDuplicateData.year
+
+ val duplicateEntries = data.filter { it: DataStoreHelper.LibrarySearchResponse ->
+ val librarySyncData = it.syncData
+
+ val checks = listOf(
+ { imdbId != null && getImdbIdFromSyncData(librarySyncData) == imdbId },
+ { tmdbId != null && getTMDbIdFromSyncData(librarySyncData) == tmdbId },
+ { malId != null && librarySyncData?.get(AccountManager.malApi.idPrefix) == malId },
+ { aniListId != null && librarySyncData?.get(AccountManager.aniListApi.idPrefix) == aniListId },
+ { normalizedName == normalizeString(it.name) && year == it.year }
+ )
+
+ checks.any { it() }
+ }
+
+ if (duplicateEntries.isEmpty() || context == null) {
+ checkDuplicatesCallback.invoke(true, emptyList())
+ return
+ }
+
+ val replaceMessage = if (duplicateEntries.size > 1) {
+ R.string.duplicate_replace_all
+ } else R.string.duplicate_replace
+
+ val message = if (duplicateEntries.size == 1) {
+ val list = when (listType) {
+ LibraryListType.BOOKMARKS -> getResultWatchState(duplicateEntries[0].id ?: 0).stringRes
+ LibraryListType.FAVORITES -> R.string.favorites_list_name
+ LibraryListType.SUBSCRIPTIONS -> R.string.subscription_list_name
+ }
+
+ context.getString(R.string.duplicate_message_single,
+ "${normalizeString(duplicateEntries[0].name)} (${context.getString(list)}) — ${duplicateEntries[0].apiName}"
+ )
+ } else {
+ val bulletPoints = duplicateEntries.joinToString("\n") {
+ val list = when (listType) {
+ LibraryListType.BOOKMARKS -> getResultWatchState(it.id ?: 0).stringRes
+ LibraryListType.FAVORITES -> R.string.favorites_list_name
+ LibraryListType.SUBSCRIPTIONS -> R.string.subscription_list_name
+ }
+
+ "• ${it.apiName}: ${normalizeString(it.name)} (${context.getString(list)})"
+ }
+
+ context.getString(R.string.duplicate_message_multiple, bulletPoints)
+ }
+
+ val builder: AlertDialog.Builder = AlertDialog.Builder(context)
+
+ val dialogClickListener =
+ DialogInterface.OnClickListener { _, which ->
+ when (which) {
+ DialogInterface.BUTTON_POSITIVE -> {
+ checkDuplicatesCallback.invoke(true, emptyList())
+ }
+ DialogInterface.BUTTON_NEGATIVE -> {
+ checkDuplicatesCallback.invoke(false, emptyList())
+ }
+ DialogInterface.BUTTON_NEUTRAL -> {
+ checkDuplicatesCallback.invoke(true, duplicateEntries.map { it.id })
+ }
+ }
+ }
+
+ builder.setTitle(R.string.duplicate_title)
+ .setMessage(message)
+ .setPositiveButton(R.string.duplicate_add, dialogClickListener)
+ .setNegativeButton(R.string.duplicate_cancel, dialogClickListener)
+ .setNeutralButton(replaceMessage, dialogClickListener)
+ .show().setDefaultFocus()
+ }
+
+ private fun getImdbIdFromSyncData(syncData: Map?): String? {
+ return normalSafeApiCall {
+ SimklApi.readIdFromString(
+ syncData?.get(AccountManager.simklApi.idPrefix)
+ )[SimklApi.Companion.SyncServices.Imdb]
+ }
+ }
+
+ private fun getTMDbIdFromSyncData(syncData: Map?): String? {
+ return normalSafeApiCall {
+ SimklApi.readIdFromString(
+ syncData?.get(AccountManager.simklApi.idPrefix)
+ )[SimklApi.Companion.SyncServices.Tmdb]
+ }
}
private fun startChromecast(
@@ -1259,7 +1512,7 @@ class ResultViewModel2 : ViewModel() {
// Do not add mark as watched on movies
if (!listOf(TvType.Movie, TvType.AnimeMovie).contains(click.data.tvType)) {
val isWatched =
- DataStoreHelper.getVideoWatchState(click.data.id) == VideoWatchState.Watched
+ getVideoWatchState(click.data.id) == VideoWatchState.Watched
val watchedText = if (isWatched) R.string.action_remove_from_watched
else R.string.action_mark_as_watched
@@ -1508,12 +1761,12 @@ class ResultViewModel2 : ViewModel() {
ACTION_MARK_AS_WATCHED -> {
val isWatched =
- DataStoreHelper.getVideoWatchState(click.data.id) == VideoWatchState.Watched
+ getVideoWatchState(click.data.id) == VideoWatchState.Watched
if (isWatched) {
- DataStoreHelper.setVideoWatchState(click.data.id, VideoWatchState.None)
+ setVideoWatchState(click.data.id, VideoWatchState.None)
} else {
- DataStoreHelper.setVideoWatchState(click.data.id, VideoWatchState.Watched)
+ setVideoWatchState(click.data.id, VideoWatchState.Watched)
}
// Kinda dirty to reload all episodes :(
@@ -1722,7 +1975,7 @@ class ResultViewModel2 : ViewModel() {
list.subList(start, end).map {
val posDur = getViewPos(it.id)
val watchState =
- DataStoreHelper.getVideoWatchState(it.id) ?: VideoWatchState.None
+ getVideoWatchState(it.id) ?: VideoWatchState.None
it.copy(
position = posDur?.position ?: 0,
duration = posDur?.duration ?: 0,
@@ -1783,8 +2036,8 @@ class ResultViewModel2 : ViewModel() {
private fun postSubscription(loadResponse: LoadResponse) {
if (loadResponse.isEpisodeBased()) {
val id = loadResponse.getId()
- val data = DataStoreHelper.getSubscribedData(id)
- DataStoreHelper.updateSubscribedData(id, data, loadResponse as? EpisodeResponse)
+ val data = getSubscribedData(id)
+ updateSubscribedData(id, data, loadResponse as? EpisodeResponse)
val isSubscribed = data != null
_subscribeStatus.postValue(isSubscribed)
}
@@ -2162,13 +2415,13 @@ class ResultViewModel2 : ViewModel() {
postResume()
}
- fun postResume() {
+ private fun postResume() {
_resumeWatching.postValue(resume())
}
private fun resume(): ResumeWatchingStatus? {
val correctId = currentId ?: return null
- val resume = DataStoreHelper.getLastWatched(correctId)
+ val resume = getLastWatched(correctId)
val resumeParentId = resume?.parentId
if (resumeParentId != correctId) return null // is null or smth went wrong with getLastWatched
val resumeId = resume.episodeId ?: return null// invalid episode id
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 4b4157d6..78f801b6 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
@@ -352,20 +352,35 @@ object DataStoreHelper {
/**
* Used to display notifications on new episodes and posters in library.
**/
- data class SubscribedData(
+ abstract class LibrarySearchResponse(
@JsonProperty("id") override var id: Int?,
- @JsonProperty("subscribedTime") val bookmarkedTime: Long,
- @JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
- @JsonProperty("lastSeenEpisodeCount") val lastSeenEpisodeCount: Map,
+ @JsonProperty("latestUpdatedTime") open val latestUpdatedTime: Long,
@JsonProperty("name") override val name: String,
@JsonProperty("url") override val url: String,
@JsonProperty("apiName") override val apiName: String,
- @JsonProperty("type") override var type: TvType? = null,
+ @JsonProperty("type") override var type: TvType?,
@JsonProperty("posterUrl") override var posterUrl: String?,
- @JsonProperty("year") val year: Int?,
- @JsonProperty("quality") override var quality: SearchQuality? = null,
- @JsonProperty("posterHeaders") override var posterHeaders: Map? = null,
- ) : SearchResponse {
+ @JsonProperty("year") open val year: Int?,
+ @JsonProperty("syncData") open val syncData: Map?,
+ @JsonProperty("quality") override var quality: SearchQuality?,
+ @JsonProperty("posterHeaders") override var posterHeaders: Map?
+ ) : SearchResponse
+
+ data class SubscribedData(
+ @JsonProperty("subscribedTime") val subscribedTime: Long,
+ @JsonProperty("lastSeenEpisodeCount") val lastSeenEpisodeCount: Map,
+ override var id: Int?,
+ override val latestUpdatedTime: Long,
+ override val name: String,
+ override val url: String,
+ override val apiName: String,
+ override var type: TvType?,
+ override var posterUrl: String?,
+ override val year: Int?,
+ override val syncData: Map? = null,
+ override var quality: SearchQuality? = null,
+ override var posterHeaders: Map? = null
+ ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders) {
fun toLibraryItem(): SyncAPI.LibraryItem? {
return SyncAPI.LibraryItem(
name,
@@ -381,18 +396,19 @@ object DataStoreHelper {
}
data class BookmarkedData(
- @JsonProperty("id") override var id: Int?,
@JsonProperty("bookmarkedTime") val bookmarkedTime: Long,
- @JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
- @JsonProperty("name") override val name: String,
- @JsonProperty("url") override val url: String,
- @JsonProperty("apiName") override val apiName: String,
- @JsonProperty("type") override var type: TvType? = null,
- @JsonProperty("posterUrl") override var posterUrl: String?,
- @JsonProperty("year") val year: Int?,
- @JsonProperty("quality") override var quality: SearchQuality? = null,
- @JsonProperty("posterHeaders") override var posterHeaders: Map? = null,
- ) : SearchResponse {
+ override var id: Int?,
+ override val latestUpdatedTime: Long,
+ override val name: String,
+ override val url: String,
+ override val apiName: String,
+ override var type: TvType?,
+ override var posterUrl: String?,
+ override val year: Int?,
+ override val syncData: Map? = null,
+ override var quality: SearchQuality? = null,
+ override var posterHeaders: Map? = null
+ ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders) {
fun toLibraryItem(id: String): SyncAPI.LibraryItem {
return SyncAPI.LibraryItem(
name,
@@ -408,18 +424,19 @@ object DataStoreHelper {
}
data class FavoritesData(
- @JsonProperty("id") override var id: Int?,
@JsonProperty("favoritesTime") val favoritesTime: Long,
- @JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
- @JsonProperty("name") override val name: String,
- @JsonProperty("url") override val url: String,
- @JsonProperty("apiName") override val apiName: String,
- @JsonProperty("type") override var type: TvType? = null,
- @JsonProperty("posterUrl") override var posterUrl: String?,
- @JsonProperty("year") val year: Int?,
- @JsonProperty("quality") override var quality: SearchQuality? = null,
- @JsonProperty("posterHeaders") override var posterHeaders: Map? = null,
- ) : SearchResponse {
+ override var id: Int?,
+ override val latestUpdatedTime: Long,
+ override val name: String,
+ override val url: String,
+ override val apiName: String,
+ override var type: TvType?,
+ override var posterUrl: String?,
+ override val year: Int?,
+ override val syncData: Map? = null,
+ override var quality: SearchQuality? = null,
+ override var posterHeaders: Map? = null
+ ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders) {
fun toLibraryItem(): SyncAPI.LibraryItem? {
return SyncAPI.LibraryItem(
name,
@@ -572,6 +589,12 @@ object DataStoreHelper {
return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
}
+ fun getAllBookmarkedData(): List {
+ return getKeys("$currentAccount/$RESULT_WATCH_STATE_DATA")?.mapNotNull {
+ getKey(it)
+ } ?: emptyList()
+ }
+
fun getAllSubscriptions(): List {
return getKeys("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA")?.mapNotNull {
getKey(it)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 23b1a7ed..e243fe79 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -686,13 +686,32 @@
Qualities
Profile background
UI was unable to be created correctly, this is a MAJOR BUG and should be reported immediately %s
-
-
- tv_no_focus_tag
You have already voted
+
Favorites
%s added to favorites
%s removed from favorites
Add to favorites
Remove from favorites
+
+ Potential Duplicate Found
+ Add
+ Replace
+ Replace All
+ @string/sort_cancel
+
+ It appears that a potentially duplicate item already exists in your library: \'%1$s.\'
+
+ \n\nWould you like to add this item anyway, replace the existing one, or cancel the action?
+
+
+ Potential duplicate items have been found in your library:
+
+ \n\n%1$s
+
+ \n\nWould you like to add this item anyway, replace the existing ones, or cancel the action?
+
+
+
+ tv_no_focus_tag
From ef36bccc905b584185f9f12fd9cff7a97d22760e Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Thu, 26 Oct 2023 02:10:08 +0200
Subject: [PATCH 15/44] Delete old MultiAnimeProvider.kt (#717)
---
.../metaproviders/MultiAnimeProvider.kt | 73 -------------------
1 file changed, 73 deletions(-)
delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt
diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt
deleted file mode 100644
index 8cfe1e9a..00000000
--- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.lagradost.cloudstream3.metaproviders
-
-import com.lagradost.cloudstream3.*
-import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
-import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
-import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
-import com.lagradost.cloudstream3.syncproviders.SyncAPI
-import com.lagradost.cloudstream3.syncproviders.providers.AniListApi
-import com.lagradost.cloudstream3.syncproviders.providers.MALApi
-import com.lagradost.cloudstream3.utils.SyncUtil
-
-// wont be implemented
-class MultiAnimeProvider : MainAPI() {
- override var name = "MultiAnime"
- override var lang = "en"
- override val usesWebView = true
- override val supportedTypes = setOf(TvType.Anime)
- private val syncApi: SyncAPI = aniListApi
-
- private val syncUtilType by lazy {
- when (syncApi) {
- is AniListApi -> "anilist"
- is MALApi -> "myanimelist"
- else -> throw ErrorLoadingException("Invalid Api")
- }
- }
-
- private val validApis
- get() =
- synchronized(APIHolder.apis) {
- APIHolder.apis.filter {
- it.lang == this.lang && it::class.java != this::class.java && it.supportedTypes.contains(
- TvType.Anime
- )
- }
- }
-
-
- private fun filterName(name: String): String {
- return Regex("""[^a-zA-Z0-9-]""").replace(name, "")
- }
-
- override suspend fun search(query: String): List? {
- return syncApi.search(query)?.map {
- AnimeSearchResponse(it.name, it.url, this.name, TvType.Anime, it.posterUrl)
- }
- }
-
- override suspend fun load(url: String): LoadResponse? {
- return syncApi.getResult(url)?.let { res ->
- val data = SyncUtil.getUrlsFromId(res.id, syncUtilType).amap { url ->
- validApis.firstOrNull { api -> url.startsWith(api.mainUrl) }?.load(url)
- }.filterNotNull()
-
- val type =
- if (data.any { it.type == TvType.AnimeMovie }) TvType.AnimeMovie else TvType.Anime
-
- newAnimeLoadResponse(
- res.title ?: throw ErrorLoadingException("No Title found"),
- url,
- type
- ) {
- posterUrl = res.posterUrl
- plot = res.synopsis
- tags = res.genres
- rating = res.publicScore
- addTrailer(res.trailers)
- addAniListId(res.id.toIntOrNull())
- recommendations = res.recommendations
- }
- }
- }
-}
\ No newline at end of file
From 4b93524e5765404dce3ca5c4c2f2cda93c654b24 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Wed, 25 Oct 2023 18:15:50 -0600
Subject: [PATCH 16/44] Convert the rest of SingleSelectionHelper to
ViewBinding (#724)
---
.../utils/SingleSelectionHelper.kt | 51 ++++++++++---------
1 file changed, 28 insertions(+), 23 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
index 5d54ffe5..f34e7238 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
@@ -7,10 +7,7 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.AbsListView
import android.widget.ArrayAdapter
-import android.widget.EditText
-import android.widget.ImageView
import android.widget.LinearLayout
-import android.widget.ListView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone
@@ -20,8 +17,10 @@ import androidx.core.view.marginRight
import androidx.core.view.marginTop
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.R
-import com.lagradost.cloudstream3.databinding.BottomTextDialogBinding
+import com.lagradost.cloudstream3.databinding.BottomInputDialogBinding
import com.lagradost.cloudstream3.databinding.BottomSelectionDialogBinding
+import com.lagradost.cloudstream3.databinding.BottomTextDialogBinding
+import com.lagradost.cloudstream3.databinding.OptionsPopupTvBinding
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
@@ -56,14 +55,14 @@ object SingleSelectionHelper {
if (this == null) return
if (isTvSettings()) {
- val builder =
- AlertDialog.Builder(this, R.style.AlertDialogCustom)
- .setView(R.layout.options_popup_tv)
+ val binding = OptionsPopupTvBinding.inflate(layoutInflater)
+ val dialog = AlertDialog.Builder(this, R.style.AlertDialogCustom)
+ .setView(binding.root)
+ .create()
- val dialog = builder.create()
dialog.show()
- dialog.findViewById(R.id.listview1)?.let { listView ->
+ binding.listview1.let { listView ->
listView.choiceMode = AbsListView.CHOICE_MODE_SINGLE
listView.adapter =
ArrayAdapter(this, R.layout.sort_bottom_single_choice_color).apply {
@@ -76,7 +75,7 @@ object SingleSelectionHelper {
}
}
- dialog.findViewById(R.id.imageView)?.apply {
+ binding.imageView.apply {
isGone = poster.isNullOrEmpty()
setImage(poster)
}
@@ -107,12 +106,12 @@ object SingleSelectionHelper {
if (this == null) return
val realShowApply = showApply || isMultiSelect
- val listView = binding.listview1//.findViewById(R.id.listview1)!!
- val textView = binding.text1//.findViewById(R.id.text1)!!
- val applyButton = binding.applyBtt//.findViewById(R.id.apply_btt)
- val cancelButton = binding.cancelBtt//findViewById(R.id.cancel_btt)
+ val listView = binding.listview1
+ val textView = binding.text1
+ val applyButton = binding.applyBtt
+ val cancelButton = binding.cancelBtt
val applyHolder =
- binding.applyBttHolder//.findViewById(R.id.apply_btt_holder)
+ binding.applyBttHolder
applyHolder.isVisible = realShowApply
if (!realShowApply) {
@@ -175,8 +174,8 @@ object SingleSelectionHelper {
}
}
-
private fun Activity?.showInputDialog(
+ binding: BottomInputDialogBinding,
dialog: Dialog,
value: String,
name: String,
@@ -186,11 +185,11 @@ object SingleSelectionHelper {
) {
if (this == null) return
- val inputView = dialog.findViewById(R.id.nginx_text_input)!!
- val textView = dialog.findViewById(R.id.text1)!!
- val applyButton = dialog.findViewById(R.id.apply_btt)!!
- val cancelButton = dialog.findViewById(R.id.cancel_btt)!!
- val applyHolder = dialog.findViewById(R.id.apply_btt_holder)!!
+ val inputView = binding.nginxTextInput
+ val textView = binding.text1
+ val applyButton = binding.applyBtt
+ val cancelButton = binding.cancelBtt
+ val applyHolder = binding.applyBttHolder
applyHolder.isVisible = true
textView.text = name
@@ -352,11 +351,17 @@ object SingleSelectionHelper {
dismissCallback: () -> Unit,
callback: (String) -> Unit,
) {
- val builder = BottomSheetDialog(this) // probably the stuff at the bottom
- builder.setContentView(R.layout.bottom_input_dialog) // input layout
+ val builder = BottomSheetDialog(this)
+
+ val binding: BottomInputDialogBinding = BottomInputDialogBinding.inflate(
+ LayoutInflater.from(this)
+ )
+
+ builder.setContentView(binding.root)
builder.show()
showInputDialog(
+ binding,
builder,
value,
name,
From 51a877f405ced441914f860770e7366f5c1f16d7 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Sat, 28 Oct 2023 16:29:16 -0600
Subject: [PATCH 17/44] Fix some strings (#730)
---
.../cloudstream3/ui/library/LibraryFragment.kt | 2 +-
app/src/main/res/values-am/strings.xml | 2 --
app/src/main/res/values-ar/strings.xml | 6 ++----
app/src/main/res/values-ars/strings.xml | 3 +--
app/src/main/res/values-bg/strings.xml | 5 ++---
app/src/main/res/values-bn/strings.xml | 2 --
app/src/main/res/values-bp/strings.xml | 6 ++----
app/src/main/res/values-cs/strings.xml | 6 ++----
app/src/main/res/values-de/strings.xml | 6 ++----
app/src/main/res/values-el/strings.xml | 6 ++----
app/src/main/res/values-eo/strings.xml | 1 -
app/src/main/res/values-es/strings.xml | 6 ++----
app/src/main/res/values-fr/strings.xml | 6 ++----
app/src/main/res/values-gl/strings.xml | 2 --
app/src/main/res/values-hi/strings.xml | 2 +-
app/src/main/res/values-hr/strings.xml | 6 ++----
app/src/main/res/values-hu/strings.xml | 5 ++---
app/src/main/res/values-in/strings.xml | 6 ++----
app/src/main/res/values-it/strings.xml | 6 ++----
app/src/main/res/values-iw/strings.xml | 6 ++----
app/src/main/res/values-ja/strings.xml | 5 ++---
app/src/main/res/values-kn/strings.xml | 2 --
app/src/main/res/values-ko/strings.xml | 5 ++---
app/src/main/res/values-lt/strings.xml | 3 +--
app/src/main/res/values-lv/strings.xml | 5 ++---
app/src/main/res/values-mk/strings.xml | 5 ++---
app/src/main/res/values-ml/strings.xml | 5 ++---
app/src/main/res/values-ms/strings.xml | 1 -
app/src/main/res/values-my/strings.xml | 6 ++----
app/src/main/res/values-nl/strings.xml | 6 ++----
app/src/main/res/values-nn/strings.xml | 5 ++---
app/src/main/res/values-no/strings.xml | 5 ++---
app/src/main/res/values-or/strings.xml | 3 +--
app/src/main/res/values-pl/strings.xml | 6 ++----
app/src/main/res/values-pt/strings.xml | 6 ++----
app/src/main/res/values-qt/strings.xml | 5 ++---
app/src/main/res/values-ro/strings.xml | 6 ++----
app/src/main/res/values-ru/strings.xml | 6 ++----
app/src/main/res/values-sk/strings.xml | 5 ++---
app/src/main/res/values-so/strings.xml | 5 ++---
app/src/main/res/values-sv/strings.xml | 5 ++---
app/src/main/res/values-ta/strings.xml | 2 --
app/src/main/res/values-tl/strings.xml | 5 ++---
app/src/main/res/values-tr/strings.xml | 6 ++----
app/src/main/res/values-uk/strings.xml | 8 +++-----
app/src/main/res/values-ur/strings.xml | 6 ++----
app/src/main/res/values-vi/strings.xml | 6 ++----
app/src/main/res/values-zh-rTW/strings.xml | 6 ++----
app/src/main/res/values-zh/strings.xml | 6 ++----
app/src/main/res/values/strings.xml | 13 +++++++------
50 files changed, 87 insertions(+), 161 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
index c3986dca..7cc57f5d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
@@ -62,7 +62,7 @@ const val LIBRARY_FOLDER = "library_folder"
enum class LibraryOpenerType(@StringRes val stringRes: Int) {
- Default(R.string.default_subtitles), // TODO FIX AFTER MERGE
+ Default(R.string.action_default),
Provider(R.string.none),
Browser(R.string.browser),
Search(R.string.search),
diff --git a/app/src/main/res/values-am/strings.xml b/app/src/main/res/values-am/strings.xml
index 32f4dcd7..3ea31b54 100644
--- a/app/src/main/res/values-am/strings.xml
+++ b/app/src/main/res/values-am/strings.xml
@@ -74,12 +74,10 @@
ማውረድ ቀጥል
የጽሑፍ ቀለም
የተጠናቀቀ
- ምንም
የፊልም ማስታወቂያ አጫውት
የቀጥታ ስርጭት አጫውት
ፋይል አጫውት
እንደገና በማየት ላይ
- ሰርዝ
ወደ ኋላ መመለሻ
መረጃ
ያስቀምጡ
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 8805ec5d..f73081f7 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -36,7 +36,6 @@
مكتمل
مهمل
أخطط لمشاهدته
- لا شيء
إعادة المشاهدة
مشاهدة الفيلم
تشغيل بث حي
@@ -74,7 +73,6 @@
ازالة
إعداد حالة المشاهدة
تطبيق
- إلغاء
نسخ
إغلاق
مسح
@@ -180,6 +178,7 @@
لم يتم العثور على أي حلقات
حذف الملف
حذف
+ إلغاء
إيقاف مؤقت
إستئناف
-٣٠
@@ -198,7 +197,7 @@
القصة
في قائمة الانتظار
الترجمة ليست موجودة
- الإفتراضي
+ الإفتراضي
فارغ
مستخدم
التطبيق
@@ -581,7 +580,6 @@
تعذر إنشاء واجهة المستخدم بشكل صحيح ، وهذا خطأ كبير ويجب الإبلاغ عنه على الفور %s
حدد الوضع لتصفية تنزيل المكونات الإضافية
تعطيل
- @string/default_subtitles
لا توجد اضافة في المستودع
المستودع لم يتم العثور عليه، تحقق من العنوان اوجرب شبكة افتراضية خاصة(vpn)
لقد صوتت بالفعل
diff --git a/app/src/main/res/values-ars/strings.xml b/app/src/main/res/values-ars/strings.xml
index a1042b7e..11dbddc0 100644
--- a/app/src/main/res/values-ars/strings.xml
+++ b/app/src/main/res/values-ars/strings.xml
@@ -34,7 +34,6 @@
الأنواع
توقف التنزيل
خطط للمشاهدة
- لا يوجد
إعادة المشاهدة
!تم العثور على تحديث جديد
\n%s->%s
@@ -125,6 +124,7 @@
موسم
تم نسخ الرابط إلى الحافظة
مسح
+ الغي
وقف
جارٍ تنزيل تحديث التطبيق…
إعادة التعيين إلى القيمة العادية
@@ -208,7 +208,6 @@
استئناف تحميل
معلومات
وقفة التحميل
- الغي
احفظ
إعدادات الترجمة
لون الخط
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index e9ae65a1..e8c5d0f5 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -41,7 +41,6 @@
Завършено
Изпуснат
План за гледане
- Нито един
Повторно гледане
Пускане на филм
Възпроизвеждане на живо
@@ -78,7 +77,6 @@
Премахване
Задайте статус на гледане
Приложи
- Отказ
Копирай
Затвори
Изчисти
@@ -187,6 +185,7 @@
Няма намерени епизоди
Изтрий файла
Изтрий
+ Отказ
Пауза
Продължи
-30
@@ -205,7 +204,7 @@
Синопсис
На опашката
Без субтитри
- По подразбиране
+ По подразбиране
Безплатно
Използвано
Приложения
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index c55c8943..b0359c1c 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -43,7 +43,6 @@
শেষ
বাদ
দেখার ইচ্ছায়
- কোন কিছুই না
পুনরায় দেখা হচ্ছে
টরেন্ট স্ট্রিম করুন
উৎসসমূহ
@@ -72,7 +71,6 @@
বাদ দিন
বুকমার্ক করুন
প্রয়োগ করুন
- বাদ দিন
কপি করুন
বন্ধ করুন
মুছুন
diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml
index 7116a167..1c26c236 100644
--- a/app/src/main/res/values-bp/strings.xml
+++ b/app/src/main/res/values-bp/strings.xml
@@ -44,7 +44,6 @@
Completado
Deixado
Planejando assistir
- Nenhum
Reassistindo
Assistir Filme
Transmitir Torrent
@@ -81,7 +80,6 @@
Remover
Selecionar marcador
Aplicar
- Cancelar
Copiar
Fechar
Limpar
@@ -185,6 +183,7 @@
Nenhum Episódio encontrado
Apagar Arquivo
Deletar
+ Cancelar
Pausar
Retomar
-30
@@ -203,7 +202,7 @@
Sinopse
Na fila
Sem Legendas
- Padrão
+ Padrão
Livre
Usado
App
@@ -569,7 +568,6 @@
Wi-Fi
Lista de videos da web
A interface de usuário não foi gerada corretamente. Isto se trata de um bug importante e deve ser reportado imediatamente %s
- Legendas padrão da conta
Características da interface de usuário
Provedor de teste
Layout
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 46bd860d..8bdc1a27 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -40,7 +40,6 @@
Dokončeno
Zahozeno
Plánuji sledovat
- Žádné
Opětovné sledování
Přehrát film
Streamovat torrent
@@ -76,7 +75,6 @@
Odebrat
Nastavit stav sledování
Použít
- Zrušit
Kopírovat
Zavřít
Vymazat
@@ -176,6 +174,7 @@
Nenalezeny žádné epizody
Smazat soubor
Smazat
+ Zrušit
Pozastavit
Pokračovat
-30
@@ -194,7 +193,7 @@
Synopse
ve frontě
Žádné titulky
- Výchozí
+ Výchozí
Volné
Použito
Aplikace
@@ -575,6 +574,5 @@
Výběr režimu pro filtrování stahování doplňků
V repozitáři nebyly nalezeny žádné doplňky
Repozitář nenalezen, zkontrolujte adresu URL a zkuste použít VPN
- @string/default_subtitles
Již jste hlasovali
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 85d9ab71..bc3119a3 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -52,7 +52,6 @@
Abgeschlossen
Abgebrochen
Geplant
- Nichts
Erneut schauen
Film abspielen
Livestream abspielen
@@ -89,7 +88,6 @@
Entfernen
Status setzen
Anwenden
- Abbrechen
Kopieren
Schließen
Leeren
@@ -192,6 +190,7 @@
E
Keine Episoden gefunden
Löschen
+ Abbrechen
Pause
Fortsetzen
-30
@@ -210,7 +209,7 @@
Zusammenfassung
In Warteschlange eingereiht
Keine Untertitel
- Standard
+ Standard
Frei
Belegt
App
@@ -552,5 +551,4 @@
Repository nicht gefunden, überprüf die URL und versuch ein VPN
Die Benutzeroberfläche konnte nicht korrekt erstellt werden. Dies ist ein SCHWERWIEGENDER FEHLER und sollte sofort gemeldet werden. %s
Deaktivieren
- @string/default_subtitles
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index e88e4fc0..72a7c4c9 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -23,7 +23,6 @@
Ολοκληρώθηκε
Διακόπηκε
Για παρακολούθηση
- Τίποτα
Αναπαραγωγή ταινίας
Μετάδοση Torrent
Πηγές
@@ -58,7 +57,6 @@
Αφαίρεση
Αναπαραγωγή επεισοδίου
Υποβολή
- Ακύρωση
Ταχύτητα αναπαραγωγής
Ρυθμίσεις υπότιτλων
Χρώμα κειμένου
@@ -155,6 +153,7 @@
Δεν βρέθηκαν επεισόδια
Διαγραφή αρχείου
Διαγραφή
+ Ακύρωση
Παύση
Συνέχιση
Αυτό θα διαγράψει μόνιμα το %s
@@ -169,7 +168,7 @@
Περίληψη
προστέθηκε στην ουρά
Δεν υπάρχουν διαθέσιμοι υπότιτλοι
- Προεπιλεγμένοι υπότιτλοι
+ Προεπιλεγμένοι υπότιτλοι
Ελεύθερος
Σε χρήση
Εφαρμογή
@@ -547,6 +546,5 @@
Το UI δεν ήταν σε θέση να δημιουργηθεί σωστά, είναι ένα σφάλμα και θα πρέπει να αναφερθεί αμέσως %s
Επιλέξτε κατάσταση για φιλτράρισμα επεκτάσεων για λήψη
Απενεργοποιημένο
- @string/default_subtitles
Τέλος
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index 49f025d0..6dcf12c3 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -22,7 +22,6 @@
Priskribo
Versio
Stato
- Nuligi
Forviŝi
Jes
Ne
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 51063b4d..69086840 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -45,7 +45,7 @@
Cargar desde Internet
Reproducir automáticamente episodio siguiente
Configuración de subtítulos de Chromecast
- Predeterminado
+ Predeterminado
Contorno
Sin Subtítulos
Elevado
@@ -138,7 +138,6 @@
Completado
Descartado
Planeando ver
- Ninguno
Volviendo a mirar
Reproducir película
Reproducir Trailer
@@ -167,7 +166,6 @@
Marcadores
Remover
Seleccionar estado de visualización
- Cancelar
Copiar
Cerrar
Limpiar
@@ -256,6 +254,7 @@
T
Borrar Archivo
Borrar
+ Cancelar
Error inesperado del reproductor
Episodio en Chromecast
Reproducir en la aplicación
@@ -549,7 +548,6 @@
La interfaz de usuario no se ha podido crear correctamente, se trata de un GRAN BUG y debe ser reportado inmediatamente %s
Seleccionar modo para filtrar los plugins descargados
Deshabilitar
- @string/default_subtitles
No se encontraron complementos en el repositorio
Repositorio no encontrado, comprueba la URL y prueba la VPN
Ya has votado
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 53442e84..2530a068 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -22,7 +22,6 @@
Terminé
Abandonné
À regarder
- Aucun
Lire
Streamer le Torrent
Sources
@@ -60,7 +59,6 @@
Marque-pages
Supprimer
Appliquer
- Annuler
Vitesse de lecture
Aperçu de l\'arrière-plan
Donner une benene aux devs
@@ -80,6 +78,7 @@
E
Supprimer le Fichier
Supprimer
+ Annuler
Pause
Reprendre
Cela va supprimer définitivement %s
@@ -94,7 +93,7 @@
Synopsis
Liste d\'attente
Pas de sous-titres
- Défault
+ Défault
Libre
Utilisé
Application
@@ -553,5 +552,4 @@
L\'interface utilisateur n\'a pas pu être créée correctement. Il s\'agit d\'un bogue majeur qui doit être signalé immédiatement %s
Sélectionnez le mode pour filtrer le téléchargement des plugins
Fond de profil
- @string/default_subtitles
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index ad7916be..92754082 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -29,13 +29,11 @@
Completado
Descartado
Planeando ver
- Ningún
Remirando
Marcadores
Borrar
Seleccionar estado de visualización
Aplicar
- Cancelar
Copiar
Cerrar
Limpar
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index 31e0e28c..5053544d 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -49,7 +49,6 @@
बुकमार्क्स
हटाएँ
लागू करें
- रद्द करें
प्लेयर स्पीड
प्रोवाइडरों का उपयोग कर खोजें
प्रकार का उपयोग करके खोजें
@@ -91,6 +90,7 @@
क्षमा करें, एप्प क्रैश हो गया है । निर्माताओं को एक अनाम बग रिपोर्ट भेजी जाएगी
फ़ाइल डिलीट करें
डिलीट
+ रद्द करें
रोकें
फिर से चलाएं
इससे %s स्थायी रूप से हट जाएगा
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 84915a38..8da04be6 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -54,7 +54,6 @@
Dovršeno
Ispušteno
Planiram pogledati
- Ništa
Ponovno gledam
Pokreni Film
Pokreni LiveStream
@@ -92,7 +91,6 @@
Ukloni
Postavi status gledanja
Primijeni
- Poništi
Kopiraj
Zatvori
Očisti
@@ -200,6 +198,7 @@
Nisu pronađene epizode
Izbriši datoteku
Izbriši
+ Poništi
Pauziraj
Nastavi
-30
@@ -218,7 +217,7 @@
Sinopsis
u redu čekanja
Bez titlova
- Zadano
+ Zadano
Slobodno
Iskorišteno
Aplikacija
@@ -567,7 +566,6 @@
Nije bilo moguće ispravno izraditi korisničko sučelje. Ovo je ZNAČAJNA GREŠKA i treba se odmah prijaviti %s
Odaberi modus za filtriranje preuzimanja dodataka
Onemogući
- @string/default_subtitles
U repozitoriju nisu pronađeni dodaci
Repozitorij nije pronađen, provjerite URL i pokušajte koristiti VPN
Ovdje možete promijeniti način na koji su izvori poredani. Ako video ima viši prioritet, pojavit će se više u odabiru izvora. Zbroj prioriteta izvora i prioriteta kvalitete je video prioritet.
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 677beaf8..4cd322a9 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -20,6 +20,7 @@
Letöltés
Keresés
Törlés
+ Mégse
Szüneteltetés
sorba állítva
Igazítás
@@ -61,7 +62,6 @@
Nézés
Befejezve
Később megnézés
- Nincs
Újranézés
Film lejátszása
Előzetes lejátszása
@@ -90,7 +90,6 @@
Eltávolítás
Megtekintés állapotának beállítása
Alkalmazás
- Mégse
Másolás
Bezárás
Törlés
@@ -162,7 +161,7 @@
Értékelés
Rajzfilmek
Élőadások
- Alapértelmezett
+ Alapértelmezett
Filmek
TV sorozat
Anime
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index d514bcc4..a6c5813d 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -39,7 +39,6 @@
Selesai
Dihentikan
Rencana untuk Menonton
- Tidak Ada
Menonton Ulang
Putar Movie
Streaming Torrent
@@ -75,7 +74,6 @@
Hapus
Atur status tontonan
Terapkan
- Batalkan
Salin
Tutup
Bersihkan
@@ -174,6 +172,7 @@
Episode Tidak Ditemukan
Hapus File
Hapus
+ Batalkan
Jeda
Lanjutkan
-30
@@ -192,7 +191,7 @@
Sinopsis
antri
Tidak Ada Subtitle
- Default
+ Default
Tersedia
Terpakai
Aplikasi
@@ -575,5 +574,4 @@
Tidak ada plugin yang ditemukan di repositori
Repositori tidak ditemukan, periksa URL dan coba VPN
Kamu sudah voting
- @string/default_subtitles
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 0c34e89a..933ac77f 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -44,7 +44,6 @@
Completato
Abbandonato
Da guardare
- Nessuno
Riguardando
Riproduci film
Riproduci Livestream
@@ -81,7 +80,6 @@
Rimuovi
Imposta stato riproduzione
Applica
- Cancella
Copia
Chiudi
Cancella
@@ -190,6 +188,7 @@
Nessun episodio trovato
Elimina file
Elimina
+ Cancella
Pausa
Riprendi
-30
@@ -208,7 +207,7 @@
Sinossi
In coda
Nessun sottotiolo
- Default
+ Default
Libero
Usato
App
@@ -572,7 +571,6 @@
Repository non trovato, controlla l\'URL e prova la VPN
Non è stato possibile creare correttamente l\'interfaccia utente, questo è un GRANDE BUG e dovrebbe essere segnalato immediatamente %s
Seleziona la modalità per filtrare il download dei plugin
- @string/default_subtitles
Disabilita
Hai già votato
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index d6d5b7f6..55666df5 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -66,7 +66,6 @@
הסר
הגדר מצב צפייה
ליישם
- בטל
העתק
לסגור
נקה
@@ -78,7 +77,6 @@
צופה
כתוביות
בהמתנה
- ללא
להוריד
מדובב
יותר מידע
@@ -146,6 +144,7 @@
לא נמצאו פרקים
מחק קובץ
מחק
+ בטל
השהה
המשך
-30
@@ -159,7 +158,7 @@
דירוג
שנה
ללא כתוביות
- ברירת מחדל
+ ברירת מחדל
חינם
משומש
סדרת טלוויזיה
@@ -519,7 +518,6 @@
עריכה
Wi-Fi
רקע הפרופיל
- @string/default_subtitles
רשומה
עזרה
התחלה
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 7f10aad4..16341b60 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -37,7 +37,6 @@
アジアドラマ
ライブ配信
NSFW
- キャンセル
アニメ
ロック
ソース
@@ -71,7 +70,6 @@
ソース
履歴
ポスター
- なし
コピー
閉じる
保存
@@ -135,6 +133,7 @@
一時停止
再生エピソード
削除
+ キャンセル
開始
状態
年
@@ -155,7 +154,7 @@
バージョン
視聴率 %s
視聴率
- デフォルト
+ デフォルト
ダウンロード失敗
ダウンロード開始
ダウンロード完了
diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml
index 399aafb1..07ff89e4 100644
--- a/app/src/main/res/values-kn/strings.xml
+++ b/app/src/main/res/values-kn/strings.xml
@@ -32,7 +32,6 @@
ಮಾಹಿತಿ
ಸೆಟ್ ವಾಚ್ ಸ್ಟೇಟಸ್
ಅನ್ವಯಿಸು
- ರದ್ದುಮಾಡು
ಸಬ್ ಟೈಟಲ್ಸ್ ಎಲೆವಷನ್
ಫಾಂಟ್ ಸೈಜ್
ಸಬ್ ಟೈಟಲ್ಸ್ ಭಾಷೆ
@@ -108,7 +107,6 @@
ಪ್ರಕಾರಗಳು
ಬ್ರೌಸರ್ ತೆರೆಯಿರಿ
ಆನ್-ಹೋಲ್ಡ್
- ನನ್
ಸಂಪರ್ಕವನ್ನು ಮರುಪ್ರಯತ್ನಿಸಿ…
ಡೌನ್ಲೋಡ್ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ
ಡೌನ್ಲೋಡ್ ವಿಫಲವಾಗಿದೆ
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 74c05d07..ef2f6cc5 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -35,7 +35,6 @@
시청 완료
포기
시청 예정
- 없음
다시보기
영화 재생
예고편 재생
@@ -95,7 +94,6 @@
백업
더빙
자막
- 취소
북마크 필터
북마크
제거
@@ -177,7 +175,7 @@
개요
대기중
자막 없음
- 기본
+ 기본
남음
사용됨
앱
@@ -352,6 +350,7 @@
아시아 드라마
시즌
삭제
+ 취소
%s %d%s
파일 삭제
일시정지
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index a702a62f..db5ac011 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -65,6 +65,7 @@
Dokumentika
Transliuoti Torrentą
Ištrinti
+ Atšaukti
Pradėti
Prideda greičio pasirinkti grotuve
Filmukas
@@ -95,7 +96,6 @@
Teksto spalva
Užbaigta
Naudoti sistemos ryškumą programos grotuve vietoj tamsumo
- Tuščia
Nepavyko atstatyti duomenis iš failo %s
Paleisti anonsa
Paleisti gyva transliacija
@@ -107,7 +107,6 @@
Peržiūrima
Atidaryti Naršyklėje
Naudoti sistemos ryškumą
- Atšaukti
Nėra duomenų
Šriftas
Perdaryti nustatymo procesą
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index ddd39942..14e5b600 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -31,7 +31,6 @@
Pabeigts
Atmests
Plāno skatīties
- Neviena
Atkārtoti skatities
Palaist Filmu
Palaist Trelleri
@@ -67,7 +66,6 @@
Noņemt
Ieliec skatīšanās statusu
Izmantot
- Atcelt
Kopēt
Saglabāt
Atskaņošanas ātrums
@@ -192,6 +190,7 @@
Epizodes netika atrastas
Dzēsti faili
Dzēst
+ Atcelt
Pauzēt
Sākt
Neizdevās
@@ -483,7 +482,7 @@
Iet
Bezmaksas
Ieslēgt elementus uz plakātiem
- Parastais
+ Parastais
Nav atjauninājumi atrasti
Izdzēst video un bildes atkritne
Izvēlētā skatīšanās kvalitāte (Mobilie Dati)
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 66a6b9ba..7badfd18 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -25,7 +25,6 @@
Завршени
Отфрлени
Планирани за гледање
- Ништо одбрано
Повторно гледање
Пушти филм
Стримај торент
@@ -58,7 +57,6 @@
Обележувачи
Отстрани
Активирај
- Откажи
Брзина на плеер
Поставки за преводи
Боја на текстот
@@ -133,6 +131,7 @@
Е
Избриши датотека
Избриши
+ Откажи
Паузирај
Продолжи
Ова трајно ќе го избрише %s
@@ -147,7 +146,7 @@
Крат
во редица
Нема преводи
- Стандардно
+ Стандардно
Слободен простор
Искористен простор
Апликациски простор
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index 3d6240f9..cdc50cea 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -54,7 +54,6 @@
ബുക്മാർക്
നീക്കം ചെയ്യുക
പ്രയോഗിക്കുക
- റദ്ദാക്കുക
പ്ലേയർ വേഗത
+ Default -->
ഒഴിവ്
ഉപയോഗത്തിൽ
ആപ്പ്
@@ -197,7 +197,6 @@
ദാതാവിനെ മാറ്റുക
ലോഡിംഗ്…
ബ്രൗസർ
- ഒന്നുമില്ല
വീണ്ടും കാണുക
സ്ട്രീം
diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml
index 222bef61..e579381e 100644
--- a/app/src/main/res/values-ms/strings.xml
+++ b/app/src/main/res/values-ms/strings.xml
@@ -49,7 +49,6 @@
Buka dengan
Padam Fail
Buka Dalam Pelayar
- Batal
Tiada Data
Info
Simpan
diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml
index 0cb44373..e0346f19 100644
--- a/app/src/main/res/values-my/strings.xml
+++ b/app/src/main/res/values-my/strings.xml
@@ -32,7 +32,6 @@
ကြည့်နေသည်
ကြည့်ပြီး
ကြည့်ခြင်းရပ်ထားသော
- ဘာမျှ
လင့်များချိတ်ဆက်ရာတွင်အချို့အယွင်း
ဖုန်း သိုလှောင်ရုံ
စာမှတ်များ စစ်ထုတ်မှု
@@ -127,8 +126,7 @@
အကျဥ်းချုပ်
နောက်အစီအစဥ်
စာတန်းထိုးမထည့်
- ပုံသေ
- @string/default_subtitles
+ ပုံသေ
ကျန်ရှိသော
အက်ပ်
ရုပ်ရှင်များ
@@ -253,7 +251,6 @@
စာတန်းထိုး ဘာသာစကား
ဒီမှာနေရာချခြင်းဖြင့်ဖောင့်များကိုသွင်းပါ %s
အတည်ပြု
- ပယ်ဖျက်ရန်
စာတန်းထိုး ပြုပြင်ခြင်း
စာသား အရောင်
အနားကွပ် အရောင်
@@ -301,6 +298,7 @@
အပိုင်းများမတွေ့ပါ
ဖိုင်ကိုဖျက်ရန်
ဖျက်ရန်
+ ပယ်ဖျက်ရန်
ရပ်ရန်
စရန်
မအောင်မြင်ပါ
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index d19726fd..d73d77a0 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -44,7 +44,6 @@
Voltooid
Dropped
Plan om te kijken
- Geen
Opnieuw kijken
Film afspelen
Livestream afspelen
@@ -82,7 +81,6 @@
Verwijder
Zet kijkstatus
Toepassen
- annuleer
Kopiëren
Sluit
Wissen
@@ -186,6 +184,7 @@
Geen afleveringen gevonden
Verwijder bestand
Verwijder
+ annuleer
Pauze
Hervatten
-30
@@ -204,7 +203,7 @@
Korte inhoud
wachtrij
Geen ondertiteling
- Standaard
+ Standaard
Vrij
Gebruikt
App
@@ -573,6 +572,5 @@
Selecteer een modus om het downloaden van plug-ins te filteren
Uitzetten
De gebruikersinterface kon niet correct worden gemaakt, dit is een ERNSTIG PROBLEEM en moet onmiddellijk gerapporteerd worden %s
- @string/default_subtitles
Je hebt al gestemd
diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml
index 592ff22c..c70ebd4b 100644
--- a/app/src/main/res/values-nn/strings.xml
+++ b/app/src/main/res/values-nn/strings.xml
@@ -63,7 +63,6 @@
Fjern
Sett visingstatus
Bruk
- Avbryt
Kopier
Tøm
Lagre
@@ -113,6 +112,7 @@
Ingen episodar blei funnen
Slett fil
Slett
+ Avbryt
Pause
Gjenoppta
-30
@@ -129,7 +129,7 @@
Om
i kø
Ingen undertekstar
- Standard
+ Standard
Brukt
Program
Filmar
@@ -166,7 +166,6 @@
Gå tilbake
Sjangrar
Dele
- Ingen
Ser om igjen
Oppdatering starta
Fortsett nedlasting
diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml
index dac15d61..9845120b 100644
--- a/app/src/main/res/values-no/strings.xml
+++ b/app/src/main/res/values-no/strings.xml
@@ -33,7 +33,6 @@
Fullført
Falt
Planlegg å se
- Ingen
Spill filmer
Strøm Torrent
Kilder
@@ -67,7 +66,6 @@
Bokmerker
Ta bort
Søke om
- Avbryt
Spillerhastighet
Innstillinger for teksting
Tekstfarge
@@ -141,6 +139,7 @@
E
Slett fil
Slett
+ Avbryt
Stopp
Gjenoppta
Dette vil slette %s
@@ -155,7 +154,7 @@
Om
I kø
Ingen undertekster
- Misligholde
+ Misligholde
Tilgjengelig
Brukt
applikasjon
diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml
index 63dba53d..22f7d1ca 100644
--- a/app/src/main/res/values-or/strings.xml
+++ b/app/src/main/res/values-or/strings.xml
@@ -19,7 +19,6 @@
ଵେଗ (%.2fଗୁଣ)
ତ୍ୟାଗିଛନ୍ତି
ଦେଖିବା ପାଇଁ ଇଚ୍ଛୁକ
- କିଛି ନାହିଁ
ଅଧିକ ସୂଚନା
ପାତ୍ର: %s
ପୋଷ୍ଟର୍
@@ -42,7 +41,7 @@
ଅ
ଅଧ୍ୟାୟର ପୋଷ୍ଟର୍
ମୁଖ୍ୟ ପୋଷ୍ଟର୍
- ଡିଫଲ୍ଟ
+ ଡିଫଲ୍ଟ
ଭାଷା
ନାହିଁ
ଵର୍ଣ୍ଣନା
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index a170d610..95a3e1c6 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -35,7 +35,6 @@
Zakończone
Porzucone
Planowane
- Brak
Ponowne oglądanie
Odtwórz film
Odtwórz transmisję na żywo
@@ -72,7 +71,6 @@
Usuń
Ustaw status oglądania
Zastosuj
- Anuluj
Kopiuj
Zamknij
Wyczyść
@@ -181,6 +179,7 @@
Nie znaleziono odcinków
Usuń plik
Usuń
+ Anuluj
Wstrzymaj
Odtwórz
-30
@@ -199,7 +198,7 @@
Streszczenie
W kolejce
Brak napisów
- Domyślne
+ Domyślne
Wolne
W użyciu
Aplikacja
@@ -552,7 +551,6 @@
Nie można było poprawnie utworzyć interfejsu użytkownika, jest to POWAŻNY BŁĄD i należy go natychmiast zgłosić %s
Wybierz tryb filtrowania pobieranych rozszerzeń
Wyłączać
- @string/default_subtitles
Nie znaleziono żadnych wtyczek w repozytorium
Już oddano głos
Nie znaleziono tego repozytorium, sprawdź adres URL lub spróbuj połączyć się przez VPN
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 908ddb0d..c73fb996 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -42,7 +42,6 @@
Concluído
Desistido
Pretendo assistir
- Nenhum
Reassistindo
Reproduzir filme
Reproduzir transmissão ao vivo
@@ -78,7 +77,6 @@
Marcadores
Remover
Aplicar
- Cancelar
Copiar
Fechar
Limpar
@@ -182,6 +180,7 @@
Nenhum Episódio encontrado
Eliminar Ficheiro
Eliminar
+ Cancelar
Pôr em Pausa
Retomar
Isto apagará %s permanentemente
@@ -198,7 +197,7 @@
Sinopse
Na fila
Sem Legendas
- Padrão
+ Padrão
Livre
Usado
App
@@ -548,7 +547,6 @@
\nNOTA: Se a soma for 10 ou mais, o leitor saltará automaticamente o carregamento quando essa ligação for carregada!
Selecionar o modo para filtrar a transferência de plug-ins
Não foi possível criar corretamente a interface do utilizador, trata-se de um GRANDE BUG e deve ser comunicado imediatamente %s
- \@ string/legendas_padrão
Desativar
Não foram encontrados plugins no repositório
Repositório não encontrado, verifique o URL e tente a VPN
diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml
index 9c68c008..e1191d2b 100644
--- a/app/src/main/res/values-qt/strings.xml
+++ b/app/src/main/res/values-qt/strings.xml
@@ -23,7 +23,6 @@
ahhahooo
ooooo haa
aaahhuoh
- aaaghh
aaaaa aaaghh
aaaghhaaahhu aaaaa
oha aauuh
@@ -51,7 +50,6 @@
oouuh ooh
aauuh ooh
oouuhaooo-ahah
- oooohh
oouuh aauuh oha
ouuhhhooooooh ooo-ahah
ohaouuhhh
@@ -122,6 +120,7 @@
A
ooha ohahaaahoooa ahahooo
oooooah
+ oooohh
aahhaaaaaahooo aauuh aaahhuaooo-ahahoooohh aoouuhoohoohooo-ahah %s
aaaghhaaaaa
aauuhaauuh
@@ -133,7 +132,7 @@
aauugghh ohaaauugghh
haaouuhhh
ahaaaaaaaaaahaaaauugghh
- ahooooh
+ ahooooh
ooo-ahahaaaaa
ahhahhh
aauugghh
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index b6971c37..75388b23 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -43,7 +43,6 @@
Finalizat
Renunțat
Planificare pentru a urmări
- Anuleaza
Reurmat
Urmărește
Stream Torrent
@@ -79,7 +78,6 @@
Eliminează
Adaugă
Aplică
- Anulează
Copiază
Închide
Elimină
@@ -181,6 +179,7 @@
Nu s-au găsit episoade
Ștergeți fișierul
Ștergeți
+ Anulează
Pauză
Continuă
-30
@@ -199,7 +198,7 @@
Rezumat
În coada de așteptare
Nu există subtitrare
- Implicit
+ Implicit
Liber
Folosit
Aplicație
@@ -569,6 +568,5 @@
Utilizați
UI nu a putut fi creată corect, acesta este un BUG MAJOR și trebuie raportat imediat %s
Selectați modul de filtrare a descărcării plugin-urilor
- @string/default_subtitles
Ați votat deja
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index e7b85b3d..64c3146e 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -12,6 +12,7 @@
Скачать неудачный
Подогнать
Удалить
+ Отмена
Все
Пауза
Актёрский состав: %s
@@ -58,7 +59,6 @@
Завершено
Брошенный
План посмотреть
- Нет
Пересмотрю
Смотреть фильм
Смотреть трейлер
@@ -82,7 +82,6 @@
Фильтр закладки
Закладки
Применить
- Отмена
Копия
Закрыть
Очистить
@@ -186,7 +185,7 @@
Рейтинг
Продолжительность
Нет субтитров
- По умолчанию
+ По умолчанию
Приложение
Аниме
Торренты
@@ -535,7 +534,6 @@
Изменить
Интернет
Задний фон профиля
- \@строка/обычные_субтитры
Помощь
Профиль %d
Выберите режим фильтера плагинов для загрузки
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index e0cc27d0..4f17971e 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -33,7 +33,6 @@
Hľadať…
Sťahovanie
Žiadne dáta
- Zrušiť
Kopírovať
Zavrieť
Uložiť
@@ -60,7 +59,6 @@
Sťahovanie zrušené
Dab
Zmazať súbor
- Žiadny
Tit
Opätovné sledovanie
Prehrať súbor
@@ -265,7 +263,7 @@
Preferované médiá
URL servera NGINX
%d %s
- Predvolené
+ Predvolené
Pridať sledovanie
Žiadna sezóna
Epizóda
@@ -293,6 +291,7 @@
Rok
Prispôsobiť obrazovke
Zmazať
+ Zrušiť
Využité
Štítok kvality
Prehrávač skrytý - dĺžka pretočenia
diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml
index db82d9fa..c3d107f5 100644
--- a/app/src/main/res/values-so/strings.xml
+++ b/app/src/main/res/values-so/strings.xml
@@ -10,6 +10,7 @@
Dejintii ma guulaysan
Raadi
Tirtir
+ Jooji
Haki
Horran
Le-ekaysii shaashadda
@@ -46,7 +47,6 @@
Dhamaystirmay
Soo dhacay
Ku talo jira
- Midna
Daaro filinka
Daar goos-gooska
Midabka daaqadda
@@ -56,7 +56,6 @@
Daalaco toorentiga
Qrl-hoosaadka
Dib u bilow dejinta
- Jooji
Midabka dambeedka
Xigashooyinka
Dib u xidhiidhinaya…
@@ -202,7 +201,7 @@
Muddada
La isticmaalay
Qrl-hoosaad ma leh
- Sidiisaa
+ Sidiisaa
Bilaash
Appka
Kartoob
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 397faa48..806a1ca3 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -26,7 +26,6 @@
Avslutad
Dropped
Plannerad
- Ingen
Spela Upp
Strömma Torrent
Källor
@@ -53,7 +52,6 @@
Bokmärken
Ta bort
Tillämpa
- Avbryt
Spelarhastighet
Undertextinställningar
Textfärg
@@ -125,6 +123,7 @@
A
Ta bort nerladdad fil
Ta bort
+ Avbryt
%s kommer att raderas permanent
\nÄr du helt säker\?
Pågående
@@ -137,7 +136,7 @@
Sammanfattning
på kö
Inga undertexter
- Standard
+ Standard
Tillgängligt
Använtt
App
diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml
index 9c9a335b..ca4a1377 100644
--- a/app/src/main/res/values-ta/strings.xml
+++ b/app/src/main/res/values-ta/strings.xml
@@ -38,7 +38,6 @@
மேலும் தகவல்கள்
மறை
நீக்கு
- ரத்து செய்க
நீக்கு
சேமிக்கவும்
உரை வண்ணம்
@@ -63,7 +62,6 @@
நடிகர்கள்: %s
பின் செல்
அமைப்புகள்
- ஏதும் இல்லை
ஏற்றுகிறது…
கைவிடப்பட்டது
பதிவிறக்கம் முடிந்தது
diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml
index 95d38478..03138259 100644
--- a/app/src/main/res/values-tl/strings.xml
+++ b/app/src/main/res/values-tl/strings.xml
@@ -36,7 +36,6 @@
Tapos nang panoorin
Ayaw nang panoorin
Balak panoorin
- None
I-play ang Movie
Stream Torrent
Sources
@@ -70,7 +69,6 @@
Bookmark
Tanggalin
Kumpirmahin
- Kanselahin
Bilis ng Playback
Subtitle Setting
Kulay ng Teksto
@@ -144,6 +142,7 @@
E
Burahin ang file
Tanggalin
+ Kanselahin
I-pause
I-resume
This will permanently delete %s
@@ -158,7 +157,7 @@
Sinopsis
nakapila
walang subtitles
- Default
+ Default
Bakante
Gamit
App
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index e0d7e4cd..c1e06a7b 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -57,7 +57,6 @@
Tamamlandı
Bırakıldı
Planlandı
- Hiçbiri
Yeniden izleniyor
Filmi oynat
Canlı yayını oynat
@@ -96,7 +95,6 @@
Kaldır
İzleme durumunu ayarla
Uygula
- İptal et
Kopyala
Kapat
Temizle
@@ -205,7 +203,7 @@
Bölüm bulunamadı
Dosyayı sil
Sil
- @string/sort_cancel
+ İptal et
Durdur
Sürdür
-30
@@ -224,7 +222,7 @@
Özet
Sıraya alındı
Alt yazı yok
- Varsayılan
+ Varsayılan
Boş
Kullanılan
Uygulama
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index c069cae0..57b128de 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -112,9 +112,7 @@
Переглянути файл
Детальніше
Фільтр закладок
- Нічого
- Скасувати
- Очистити
+ Очистити
Налаштування субтитрів
Колір фону
Висота субтитрів
@@ -174,6 +172,7 @@
Е
Видалити файл
Видалити
+ Скасувати
Відновити
-30
Це назавжди видалить %s
@@ -186,7 +185,7 @@
Тривалість
у черзі
Без субтитрів
- За замовчуванням
+ За замовчуванням
Вільно
Зайнято
Застосунок
@@ -549,7 +548,6 @@
Не вдалося створити UI коректно, це ВАЖЛИВА ПОМИЛКА, про яку слід негайно повідомити %s
Виберіть режим для фільтрації завантаження плагінів
Вимкнути
- @string/default_subtitles
Репозиторій не знайдено, перевірте URL-адресу та спробуйте VPN
Не знайдено жодних плагінів у репозиторії
Ви вже проголосували
diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml
index b9585d07..b437e2d0 100644
--- a/app/src/main/res/values-ur/strings.xml
+++ b/app/src/main/res/values-ur/strings.xml
@@ -34,7 +34,6 @@
معطل
چھوڑ دیا گیا
دیکھنے کا منصوبہ
- کوئی نہیں
دوبارہ دیکھنا
مووی لگائے
ٹریلر چلائیں
@@ -67,7 +66,6 @@
ریمو
واچ اسٹیٹس کو سیٹ کریں
لاگو کریں
- منسوخ کریں
کاپی
بند کریں
صاف کریں
@@ -193,6 +191,7 @@
کوئی اقساط نہیں ملی
فائل کو ڈیلیٹ کریں
مٹا دیں
+ منسوخ کریں
توقف
از سر نو شروع کریں
-30
@@ -211,7 +210,7 @@
خلاصہ
قطار میں
کوئی سب ٹائٹلز نہیں
- ڈیفالٹ
+ ڈیفالٹ
مفت
استعمال شُدہ
کارٹون
@@ -535,7 +534,6 @@
ترتیب دیں
وائی فائی
پروفائل پس منظر
- @string/default_subtitles
مدد
پروفائل %d
پلگ انز کو ڈاؤن لوڈ کرنے کے لئے موڈ منتخب کریں
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 217d2791..8186d6ed 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -45,7 +45,6 @@
Đã xem
Bỏ qua
Xem sau
- Mặc định
Xem lại
Xem Ngay
Phát trực tiếp
@@ -83,7 +82,6 @@
Xóa
Đặt trạng thái xem
Áp dụng
- Hủy bỏ
Sao lưu
Đóng
Huỷ bỏ
@@ -190,6 +188,7 @@
Không có tập nào
Xóa Tệp
Xóa
+ Hủy bỏ
Tạm Dừng
Tiếp Tục
-30
@@ -208,7 +207,7 @@
Thông tin
Hàng chờ
Không có phụ đề
- Mặc Định
+ Mặc Định
Còn trống
Đã sử dụng
App
@@ -567,5 +566,4 @@
Không tìm thấy plugin
Không thể khởi tạo UI, đây là một LỖI LỚN và cần được báo cáo ngay lập tức tới %s
Chọn chế độ để lọc plugin tải xuống
- @string/default_subtitles
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 1fd01d8a..52897633 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -57,7 +57,6 @@
觀看完畢
放棄觀看
計畫觀看
- 無
重新觀看
播放電影
播放直播
@@ -96,7 +95,6 @@
移除
設定觀看狀態
套用
- 取消
複製
關閉
清除
@@ -205,7 +203,7 @@
未找到劇集
刪除文件
刪除
- @string/sort_cancel
+ 取消
暫停
繼續
-30
@@ -224,7 +222,7 @@
簡介
已加入佇列
無字幕
- 預設
+ 預設
空閒
已使用
應用程式
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index 033a5f50..682bcab7 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -57,7 +57,6 @@
观看完毕
放弃观看
计划观看
- 无
重新观看
播放电影
播放直播
@@ -96,7 +95,6 @@
移除
设置观看状态
应用
- 取消
复制
关闭
清除
@@ -206,7 +204,7 @@
未找到剧集
删除文件
删除
- @string/sort_cancel
+ 取消
暂停
继续
-30
@@ -225,7 +223,7 @@
简介
已加入队列
无字幕
- 默认
+ 默认
空闲
已使用
应用
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e243fe79..f0e92d36 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -123,7 +123,7 @@
Completed
Dropped
Plan to Watch
- None
+ @string/none
Rewatching
Play Movie
Play Trailer
@@ -164,7 +164,7 @@
Remove
Set watch status
Apply
- Cancel
+ @string/cancel
Copy
Close
Clear
@@ -287,7 +287,7 @@
No Episodes found
Delete File
Delete
- @string/sort_cancel
+ Cancel
Pause
Start
Failed
@@ -307,8 +307,9 @@
Synopsis
queued
No Subtitles
- Default
- @string/default_subtitles
+ Default
+ @string/action_default
+ @string/action_default
Free
Used
App
@@ -698,7 +699,7 @@
Add
Replace
Replace All
- @string/sort_cancel
+ @string/cancel
It appears that a potentially duplicate item already exists in your library: \'%1$s.\'
From f0ebfa47c8ccffd9ae9795d725358f4df54ec259 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Sat, 28 Oct 2023 16:40:20 -0600
Subject: [PATCH 18/44] Bump material to 1.10.0 (#728)
---
app/build.gradle.kts | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 73c53292..efd98724 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -50,14 +50,15 @@ android {
}
}
- // https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading
- compileSdk = 34 // android 14 is fucked
+ compileSdk = 34
buildToolsVersion = "34.0.0"
defaultConfig {
applicationId = "com.lagradost.cloudstream3"
minSdk = 21
- targetSdk = 33
+
+ // https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading
+ targetSdk = 33 // android 14 is fucked
versionCode = 62
versionName = "4.2.1"
@@ -157,14 +158,12 @@ dependencies {
implementation("androidx.test.ext:junit-ktx:1.1.5")
testImplementation("org.json:json:20230618")
- implementation("androidx.core:core-ktx:1.12.0") // need 34 for higher
- implementation("androidx.appcompat:appcompat:1.6.1") // need target 32 for 1.5.0
+ implementation("androidx.core:core-ktx:1.12.0")
+ implementation("androidx.appcompat:appcompat:1.6.1")
- // dont change this to 1.6.0 it looks ugly af
- implementation("com.google.android.material:material:1.5.0")
+ implementation("com.google.android.material:material:1.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
- // need 34 for higher
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
From d542febcda773e8e5fac34c6acabb39a005596cc Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Sat, 28 Oct 2023 16:41:19 -0600
Subject: [PATCH 19/44] Reload library when deleting bookmarks (#725)
---
.../com/lagradost/cloudstream3/ui/home/HomeViewModel.kt | 6 ------
.../com/lagradost/cloudstream3/utils/DataStoreHelper.kt | 8 +-------
2 files changed, 1 insertion(+), 13 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
index ad75aa9d..f471fefd 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
@@ -40,7 +40,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
import com.lagradost.cloudstream3.utils.DataStoreHelper
-import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
@@ -102,11 +101,6 @@ class HomeViewModel : ViewModel() {
loadStoredData()
}
- fun deleteBookmarks() {
- deleteAllBookmarkedData()
- loadStoredData()
- }
-
var repo: APIRepository? = null
private val _apiName = MutableLiveData()
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 78f801b6..444c2ab2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
@@ -483,15 +483,9 @@ object DataStoreHelper {
removeKeys(folder)
}
- fun deleteAllBookmarkedData() {
- val folder1 = "$currentAccount/$RESULT_WATCH_STATE"
- val folder2 = "$currentAccount/$RESULT_WATCH_STATE_DATA"
- removeKeys(folder1)
- removeKeys(folder2)
- }
-
fun deleteBookmarkedData(id: Int?) {
if (id == null) return
+ AccountManager.localListApi.requireLibraryRefresh = true
removeKey("$currentAccount/$RESULT_WATCH_STATE", id.toString())
removeKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
}
From b2e0b7dec8fc76e5ed43a801c0718acc0ad6ce8b Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Sat, 28 Oct 2023 16:52:58 -0600
Subject: [PATCH 20/44] Fix "Multiple substitutions specified in non-positional
format" (#727)
---
app/src/main/res/values/strings.xml | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f0e92d36..08ed6228 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -77,11 +77,11 @@
%d
%.1f/10.0
%d
- %s Ep %d
+ %1$s Ep %2$d
Cast: %s
Episode %d will be released in
- %dd %dh %dm
- %dh %dm
+ %1$dd %2$dh %3$dm
+ %1$dh %2$dm
%dm
Poster
@@ -97,7 +97,7 @@
Speed (%.2fx)
Rated: %.1f
- New update found!\n%s -> %s
+ New update found!\n%1$s -> %2$s
Filler
%d min
CloudStream
@@ -276,12 +276,12 @@
developers
Season
- %s %d%s
+ %1$s %2$d%3$s
No Season
Episode
Episodes
- %d-%d
- %d %s
+ %1$d-%2$d
+ %1$d %2$s
S
E
No Episodes found
@@ -471,7 +471,7 @@
Kitsu
Trakt
-->
- %s %s
+ %1$s %2$s
account
Log out
Log in
@@ -572,8 +572,8 @@
Plugin Deleted
Could not load %s
18+
- Started downloading %d %s…
- Downloaded %d %s
+ Started downloading %1$d %2$s…
+ Downloaded %1$d %2$s
All %s already downloaded
No plugins found in repository
Repository not found, check the URL and try VPN
@@ -699,16 +699,16 @@
Add
Replace
Replace All
- @string/cancel
+ @string/sort_cancel
It appears that a potentially duplicate item already exists in your library: \'%1$s.\'
\n\nWould you like to add this item anyway, replace the existing one, or cancel the action?
-
+
Potential duplicate items have been found in your library:
- \n\n%1$s
+ \n\n%s
\n\nWould you like to add this item anyway, replace the existing ones, or cancel the action?
From 137d833d4ae20d6a4e04bcaabe246c49963c71ad Mon Sep 17 00:00:00 2001
From: self-similarity <137652432+self-similarity@users.noreply.github.com>
Date: Sat, 28 Oct 2023 22:55:49 +0000
Subject: [PATCH 21/44] Improved Simkl autosync and fixed syncing seasons
(#722)
* Improved simkl autosync and fixed syncing seasons
---
.../com/lagradost/cloudstream3/MainAPI.kt | 21 ++++++++
.../syncproviders/providers/SimklApi.kt | 54 +++++++++++--------
.../cloudstream3/ui/player/GeneratorPlayer.kt | 2 +-
.../cloudstream3/ui/result/ResultFragment.kt | 8 ++-
.../ui/result/ResultViewModel2.kt | 22 ++++++--
5 files changed, 76 insertions(+), 31 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
index bfe86224..35a628a3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
@@ -1465,6 +1465,15 @@ interface EpisodeResponse {
var nextAiring: NextAiring?
var seasonNames: List?
fun getLatestEpisodes(): Map
+
+ /** Count all episodes in all previous seasons up until this episode to get a total count.
+ * Example:
+ * Season 1: 10 episodes.
+ * Season 2: 6 episodes.
+ *
+ * getTotalEpisodeIndex(episode = 3, season = 2) -> 10 + 3 = 13
+ * */
+ fun getTotalEpisodeIndex(episode: Int, season: Int): Int
}
@JvmName("addSeasonNamesString")
@@ -1544,6 +1553,12 @@ data class AnimeLoadResponse(
.takeUnless { it == Int.MIN_VALUE }
}.toMap()
}
+
+ override fun getTotalEpisodeIndex(episode: Int, season: Int): Int {
+ return this.episodes.maxOf { (_, episodes) ->
+ episodes.count { ((it.season ?: Int.MIN_VALUE) < season) && it.season != 0 }
+ } + episode
+ }
}
/**
@@ -1752,6 +1767,12 @@ data class TvSeriesLoadResponse(
.takeUnless { it == Int.MIN_VALUE }
return mapOf(DubStatus.None to max)
}
+
+ override fun getTotalEpisodeIndex(episode: Int, season: Int): Int {
+ return episodes.count {
+ (it.season ?: Int.MIN_VALUE) < season && it.season != 0
+ } + episode
+ }
}
suspend fun MainAPI.newTvSeriesLoadResponse(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
index bd7979f5..3a37a228 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
@@ -376,6 +376,8 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
private var status: Int? = null,
private var addEpisodes: Pair?, List?>? = null,
private var removeEpisodes: Pair?, List?>? = null,
+ // Required for knowing if the status should be overwritten
+ private var onList: Boolean = false
) {
fun interceptor(interceptor: Interceptor) = apply { this.interceptor = interceptor }
fun apiUrl(url: String) = apply { this.url = url }
@@ -387,6 +389,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
}
fun status(newStatus: Int?, oldStatus: Int?) = apply {
+ onList = oldStatus != null
// Only set status if its new
if (newStatus != oldStatus) {
this.status = newStatus
@@ -412,6 +415,11 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
// Do not add episodes if there is no change
if (newEpisodes > (oldEpisodes ?: 0)) {
this.addEpisodes = getEpisodes(allEpisodes.take(newEpisodes))
+
+ // Set to watching if episodes are added and there is no current status
+ if (!onList) {
+ status = SimklListStatusType.Watching.value
+ }
}
if ((oldEpisodes ?: 0) > newEpisodes) {
this.removeEpisodes = getEpisodes(allEpisodes.drop(newEpisodes))
@@ -431,6 +439,28 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
interceptor = interceptor
).isSuccessful
} else {
+ val statusResponse = status?.let { setStatus ->
+ val newStatus =
+ SimklListStatusType.values()
+ .firstOrNull { it.value == setStatus }?.originalName
+ ?: SimklListStatusType.Watching.originalName!!
+
+ app.post(
+ "${this.url}/sync/add-to-list",
+ json = StatusRequest(
+ shows = listOf(
+ StatusMediaObject(
+ null,
+ null,
+ ids,
+ newStatus,
+ )
+ ), movies = emptyList()
+ ),
+ interceptor = interceptor
+ ).isSuccessful
+ } ?: true
+
val episodeRemovalResponse = removeEpisodes?.let { (seasons, episodes) ->
app.post(
"${this.url}/sync/history/remove",
@@ -472,28 +502,6 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
true
}
- val statusResponse = status?.let { setStatus ->
- val newStatus =
- SimklListStatusType.values()
- .firstOrNull { it.value == setStatus }?.originalName
- ?: SimklListStatusType.Watching.originalName!!
-
- app.post(
- "${this.url}/sync/add-to-list",
- json = StatusRequest(
- shows = listOf(
- StatusMediaObject(
- null,
- null,
- ids,
- newStatus,
- )
- ), movies = emptyList()
- ),
- interceptor = interceptor
- ).isSuccessful
- } ?: true
-
statusResponse && episodeRemovalResponse && historyResponse
}
}
@@ -1051,4 +1059,4 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
return true
}
-}
\ No newline at end of file
+}
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 1c751897..7c8d975a 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
@@ -1023,7 +1023,7 @@ class GeneratorPlayer : FullScreenPlayer() {
ctx.getString(R.string.episode_sync_enabled_key), true
)
) maxEpisodeSet = meta.episode
- sync.modifyMaxEpisode(meta.episode)
+ sync.modifyMaxEpisode(meta.totalEpisodeIndex ?: meta.episode)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
index 7617bc11..a1574eec 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
@@ -47,7 +47,9 @@ data class ResultEpisode(
/**
* Conveys if the episode itself is marked as watched
**/
- val videoWatchState: VideoWatchState
+ val videoWatchState: VideoWatchState,
+ /** Sum of all previous season episode counts + episode */
+ val totalEpisodeIndex: Int? = null,
)
fun ResultEpisode.getRealPosition(): Long {
@@ -82,6 +84,7 @@ fun buildResultEpisode(
isFiller: Boolean? = null,
tvType: TvType,
parentId: Int,
+ totalEpisodeIndex: Int? = null,
): ResultEpisode {
val posDur = getViewPos(id)
val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None
@@ -103,7 +106,8 @@ fun buildResultEpisode(
isFiller,
tvType,
parentId,
- videoWatchState
+ videoWatchState,
+ totalEpisodeIndex
)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 1631b706..d744fac5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -2215,6 +2215,10 @@ class ResultViewModel2 : ViewModel() {
val id =
mainId + episode + idIndex * 1_000_000 + (i.season?.times(10_000)
?: 0)
+
+ val totalIndex =
+ i.season?.let { season -> loadResponse.getTotalEpisodeIndex(episode, season) }
+
if (!existingEpisodes.contains(id)) {
existingEpisodes.add(id)
val seasonData = loadResponse.seasonNames.getSeason(i.season)
@@ -2234,7 +2238,8 @@ class ResultViewModel2 : ViewModel() {
i.description,
fillers.getOrDefault(episode, false),
loadResponse.type,
- mainId
+ mainId,
+ totalIndex
)
val season = eps.seasonIndex ?: 0
@@ -2263,6 +2268,9 @@ class ResultViewModel2 : ViewModel() {
val seasonData =
loadResponse.seasonNames.getSeason(episode.season)
+ val totalIndex =
+ episode.season?.let { season -> loadResponse.getTotalEpisodeIndex(episodeIndex, season) }
+
val ep =
buildResultEpisode(
loadResponse.name,
@@ -2279,7 +2287,8 @@ class ResultViewModel2 : ViewModel() {
episode.description,
null,
loadResponse.type,
- mainId
+ mainId,
+ totalIndex
)
val season = ep.seasonIndex ?: 0
@@ -2310,7 +2319,8 @@ class ResultViewModel2 : ViewModel() {
null,
null,
loadResponse.type,
- mainId
+ mainId,
+ null
)
)
}
@@ -2332,7 +2342,8 @@ class ResultViewModel2 : ViewModel() {
null,
null,
loadResponse.type,
- mainId
+ mainId,
+ null
)
)
}
@@ -2354,7 +2365,8 @@ class ResultViewModel2 : ViewModel() {
null,
null,
loadResponse.type,
- mainId
+ mainId,
+ null
)
)
}
From f0e429436f92855708cb4e22ac121c59c8732eff Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Sat, 28 Oct 2023 17:20:04 -0600
Subject: [PATCH 22/44] Use TV results layout on emulator (#710)
* Use TV results layout on emulator
* Add subscription support to emulator
* Update
---
.../ui/result/ResultFragmentTv.kt | 45 ++++++++++++++++++-
.../lagradost/cloudstream3/utils/AppUtils.kt | 2 +-
.../main/res/layout/fragment_result_tv.xml | 15 ++++++-
app/src/main/res/values/strings.xml | 2 +
4 files changed, 60 insertions(+), 4 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 c13854e0..396ec863 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
@@ -28,6 +28,7 @@ import com.lagradost.cloudstream3.databinding.FragmentResultTvBinding
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
+import com.lagradost.cloudstream3.services.SubscriptionWorkManager
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
@@ -37,6 +38,7 @@ import com.lagradost.cloudstream3.ui.result.ResultFragment.updateUIEvent
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED
import com.lagradost.cloudstream3.ui.search.SearchAdapter
import com.lagradost.cloudstream3.ui.search.SearchHelper
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
@@ -287,7 +289,8 @@ class ResultFragmentTv : Fragment() {
resultResumeSeries,
resultPlayTrailer,
resultBookmarkButton,
- resultFavoriteButton
+ resultFavoriteButton,
+ resultSubscribeButton
)
for (requestView in views) {
if (!requestView.isVisible) continue
@@ -429,6 +432,7 @@ class ResultFragmentTv : Fragment() {
binding?.resultEpisodesShow,
binding?.resultBookmarkButton,
binding?.resultFavoriteButton,
+ binding?.resultSubscribeButton,
).firstOrNull {
it?.isVisible == true
}
@@ -574,6 +578,45 @@ class ResultFragmentTv : Fragment() {
}
}
+ observeNullable(viewModel.subscribeStatus) { isSubscribed ->
+ binding?.resultSubscribeButton?.apply {
+ isVisible = isSubscribed != null && context.isEmulatorSettings()
+ if (isSubscribed == null) return@observeNullable
+
+ val drawable = if (isSubscribed) {
+ R.drawable.ic_baseline_notifications_active_24
+ } else {
+ R.drawable.baseline_notifications_none_24
+ }
+
+ val text = if (isSubscribed) {
+ R.string.action_unsubscribe
+ } else {
+ R.string.action_subscribe
+ }
+
+ setIconResource(drawable)
+ setText(text)
+ setOnClickListener {
+ viewModel.toggleSubscriptionStatus(context) { newStatus: Boolean? ->
+ if (newStatus == null) return@toggleSubscriptionStatus
+
+ val message = if (newStatus) {
+ // Kinda icky to have this here, but it works.
+ SubscriptionWorkManager.enqueuePeriodicWork(context)
+ R.string.subscription_new
+ } else {
+ R.string.subscription_deleted
+ }
+
+ val name = (viewModel.page.value as? Resource.Success)?.value?.title
+ ?: txt(R.string.no_data).asStringNull(context) ?: ""
+ CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
+ }
+ }
+ }
+ }
+
observeNullable(viewModel.movie) { data ->
binding?.apply {
resultPlayMovie.isVisible = data is Resource.Success
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt
index 48917889..1be966b6 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt
@@ -583,7 +583,7 @@ object AppUtils {
//private val viewModel: ResultViewModel by activityViewModels()
private fun getResultsId(): Int {
- return if (isTrueTvSettings()) {
+ return if (isTvSettings()) {
R.id.global_to_navigation_results_tv
} else {
R.id.global_to_navigation_results_phone
diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml
index 51add4cb..9311d549 100644
--- a/app/src/main/res/layout/fragment_result_tv.xml
+++ b/app/src/main/res/layout/fragment_result_tv.xml
@@ -325,19 +325,30 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
style="@style/ResultButtonTV"
android:nextFocusRight="@id/result_description"
android:nextFocusUp="@id/result_bookmark_button"
- android:nextFocusDown="@id/result_episodes_show"
+ android:nextFocusDown="@id/result_subscribe_button"
android:text="@string/action_add_to_favorites"
android:visibility="visible"
app:icon="@drawable/ic_baseline_favorite_border_24" />
+
+
Subscribed to %s
Unsubscribed from %s
Episode %d released!
+ Subscribe
+ Unsubscribe
Profile %d
Wi-Fi
Mobile data
From 87c5aada8f9915ebd7fe132fd2185a54c8cedbbc Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Sun, 29 Oct 2023 10:35:48 -0600
Subject: [PATCH 23/44] Bump androidx.preference:preference-ktx (#734)
---
app/build.gradle.kts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index efd98724..b82d26e1 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -179,7 +179,7 @@ dependencies {
// DONT UPDATE, WILL CRASH ANDROID TV ????
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
- implementation("androidx.preference:preference-ktx:1.2.0")
+ implementation("androidx.preference:preference-ktx:1.2.1")
implementation("com.github.bumptech.glide:glide:4.13.1")
kapt("com.github.bumptech.glide:compiler:4.13.1")
From a8fdf5e8f224f5bf22e467181ebb3d4bf992692c Mon Sep 17 00:00:00 2001
From: IndusAryan <125901294+IndusAryan@users.noreply.github.com>
Date: Mon, 30 Oct 2023 20:55:50 +0530
Subject: [PATCH 24/44] remove useless parent (#735)
---
app/src/main/res/layout/fragment_home_head.xml | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/app/src/main/res/layout/fragment_home_head.xml b/app/src/main/res/layout/fragment_home_head.xml
index 90386ccf..0edaf230 100644
--- a/app/src/main/res/layout/fragment_home_head.xml
+++ b/app/src/main/res/layout/fragment_home_head.xml
@@ -92,15 +92,7 @@
android:layout_height="100dp"
android:layout_gravity="bottom"
android:gravity="center"
- android:orientation="vertical">
-
-
+ android:orientation="horizontal">
-
+
From 65313b4579bf9aed14ee0541a9bb2df2ede5f8a1 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Mon, 30 Oct 2023 17:32:01 -0600
Subject: [PATCH 25/44] Add account selection activity and support profile
locks (#736)
---
app/src/main/AndroidManifest.xml | 22 ++-
.../cloudstream3/ui/WhoIsWatchingAdapter.kt | 8 +-
.../cloudstream3/ui/account/AccountAdapter.kt | 64 ++++++++
.../cloudstream3/ui/account/AccountDialog.kt | 115 +++++++++++++++
.../ui/account/AccountSelectActivity.kt | 91 ++++++++++++
.../cloudstream3/utils/DataStoreHelper.kt | 139 +++++++++++++-----
app/src/main/res/layout/account_list_item.xml | 56 +++++++
.../res/layout/activity_account_select.xml | 28 ++++
.../res/layout/activity_account_select_tv.xml | 27 ++++
app/src/main/res/layout/lock_pin_dialog.xml | 25 ++++
.../res/layout/who_is_watching_account.xml | 9 ++
.../layout/who_is_watching_account_edit.xml | 6 +
app/src/main/res/values/strings.xml | 9 ++
13 files changed, 553 insertions(+), 46 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt
create mode 100644 app/src/main/res/layout/account_list_item.xml
create mode 100644 app/src/main/res/layout/activity_account_select.xml
create mode 100644 app/src/main/res/layout/activity_account_select_tv.xml
create mode 100644 app/src/main/res/layout/lock_pin_dialog.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e0d43338..453c1fae 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -96,12 +96,6 @@
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true">
-
-
-
-
-
-
@@ -165,6 +159,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/WhoIsWatchingAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/WhoIsWatchingAdapter.kt
index 6b3090a9..c5c38dc0 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/WhoIsWatchingAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/WhoIsWatchingAdapter.kt
@@ -55,7 +55,6 @@ class WhoIsWatchingAdapter(
editCallBack = editCallBack,
)
-
override fun onBindViewHolder(holder: WhoIsWatchingHolder, position: Int) =
holder.bind(currentList.getOrNull(position))
@@ -70,10 +69,15 @@ class WhoIsWatchingAdapter(
fun bind(card: DataStoreHelper.Account?) {
when (binding) {
is WhoIsWatchingAccountBinding -> binding.apply {
- if(card == null) return@apply
+ if (card == null) return@apply
outline.isVisible = card.keyIndex == DataStoreHelper.selectedKeyIndex
profileText.text = card.name
profileImageBackground.setImage(card.image)
+
+ // Handle the lock indicator
+ val isLocked = card.lockPin != null
+ lockIcon.isVisible = isLocked
+
root.setOnClickListener {
selectCallBack(card)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
new file mode 100644
index 00000000..72551199
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
@@ -0,0 +1,64 @@
+package com.lagradost.cloudstream3.ui.account
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import com.lagradost.cloudstream3.databinding.AccountListItemBinding
+import com.lagradost.cloudstream3.ui.result.setImage
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
+import com.lagradost.cloudstream3.utils.DataStoreHelper
+
+class AccountAdapter(
+ private val accounts: List,
+ private val onItemClick: (DataStoreHelper.Account) -> Unit
+) : RecyclerView.Adapter() {
+
+ inner class AccountViewHolder(private val binding: AccountListItemBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+
+ fun bind(account: DataStoreHelper.Account) {
+ val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
+
+ binding.accountName.text = account.name
+ binding.accountImage.setImage(account.image)
+ binding.lockIcon.isVisible = account.lockPin != null
+ binding.outline.isVisible = isLastUsedAccount
+
+ if (isTvSettings()) {
+ binding.root.isFocusableInTouchMode = true
+ if (isLastUsedAccount) {
+ binding.root.requestFocus()
+ }
+ }
+
+ binding.root.setOnClickListener {
+ onItemClick(account)
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountViewHolder {
+ val binding = AccountListItemBinding.inflate(
+ LayoutInflater.from(parent.context), parent, false
+ )
+
+ if (isTvSettings()) {
+ val layoutParams = binding.root.layoutParams as RecyclerView.LayoutParams
+ val marginInDp = 5 // Set the margin to 5dp
+ val marginInPixels = (marginInDp * parent.resources.displayMetrics.density).toInt()
+ layoutParams.setMargins(marginInPixels, marginInPixels, marginInPixels, marginInPixels)
+ binding.root.layoutParams = layoutParams
+ }
+
+ return AccountViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: AccountViewHolder, position: Int) {
+ holder.bind(accounts[position])
+ }
+
+ override fun getItemCount(): Int {
+ return accounts.size
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt
new file mode 100644
index 00000000..dfd8831b
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt
@@ -0,0 +1,115 @@
+package com.lagradost.cloudstream3.ui.account
+
+import android.content.Context
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.LayoutInflater
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
+import android.widget.TextView
+import androidx.annotation.StringRes
+import androidx.appcompat.app.AlertDialog
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.databinding.LockPinDialogBinding
+import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
+
+object AccountDialog {
+ // TODO add account creation dialog to allow creating accounts directly from AccountSelectActivity
+
+ fun showPinInputDialog(
+ context: Context,
+ currentPin: String?,
+ editAccount: Boolean,
+ callback: (String?) -> Unit
+ ) {
+ fun TextView.visibleWithText(@StringRes textRes: Int) {
+ visibility = View.VISIBLE
+ setText(textRes)
+ }
+
+ fun View.isVisible() = visibility == View.VISIBLE
+
+ val binding = LockPinDialogBinding.inflate(LayoutInflater.from(context))
+
+ val isPinSet = currentPin != null
+ val isNewPin = editAccount && !isPinSet
+ val isEditPin = editAccount && isPinSet
+
+ val titleRes = if (isEditPin) R.string.enter_current_pin else R.string.enter_pin
+
+ val dialog = AlertDialog.Builder(context, R.style.AlertDialogCustom)
+ .setView(binding.root)
+ .setTitle(titleRes)
+ .setNegativeButton(R.string.cancel) { _, _ ->
+ callback.invoke(null)
+ }
+ .setOnCancelListener {
+ callback.invoke(null)
+ }
+ .setOnDismissListener {
+ if (binding.pinEditTextError.isVisible()) {
+ callback.invoke(null)
+ }
+ }
+ .create()
+
+ var isPinValid = false
+
+ binding.pinEditText.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
+ val enteredPin = s.toString()
+ val isEnteredPinValid = enteredPin.length == 4
+
+ if (isEnteredPinValid) {
+ if (isPinSet) {
+ if (enteredPin != currentPin) {
+ binding.pinEditTextError.visibleWithText(R.string.pin_error_incorrect)
+ binding.pinEditText.text = null
+ isPinValid = false
+ } else {
+ binding.pinEditTextError.visibility = View.GONE
+ isPinValid = true
+
+ callback.invoke(enteredPin)
+ dialog.dismissSafe()
+ }
+ } else {
+ binding.pinEditTextError.visibility = View.GONE
+ isPinValid = true
+ }
+ } else if (isNewPin) {
+ binding.pinEditTextError.visibleWithText(R.string.pin_error_length)
+ isPinValid = false
+ }
+ }
+
+ override fun afterTextChanged(s: Editable?) {}
+ })
+
+ // Detect IME_ACTION_DONE
+ binding.pinEditText.setOnEditorActionListener { _, actionId, _ ->
+ if (actionId == EditorInfo.IME_ACTION_DONE && isPinValid) {
+ val enteredPin = binding.pinEditText.text.toString()
+ callback.invoke(enteredPin)
+ dialog.dismissSafe()
+ }
+ true
+ }
+
+ // We don't want to accidentally have the dialog dismiss when clicking outside of it.
+ // That is what the cancel button is for.
+ dialog.setCanceledOnTouchOutside(false)
+
+ dialog.show()
+
+ // Auto focus on PIN input and show keyboard
+ binding.pinEditText.requestFocus()
+ binding.pinEditText.postDelayed({
+ val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ imm.showSoftInput(binding.pinEditText, InputMethodManager.SHOW_IMPLICIT)
+ }, 200)
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..152c19dc
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt
@@ -0,0 +1,91 @@
+package com.lagradost.cloudstream3.ui.account
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.lagradost.cloudstream3.CommonActivity
+import com.lagradost.cloudstream3.CommonActivity.loadThemes
+import com.lagradost.cloudstream3.MainActivity
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.databinding.ActivityAccountSelectBinding
+import com.lagradost.cloudstream3.databinding.ActivityAccountSelectTvBinding
+import com.lagradost.cloudstream3.ui.account.AccountDialog.showPinInputDialog
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
+import com.lagradost.cloudstream3.utils.DataStoreHelper
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getAccounts
+import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
+
+class AccountSelectActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ CommonActivity.init(this)
+ loadThemes(this)
+
+ window.navigationBarColor = colorFromAttribute(R.attr.primaryBlackBackground)
+
+ val binding = if (isTvSettings()) {
+ ActivityAccountSelectTvBinding.inflate(layoutInflater)
+ } else ActivityAccountSelectBinding.inflate(layoutInflater)
+
+ setContentView(binding.root)
+
+ val recyclerView: RecyclerView = binding.root.findViewById(R.id.account_recycler_view)
+
+ val accounts = getAccounts(this@AccountSelectActivity)
+
+ // Don't show account selection if there is only
+ // one account that exists
+ if (accounts.count() <= 1) {
+ navigateToMainActivity()
+ return
+ }
+
+ val adapter = AccountAdapter(accounts) { selectedAccount ->
+ // Handle the selected account
+ onAccountSelected(selectedAccount)
+ }
+ recyclerView.adapter = adapter
+
+ recyclerView.layoutManager = if (isTvSettings()) {
+ LinearLayoutManager(this)
+ } else GridLayoutManager(this, 2)
+ }
+
+ private fun onAccountSelected(selectedAccount: DataStoreHelper.Account) {
+ if (selectedAccount.lockPin != null) {
+ // The selected account has a PIN set, prompt the user to enter the PIN
+ showPinInputDialog(this@AccountSelectActivity, selectedAccount.lockPin, false) { pin ->
+ if (pin == null) return@showPinInputDialog
+ // Pin is correct, proceed to main activity
+ setAccount(selectedAccount)
+ navigateToMainActivity()
+ }
+ } else {
+ // No PIN set for the selected account, proceed to main activity
+ setAccount(selectedAccount)
+ navigateToMainActivity()
+ }
+ }
+
+ private fun setAccount(account: DataStoreHelper.Account) {
+ // Don't reload if it is the same account
+ if (DataStoreHelper.selectedKeyIndex == account.keyIndex) {
+ return
+ }
+
+ DataStoreHelper.selectedKeyIndex = account.keyIndex
+
+ MainActivity.bookmarksUpdatedEvent(true)
+ MainActivity.reloadHomeEvent(true)
+ }
+
+ private fun navigateToMainActivity() {
+ val mainIntent = Intent(this, MainActivity::class.java)
+ startActivity(mainIntent)
+ finish() // Finish the account selection activity
+ }
+}
\ 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 444c2ab2..e687bcfb 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
@@ -6,6 +6,7 @@ import android.text.Editable
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone
+import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import com.fasterxml.jackson.annotation.JsonProperty
import com.google.android.material.bottomsheet.BottomSheetDialog
@@ -26,8 +27,8 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.WhoIsWatchingAdapter
+import com.lagradost.cloudstream3.ui.account.AccountDialog.showPinInputDialog
import com.lagradost.cloudstream3.ui.library.ListSorting
-import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.UiImage
import com.lagradost.cloudstream3.ui.result.VideoWatchState
import com.lagradost.cloudstream3.ui.result.setImage
@@ -136,6 +137,8 @@ object DataStoreHelper {
val customImage: String? = null,
@JsonProperty("defaultImageIndex")
val defaultImageIndex: Int,
+ @JsonProperty("lockPin")
+ val lockPin: String? = null,
) {
val image: UiImage
get() = customImage?.let { UiImage.Image(it) } ?: UiImage.Drawable(
@@ -230,36 +233,86 @@ object DataStoreHelper {
binding.profilePic.setImage(account.image)
binding.profilePic.setOnClickListener {
- // rolls the image forwards once
+ // Roll the image forwards once
currentEditAccount =
currentEditAccount.copy(defaultImageIndex = (currentEditAccount.defaultImageIndex + 1) % profileImages.size)
binding.profilePic.setImage(currentEditAccount.image)
}
binding.applyBtt.setOnClickListener {
- val currentAccounts = accounts.toMutableList()
-
- val overrideIndex =
- currentAccounts.indexOfFirst { it.keyIndex == currentEditAccount.keyIndex }
-
- // if an account is found that has the same keyIndex then override that one, if not then append it
- if (overrideIndex != -1) {
- currentAccounts[overrideIndex] = currentEditAccount
+ if (currentEditAccount.lockPin != null) {
+ // Ask for the current PIN
+ showPinInputDialog(context, currentEditAccount.lockPin, false) { pin ->
+ if (pin == null) return@showPinInputDialog
+ // PIN is correct, proceed to update the account
+ performAccountUpdate(currentEditAccount)
+ dialog.dismissSafe()
+ }
} else {
- currentAccounts.add(currentEditAccount)
+ // No lock PIN set, proceed to update the account
+ performAccountUpdate(currentEditAccount)
+ dialog.dismissSafe()
}
-
- // Save the current homepage for new accounts
- val currentHomePage = DataStoreHelper.currentHomePage
-
- // set the new default account as well as add the key for the new account
- setAccount(currentEditAccount, false)
- DataStoreHelper.currentHomePage = currentHomePage
-
- accounts = currentAccounts.toTypedArray()
-
- dialog.dismissSafe()
}
+
+ // Handle setting or changing the PIN
+
+ if (currentEditAccount.keyIndex == getDefaultAccount(context).keyIndex) {
+ binding.lockProfileCheckbox.isVisible = false
+ if (currentEditAccount.lockPin != null) {
+ currentEditAccount = currentEditAccount.copy(lockPin = null)
+ }
+ }
+
+ var canSetPin = true
+
+ binding.lockProfileCheckbox.isChecked = currentEditAccount.lockPin != null
+
+ binding.lockProfileCheckbox.setOnCheckedChangeListener { _, isChecked ->
+ if (isChecked) {
+ if (canSetPin) {
+ showPinInputDialog(context, null, true) { pin ->
+ if (pin == null) {
+ binding.lockProfileCheckbox.isChecked = false
+ return@showPinInputDialog
+ }
+
+ currentEditAccount = currentEditAccount.copy(lockPin = pin)
+ }
+ }
+ } else {
+ if (currentEditAccount.lockPin != null) {
+ // Ask for the current PIN
+ showPinInputDialog(context, currentEditAccount.lockPin, true) { pin ->
+ if (pin == null || pin != currentEditAccount.lockPin) {
+ canSetPin = false
+ binding.lockProfileCheckbox.isChecked = true
+ } else {
+ currentEditAccount = currentEditAccount.copy(lockPin = null)
+ }
+ }
+ }
+ }
+ }
+
+ canSetPin = true
+ }
+
+ private fun performAccountUpdate(account: Account) {
+ val currentAccounts = accounts.toMutableList()
+
+ val overrideIndex = currentAccounts.indexOfFirst { it.keyIndex == account.keyIndex }
+
+ if (overrideIndex != -1) {
+ currentAccounts[overrideIndex] = account
+ } else {
+ currentAccounts.add(account)
+ }
+
+ val currentHomePage = this.currentHomePage
+ setAccount(account, false)
+ this.currentHomePage = currentHomePage
+ accounts = currentAccounts.toTypedArray()
}
private fun getDefaultAccount(context: Context): Account {
@@ -272,10 +325,18 @@ object DataStoreHelper {
}
}
+ fun getAccounts(context: Context): List {
+ return accounts.toMutableList().apply {
+ val item = getDefaultAccount(context)
+ remove(item)
+ add(0, item)
+ }
+ }
+
fun showWhoIsWatching(context: Context) {
- val binding: WhoIsWatchingBinding = WhoIsWatchingBinding.inflate(
- LayoutInflater.from(context)
- )
+ val binding: WhoIsWatchingBinding = WhoIsWatchingBinding.inflate(LayoutInflater.from(context))
+ val builder = BottomSheetDialog(context)
+ builder.setContentView(binding.root)
val showAccount = accounts.toMutableList().apply {
val item = getDefaultAccount(context)
@@ -283,22 +344,25 @@ object DataStoreHelper {
add(0, item)
}
- val builder =
- BottomSheetDialog(context)
- builder.setContentView(binding.root)
val accountName = context.getString(R.string.account)
- binding.profilesRecyclerview.setLinearListLayout(
- isHorizontal = true,
- nextUp = FOCUS_SELF,
- nextDown = FOCUS_SELF,
- nextLeft = FOCUS_SELF,
- nextRight = FOCUS_SELF
- )
+ binding.profilesRecyclerview.setLinearListLayout(isHorizontal = true)
binding.profilesRecyclerview.adapter = WhoIsWatchingAdapter(
selectCallBack = { account ->
- setAccount(account, true)
- builder.dismissSafe()
+ // Check if the selected account has a lock PIN set
+ if (account.lockPin != null) {
+ // Prompt for the lock pin
+ showPinInputDialog(context, account.lockPin, false) { pin ->
+ if (pin == null) return@showPinInputDialog
+ // Pin is correct, unlock the profile
+ setAccount(account, true)
+ builder.dismissSafe()
+ }
+ } else {
+ // No lock PIN set, directly set the account
+ setAccount(account, true)
+ builder.dismissSafe()
+ }
},
addAccountCallback = {
val currentAccounts = accounts
@@ -334,7 +398,6 @@ object DataStoreHelper {
builder.show()
}
-
data class PosDur(
@JsonProperty("position") val position: Long,
@JsonProperty("duration") val duration: Long
diff --git a/app/src/main/res/layout/account_list_item.xml b/app/src/main/res/layout/account_list_item.xml
new file mode 100644
index 00000000..3331b85b
--- /dev/null
+++ b/app/src/main/res/layout/account_list_item.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_account_select.xml b/app/src/main/res/layout/activity_account_select.xml
new file mode 100644
index 00000000..9138f82d
--- /dev/null
+++ b/app/src/main/res/layout/activity_account_select.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_account_select_tv.xml b/app/src/main/res/layout/activity_account_select_tv.xml
new file mode 100644
index 00000000..87340ad2
--- /dev/null
+++ b/app/src/main/res/layout/activity_account_select_tv.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/lock_pin_dialog.xml b/app/src/main/res/layout/lock_pin_dialog.xml
new file mode 100644
index 00000000..db2af48e
--- /dev/null
+++ b/app/src/main/res/layout/lock_pin_dialog.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/who_is_watching_account.xml b/app/src/main/res/layout/who_is_watching_account.xml
index 4970d004..8152ed27 100644
--- a/app/src/main/res/layout/who_is_watching_account.xml
+++ b/app/src/main/res/layout/who_is_watching_account.xml
@@ -35,6 +35,15 @@
android:background="@drawable/outline_card"
android:visibility="gone" />
+
+
+
+
tv_no_focus_tag
+
+
+ Enter PIN
+ Enter Current PIN
+ Lock Profile
+ PIN
+ Incorrect PIN. Please try again.
+ PIN must be 4 characters
+ Select an Account
From 8b73c35e43ecdd1f8658b3ee75552ded132aaa22 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Tue, 31 Oct 2023 00:34:01 +0100
Subject: [PATCH 26/44] faster account skip startup
---
.../ui/account/AccountSelectActivity.kt | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
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 152c19dc..a2c34bf0 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
@@ -22,6 +22,15 @@ class AccountSelectActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ val accounts = getAccounts(this@AccountSelectActivity)
+
+ // Don't show account selection if there is only
+ // one account that exists
+ if (accounts.count() <= 1) {
+ navigateToMainActivity()
+ return
+ }
+
CommonActivity.init(this)
loadThemes(this)
@@ -35,14 +44,6 @@ class AccountSelectActivity : AppCompatActivity() {
val recyclerView: RecyclerView = binding.root.findViewById(R.id.account_recycler_view)
- val accounts = getAccounts(this@AccountSelectActivity)
-
- // Don't show account selection if there is only
- // one account that exists
- if (accounts.count() <= 1) {
- navigateToMainActivity()
- return
- }
val adapter = AccountAdapter(accounts) { selectedAccount ->
// Handle the selected account
From 6ce9f29331db4ad553447b17b5108a78945ad6b9 Mon Sep 17 00:00:00 2001
From: KingLucius
Date: Thu, 2 Nov 2023 21:50:49 +0200
Subject: [PATCH 27/44] Fix settings top bar on TV (#738)
---
.../cloudstream3/ui/settings/SettingsAccount.kt | 2 ++
.../cloudstream3/ui/settings/SettingsFragment.kt | 10 ++++++++++
.../cloudstream3/ui/settings/SettingsGeneral.kt | 3 ++-
.../cloudstream3/ui/settings/SettingsPlayer.kt | 2 ++
.../cloudstream3/ui/settings/SettingsProviders.kt | 2 ++
.../lagradost/cloudstream3/ui/settings/SettingsUI.kt | 2 ++
.../cloudstream3/ui/settings/SettingsUpdates.kt | 2 ++
7 files changed, 22 insertions(+), 1 deletion(-)
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 b3225d5c..aa5a3182 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
@@ -30,6 +30,7 @@ import com.lagradost.cloudstream3.syncproviders.OAuth2API
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
@@ -248,6 +249,7 @@ class SettingsAccount : PreferenceFragmentCompat() {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_account)
setPaddingBottom()
+ setToolBarScrollFlags()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
index a4d19eba..cb48b086 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
@@ -12,10 +12,12 @@ import android.widget.ImageView
import androidx.annotation.StringRes
import androidx.core.view.children
import androidx.core.view.isVisible
+import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
+import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.MainSettingsBinding
@@ -54,7 +56,15 @@ class SettingsFragment : Fragment() {
listView?.setPadding(0, 0, 0, 100.toPx)
}
}
+ fun PreferenceFragmentCompat.setToolBarScrollFlags() {
+ if (isTvSettings()) {
+ val settingsAppbar = view?.findViewById(R.id.settings_toolbar)
+ settingsAppbar?.updateLayoutParams {
+ scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL
+ }
+ }
+ }
fun Fragment?.setUpToolbar(title: String) {
if (this == null) return
val settingsToolbar = view?.findViewById(R.id.settings_toolbar) ?: return
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 17efd276..224ca74a 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
@@ -28,6 +28,7 @@ import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.ui.EasterEggMonke
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
@@ -115,6 +116,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_general)
setPaddingBottom()
+ setToolBarScrollFlags()
}
data class CustomSite(
@@ -192,7 +194,6 @@ class SettingsGeneral : PreferenceFragmentCompat() {
return@setOnPreferenceClickListener true
}
-
fun showAdd() {
val providers = synchronized(allProviders) { allProviders.distinctBy { it.javaClass }.sortedBy { it.name } }
activity?.showDialog(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
index e10a5a1a..3d0bcb1f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
@@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
@@ -23,6 +24,7 @@ class SettingsPlayer : PreferenceFragmentCompat() {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_player)
setPaddingBottom()
+ setToolBarScrollFlags()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard()
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 7e57fc5b..7dc73a46 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
@@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
@@ -25,6 +26,7 @@ class SettingsProviders : PreferenceFragmentCompat() {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_providers)
setPaddingBottom()
+ setToolBarScrollFlags()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
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 e2fd24ca..8c720313 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
@@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
@@ -23,6 +24,7 @@ class SettingsUI : PreferenceFragmentCompat() {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_ui)
setPaddingBottom()
+ setToolBarScrollFlags()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
index 2f796801..9f72c1d5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
@@ -22,6 +22,7 @@ import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.services.BackupWorkManager
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.BackupUtils
import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt
@@ -42,6 +43,7 @@ class SettingsUpdates : PreferenceFragmentCompat() {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_updates)
setPaddingBottom()
+ setToolBarScrollFlags()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
From 199f5b3a9d23297cb8f772e102619339452e9ff2 Mon Sep 17 00:00:00 2001
From: "Weblate (bot)"
Date: Thu, 2 Nov 2023 20:58:52 +0100
Subject: [PATCH 28/44] Translations update from Hosted Weblate (#681)
Co-authored-by: Ahmed Abd El-Fattah
Co-authored-by: Fjuro
Co-authored-by: Fqwe1
Co-authored-by: Giuseppe Terrana
Co-authored-by: Luna712 <142361265+Luna712@users.noreply.github.com>
Co-authored-by: Rex_sa
Co-authored-by: gallegonovato
Co-authored-by: ngocanhtve
---
app/src/main/res/values-ar/strings.xml | 50 ++++++++++++----
app/src/main/res/values-cs/strings.xml | 49 ++++++++++++----
app/src/main/res/values-es/strings.xml | 58 ++++++++++++++-----
app/src/main/res/values-it/strings.xml | 34 +++++++++--
app/src/main/res/values-uk/strings.xml | 54 ++++++++++++-----
app/src/main/res/values-vi/strings.xml | 33 ++++++++++-
app/src/main/res/values/strings.xml | 6 +-
.../metadata/android/es-ES/changelogs/2.txt | 1 +
.../android/es-ES/full_description.txt | 10 ++++
.../android/es-ES/short_description.txt | 1 +
fastlane/metadata/android/es-ES/title.txt | 1 +
11 files changed, 234 insertions(+), 63 deletions(-)
create mode 100644 fastlane/metadata/android/es-ES/changelogs/2.txt
create mode 100644 fastlane/metadata/android/es-ES/full_description.txt
create mode 100644 fastlane/metadata/android/es-ES/short_description.txt
create mode 100644 fastlane/metadata/android/es-ES/title.txt
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index f73081f7..15d22412 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -12,8 +12,8 @@
سرعة (%.2fx)
تقييم: %.1f
- !تم العثور على تحديث جديد
-\n%s -> %s
+ يوجد تحديث جديد!
+\n%1$s -> %2$s
%d دقيقة
CloudStream
تشغيل بواسطة CloudStream
@@ -319,7 +319,7 @@
Kitsu
Trakt
-->
- %s %s
+ %1$s %2$s
حساب
تسجيل الخروج
تسجيل الدخول
@@ -418,8 +418,8 @@
تم إزالة الإضافة
تعذر التحميل %s
18+
- بدأ تنزيل %d %s …
- تم التنزيل %d %s
+ بدأ تنزيل %1$d %2$s…
+ تم تنزيل %1$d %2$s
جميع %s محملة بالفعل
تحميل مكثف
إضافة
@@ -461,11 +461,11 @@
السجل
عرض زر تخطي المقدمة/الخاتمة
طاقم العمل: %s
- %d يوم %d ساعة %d دقيقة
- %d ساعة %d دقيقة
+ %1$d يوم %2$d ساعة %3$d دقيقة
+ %1$d ساعة %2$d دقيقة
الفيلير
فتح(تشغيل)
- %s %d%s
+ %1$s %2$d%3$s
المكونات الإضافية المحدثة %d
VLC
MPV
@@ -482,7 +482,7 @@
علّمه كفيديو تمت مشاهدته
نعم
ﻻ
- %s الحلقة %d
+ %1$s الحلقة %2$d
سيتم إصدار الحلقة %d في
تعذر تثبيت الإصدار الجديد من التطبيق
تثبيت الإضافة أولا
@@ -493,8 +493,8 @@
تنزيل تحديث التطبيق…
تثبيت تحديث التطبيق…
%d دقيقة
- %d-%d
- %d %s
+ %1$d-%2$d
+ %1$d %2$s
هل أنت متأكد أنك تريد الخروج؟
قم بتثبيت جميع المكونات الإضافية التي لم يتم تثبيتها بعد تلقائيا من المستودعات المضافة.
مثبت الحزم
@@ -584,4 +584,30 @@
المستودع لم يتم العثور عليه، تحقق من العنوان اوجرب شبكة افتراضية خاصة(vpn)
لقد صوتت بالفعل
معدل النسخ الإحتياطي
-
+ تمت إزالة %s من المفضلة
+ المفضلة
+ تمت إضافة %s إلى المفضلة
+ احتمال وجود تكرارات في مكتبتك.
+\n
+\n%s
+\n
+\nهل تريد الاضافة على اي حال مستبدلاً النسخة الموجودة بالفعل, أم تفضل إلغاء العملية؟
+ احتمال أن يكون موجود بالفعل
+ قفل الحساب
+ اضافة الى المفضلة
+ تبديل الكل
+ رقم PIN غير صحيح. برجاء المحاولة مرة اخرى.
+ إلغاء الاشتراك
+ رقم ال PIN يجب ان يكون 4 ارقام
+ استبدال
+ اضافة
+ إشترك
+ إزالة من المفضلة
+ اختار حساب
+ من الظاهر أن \"%1$s\" موجود بالفعل في مكتبتك.
+\n
+\nهل تريد الاضافة على أي حال مستبدلاً القديم أو إلغاء العملية؟
+ ادخال ال PIN
+ PIN
+ أدخل ال PIN الحالي
+
\ No newline at end of file
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 8bdc1a27..ae93803d 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -2,7 +2,7 @@
- %s Ep %d
+ %1$s Ep %2$d
Hrají: %s
Plakát
@@ -17,7 +17,7 @@
Rychlost (%.2fx)
Hodnocení: %.1f
Nalezena nová aktualizace!
-\n%s -> %s
+\n%1$s -> %2$s
Výplň
%d min
CloudStream
@@ -292,7 +292,7 @@
Kitsu
Trakt
-->
- %s %s
+ %1$s %2$s
účet
Odhlásit se
Přihlásit se
@@ -410,17 +410,17 @@
Příliš mnoho textu. Nepodařilo se uložit do schránky.
Ano
Prohlížeč
- %d-%d
+ %1$d-%2$d
Knihovna
Zobrazit plakáty z Kitsu
Automaticky stahovat doplňky
Znovu provést proces nastavení
Instalátor APK
- %d %s
+ %1$d %2$s
Některé telefony nepodporují nový instalátor balíčků. Pokud se aktualizace nenainstalují, zkuste použít starší možnost.
Mezipaměť
Epizoda %d bude vydána za
- %dh %dm
+ %1$dh %2$dm
Přehrát přímý přenos
Rozšíření
Akce
@@ -436,7 +436,7 @@
Co chcete vidět
Doplněk stažen
18+
- Spuštěno stahování %d %s…
+ Spuštěno stahování %1$d %2$s…
CloudStream nemá ve výchozím nastavení nainstalované žádné weby. Stránky je třeba nainstalovat z úložišť.
\n
\nKvůli nesmyslnému podání stížnosti DMCA společností Sky UK Limited 🤮 nemůžeme v aplikaci propojit stránky repozitářů.
@@ -505,12 +505,12 @@
Aktualizace aplikace
Hotovo
Podporováno
- %s %d%s
+ %1$s %2$d%3$s
Živý přenos
NSFW
Rozšíření
Přehrát trailer
- %dd %dh %dm
+ %1$dd %2$dh %3$dm
Zobrazit komunitní repozitáře
Aktualizace zahájena
Stream
@@ -520,7 +520,7 @@
Referent
Další
Sledovat videa v těchto jazycích
- Staženo %d %s
+ Staženo %1$d %2$s
Všechny %s jsou již staženy
Hromadné stahování
doplněk
@@ -575,4 +575,31 @@
V repozitáři nebyly nalezeny žádné doplňky
Repozitář nenalezen, zkontrolujte adresu URL a zkuste použít VPN
Již jste hlasovali
-
+ %s odebráno z oblíbených
+ Oblíbené
+ %s přidáno do oblíbených
+ Ve vaší knihovně byl nalezen potenciální duplikát:
+\n
+\n%s
+\n
+\nChcete přesto přidat tuto položku, nahradit existující nebo zrušit akci\?
+ Frekvence záloh
+ Nalezena potenciální duplicita
+ Zamknout profil
+ Přidat do oblíbených
+ Nahradit vše
+ Nesprávný PIN. Zkuste to prosím znovu.
+ Zrušit odběr
+ PIN musí obsahovat 4 znaky
+ Nahradit
+ Přidat
+ Odebírat
+ Odebrat z oblíbených
+ Vyberte účet
+ Vypadá to, že ve vaší knihovně již existuje potenciální duplikát: „%1$s“.
+\n
+\nChcete přesto přidat tuto položku, nahradit existující nebo zrušit akci\?
+ Zadejte PIN
+ PIN
+ Zadejte současný PIN
+
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 69086840..e292e0ba 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -4,10 +4,10 @@
Descargue la lista de sitios que quiera utilizar
Descargado:%d
Descargado
- Descargado %d %s
+ Descargado %1$d %2$s
Borrar repositorio
El episodio %d se lanzará en
- %dh %dm
+ %1$dh %2$dm
%dm
Poster
Extensiones
@@ -97,12 +97,12 @@
Poster
Siguiente al azar
Todos los Idiomas
- Volver
+ Regresar
Cambiar proveedor
Vista previa del fondo
Nota:%.1f
- Nueva actualización encontrada!
-\n%s -> %s
+ ¡Nueva actualización encontrada!
+\n%1$s -> %2$s
Descargar
Pausar Descarga
Formato de fuente
@@ -110,8 +110,8 @@
Tamaño de Fuente
Velocidad (%.2fx)
Omitir carga
- %s Ep %d
- %dd %dh %dm
+ %1$s Ep. %2$d
+ %1$dd %2$dh %3$dm
Elenco %s
Relleno
%d min
@@ -145,7 +145,7 @@
Transmitir Torrent
Fuentes
Reintentar conexión…
- Volver
+ Regresar
Descargando
Descarga pausada
Descarga iniciada
@@ -218,8 +218,8 @@
Reproducir Episodio
Episodio
Episodios
- %d-%d
- %d %s
+ %1$d-%2$d
+ %1$d %2$s
E
Falló la restauración de los datos desde el archivo %s
Datos guardados
@@ -233,7 +233,7 @@
Mostrar los resultados de la búsqueda por proveedor
Solo envíar los datos si la App se cierra / falla inesperadamente
No enviar datos
- Mostrar Trailers (avances)
+ Mostrar los trailers
Mostrar pósters de Kitsu
Actualizar a las versiones preliminares
Buscar actualizaciones preliminares (beta) en lugar de solo versiones completas (stable releases)
@@ -249,7 +249,7 @@
Reiniciar a valores predefinidos
Lo sentimos, la aplicación se bloqueó. Se enviará un informe de error anónimo a los desarrolladores
Temporada
- %s %d%s
+ %1$s %2$d%3$s
Ninguna Temporada
T
Borrar Archivo
@@ -311,7 +311,7 @@
Cambiar cuenta
Añadir cuenta
Sincronizar
- Calificación
+ Clasificado
%s autenticado
No se pudo autenticar a %s
Recomendado
@@ -345,7 +345,7 @@
Color primario
Tema de la aplicación
hola@mundo.com
- %s %s
+ %1$s %2$s
Todo
1000ms
Sin retraso de subtítulos
@@ -448,7 +448,7 @@
Hecho
Plugin Cargado
18+
- Iniciada la descarga %d %s…
+ Comenzó la descarga de %1$d %2$s…
Descarga por lotes
plugin
plugins
@@ -552,4 +552,30 @@
Repositorio no encontrado, comprueba la URL y prueba la VPN
Ya has votado
Frecuencia de la copia de seguridad
-
+ %s eliminado de favoritos
+ Favoritos
+ %s añadido a favoritos
+ Se han encontrado posibles elementos duplicados en su biblioteca:
+\n
+\n%s
+\n
+\n¿Desea añadir este elemento de todos modos, sustituir los existentes o cancelar la acción\?
+ Posible duplicado encontrado
+ Perfil de bloqueo
+ Añadido a favoritos
+ Sustituir todo
+ PIN incorrecto. Por favor, inténtelo de nuevo.
+ Cancelar la suscripción
+ El PIN debe tener 4 caracteres
+ Sustituir
+ Añadir
+ Suscríbase
+ Eliminar de favoritos
+ Seleccione una cuenta
+ Parece que ya existe un elemento potencialmente duplicado en su biblioteca: \'%s.\'
+\n
+\n¿Desea añadir este elemento de todos modos, sustituir el existente o cancelar la acción\?
+ Introducir el PIN
+ PIN
+ Introduzca el PIN actual
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 933ac77f..72db056c 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -20,7 +20,7 @@
Velocità (%.2fx)
Valutato: %.1f
Nuovo aggiornamento trovato!
-\n%s -> %s
+\n%1$s -> %2$s
Filler
%d min
@@ -409,8 +409,8 @@
Plugin eliminato
Impossibile caricare %s
18+
- Download iniziato %d %s…
- Scaricato %d %s
+ Download iniziato %1$d %2$s…
+ Scaricato %1$d %2$s
Tutti %s già scaricati
Download in blocco
plugin
@@ -573,4 +573,30 @@
Seleziona la modalità per filtrare il download dei plugin
Disabilita
Hai già votato
-
+ %s rimosso dai preferiti
+ Preferiti
+ %s aggiunto ai preferiti
+ Dei possibili duplicati sono stati trovati nella tua libreria:
+\n
+\n%s
+\n
+\nVorresti aggiungere l\'oggetto alla libreria comunque, rimpiazzare l\'esistente, o cancellare l\'azione\?
+ Frequenza di backup
+ Trovato Possibile Duplicato
+ Aggiungi ai preferiti
+ Rimpiazza tutti
+ PIN non corretto. Riprova.
+ Disiscriviti
+ Il PIN deve essere almeno di 4 caratteri
+ Rimpiazza
+ Aggiungi
+ Iscriviti
+ Rimuovi dai preferiti
+ Seleziona un Account
+ Sembra che un oggetto potenziale duplicato sia già presente nella tua libreria: \'%1$s.\'
+\n
+\nVorresti aggiungere l\'oggetto lo stesso, rimpiazzare l\'esistente, o cancellare l\'azione\?
+ Inserisci PIN
+ PIN
+ Inserisci PIN Corrente
+
\ No newline at end of file
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 57b128de..c53de272 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -9,16 +9,16 @@
Актори: %s
Епізод %d вийде через
Poster
- %s Еп. %d
- %dд %dгод %dхв
- %dгод %dхв
+ %1$s Еп. %2$d
+ %1$dд %2$dгод %3$dхв
+ %1$dгод %2$dхв
%dхв
Головний постер
Наступний випадковий
Попередній перегляд фону
Швидкість (%.2fx)
Знайдено нове оновлення!
-\n%s –> %s
+\n%1$s –> %2$s
Пошук
Завантаження
%d хв
@@ -112,7 +112,7 @@
Переглянути файл
Детальніше
Фільтр закладок
- Очистити
+ Очистити
Налаштування субтитрів
Колір фону
Висота субтитрів
@@ -167,7 +167,7 @@
Скинути до значення за замовчуванням
Немає сезону
епізодів
- %d %s
+ %1$d %2$s
С
Е
Видалити файл
@@ -238,9 +238,9 @@
Рік
+30
Вибачте, у застосунку стався збій. Анонімне повідомлення про помилку буде відправлено розробникам
- %s %d%s
+ %1$s %2$d%3$s
Епізод
- %d-%d
+ %1$d-%2$d
Епізодів не знайдено
Пауза
Сезон
@@ -364,7 +364,7 @@
Макет
Постачальники
example.com
- %s %s
+ %2$s %1$s
Депресивний
обліковий запис
Створити
@@ -414,8 +414,8 @@
Плагін завантажено
Плагін завантажено
Не вдалося завантажити %s
- Почалося завантаження %d %s…
- Завантажено %d %s
+ Почалося завантаження %1$d %2$s…
+ Завантажено %1$d %2$s
Всі %s вже завантажено
Завантажити пакети
плагін
@@ -499,7 +499,7 @@
Ваша бібліотека порожня :(
\nУвійдіть в обліковий запис бібліотеки або додайте фільми до вашої локальної бібліотеки.
Алфавітом (від Я до А)
- Виберіть бібліотеку
+ Оберіть бібліотеку
Відкрити
Браузер
Цей список порожній. Спробуйте перейти до іншого.
@@ -546,10 +546,36 @@
Якості
Фон профілю
Не вдалося створити UI коректно, це ВАЖЛИВА ПОМИЛКА, про яку слід негайно повідомити %s
- Виберіть режим для фільтрації завантаження плагінів
+ Оберіть режим для фільтрації завантаження плагінів
Вимкнути
Репозиторій не знайдено, перевірте URL-адресу та спробуйте VPN
Не знайдено жодних плагінів у репозиторії
Ви вже проголосували
Частота резервного копіювання
-
+ %s вилучено з обраного
+ Обране
+ %s додано до обраного
+ У вашій бібліотеці знайдено потенційні дублікати:
+\n
+\n%s
+\n
+\nВсе одно хочете додати цей елемент, замінити наявні чи скасувати дію\?
+ Знайдено потенційний дублікат
+ Розблокувати профіль
+ Додати до обраного
+ Замінити усе
+ Неправильний PIN-код. Спробуйте ще раз.
+ Відписатись
+ PIN-код повинен складатися з 4 символів
+ Замінити
+ Додати
+ Підписатись
+ Вилучити з обраного
+ Оберіть обліковий запис
+ Виявилося, що у вашій бібліотеці вже є потенційно повторюваний елемент: \'%1$s.\'
+\n
+\nВсе одно хочете додати цей елемент, замінити наявний чи скасувати дію\?
+ Введіть PIN-код
+ PIN-код
+ Введіть поточний PIN-код
+
\ No newline at end of file
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 8186d6ed..81575604 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -409,8 +409,8 @@
Plugin đã xoá
Không tải được %s
18+
- Bắt đầu tải %d %s…
- Tải xuống %d %s thành công
+ Đã bắt đầu tải xuống %1$d %2$s…
+ Đã tải xuống %1$d %2$s
Toàn bộ %s đã được tải xuống
Tải hàng loạt
plugin
@@ -566,4 +566,31 @@
Không tìm thấy plugin
Không thể khởi tạo UI, đây là một LỖI LỚN và cần được báo cáo ngay lập tức tới %s
Chọn chế độ để lọc plugin tải xuống
-
+ %s đã loại bỏ khỏi mục yêu thích
+ Yêu thích
+ %s đã thêm vào mục yêu thích
+ Các mục có thể trùng lặp đã được tìm thấy trong thư viện của bạn:
+\n
+\n%s
+\n
+\nBạn vẫn muốn thêm mục này, thay thế những mục hiện có hay hủy hành động\?
+ Tần suất sao lưu
+ Đã tìm thấy bản sao tiềm năng
+ Khóa hồ sơ
+ Thêm vào mục yêu thích
+ Thay thế tất cả
+ Mã PIN không chính xác. Vui lòng thử lại.
+ Hủy đăng ký
+ Mã PIN phải có 4 ký tự
+ Thay thế
+ Thêm vào
+ Đăng ký
+ Loại bỏ khỏi mục yêu thích
+ Chọn một tài khoản
+ Có vẻ như một mục có khả năng trùng lặp đã tồn tại trong thư viện của bạn: \'%1$s.\'
+\n
+\nBạn vẫn muốn thêm mục này, thay thế mục hiện có hay hủy hành động\?
+ Nhập PIN
+ PIN
+ Nhập mã PIN hiện tại
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 351315f4..9e0575da 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -701,9 +701,9 @@
Add
Replace
Replace All
- @string/sort_cancel
-
- It appears that a potentially duplicate item already exists in your library: \'%1$s.\'
+ @string/cancel
+
+ It appears that a potentially duplicate item already exists in your library: \'%s.\'
\n\nWould you like to add this item anyway, replace the existing one, or cancel the action?
diff --git a/fastlane/metadata/android/es-ES/changelogs/2.txt b/fastlane/metadata/android/es-ES/changelogs/2.txt
new file mode 100644
index 00000000..68e0b99b
--- /dev/null
+++ b/fastlane/metadata/android/es-ES/changelogs/2.txt
@@ -0,0 +1 @@
+- ¡Cambios añadidos!
diff --git a/fastlane/metadata/android/es-ES/full_description.txt b/fastlane/metadata/android/es-ES/full_description.txt
new file mode 100644
index 00000000..9f6accba
--- /dev/null
+++ b/fastlane/metadata/android/es-ES/full_description.txt
@@ -0,0 +1,10 @@
+CloudStream-3 te permite ver y descargar películas, series de TV y anime.
+
+La aplicación viene sin ningún tipo de anuncios y análisis y
+soporta múltiples tráilers y páginas de películas, y más, por ejemplo
+
+Marcadores
+
+Descargas de subtítulos
+
+Compatible con Chromecast
diff --git a/fastlane/metadata/android/es-ES/short_description.txt b/fastlane/metadata/android/es-ES/short_description.txt
new file mode 100644
index 00000000..eb1d11e8
--- /dev/null
+++ b/fastlane/metadata/android/es-ES/short_description.txt
@@ -0,0 +1 @@
+Vea y descargue películas, series de televisión y anime.
diff --git a/fastlane/metadata/android/es-ES/title.txt b/fastlane/metadata/android/es-ES/title.txt
new file mode 100644
index 00000000..dde89d58
--- /dev/null
+++ b/fastlane/metadata/android/es-ES/title.txt
@@ -0,0 +1 @@
+CloudStream
From 6f40d2750f1ae5b04aa4c75a93e6ef1baedeea1b Mon Sep 17 00:00:00 2001
From: "recloudstream[bot]"
<111277985+recloudstream[bot]@users.noreply.github.com>
Date: Thu, 2 Nov 2023 19:59:11 +0000
Subject: [PATCH 29/44] chore(locales): fix locale issues
---
app/src/main/res/values-ar/strings.xml | 2 +-
app/src/main/res/values-cs/strings.xml | 2 +-
app/src/main/res/values-es/strings.xml | 2 +-
app/src/main/res/values-it/strings.xml | 2 +-
app/src/main/res/values-uk/strings.xml | 2 +-
app/src/main/res/values-vi/strings.xml | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 15d22412..56e19280 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -610,4 +610,4 @@
ادخال ال PIN
PIN
أدخل ال PIN الحالي
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index ae93803d..275553a9 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -602,4 +602,4 @@
Zadejte PIN
PIN
Zadejte současný PIN
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index e292e0ba..32e2b61d 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -578,4 +578,4 @@
Introducir el PIN
PIN
Introduzca el PIN actual
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 72db056c..d6be0eed 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -599,4 +599,4 @@
Inserisci PIN
PIN
Inserisci PIN Corrente
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index c53de272..425c5257 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -578,4 +578,4 @@
Введіть PIN-код
PIN-код
Введіть поточний PIN-код
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 81575604..d27660c7 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -593,4 +593,4 @@
Nhập PIN
PIN
Nhập mã PIN hiện tại
-
\ No newline at end of file
+
From 908f83c50ea960b939ab2d7561a66eff180e0c6e Mon Sep 17 00:00:00 2001
From: KingLucius
Date: Thu, 2 Nov 2023 22:03:00 +0200
Subject: [PATCH 30/44] Fix scroll for Library TV layout (#695)
* Fix scroll for Library TV layout
* Fixed without NestedScrollView
---
.../ui/library/LibraryFragment.kt | 10 ++++++++
.../ui/library/ViewpagerAdapter.kt | 17 +++++++++++--
.../main/res/layout/fragment_library_tv.xml | 25 ++++++++++---------
3 files changed, 38 insertions(+), 14 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
index 7cc57f5d..864ca065 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
@@ -27,6 +27,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.viewpager2.widget.ViewPager2
import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import com.lagradost.cloudstream3.APIHolder
@@ -137,6 +138,10 @@ class LibraryFragment : Fragment() {
binding?.libraryRoot?.findViewById(R.id.search_src_text)?.apply {
tag = "tv_no_focus_tag"
+ //Expand the Appbar when search bar is focused, fixing scroll up issue
+ setOnFocusChangeListener { _, _ ->
+ binding?.searchBar?.setExpanded(true)
+ }
}
// Set the color for the search exit icon to the correct theme text color
@@ -342,6 +347,7 @@ class LibraryFragment : Fragment() {
binding?.apply {
viewpager.offscreenPageLimit = 2
viewpager.reduceDragSensitivity()
+ searchBar.setExpanded(true)
}
val startLoading = Runnable {
@@ -441,6 +447,10 @@ class LibraryFragment : Fragment() {
val distance = abs(position - currentItem)
hideViewpager(distance)
}
+ //Expand the appBar on tab focus
+ tab.view.setOnFocusChangeListener { view, b ->
+ binding?.searchBar?.setExpanded(true)
+ }
}.attach()
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt
index 76028487..6731eae2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt
@@ -1,14 +1,18 @@
package com.lagradost.cloudstream3.ui.library
import android.os.Build
+import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.doOnAttach
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnFlingListener
+import com.google.android.material.appbar.AppBarLayout
+import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.LibraryViewpagerPageBinding
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
class ViewpagerAdapter(
@@ -67,6 +71,17 @@ class ViewpagerAdapter(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
val diff = scrollY - oldScrollY
+
+ //Expand the top Appbar based on scroll direction up/down, simulate phone behavior
+ if (SettingsFragment.isTvSettings()) {
+ binding.root.rootView.findViewById(R.id.search_bar)
+ .apply {
+ if (diff <= 0)
+ setExpanded(true)
+ else
+ setExpanded(false)
+ }
+ }
if (diff == 0) return@setOnScrollChangeListener
scrollCallback.invoke(diff > 0)
@@ -80,8 +95,6 @@ class ViewpagerAdapter(
}
}
}
-
-
}
}
diff --git a/app/src/main/res/layout/fragment_library_tv.xml b/app/src/main/res/layout/fragment_library_tv.xml
index 22b9feb1..6d2198e9 100644
--- a/app/src/main/res/layout/fragment_library_tv.xml
+++ b/app/src/main/res/layout/fragment_library_tv.xml
@@ -27,7 +27,8 @@
android:id="@+id/search_status_bar_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ app:layout_scrollFlags="scroll|enterAlways">
+
-
+
-
Date: Fri, 3 Nov 2023 01:37:34 +0530
Subject: [PATCH 31/44] bump navigation lib (#749)
---
app/build.gradle.kts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index b82d26e1..e0e4bb1d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -164,8 +164,8 @@ dependencies {
implementation("com.google.android.material:material:1.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
- implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
- implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
+ implementation("androidx.navigation:navigation-fragment-ktx:2.7.5")
+ implementation("androidx.navigation:navigation-ui-ktx:2.7.5")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
From 5b0cbbf09f7632af4eea8c14265e2859d0cf6f6b Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Thu, 2 Nov 2023 14:14:16 -0600
Subject: [PATCH 32/44] Use nicer grid layout for account select on TV (#737)
---
.../cloudstream3/ui/account/AccountAdapter.kt | 8 ------
.../ui/account/AccountSelectActivity.kt | 18 ++++++-------
.../res/layout/activity_account_select.xml | 2 +-
.../res/layout/activity_account_select_tv.xml | 27 -------------------
4 files changed, 10 insertions(+), 45 deletions(-)
delete mode 100644 app/src/main/res/layout/activity_account_select_tv.xml
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
index 72551199..aea55392 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
@@ -43,14 +43,6 @@ class AccountAdapter(
LayoutInflater.from(parent.context), parent, false
)
- if (isTvSettings()) {
- val layoutParams = binding.root.layoutParams as RecyclerView.LayoutParams
- val marginInDp = 5 // Set the margin to 5dp
- val marginInPixels = (marginInDp * parent.resources.displayMetrics.density).toInt()
- layoutParams.setMargins(marginInPixels, marginInPixels, marginInPixels, marginInPixels)
- binding.root.layoutParams = layoutParams
- }
-
return AccountViewHolder(binding)
}
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 a2c34bf0..457d4b81 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
@@ -4,14 +4,12 @@ import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.CommonActivity.loadThemes
import com.lagradost.cloudstream3.MainActivity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.ActivityAccountSelectBinding
-import com.lagradost.cloudstream3.databinding.ActivityAccountSelectTvBinding
import com.lagradost.cloudstream3.ui.account.AccountDialog.showPinInputDialog
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.DataStoreHelper
@@ -36,13 +34,11 @@ class AccountSelectActivity : AppCompatActivity() {
window.navigationBarColor = colorFromAttribute(R.attr.primaryBlackBackground)
- val binding = if (isTvSettings()) {
- ActivityAccountSelectTvBinding.inflate(layoutInflater)
- } else ActivityAccountSelectBinding.inflate(layoutInflater)
+ val binding = ActivityAccountSelectBinding.inflate(layoutInflater)
setContentView(binding.root)
- val recyclerView: RecyclerView = binding.root.findViewById(R.id.account_recycler_view)
+ val recyclerView: RecyclerView = binding.accountRecyclerView
val adapter = AccountAdapter(accounts) { selectedAccount ->
@@ -51,9 +47,13 @@ class AccountSelectActivity : AppCompatActivity() {
}
recyclerView.adapter = adapter
- recyclerView.layoutManager = if (isTvSettings()) {
- LinearLayoutManager(this)
- } else GridLayoutManager(this, 2)
+ if (isTvSettings()) {
+ val spanSize = if (accounts.count() <= 6) {
+ accounts.count()
+ } else 6
+
+ recyclerView.layoutManager = GridLayoutManager(this, spanSize)
+ }
}
private fun onAccountSelected(selectedAccount: DataStoreHelper.Account) {
diff --git a/app/src/main/res/layout/activity_account_select.xml b/app/src/main/res/layout/activity_account_select.xml
index 9138f82d..d5870f24 100644
--- a/app/src/main/res/layout/activity_account_select.xml
+++ b/app/src/main/res/layout/activity_account_select.xml
@@ -17,7 +17,7 @@
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp" />
-
-
-
-
-
-
-
-
-
\ No newline at end of file
From a6786aaf9860df0992e704bc3b3f1bed50360203 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Thu, 2 Nov 2023 17:28:25 -0600
Subject: [PATCH 33/44] Add done button for when creating new PINs (#742)
* Add done button for when creating new PINs
* Cleanup
---
.../cloudstream3/ui/account/AccountDialog.kt | 101 ++++++++++--------
1 file changed, 59 insertions(+), 42 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt
index dfd8831b..76686aef 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt
@@ -1,18 +1,17 @@
package com.lagradost.cloudstream3.ui.account
import android.content.Context
-import android.text.Editable
-import android.text.TextWatcher
import android.view.LayoutInflater
-import android.view.View
import android.view.inputmethod.EditorInfo
-import android.view.inputmethod.InputMethodManager
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
+import androidx.core.view.isVisible
+import androidx.core.widget.doOnTextChanged
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.LockPinDialogBinding
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
+import com.lagradost.cloudstream3.utils.UIHelper.showInputMethod
object AccountDialog {
// TODO add account creation dialog to allow creating accounts directly from AccountSelectActivity
@@ -21,14 +20,18 @@ object AccountDialog {
context: Context,
currentPin: String?,
editAccount: Boolean,
+ errorText: String? = null,
callback: (String?) -> Unit
) {
fun TextView.visibleWithText(@StringRes textRes: Int) {
- visibility = View.VISIBLE
+ isVisible = true
setText(textRes)
}
- fun View.isVisible() = visibility == View.VISIBLE
+ fun TextView.visibleWithText(text: String?) {
+ isVisible = true
+ setText(text)
+ }
val binding = LockPinDialogBinding.inflate(LayoutInflater.from(context))
@@ -38,7 +41,9 @@ object AccountDialog {
val titleRes = if (isEditPin) R.string.enter_current_pin else R.string.enter_pin
- val dialog = AlertDialog.Builder(context, R.style.AlertDialogCustom)
+ var isPinValid = false
+
+ val builder = AlertDialog.Builder(context, R.style.AlertDialogCustom)
.setView(binding.root)
.setTitle(titleRes)
.setNegativeButton(R.string.cancel) { _, _ ->
@@ -48,46 +53,59 @@ object AccountDialog {
callback.invoke(null)
}
.setOnDismissListener {
- if (binding.pinEditTextError.isVisible()) {
+ if (!isPinValid) {
callback.invoke(null)
}
}
- .create()
- var isPinValid = false
-
- binding.pinEditText.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
-
- override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
- val enteredPin = s.toString()
- val isEnteredPinValid = enteredPin.length == 4
-
- if (isEnteredPinValid) {
- if (isPinSet) {
- if (enteredPin != currentPin) {
- binding.pinEditTextError.visibleWithText(R.string.pin_error_incorrect)
- binding.pinEditText.text = null
- isPinValid = false
- } else {
- binding.pinEditTextError.visibility = View.GONE
- isPinValid = true
-
- callback.invoke(enteredPin)
- dialog.dismissSafe()
- }
- } else {
- binding.pinEditTextError.visibility = View.GONE
- isPinValid = true
- }
- } else if (isNewPin) {
- binding.pinEditTextError.visibleWithText(R.string.pin_error_length)
- isPinValid = false
+ if (isNewPin) {
+ if (errorText != null) binding.pinEditTextError.visibleWithText(errorText)
+ builder.setPositiveButton(R.string.setup_done) { _, _ ->
+ if (!isPinValid) {
+ // If the done button is pressed and there is an error,
+ // ask again, and mention the error that caused this.
+ showPinInputDialog(
+ context = binding.root.context,
+ currentPin = null,
+ editAccount = true,
+ errorText = binding.pinEditTextError.text.toString(),
+ callback = callback
+ )
+ } else {
+ val enteredPin = binding.pinEditText.text.toString()
+ callback.invoke(enteredPin)
}
}
+ }
- override fun afterTextChanged(s: Editable?) {}
- })
+ val dialog = builder.create()
+
+ binding.pinEditText.doOnTextChanged { text, _, _, _ ->
+ val enteredPin = text.toString()
+ val isEnteredPinValid = enteredPin.length == 4
+
+ if (isEnteredPinValid) {
+ if (isPinSet) {
+ if (enteredPin != currentPin) {
+ binding.pinEditTextError.visibleWithText(R.string.pin_error_incorrect)
+ binding.pinEditText.text = null
+ isPinValid = false
+ } else {
+ binding.pinEditTextError.isVisible = false
+ isPinValid = true
+
+ callback.invoke(enteredPin)
+ dialog.dismissSafe()
+ }
+ } else {
+ binding.pinEditTextError.isVisible = false
+ isPinValid = true
+ }
+ } else if (isNewPin) {
+ binding.pinEditTextError.visibleWithText(R.string.pin_error_length)
+ isPinValid = false
+ }
+ }
// Detect IME_ACTION_DONE
binding.pinEditText.setOnEditorActionListener { _, actionId, _ ->
@@ -108,8 +126,7 @@ object AccountDialog {
// Auto focus on PIN input and show keyboard
binding.pinEditText.requestFocus()
binding.pinEditText.postDelayed({
- val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- imm.showSoftInput(binding.pinEditText, InputMethodManager.SHOW_IMPLICIT)
+ showInputMethod(binding.pinEditText)
}, 200)
}
}
\ No newline at end of file
From 11136fe63d494ac800df3a52560237500ca024a3 Mon Sep 17 00:00:00 2001
From: self-similarity <137652432+self-similarity@users.noreply.github.com>
Date: Sun, 5 Nov 2023 22:33:11 +0000
Subject: [PATCH 34/44] Fix selecting sources in cast (#752)
---
.../java/com/lagradost/cloudstream3/utils/ExtractorApi.kt | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
index d89e67fa..923c3531 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.utils
import android.net.Uri
+import com.fasterxml.jackson.annotation.JsonIgnore
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.USER_AGENT
@@ -377,7 +378,8 @@ open class ExtractorLink constructor(
) : VideoDownloadManager.IDownloadableMinimum {
val isM3u8 : Boolean get() = type == ExtractorLinkType.M3U8
val isDash : Boolean get() = type == ExtractorLinkType.DASH
-
+
+ @JsonIgnore
fun getAllHeaders() : Map {
if (referer.isBlank()) {
return headers
@@ -920,4 +922,4 @@ abstract class ExtractorApi {
open fun getExtractorUrl(id: String): String {
return id
}
-}
\ No newline at end of file
+}
From 22a0c25d8309097efbe92ac6af945e8438307b60 Mon Sep 17 00:00:00 2001
From: Sofie <117321707+Sofie99@users.noreply.github.com>
Date: Fri, 10 Nov 2023 21:28:27 +0700
Subject: [PATCH 35/44] extractor: fixed Rabbitstream (#757)
* Extractor: added Rabbitstream
* Extractor: added Rabbitstream
* fixed Rabbitstream
---------
Co-authored-by: Sofie99
---
.../cloudstream3/extractors/Rabbitstream.kt | 36 ++++++++++---------
1 file changed, 19 insertions(+), 17 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt
index 0154b4e8..d5b52dd7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt
@@ -16,13 +16,13 @@ import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
-// No License found in https://github.com/enimax-anime/key
-// special credits to @enimax for providing key
+// Code found in https://github.com/theonlymo/keys
+// special credits to @theonlymo for providing key
class Megacloud : Rabbitstream() {
override val name = "Megacloud"
override val mainUrl = "https://megacloud.tv"
override val embed = "embed-2/ajax/e-1"
- override val key = "https://raw.githubusercontent.com/enimax-anime/key/e6/key.txt"
+ override val key = "https://raw.githubusercontent.com/theonlymo/keys/e1/key"
}
class Dokicloud : Rabbitstream() {
@@ -35,7 +35,7 @@ open class Rabbitstream : ExtractorApi() {
override val mainUrl = "https://rabbitstream.net"
override val requiresReferer = false
open val embed = "ajax/embed-4"
- open val key = "https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt"
+ open val key = "https://raw.githubusercontent.com/theonlymo/keys/e4/key"
override suspend fun getUrl(
url: String,
@@ -86,21 +86,23 @@ open class Rabbitstream : ExtractorApi() {
private suspend fun getRawKey(): String = app.get(key).text
- private fun extractRealKey(originalString: String?, stops: String): Pair {
- val table = parseJson>>(stops)
- val decryptedKey = StringBuilder()
- var offset = 0
- var encryptedString = originalString
+ private fun extractRealKey(sources: String, stops: String): Pair {
+ val decryptKey = parseJson>>(stops)
+ val sourcesArray = sources.toCharArray()
- table.forEach { (start, end) ->
- decryptedKey.append(encryptedString?.substring(start - offset, end - offset))
- encryptedString = encryptedString?.substring(
- 0,
- start - offset
- ) + encryptedString?.substring(end - offset)
- offset += end - start
+ var extractedKey = ""
+ var currentIndex = 0
+ for (index in decryptKey) {
+ val start = index[0] + currentIndex
+ val end = start + index[1]
+ for (i in start until end) {
+ extractedKey += sourcesArray[i].toString()
+ sourcesArray[i] = ' '
+ }
+ currentIndex += index[1]
}
- return decryptedKey.toString() to encryptedString.toString()
+
+ return extractedKey to sourcesArray.joinToString("")
}
private inline fun decryptMapped(input: String, key: String): T? {
From 7e2908c0bbd3fd242cd4576b58039cfe01a378b0 Mon Sep 17 00:00:00 2001
From: KingLucius
Date: Fri, 10 Nov 2023 16:36:38 +0200
Subject: [PATCH 36/44] Fix top bar in Extensions & Test settings (#753)
---
.../cloudstream3/ui/settings/SettingsFragment.kt | 9 +++++++++
.../ui/settings/extensions/ExtensionsFragment.kt | 3 ++-
.../ui/settings/extensions/PluginsFragment.kt | 2 ++
.../cloudstream3/ui/settings/testing/TestFragment.kt | 2 ++
4 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
index cb48b086..6ea6363e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
@@ -65,6 +65,15 @@ class SettingsFragment : Fragment() {
}
}
}
+ fun Fragment?.setToolBarScrollFlags() {
+ if (isTvSettings()) {
+ val settingsAppbar = this?.view?.findViewById(R.id.settings_toolbar)
+
+ settingsAppbar?.updateLayoutParams {
+ scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL
+ }
+ }
+ }
fun Fragment?.setUpToolbar(title: String) {
if (this == null) return
val settingsToolbar = view?.findViewById(R.id.settings_toolbar) ?: return
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
index 553e7675..f0b8a0bd 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
@@ -30,6 +30,7 @@ import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.ui.result.setText
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
@@ -85,7 +86,7 @@ class ExtensionsFragment : Fragment() {
//context?.fixPaddingStatusbar(extensions_root)
setUpToolbar(R.string.extensions)
-
+ setToolBarScrollFlags()
binding?.repoRecyclerView?.apply {
setLinearListLayout(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
index 172ea659..b490386f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
@@ -18,6 +18,7 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.ui.settings.appLanguages
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
@@ -73,6 +74,7 @@ class PluginsFragment : Fragment() {
return
}
+ setToolBarScrollFlags()
setUpToolbar(name)
binding?.settingsToolbar?.apply {
setOnMenuItemClickListener { menuItem ->
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt
index 59b1b856..3fbd1131 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt
@@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
@@ -27,6 +28,7 @@ class TestFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setUpToolbar(R.string.category_provider_test)
+ setToolBarScrollFlags()
super.onViewCreated(view, savedInstanceState)
binding?.apply {
From c4aab5e5a827a051dd089c568dcb1d22d0215c42 Mon Sep 17 00:00:00 2001
From: IndusAryan <125901294+IndusAryan@users.noreply.github.com>
Date: Fri, 10 Nov 2023 21:32:51 +0530
Subject: [PATCH 37/44] feat: make cloudstream fast boi, ksp migration (#689)
* migrate from kapt to ksp
* fook codefactor
---
app/build.gradle.kts | 19 +++++++++++++------
.../lagradost/cloudstream3/AcraApplication.kt | 3 ---
.../ui/settings/extensions/PluginAdapter.kt | 3 +--
.../lagradost/cloudstream3/utils/UIHelper.kt | 4 ++--
.../utils/VideoDownloadManager.kt | 2 +-
build.gradle.kts | 4 ++++
6 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index e0e4bb1d..12859af5 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -5,6 +5,7 @@ import java.net.URL
plugins {
id("com.android.application")
+ id("com.google.devtools.ksp")
id("kotlin-android")
id("kotlin-kapt")
id("org.jetbrains.dokka")
@@ -87,6 +88,11 @@ android {
)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ ksp {
+ arg("room.schemaLocation", "$projectDir/schemas")
+ arg("exportSchema", "true")
+ }
+
kapt {
includeCompileClasspath = true
}
@@ -181,9 +187,13 @@ dependencies {
implementation("androidx.preference:preference-ktx:1.2.1")
- implementation("com.github.bumptech.glide:glide:4.13.1")
- kapt("com.github.bumptech.glide:compiler:4.13.1")
- implementation("com.github.bumptech.glide:okhttp3-integration:4.13.0")
+ implementation("com.github.bumptech.glide:glide:4.15.1")
+ ksp("com.github.bumptech.glide:ksp:4.15.1")
+ implementation("com.github.bumptech.glide:okhttp3-integration:4.15.1")
+ // for ksp
+ ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
+ implementation("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
+ implementation("com.google.guava:guava:32.1.2-android")
implementation("jp.wasabeef:glide-transformations:4.3.0")
@@ -207,9 +217,6 @@ dependencies {
implementation("ch.acra:acra-core:5.11.2")
implementation("ch.acra:acra-toast:5.11.2")
- compileOnly("com.google.auto.service:auto-service-annotations:1.1.1")
- //either for java sources:
- annotationProcessor("com.google.auto.service:auto-service:1.1.1")
//or for kotlin sources (requires kapt gradle plugin):
kapt("com.google.auto.service:auto-service:1.1.1")
diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
index 5f3162b4..c93f0f9b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
@@ -8,7 +8,6 @@ import android.content.Intent
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
-import com.google.auto.service.AutoService
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.plugins.PluginManager
@@ -37,7 +36,6 @@ import java.lang.ref.WeakReference
import kotlin.concurrent.thread
import kotlin.system.exitProcess
-
class CustomReportSender : ReportSender {
// Sends all your crashes to google forms
override fun send(context: Context, errorContent: CrashReportData) {
@@ -65,7 +63,6 @@ class CustomReportSender : ReportSender {
}
}
-@AutoService(ReportSenderFactory::class)
class CustomSenderFactory : ReportSenderFactory {
override fun create(context: Context, config: CoreConfiguration): ReportSender {
return CustomReportSender()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt
index eb0082b8..c3fb4fc2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt
@@ -21,7 +21,6 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueT
import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
-import com.lagradost.cloudstream3.utils.GlideApp
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
import com.lagradost.cloudstream3.utils.UIHelper.setImage
@@ -87,7 +86,7 @@ class PluginAdapter(
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
if (holder is PluginViewHolder) {
holder.binding.entryIcon.let { pluginIcon ->
- GlideApp.with(pluginIcon).clear(pluginIcon)
+ com.bumptech.glide.Glide.with(pluginIcon).clear(pluginIcon)
}
}
super.onViewRecycled(holder)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
index d5357e0c..231a634d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
@@ -301,7 +301,7 @@ object UIHelper {
} ?: return false
return try {
- var builder = GlideApp.with(this)
+ var builder = com.bumptech.glide.Glide.with(this)
.load(glideImage)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.ALL).let { req ->
@@ -368,7 +368,7 @@ object UIHelper {
) {
if (this == null || url.isNullOrBlank()) return
try {
- val res = GlideApp.with(this)
+ val res = com.bumptech.glide.Glide.with(this)
.load(GlideUrl(url) { headers ?: emptyMap() })
.apply(bitmapTransform(BlurTransformation(radius, sample)))
.transition(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt
index d108daed..50a8df02 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt
@@ -234,7 +234,7 @@ object VideoDownloadManager {
return cachedBitmaps[url]
}
- val bitmap = GlideApp.with(this)
+ val bitmap = com.bumptech.glide.Glide.with(this)
.asBitmap()
.load(GlideUrl(url) { headers ?: emptyMap() })
.into(720, 720)
diff --git a/build.gradle.kts b/build.gradle.kts
index 762e4588..5d7ea527 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -22,6 +22,10 @@ allprojects {
}
}
+plugins {
+ id("com.google.devtools.ksp") version "1.8.20-1.0.11" apply false
+}
+
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}
\ No newline at end of file
From 3adf036135d7e4776a4cd8ac0aa1cb8a582a9c0a Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Fri, 10 Nov 2023 16:48:53 -0700
Subject: [PATCH 38/44] Fix some deprecations and other warnings (#750)
* Fix some deprecations and other warnings
---
app/src/main/AndroidManifest.xml | 16 ++--
.../lagradost/cloudstream3/MainActivity.kt | 75 ++++++++++++-------
.../ui/download/DownloadChildFragment.kt | 6 +-
.../ui/player/DownloadedPlayerActivity.kt | 14 +++-
.../ui/result/ResultTrailerPlayer.kt | 37 ++++++---
.../ui/settings/SettingsFragment.kt | 4 +-
.../ui/settings/extensions/PluginsFragment.kt | 4 +-
.../cloudstream3/utils/IOnBackPressed.kt | 5 --
.../lagradost/cloudstream3/utils/UIHelper.kt | 4 +-
.../main/res/xml/data_extraction_rules.xml | 3 +
10 files changed, 106 insertions(+), 62 deletions(-)
delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/IOnBackPressed.kt
create mode 100644 app/src/main/res/xml/data_extraction_rules.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 453c1fae..a71c5ecb 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,7 +6,7 @@
-
+
@@ -17,7 +17,11 @@
-
+
+
+
+ tools:targetApi="tiramisu">
-
+ android:exported="false">
+
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index a41028bd..3a10aa10 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -19,6 +19,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.Toast
+import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.IdRes
import androidx.annotation.MainThread
@@ -132,7 +133,6 @@ import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
import com.lagradost.cloudstream3.utils.Event
-import com.lagradost.cloudstream3.utils.IOnBackPressed
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState
@@ -650,34 +650,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
builder.show().setDefaultFocus()
}
- private fun backPressed() {
- this.window?.navigationBarColor =
- this.colorFromAttribute(R.attr.primaryGrayBackground)
- this.updateLocale()
- this.updateLocale()
-
- val navHostFragment =
- supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment
- val navController = navHostFragment?.navController
- val isAtHome =
- navController?.currentDestination?.matchDestination(R.id.navigation_home) == true
-
- if (isAtHome && isTvSettings()) {
- showConfirmExitDialog()
- } else {
- super.onBackPressed()
- }
- }
-
- override fun onBackPressed() {
- ((supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.childFragmentManager?.primaryNavigationFragment as? IOnBackPressed)?.onBackPressed()
- ?.let { runNormal ->
- if (runNormal) backPressed()
- } ?: run {
- backPressed()
- }
- }
-
override fun onDestroy() {
val broadcastIntent = Intent()
broadcastIntent.action = "restart_service"
@@ -1087,6 +1059,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} catch (_: Throwable) {
}
}
+
override fun onCreate(savedInstanceState: Bundle?) {
app.initClient(this)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
@@ -1384,6 +1357,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
this.putString(SearchFragment.SEARCH_QUERY, nextSearchQuery)
}
}
+
+ if (isTvSettings()) {
+ if (navDestination.matchDestination(R.id.navigation_home)) {
+ attachBackPressedCallback()
+ } else detachBackPressedCallback()
+ }
}
//val navController = findNavController(R.id.nav_host_fragment)
@@ -1598,6 +1577,44 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
// }
// }
+ onBackPressedDispatcher.addCallback(
+ this,
+ object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ window?.navigationBarColor = colorFromAttribute(R.attr.primaryGrayBackground)
+ updateLocale()
+
+ // If we don't disable we end up in a loop with default behavior calling
+ // this callback as well, so we disable it, run default behavior,
+ // then re-enable this callback so it can be used for next back press.
+ isEnabled = false
+ onBackPressedDispatcher.onBackPressed()
+ isEnabled = true
+ }
+ }
+ )
+ }
+
+ private var backPressedCallback: OnBackPressedCallback? = null
+
+ private fun attachBackPressedCallback() {
+ if (backPressedCallback == null) {
+ backPressedCallback = object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ showConfirmExitDialog()
+ window?.navigationBarColor =
+ colorFromAttribute(R.attr.primaryGrayBackground)
+ updateLocale()
+ }
+ }
+ }
+
+ backPressedCallback?.isEnabled = true
+ onBackPressedDispatcher.addCallback(this, backPressedCallback ?: return)
+ }
+
+ private fun detachBackPressedCallback() {
+ backPressedCallback?.isEnabled = false
}
suspend fun checkGithubConnectivity(): Boolean {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt
index f62482ed..c3ec2bbd 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt
@@ -60,7 +60,7 @@ class DownloadChildFragment : Fragment() {
}
}.sortedBy { it.data.episode + (it.data.season ?: 0) * 100000 }
if (eps.isEmpty()) {
- activity?.onBackPressed()
+ activity?.onBackPressedDispatcher?.onBackPressed()
return@main
}
@@ -78,7 +78,7 @@ class DownloadChildFragment : Fragment() {
val folder = arguments?.getString("folder")
val name = arguments?.getString("name")
if (folder == null) {
- activity?.onBackPressed() // TODO FIX
+ activity?.onBackPressedDispatcher?.onBackPressed() // TODO FIX
return
}
fixPaddingStatusbar(binding?.downloadChildRoot)
@@ -87,7 +87,7 @@ class DownloadChildFragment : Fragment() {
title = name
setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
setNavigationOnClickListener {
- activity?.onBackPressed()
+ activity?.onBackPressedDispatcher?.onBackPressed()
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt
index 4c3376bb..1e2ea540 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt
@@ -6,6 +6,7 @@ import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
+import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R
@@ -34,10 +35,6 @@ class DownloadedPlayerActivity : AppCompatActivity() {
CommonActivity.onUserLeaveHint(this)
}
- override fun onBackPressed() {
- finish()
- }
-
private fun playLink(url: String) {
this.navigate(
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
@@ -109,6 +106,15 @@ class DownloadedPlayerActivity : AppCompatActivity() {
finish()
return
}
+
+ onBackPressedDispatcher.addCallback(
+ this,
+ object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ finish()
+ }
+ }
+ )
}
override fun onResume() {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt
index c30e70e5..ef3db0b4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt
@@ -7,6 +7,7 @@ import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.activity.OnBackPressedCallback
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.lagradost.cloudstream3.CommonActivity.screenHeight
@@ -15,10 +16,8 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
import com.lagradost.cloudstream3.ui.player.PlayerEventSource
import com.lagradost.cloudstream3.ui.player.SubtitleData
-import com.lagradost.cloudstream3.utils.IOnBackPressed
-
-open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
+open class ResultTrailerPlayer : ResultFragmentPhone() {
override var lockRotation = false
override var isFullScreenPlayer = false
@@ -28,7 +27,7 @@ open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
const val TAG = "RESULT_TRAILER"
}
- var playerWidthHeight: Pair? = null
+ private var playerWidthHeight: Pair? = null
override fun nextEpisode() {}
@@ -154,6 +153,10 @@ open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
}
fixPlayerSize()
uiReset()
+
+ if (isFullScreenPlayer) {
+ attachBackPressedCallback()
+ } else detachBackPressedCallback()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -172,12 +175,26 @@ open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
}
}
- override fun onBackPressed(): Boolean {
- return if (isFullScreenPlayer) {
- updateFullscreen(false)
- false
- } else {
- true
+ private var backPressedCallback: OnBackPressedCallback? = null
+
+ private fun attachBackPressedCallback() {
+ if (backPressedCallback == null) {
+ backPressedCallback = object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ updateFullscreen(false)
+ }
+ }
}
+
+ backPressedCallback?.isEnabled = true
+
+ activity?.onBackPressedDispatcher?.addCallback(
+ activity ?: return,
+ backPressedCallback ?: return
+ )
+ }
+
+ private fun detachBackPressedCallback() {
+ backPressedCallback?.isEnabled = false
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
index 6ea6363e..37c71134 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
@@ -82,7 +82,7 @@ class SettingsFragment : Fragment() {
setTitle(title)
setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
setNavigationOnClickListener {
- activity?.onBackPressed()
+ activity?.onBackPressedDispatcher?.onBackPressed()
}
}
fixPaddingStatusbar(settingsToolbar)
@@ -97,7 +97,7 @@ class SettingsFragment : Fragment() {
setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
children.firstOrNull { it is ImageView }?.tag = getString(R.string.tv_no_focus_tag)
setNavigationOnClickListener {
- activity?.onBackPressed()
+ activity?.onBackPressedDispatcher?.onBackPressed()
}
}
fixPaddingStatusbar(settingsToolbar)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
index b490386f..c5256ffa 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
@@ -70,7 +70,7 @@ class PluginsFragment : Fragment() {
val isLocal = arguments?.getBoolean(PLUGINS_BUNDLE_LOCAL) == true
if (url == null || name == null) {
- activity?.onBackPressed()
+ activity?.onBackPressedDispatcher?.onBackPressed()
return
}
@@ -119,7 +119,7 @@ class PluginsFragment : Fragment() {
if (searchView?.isIconified == false) {
searchView.isIconified = true
} else {
- activity?.onBackPressed()
+ activity?.onBackPressedDispatcher?.onBackPressed()
}
}
searchView?.setOnQueryTextFocusChangeListener { _, hasFocus ->
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/IOnBackPressed.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/IOnBackPressed.kt
deleted file mode 100644
index b4922945..00000000
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/IOnBackPressed.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.lagradost.cloudstream3.utils
-
-interface IOnBackPressed {
- fun onBackPressed(): Boolean
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
index 231a634d..134d7127 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
@@ -418,7 +418,7 @@ object UIHelper {
}
fun FragmentActivity.popCurrentPage() {
- this.onBackPressed()
+ this.onBackPressedDispatcher.onBackPressed()
/*val currentFragment = supportFragmentManager.fragments.lastOrNull {
it.isVisible
} ?: return
@@ -438,7 +438,7 @@ object UIHelper {
val currentFragment = supportFragmentManager.fragments.lastOrNull {
it.isVisible
}
- ?: //this.onBackPressed()
+ ?: //this.onBackPressedDispatcher.onBackPressed()
return
/*
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 00000000..ae9ece33
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
From 2b60e3a893416b2663e536b17ece81e12351bc11 Mon Sep 17 00:00:00 2001
From: self-similarity <137652432+self-similarity@users.noreply.github.com>
Date: Fri, 10 Nov 2023 23:49:37 +0000
Subject: [PATCH 39/44] Fix faulty automatic subtitle selection (#760)
---
.../java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
index 8d1eb7df..03b89a68 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
@@ -1254,7 +1254,7 @@ class CS3IPlayer : IPlayer {
.setMimeType(sub.mimeType)
.setLanguage("_${sub.name}")
.setId(sub.getId())
- .setSelectionFlags(SELECTION_FLAG_DEFAULT)
+ .setSelectionFlags(0)
.build()
when (sub.origin) {
SubtitleOrigin.DOWNLOADED_FILE -> {
From 6db295a799e0dfcc65f34c5b191a57d7f86cf016 Mon Sep 17 00:00:00 2001
From: Luna712 <142361265+Luna712@users.noreply.github.com>
Date: Sat, 11 Nov 2023 09:30:36 -0700
Subject: [PATCH 40/44] Upgrade gradle (#726)
---
.idea/gradle.xml | 1 +
app/build.gradle.kts | 16 ++++++++--------
build.gradle.kts | 9 ++++-----
gradle/wrapper/gradle-wrapper.properties | 2 +-
4 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index a8a2961a..c5c0ff3b 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -8,6 +8,7 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/account_list_item.xml b/app/src/main/res/layout/account_list_item.xml
index 3331b85b..f133d6c3 100644
--- a/app/src/main/res/layout/account_list_item.xml
+++ b/app/src/main/res/layout/account_list_item.xml
@@ -9,9 +9,9 @@
android:animateLayoutChanges="true"
android:backgroundTint="?attr/primaryGrayBackground"
android:foreground="?attr/selectableItemBackground"
- app:cardCornerRadius="@dimen/rounded_image_radius"
android:layout_margin="10dp"
android:focusable="true"
+ app:cardCornerRadius="@dimen/rounded_image_radius"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1"
app:layout_constraintEnd_toEndOf="parent"
@@ -19,38 +19,38 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
-
+
-
+
-
+
-
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/who_is_watching_account_add.xml b/app/src/main/res/layout/account_list_item_add.xml
similarity index 79%
rename from app/src/main/res/layout/who_is_watching_account_add.xml
rename to app/src/main/res/layout/account_list_item_add.xml
index 91c7e419..dea64484 100644
--- a/app/src/main/res/layout/who_is_watching_account_add.xml
+++ b/app/src/main/res/layout/account_list_item_add.xml
@@ -2,16 +2,15 @@
+ android:visibility="gone"
+ tools:visibility="visible" />
+ android:visibility="gone"
+ tools:visibility="visible" />
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_account_select.xml b/app/src/main/res/layout/activity_account_select.xml
index d5870f24..bd6007dc 100644
--- a/app/src/main/res/layout/activity_account_select.xml
+++ b/app/src/main/res/layout/activity_account_select.xml
@@ -1,28 +1,49 @@
-
+ android:layout_height="match_parent">
-
+
-
+
-
\ No newline at end of file
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/who_is_watching.xml b/app/src/main/res/layout/who_is_watching.xml
deleted file mode 100644
index f61cf6e4..00000000
--- a/app/src/main/res/layout/who_is_watching.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index b349aecc..631201b1 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -20,4 +20,6 @@
50dp
1dp
+
+ 100dp
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9e0575da..ce660a67 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -65,6 +65,7 @@
filter_sub_lang_key
pref_filter_search_quality_key
enable_nsfw_on_providers_key
+ skip_startup_account_select_key
enable_skip_op_from_database
%d %s | %s
@@ -720,10 +721,16 @@
Enter PIN
+ Enter PIN for %s
Enter Current PIN
Lock Profile
PIN
Incorrect PIN. Please try again.
PIN must be 4 characters
Select an Account
+ Manage Accounts
+ Edit account
+ Logged in as %s
+ Skip account selection at startup
+ Use Default Account
diff --git a/app/src/main/res/xml/settings_account.xml b/app/src/main/res/xml/settings_account.xml
index d3dbcb31..ec882088 100644
--- a/app/src/main/res/xml/settings_account.xml
+++ b/app/src/main/res/xml/settings_account.xml
@@ -1,6 +1,12 @@
+
+
From e11d36aed8b7452392259b8801537dd6cd91f332 Mon Sep 17 00:00:00 2001
From: self-similarity <137652432+self-similarity@users.noreply.github.com>
Date: Sun, 12 Nov 2023 15:36:21 +0000
Subject: [PATCH 44/44] Save selected subtitle language (#765)
---
.../cloudstream3/ui/player/CS3IPlayer.kt | 3 +-
.../ui/player/DownloadFileGenerator.kt | 3 +-
.../cloudstream3/ui/player/GeneratorPlayer.kt | 43 +++++++++++++++----
.../ui/player/PlayerSubtitleHelper.kt | 7 ++-
4 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
index 03b89a68..74ea71e7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
@@ -1014,7 +1014,8 @@ class CS3IPlayer : IPlayer {
format.id!!,
SubtitleOrigin.EMBEDDED_IN_VIDEO,
format.sampleMimeType ?: MimeTypes.APPLICATION_SUBRIP,
- emptyMap()
+ emptyMap(),
+ format.language
)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt
index b0223bb5..5585924e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt
@@ -100,7 +100,8 @@ class DownloadFileGenerator(
uri.toString(),
SubtitleOrigin.DOWNLOADED_FILE,
name.toSubtitleMimeType(),
- emptyMap()
+ emptyMap(),
+ null
)
)
}
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 7c8d975a..43e51e55 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
@@ -23,6 +23,7 @@ import androidx.media3.common.Format.NO_VALUE
import androidx.media3.common.MimeTypes
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
+import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.databinding.DialogOnlineSubtitlesBinding
import com.lagradost.cloudstream3.databinding.FragmentPlayerBinding
@@ -38,6 +39,7 @@ import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper
import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog
import com.lagradost.cloudstream3.ui.result.*
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
+import com.lagradost.cloudstream3.ui.subtitles.SUBTITLE_AUTO_SELECT_KEY
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getAutoSelectLanguageISO639_1
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
@@ -100,10 +102,33 @@ class GeneratorPlayer : FullScreenPlayer() {
binding?.playerLoadingOverlay?.isVisible = true
}
- private fun setSubtitles(sub: SubtitleData?): Boolean {
- currentSelectedSubtitles = sub
- //Log.i(TAG, "setSubtitles = $sub")
- return player.setPreferredSubtitles(sub)
+ private fun setSubtitles(subtitle: SubtitleData?): Boolean {
+ // If subtitle is changed -> Save the language
+ if (subtitle != currentSelectedSubtitles) {
+ val subtitleLanguage639 = if (subtitle == null) {
+ // "" is No Subtitles
+ ""
+ } else if (subtitle.languageCode != null) {
+ // Could be "English 4" which is why it is trimmed.
+ val trimmedLanguage = subtitle.languageCode.replace(Regex("\\d"), "").trim()
+
+ languages.firstOrNull { language ->
+ language.languageName.equals(trimmedLanguage, ignoreCase = true) ||
+ language.ISO_639_1 == subtitle.languageCode
+ }?.ISO_639_1
+ } else {
+ null
+ }
+
+ if (subtitleLanguage639 != null) {
+ setKey(SUBTITLE_AUTO_SELECT_KEY, subtitleLanguage639)
+ preferredAutoSelectSubtitles = subtitleLanguage639
+ }
+ }
+
+ currentSelectedSubtitles = subtitle
+ //Log.i(TAG, "setSubtitles = $subtitle")
+ return player.setPreferredSubtitles(subtitle)
}
override fun embeddedSubtitlesFetched(subtitles: List) {
@@ -448,7 +473,8 @@ class GeneratorPlayer : FullScreenPlayer() {
url = url,
origin = SubtitleOrigin.URL,
mimeType = url.toSubtitleMimeType(),
- headers = currentSubtitle.headers
+ headers = currentSubtitle.headers,
+ currentSubtitle.lang
)
runOnMainThread {
addAndSelectSubtitles(subtitle)
@@ -536,7 +562,8 @@ class GeneratorPlayer : FullScreenPlayer() {
uri.toString(),
SubtitleOrigin.DOWNLOADED_FILE,
name.toSubtitleMimeType(),
- emptyMap()
+ emptyMap(),
+ null
)
addAndSelectSubtitles(subtitleData)
@@ -946,7 +973,7 @@ class GeneratorPlayer : FullScreenPlayer() {
var maxEpisodeSet: Int? = null
var hasRequestedStamps: Boolean = false
- override fun playerPositionChanged(position: Long, duration : Long) {
+ override fun playerPositionChanged(position: Long, duration: Long) {
// Don't save livestream data
if ((currentMeta as? ResultEpisode)?.tvType?.isLiveStream() == true) return
@@ -1209,7 +1236,7 @@ class GeneratorPlayer : FullScreenPlayer() {
}
}
- override fun playerDimensionsLoaded(width: Int, height : Int) {
+ override fun playerDimensionsLoaded(width: Int, height: Int) {
setPlayerDimen(width to height)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerSubtitleHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerSubtitleHelper.kt
index e532d1a3..25d7e3dd 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerSubtitleHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerSubtitleHelper.kt
@@ -30,13 +30,15 @@ enum class SubtitleOrigin {
* @param name To be displayed in the player
* @param url Url for the subtitle, when EMBEDDED_IN_VIDEO this variable is used as the real backend id
* @param headers if empty it will use the base onlineDataSource headers else only the specified headers
+ * @param languageCode Not guaranteed to follow any standard. Could be something like "English 4" or "en".
* */
data class SubtitleData(
val name: String,
val url: String,
val origin: SubtitleOrigin,
val mimeType: String,
- val headers: Map
+ val headers: Map,
+ val languageCode: String?
) {
/** Internal ID for exoplayer, unique for each link*/
fun getId(): String {
@@ -80,7 +82,8 @@ class PlayerSubtitleHelper {
url = subtitleFile.url,
origin = SubtitleOrigin.URL,
mimeType = subtitleFile.url.toSubtitleMimeType(),
- headers = emptyMap()
+ headers = emptyMap(),
+ languageCode = subtitleFile.lang
)
}
}