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 01/20] 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 02/20] 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 03/20] 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 04/20] 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 05/20] 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 06/20] 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 07/20] 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 08/20] 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 09/20] 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 10/20] 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 11/20] 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 12/20] 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 13/20] 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 14/20] 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 15/20] 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 16/20] 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 17/20] 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 18/20] 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 19/20] 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 20/20] 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