From f5c4864a3cf88dce6ebc903b5efad3fba65266db Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Fri, 4 Aug 2023 05:37:41 +0200 Subject: [PATCH] tv focus changes + gradle bump + pip crash fix --- app/build.gradle.kts | 36 ++-- .../lagradost/cloudstream3/CommonActivity.kt | 95 ++++++++--- .../ui/download/DownloadChildFragment.kt | 9 +- .../ui/download/DownloadFragment.kt | 13 +- .../ui/home/HomeChildItemAdapter.kt | 8 +- .../cloudstream3/ui/home/HomeFragment.kt | 24 ++- .../ui/home/HomeParentItemAdapter.kt | 19 ++- .../ui/home/HomeParentItemAdapterPreview.kt | 5 +- .../cloudstream3/ui/player/PlayerPipHelper.kt | 32 ++-- .../ui/result/LinearListLayout.kt | 104 ++++++++--- .../ui/result/ResultFragmentPhone.kt | 19 ++- .../ui/result/ResultFragmentTv.kt | 32 +++- .../cloudstream3/ui/search/SearchFragment.kt | 7 +- .../settings/extensions/ExtensionsFragment.kt | 14 +- .../extensions/PluginDetailsFragment.kt | 161 +++++++++--------- .../ui/settings/extensions/PluginsFragment.kt | 102 ++++++----- .../cloudstream3/utils/DataStoreHelper.kt | 9 +- .../res/layout/fragment_child_downloads.xml | 57 ++++--- .../main/res/layout/fragment_extensions.xml | 11 +- app/src/main/res/layout/fragment_plugins.xml | 10 +- .../main/res/layout/fragment_result_tv.xml | 20 +-- app/src/main/res/layout/fragment_search.xml | 33 ++-- .../main/res/layout/fragment_search_tv.xml | 41 +++-- app/src/main/res/layout/standard_toolbar.xml | 31 ++-- app/src/main/res/layout/tvtypes_chips.xml | 1 + .../main/res/layout/tvtypes_chips_scroll.xml | 11 +- build.gradle.kts | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 28 files changed, 590 insertions(+), 320 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c669c870..5c864117 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,4 +1,3 @@ -import com.android.build.gradle.api.BaseVariantOutput import org.jetbrains.dokka.gradle.DokkaTask import java.io.ByteArrayOutputStream import java.net.URL @@ -52,7 +51,7 @@ android { targetSdk = 33 versionCode = 59 - versionName = "4.1.1" + versionName = "4.1.2" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") @@ -108,14 +107,19 @@ android { versionCode = (System.currentTimeMillis() / 60000).toInt() } } + //toolchain { + // languageVersion.set(JavaLanguageVersion.of(17)) + // } + // jvmToolchain(17) + compileOptions { isCoreLibraryDesugaringEnabled = true - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "17" freeCompilerArgs = listOf("-Xjvm-default=compatibility") } lint { @@ -131,22 +135,22 @@ repositories { dependencies { implementation("com.google.android.mediahome:video:1.0.0") - implementation("androidx.test.ext:junit-ktx:1.1.3") + implementation("androidx.test.ext:junit-ktx:1.1.5") testImplementation("org.json:json:20180813") - implementation("androidx.core:core-ktx:1.8.0") - implementation("androidx.appcompat:appcompat:1.4.2") // need target 32 for 1.5.0 + implementation("androidx.core:core-ktx:1.10.1") + 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 implementation("com.google.android.material:material:1.5.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") - implementation("androidx.navigation:navigation-fragment-ktx:2.5.1") - implementation("androidx.navigation:navigation-ui-ktx:2.5.1") - implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1") - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1") + 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") testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.3") - androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation("androidx.test:core") //implementation("io.karn:khttp-android:0.1.2") //okhttp instead @@ -201,8 +205,8 @@ dependencies { //implementation("com.github.TorrentStream:TorrentStream-Android:2.7.0") // Downloading - implementation("androidx.work:work-runtime:2.8.0") - implementation("androidx.work:work-runtime-ktx:2.8.0") + implementation("androidx.work:work-runtime:2.8.1") + implementation("androidx.work:work-runtime-ktx:2.8.1") // Networking // implementation("com.squareup.okhttp3:okhttp:4.9.2") diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 4d7afaba..0bcd4152 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -19,8 +19,11 @@ import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat +import androidx.core.view.children import androidx.preference.PreferenceManager import com.google.android.gms.cast.framework.CastSession +import com.google.android.material.chip.ChipGroup +import com.google.android.material.navigationrail.NavigationRailView import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.mvvm.logError @@ -39,6 +42,13 @@ import org.schabi.newpipe.extractor.NewPipe import java.lang.ref.WeakReference import java.util.* +enum class FocusDirection { + Start, + End, + Up, + Down, +} + object CommonActivity { private var _activity: WeakReference? = null @@ -318,17 +328,70 @@ object CommonActivity { currentLook = currentLook.parent as? View ?: break }*/ + /** skips the initial stage of searching for an id using the view, see getNextFocus for specification */ + fun continueGetNextFocus( + root: Any?, + view: View, + direction: FocusDirection, + nextId: Int, + depth: Int = 0 + ): View? { + if (nextId == NO_ID) return null + + // do an initial search for the view, in case the localLook is too deep we can use this as + // an early break and backup view + var next = + when (root) { + is Activity -> root.findViewById(nextId) + is View -> root.rootView.findViewById(nextId) + else -> null + } ?: return null + + next = localLook(view, nextId) ?: next + + // if cant focus but visible then break and let android decide + // the exception if is the view is a parent and has children that wants focus + val hasChildrenThatWantsFocus = (next as? ViewGroup)?.let { parent -> + parent.descendantFocusability == ViewGroup.FOCUS_AFTER_DESCENDANTS && parent.childCount > 0 + } ?: false + if (!next.isFocusable && next.isShown && !hasChildrenThatWantsFocus) return null + + // if not shown then continue because we will "skip" over views to get to a replacement + if (!next.isShown) { + // we don't want a while true loop, so we let android decide if we find a recursive view + if (next == view) return null + return getNextFocus(root, next, direction, depth + 1) + } + + (when (next) { + is ChipGroup -> { + next.children.firstOrNull { it.isFocusable && it.isShown } + } + + is NavigationRailView -> { + next.findViewById(next.selectedItemId) ?: next.findViewById(R.id.navigation_home) + } + + else -> null + })?.let { + return it + } + + // nothing wrong with the view found, return it + return next + } + /** recursively looks for a next focus up to a depth of 10, * this is used to override the normal shit focus system * because this application has a lot of invisible views that messes with some tv devices*/ - private fun getNextFocus( - act: Activity?, + fun getNextFocus( + root: Any?, view: View?, direction: FocusDirection, depth: Int = 0 ): View? { // if input is invalid let android decide + depth test to not crash if loop is found - if (view == null || depth >= 10 || act == null) { + if (view == null || depth >= 10 || root == null) { return null } @@ -363,31 +426,9 @@ object CommonActivity { if (nextId == NO_ID) return null } - - var next = act.findViewById(nextId) ?: return null - - next = localLook(view, nextId) ?: next - - // if cant focus but visible then break and let android decide - // the exception if is the view is a parent and has children that wants focus - val hasChildrenThatWantsFocus = (next as? ViewGroup)?.let { parent -> - parent.descendantFocusability == ViewGroup.FOCUS_AFTER_DESCENDANTS && parent.childCount > 0 - } ?: false - if (!next.isFocusable && next.isShown && !hasChildrenThatWantsFocus) return null - - // if not shown then continue because we will "skip" over views to get to a replacement - if (!next.isShown) return getNextFocus(act, next, direction, depth + 1) - - // nothing wrong with the view found, return it - return next + return continueGetNextFocus(root, view, direction, nextId, depth) } - private enum class FocusDirection { - Start, - End, - Up, - Down, - } fun onKeyDown(act: Activity?, keyCode: Int, event: KeyEvent?) { //println("Keycode: $keyCode") @@ -517,7 +558,7 @@ object CommonActivity { else -> null } - // println("NEXT FOCUS : $nextView") + // println("NEXT FOCUS : $nextView") if (nextView != null) { nextView.requestFocus() keyEventListener?.invoke(Pair(event, true)) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt index 1d813ef1..f62482ed 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt @@ -5,11 +5,12 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.FragmentChildDownloadsBinding import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick +import com.lagradost.cloudstream3.ui.result.FOCUS_SELF +import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKeys @@ -110,7 +111,11 @@ class DownloadChildFragment : Fragment() { downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it } binding?.downloadChildList?.adapter = adapter - binding?.downloadChildList?.layoutManager = GridLayoutManager(context, 1) + binding?.downloadChildList?.setLinearListLayout( + isHorizontal = false, + nextDown = FOCUS_SELF, + nextRight = FOCUS_SELF + )//layoutManager = GridLayoutManager(context, 1) updateList(folder) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt index c8b381a6..27c2e1a3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt @@ -40,6 +40,8 @@ import com.lagradost.cloudstream3.databinding.FragmentDownloadsBinding import com.lagradost.cloudstream3.databinding.StreamInputBinding import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.ui.player.BasicLink +import com.lagradost.cloudstream3.ui.result.FOCUS_SELF +import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import java.net.URI @@ -74,7 +76,7 @@ class DownloadFragment : Fragment() { super.onDestroyView() } - var binding : FragmentDownloadsBinding? = null + var binding: FragmentDownloadsBinding? = null override fun onCreateView( inflater: LayoutInflater, @@ -151,6 +153,7 @@ class DownloadFragment : Fragment() { ) } } + 1 -> { (activity as AppCompatActivity?)?.loadResult( click.data.url, @@ -187,7 +190,13 @@ class DownloadFragment : Fragment() { binding?.downloadList?.apply { this.adapter = adapter - layoutManager = GridLayoutManager(context, 1) + setLinearListLayout( + isHorizontal = false, + nextRight = FOCUS_SELF, + nextUp = FOCUS_SELF, + nextDown = FOCUS_SELF + ) + //layoutManager = GridLayoutManager(context, 1) } // Should be visible in emulator layout diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeChildItemAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeChildItemAdapter.kt index 607cda01..f84966eb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeChildItemAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeChildItemAdapter.kt @@ -88,14 +88,14 @@ class HomeChildItemAdapter( private val nextFocusUp: Int? = null, private val nextFocusDown: Int? = null, private val isHorizontal: Boolean = false, - private val isRtl : Boolean + private val isRtl: Boolean ) : RecyclerView.ViewHolder(binding.root) { fun bind(card: SearchResponse, position: Int) { // TV focus fixing - val nextFocusBehavior = when (position) { + /*val nextFocusBehavior = when (position) { 0 -> true itemCount - 1 -> false else -> null @@ -113,7 +113,7 @@ class HomeChildItemAdapter( } else { itemView.nextFocusRightId = -1 itemView.nextFocusLeftId = -1 - } + }*/ when (binding) { @@ -171,7 +171,7 @@ class HomeChildItemAdapter( card, position, itemView, - nextFocusBehavior, + null, // nextFocusBehavior, nextFocusUp, nextFocusDown ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index a6e1b5e6..6f9a1654 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -310,6 +310,17 @@ class HomeFragment : Fragment() { selectedTypes: List, validTypes: List, callback: (List) -> Unit + ) { + bindChips(header, selectedTypes, validTypes, callback, null, null) + } + + fun bindChips( + header: TvtypesChipsBinding?, + selectedTypes: List, + validTypes: List, + callback: (List) -> Unit, + nextFocusDown: Int?, + nextFocusUp: Int? ) { if (header == null) return val pairList = getPairList(header) @@ -317,6 +328,17 @@ class HomeFragment : Fragment() { val isValid = validTypes.any { types.contains(it) } button?.isVisible = isValid button?.isChecked = isValid && selectedTypes.any { types.contains(it) } + button?.isFocusable = true + if (isTrueTvSettings()) { + button?.isFocusableInTouchMode = true + } + + if (nextFocusDown != null) + button?.nextFocusDownId = nextFocusDown + + if (nextFocusUp != null) + button?.nextFocusUpId = nextFocusUp + button?.setOnCheckedChangeListener { _, _ -> val list = ArrayList() for ((sbutton, vvalidTypes) in pairList) { @@ -462,7 +484,7 @@ class HomeFragment : Fragment() { private val apiChangeClickListener = View.OnClickListener { view -> view.context.selectHomepage(currentApiName) { api -> - homeViewModel.loadAndCancel(api, forceReload = true,fromUI = true) + homeViewModel.loadAndCancel(api, forceReload = true, fromUI = true) } /*val validAPIs = view.context?.filterProviderByPreferredMedia()?.toMutableList() ?: mutableListOf() 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 f6c3fead..163a60a1 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 @@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.HomePageList import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.HomepageParentBinding +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 @@ -154,7 +155,7 @@ open class ParentItemAdapter( class ParentViewHolder constructor( val binding: HomepageParentBinding, - // val viewModel: HomeViewModel, + // val viewModel: HomeViewModel, private val clickCallback: (SearchClickCallback) -> Unit, private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit, private val expandCallback: ((String) -> Unit)? = null, @@ -162,7 +163,8 @@ open class ParentItemAdapter( RecyclerView.ViewHolder(binding.root) { val title: TextView = binding.homeChildMoreInfo private val recyclerView: RecyclerView = binding.homeChildRecyclerview - + private val startFocus = R.id.nav_rail_view + private val endFocus = FOCUS_SELF fun update(expand: HomeViewModel.ExpandableHomepageList) { val info = expand.list (recyclerView.adapter as? HomeChildItemAdapter?)?.apply { @@ -176,8 +178,13 @@ open class ParentItemAdapter( nextFocusDown = recyclerView.nextFocusDownId, ).apply { isHorizontal = info.isHorizontalImages + hasNext = expand.hasNext } - recyclerView.setLinearListLayout() + recyclerView.setLinearListLayout( + isHorizontal = true, + nextLeft = startFocus, + nextRight = endFocus, + ) } } @@ -192,7 +199,11 @@ open class ParentItemAdapter( isHorizontal = info.isHorizontalImages hasNext = expand.hasNext } - recyclerView.setLinearListLayout() + recyclerView.setLinearListLayout( + isHorizontal = true, + nextLeft = startFocus, + nextRight = endFocus, + ) title.text = info.name recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { 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 8557d26f..1684dfe5 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 @@ -30,6 +30,7 @@ import com.lagradost.cloudstream3.mvvm.debugException import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage +import com.lagradost.cloudstream3.ui.result.FOCUS_SELF import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.setLinearListLayout @@ -439,8 +440,8 @@ class HomeParentItemAdapterPreview( resumeRecyclerView.adapter = resumeAdapter bookmarkRecyclerView.adapter = bookmarkAdapter - resumeRecyclerView.setLinearListLayout() - bookmarkRecyclerView.setLinearListLayout() + resumeRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF) + bookmarkRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF) fixPaddingStatusbarMargin(topPadding) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerPipHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerPipHelper.kt index 4bed0c9d..93857234 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerPipHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerPipHelper.kt @@ -11,6 +11,7 @@ import android.util.Rational import androidx.annotation.RequiresApi import androidx.annotation.StringRes import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import kotlin.math.roundToInt class PlayerPipHelper { @@ -88,22 +89,25 @@ class PlayerPipHelper { val ratioAccuracy = 100000 // To convert the float to int // java.lang.IllegalArgumentException: setPictureInPictureParams: Aspect ratio is too extreme (must be between 0.418410 and 2.390000) - val fixedRational = aspectRatio?.toFloat()?.coerceIn(mixAspectRatio, maxAspectRatio)?.let { - Rational((it * ratioAccuracy).roundToInt(), ratioAccuracy) - } + val fixedRational = + aspectRatio?.toFloat()?.coerceIn(mixAspectRatio, maxAspectRatio)?.let { + Rational((it * ratioAccuracy).roundToInt(), ratioAccuracy) + } - activity.setPictureInPictureParams( - PictureInPictureParams.Builder() - .apply { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - setSeamlessResizeEnabled(true) - setAutoEnterEnabled(isPlaying) + normalSafeApiCall { + activity.setPictureInPictureParams( + PictureInPictureParams.Builder() + .apply { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + setSeamlessResizeEnabled(true) + setAutoEnterEnabled(isPlaying) + } } - } - .setAspectRatio(fixedRational) - .setActions(actions) - .build() - ) + .setAspectRatio(fixedRational) + .setActions(actions) + .build() + ) + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt index 26cb7900..b4e3062b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt @@ -4,19 +4,45 @@ import android.content.Context import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.CommonActivity +import com.lagradost.cloudstream3.CommonActivity.activity +import com.lagradost.cloudstream3.FocusDirection import com.lagradost.cloudstream3.mvvm.logError -fun RecyclerView?.setLinearListLayout(isHorizontal: Boolean = true) { - if (this == null) return +const val FOCUS_SELF = View.NO_ID - 1 +const val FOCUS_INHERIT = FOCUS_SELF - 1 +fun RecyclerView?.setLinearListLayout( + isHorizontal: Boolean = true, + nextLeft: Int = FOCUS_INHERIT, + nextRight: Int = FOCUS_INHERIT, + nextUp: Int = FOCUS_INHERIT, + nextDown: Int = FOCUS_INHERIT +) { + if (this == null) return + val ctx = this.context ?: return this.layoutManager = - this.context?.let { LinearListLayout(it).apply { if (isHorizontal) setHorizontal() else setVertical() } } - // ?: this.layoutManager + LinearListLayout(ctx).apply { + if (isHorizontal) setHorizontal() else setVertical() + nextFocusLeft = + if (nextLeft == FOCUS_INHERIT) this@setLinearListLayout.nextFocusLeftId else nextLeft + nextFocusRight = + if (nextRight == FOCUS_INHERIT) this@setLinearListLayout.nextFocusRightId else nextRight + nextFocusUp = + if (nextUp == FOCUS_INHERIT) this@setLinearListLayout.nextFocusUpId else nextUp + nextFocusDown = + if (nextDown == FOCUS_INHERIT) this@setLinearListLayout.nextFocusDownId else nextDown + } } open class LinearListLayout(context: Context?) : LinearLayoutManager(context) { + var nextFocusLeft: Int = View.NO_ID + var nextFocusRight: Int = View.NO_ID + var nextFocusUp: Int = View.NO_ID + var nextFocusDown: Int = View.NO_ID + fun setHorizontal() { orientation = HORIZONTAL } @@ -56,8 +82,37 @@ open class LinearListLayout(context: Context?) : linearSmoothScroller.targetPosition = position startSmoothScroll(linearSmoothScroller) }*/ + + /** from the current focus go to a direction */ + private fun getNextDirection(focused: View?, direction: FocusDirection): View? { + val id = when (direction) { + FocusDirection.Start -> if (isLayoutRTL) nextFocusRight else nextFocusLeft + FocusDirection.End -> if (isLayoutRTL) nextFocusLeft else nextFocusRight + FocusDirection.Up -> nextFocusUp + FocusDirection.Down -> nextFocusDown + } + + return when (id) { + View.NO_ID -> null + FOCUS_SELF -> focused + else -> CommonActivity.continueGetNextFocus( + activity ?: focused, + focused ?: return null, + direction, + id + ) + } + } + override fun onInterceptFocusSearch(focused: View, direction: Int): View? { val dir = if (orientation == HORIZONTAL) { + if (direction == View.FOCUS_DOWN) getNextDirection(focused, FocusDirection.Down)?.let { newFocus -> + return newFocus + } + if (direction == View.FOCUS_UP) getNextDirection(focused, FocusDirection.Up)?.let { newFocus -> + return newFocus + } + if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) { // This scrolls the recyclerview before doing focus search, which // allows the focus search to work better. @@ -69,34 +124,45 @@ open class LinearListLayout(context: Context?) : } var ret = if (direction == View.FOCUS_RIGHT) 1 else -1 // only flip on horizontal layout - if (this.isLayoutRTL) { + if (isLayoutRTL) { ret = -ret } ret } else { - if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null + if (direction == View.FOCUS_RIGHT) getNextDirection(focused, FocusDirection.End)?.let { newFocus -> + return newFocus + } + if (direction == View.FOCUS_LEFT) getNextDirection(focused, FocusDirection.Start)?.let { newFocus -> + return newFocus + } + + if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) { + (focused.parent as? RecyclerView)?.focusSearch(direction) + return null + } + + //if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null if (direction == View.FOCUS_DOWN) 1 else -1 } - return try { - getPosition(getCorrectParent(focused))?.let { position -> - val lookfor = dir + position - //clamp(dir + position, 0, recyclerView.adapter?.itemCount ?: return null) + try { + val position = getPosition(getCorrectParent(focused)) ?: return null + val lookFor = dir + position - // refocus on the same view if going out of bounds, note that we only do it - // for out of bounds one way as we may override the start where item == -1 - if (lookfor >= itemCount) { - return getViewFromPos(itemCount - 1) ?: focused - } - - getViewFromPos(lookfor) ?: run { - scrollToPosition(lookfor) + // if out of bounds then refocus as specified + return if (lookFor >= itemCount) { + getNextDirection(focused, if(orientation == HORIZONTAL) FocusDirection.End else FocusDirection.Down) + } else if (lookFor < 0) { + getNextDirection(focused, if(orientation == HORIZONTAL) FocusDirection.Start else FocusDirection.Up) + } else { + getViewFromPos(lookFor) ?: run { + scrollToPosition(lookFor) null } } } catch (e: Exception) { logError(e) - null + return null } } 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 e1514d63..3ddaee61 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 @@ -294,7 +294,7 @@ open class ResultFragmentPhone : FullScreenPlayer(), super.onStop() } - private fun updateUI(id : Int?) { + private fun updateUI(id: Int?) { syncModel.updateUserData() viewModel.reloadEpisodes() } @@ -338,7 +338,12 @@ open class ResultFragmentPhone : FullScreenPlayer(), ) } - resultCastItems.layoutManager = object : LinearListLayout(view.context) { + resultCastItems.setLinearListLayout( + isHorizontal = true, + nextLeft = FOCUS_SELF, + nextRight = FOCUS_SELF + ) + /*resultCastItems.layoutManager = object : LinearListLayout(view.context) { override fun onRequestChildFocus( parent: RecyclerView, state: RecyclerView.State, @@ -356,7 +361,7 @@ open class ResultFragmentPhone : FullScreenPlayer(), } }.apply { this.orientation = RecyclerView.HORIZONTAL - } + }*/ resultCastItems.adapter = ActorAdaptor() resultEpisodes.adapter = @@ -597,8 +602,14 @@ open class ResultFragmentPhone : FullScreenPlayer(), EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep) ) } + DOWNLOAD_ACTION_LONG_CLICK -> { - viewModel.handleAction(EpisodeClickEvent(ACTION_DOWNLOAD_MIRROR, ep)) + viewModel.handleAction( + EpisodeClickEvent( + ACTION_DOWNLOAD_MIRROR, + ep + ) + ) } else -> DownloadButtonSetup.handleDownloadClick(click) 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 698a0ab5..be3de52b 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 @@ -307,7 +307,29 @@ class ResultFragmentTv : Fragment() { } } - resultEpisodes.setLinearListLayout(isHorizontal = false)/*.layoutManager = + resultEpisodes.setLinearListLayout( + isHorizontal = false, + nextUp = FOCUS_SELF, + nextDown = FOCUS_SELF, + nextRight = FOCUS_SELF, + ) + resultDubSelection.setLinearListLayout( + isHorizontal = false, + nextUp = FOCUS_SELF, + nextDown = FOCUS_SELF, + ) + resultRangeSelection.setLinearListLayout( + isHorizontal = false, + nextUp = FOCUS_SELF, + nextDown = FOCUS_SELF, + ) + resultSeasonSelection.setLinearListLayout( + isHorizontal = false, + nextUp = FOCUS_SELF, + nextDown = FOCUS_SELF, + ) + + /*.layoutManager = LinearListLayout(resultEpisodes.context, resultEpisodes.isRtl()).apply { setVertical() }*/ @@ -367,6 +389,11 @@ class ResultFragmentTv : Fragment() { ) resultCastItems.layoutManager = object : LinearListLayout(view.context) { + + override fun onInterceptFocusSearch(focused: View, direction: Int): View? { + return super.onInterceptFocusSearch(focused, direction) + } + override fun onRequestChildFocus( parent: RecyclerView, state: RecyclerView.State, @@ -383,8 +410,9 @@ class ResultFragmentTv : Fragment() { } } }.apply { - this.orientation = RecyclerView.HORIZONTAL + setHorizontal() } + resultCastItems.adapter = ActorAdaptor { toggleEpisodes(false) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index 2f588c19..63213eb9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -45,6 +45,8 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.currentSpan import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.updateChips import com.lagradost.cloudstream3.ui.home.ParentItemAdapter +import com.lagradost.cloudstream3.ui.result.FOCUS_SELF +import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.ownHide @@ -519,9 +521,12 @@ class SearchFragment : Fragment() { binding?.apply { searchHistoryRecycler.adapter = historyAdapter - searchHistoryRecycler.layoutManager = GridLayoutManager(context, 1) + searchHistoryRecycler.setLinearListLayout(isHorizontal = false, nextRight = FOCUS_SELF) + //searchHistoryRecycler.layoutManager = GridLayoutManager(context, 1) searchMasterRecycler.adapter = masterAdapter + //searchMasterRecycler.setLinearListLayout(isHorizontal = false, nextRight = FOCUS_SELF) + searchMasterRecycler.layoutManager = GridLayoutManager(context, 1) // Automatically search the specified query, this allows the app search to launch from intent diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt index 3c0b5b95..7b72fc3b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt @@ -23,6 +23,8 @@ import com.lagradost.cloudstream3.databinding.FragmentExtensionsBinding import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observeNullable import com.lagradost.cloudstream3.plugins.RepositoryManager +import com.lagradost.cloudstream3.ui.result.FOCUS_SELF +import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar @@ -82,6 +84,14 @@ class ExtensionsFragment : Fragment() { setUpToolbar(R.string.extensions) + binding?.repoRecyclerView?.setLinearListLayout( + isHorizontal = false, + nextUp = R.id.settings_toolbar, //FOCUS_SELF, // back has no id so we cant :pensive: + nextDown = R.id.plugin_storage_appbar, + nextRight = FOCUS_SELF, + nextLeft = R.id.nav_rail_view + ) + binding?.repoRecyclerView?.adapter = RepoAdapter(false, { findNavController().navigate( R.id.navigation_settings_extensions_to_navigation_settings_plugins, @@ -126,11 +136,11 @@ class ExtensionsFragment : Fragment() { (binding?.repoRecyclerView?.adapter as? RepoAdapter)?.updateList(it) } - binding?.repoRecyclerView?.apply { + /*binding?.repoRecyclerView?.apply { context?.let { ctx -> layoutManager = LinearRecycleViewLayoutManager(ctx, nextFocusUpId, nextFocusDownId) } - } + }*/ // list_repositories?.setOnClickListener { // // Open webview on tv if browser fails diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt index 00e1806d..d8047c11 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt @@ -62,100 +62,101 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen super.onViewCreated(view, savedInstanceState) val metadata = data.plugin.second binding?.apply { - if (!pluginIcon.setImage(//plugin_icon?.height ?: - metadata.iconUrl?.replace( - "%size%", - "$iconSize" - )?.replace( - "%exact_size%", - "$iconSizeExact" - ), - null, - errorImageDrawable = R.drawable.ic_baseline_extension_24 - ) - ) { - pluginIcon.setImageResource(R.drawable.ic_baseline_extension_24) - } - pluginName.text = metadata.name.removeSuffix("Provider") - pluginVersion.text = metadata.version.toString() - pluginDescription.text = metadata.description ?: getString(R.string.no_data) - pluginSize.text = - if (metadata.fileSize == null) getString(R.string.no_data) else formatFileSize( - context, - metadata.fileSize - ) - pluginAuthor.text = - if (metadata.authors.isEmpty()) getString(R.string.no_data) else metadata.authors.joinToString( - ", " - ) - pluginStatus.text = resources.getStringArray(R.array.extension_statuses)[metadata.status] - pluginTypes.text = - if (metadata.tvTypes.isNullOrEmpty()) getString(R.string.no_data) else metadata.tvTypes.joinToString( - ", " - ) - pluginLang.text = if (metadata.language == null) - getString(R.string.no_data) - else - "${getFlagFromIso(metadata.language)} ${fromTwoLettersToLanguage(metadata.language)}" - - githubBtn.setOnClickListener { - if (metadata.repositoryUrl != null) { - openBrowser(metadata.repositoryUrl) + if (!pluginIcon.setImage(//plugin_icon?.height ?: + metadata.iconUrl?.replace( + "%size%", + "$iconSize" + )?.replace( + "%exact_size%", + "$iconSizeExact" + ), + null, + errorImageDrawable = R.drawable.ic_baseline_extension_24 + ) + ) { + pluginIcon.setImageResource(R.drawable.ic_baseline_extension_24) } - } + pluginName.text = metadata.name.removeSuffix("Provider") + pluginVersion.text = metadata.version.toString() + pluginDescription.text = metadata.description ?: getString(R.string.no_data) + pluginSize.text = + if (metadata.fileSize == null) getString(R.string.no_data) else formatFileSize( + context, + metadata.fileSize + ) + pluginAuthor.text = + if (metadata.authors.isEmpty()) getString(R.string.no_data) else metadata.authors.joinToString( + ", " + ) + pluginStatus.text = + resources.getStringArray(R.array.extension_statuses)[metadata.status] + pluginTypes.text = + if (metadata.tvTypes.isNullOrEmpty()) getString(R.string.no_data) else metadata.tvTypes.joinToString( + ", " + ) + pluginLang.text = if (metadata.language == null) + getString(R.string.no_data) + else + "${getFlagFromIso(metadata.language)} ${fromTwoLettersToLanguage(metadata.language)}" - if (!metadata.canVote()) { - downvote.alpha = .6f - upvote.alpha = .6f - } + githubBtn.setOnClickListener { + if (metadata.repositoryUrl != null) { + openBrowser(metadata.repositoryUrl) + } + } - if (data.isDownloaded) { - // On local plugins page the filepath is provided instead of url. - val plugin = - PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url] - if (plugin?.openSettings != null && context != null) { - actionSettings.isVisible = true - actionSettings.setOnClickListener { - try { - plugin.openSettings!!.invoke(requireContext()) - } catch (e: Throwable) { - Log.e( - "PluginAdapter", - "Failed to open ${metadata.name} settings: ${ - Log.getStackTraceString(e) - }" - ) + if (!metadata.canVote()) { + downvote.alpha = .6f + upvote.alpha = .6f + } + + if (data.isDownloaded) { + // On local plugins page the filepath is provided instead of url. + val plugin = + PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url] + if (plugin?.openSettings != null && context != null) { + actionSettings.isVisible = true + actionSettings.setOnClickListener { + try { + plugin.openSettings!!.invoke(requireContext()) + } catch (e: Throwable) { + Log.e( + "PluginAdapter", + "Failed to open ${metadata.name} settings: ${ + Log.getStackTraceString(e) + }" + ) + } } + } else { + actionSettings.isVisible = false } } else { actionSettings.isVisible = false } - } else { - actionSettings.isVisible = false - } - upvote.setOnClickListener { + upvote.setOnClickListener { + ioSafe { + metadata.vote(VotingApi.VoteType.UPVOTE).main { + updateVoting(it) + } + } + } + downvote.setOnClickListener { + ioSafe { + metadata.vote(VotingApi.VoteType.DOWNVOTE).main { + updateVoting(it) + } + + } + } + ioSafe { - metadata.vote(VotingApi.VoteType.UPVOTE).main { + metadata.getVotes().main { updateVoting(it) } } } - downvote.setOnClickListener { - ioSafe { - metadata.vote(VotingApi.VoteType.DOWNVOTE).main { - updateVoting(it) - } - - } - } - - ioSafe { - metadata.getVotes().main { - updateVoting(it) - } - } - } } private fun updateVoting(value: Int) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index 1a6215db..172ea659 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -15,6 +15,8 @@ import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.databinding.FragmentPluginsBinding import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips +import com.lagradost.cloudstream3.ui.result.FOCUS_SELF +import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.appLanguages @@ -32,7 +34,7 @@ class PluginsFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle?, ): View { - val localBinding = FragmentPluginsBinding.inflate(inflater,container,false) + val localBinding = FragmentPluginsBinding.inflate(inflater, container, false) binding = localBinding return localBinding.root//inflater.inflate(R.layout.fragment_plugins, container, false) } @@ -73,48 +75,51 @@ class PluginsFragment : Fragment() { setUpToolbar(name) binding?.settingsToolbar?.apply { - setOnMenuItemClickListener { menuItem -> - when (menuItem?.itemId) { - R.id.download_all -> { - PluginsViewModel.downloadAll(activity, url, pluginViewModel) - } - R.id.lang_filter -> { - val tempLangs = appLanguages.toMutableList() - val languageCodes = mutableListOf("none") + tempLangs.map { (_, _, iso) -> iso } - val languageNames = - mutableListOf(getString(R.string.no_data)) + tempLangs.map { (emoji, name, iso) -> - val flag = - emoji.ifBlank { SubtitleHelper.getFlagFromIso(iso) ?: "ERROR" } - "$flag $name" - } - val selectedList = - pluginViewModel.languages.map { it -> languageCodes.indexOf(it) } - - activity?.showMultiDialog( - languageNames, - selectedList, - getString(R.string.provider_lang_settings), - {}) { newList -> - pluginViewModel.languages = newList.map { it -> languageCodes[it] } - pluginViewModel.updateFilteredPlugins() + setOnMenuItemClickListener { menuItem -> + when (menuItem?.itemId) { + R.id.download_all -> { + PluginsViewModel.downloadAll(activity, url, pluginViewModel) } + + R.id.lang_filter -> { + val tempLangs = appLanguages.toMutableList() + val languageCodes = + mutableListOf("none") + tempLangs.map { (_, _, iso) -> iso } + val languageNames = + mutableListOf(getString(R.string.no_data)) + tempLangs.map { (emoji, name, iso) -> + val flag = + emoji.ifBlank { SubtitleHelper.getFlagFromIso(iso) ?: "ERROR" } + "$flag $name" + } + val selectedList = + pluginViewModel.languages.map { languageCodes.indexOf(it) } + + activity?.showMultiDialog( + languageNames, + selectedList, + getString(R.string.provider_lang_settings), + {}) { newList -> + pluginViewModel.languages = newList.map { languageCodes[it] } + pluginViewModel.updateFilteredPlugins() + } + } + + else -> {} } - else -> {} + return@setOnMenuItemClickListener true } - return@setOnMenuItemClickListener true - } - val searchView = - menu?.findItem(R.id.search_button)?.actionView as? SearchView + val searchView = + menu?.findItem(R.id.search_button)?.actionView as? SearchView - // Don't go back if active query - setNavigationOnClickListener { - if (searchView?.isIconified == false) { - searchView.isIconified = true - } else { - activity?.onBackPressed() + // Don't go back if active query + setNavigationOnClickListener { + if (searchView?.isIconified == false) { + searchView.isIconified = true + } else { + activity?.onBackPressed() + } } - } searchView?.setOnQueryTextFocusChangeListener { _, hasFocus -> if (!hasFocus) pluginViewModel.search(null) } @@ -137,7 +142,11 @@ class PluginsFragment : Fragment() { // Because onActionViewCollapsed doesn't wanna work we need this workaround :( - + binding?.pluginRecyclerView?.setLinearListLayout( + isHorizontal = false, + nextDown = FOCUS_SELF, + nextRight = FOCUS_SELF, + ) binding?.pluginRecyclerView?.adapter = PluginAdapter { @@ -167,11 +176,18 @@ class PluginsFragment : Fragment() { pluginViewModel.updatePluginList(context, url) binding?.tvtypesChipsScroll?.root?.isVisible = true - bindChips(binding?.tvtypesChipsScroll?.tvtypesChips, emptyList(), TvType.values().toList()) { list -> - pluginViewModel.tvTypes.clear() - pluginViewModel.tvTypes.addAll(list.map { it.name }) - pluginViewModel.updateFilteredPlugins() - } + bindChips( + binding?.tvtypesChipsScroll?.tvtypesChips, + emptyList(), + TvType.values().toList(), + callback = { list -> + pluginViewModel.tvTypes.clear() + pluginViewModel.tvTypes.addAll(list.map { it.name }) + pluginViewModel.updateFilteredPlugins() + }, + nextFocusDown = R.id.plugin_recycler_view, + nextFocusUp = null, + ) } } 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 137e1457..991651dc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -24,6 +24,7 @@ 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.result.FOCUS_SELF import com.lagradost.cloudstream3.ui.result.UiImage import com.lagradost.cloudstream3.ui.result.VideoWatchState import com.lagradost.cloudstream3.ui.result.setImage @@ -194,7 +195,13 @@ object DataStoreHelper { builder.setContentView(binding.root) val accountName = context.getString(R.string.account) - binding.profilesRecyclerview.setLinearListLayout(isHorizontal = true) + binding.profilesRecyclerview.setLinearListLayout( + isHorizontal = true, + nextUp = FOCUS_SELF, + nextDown = FOCUS_SELF, + nextLeft = FOCUS_SELF, + nextRight = FOCUS_SELF + ) binding.profilesRecyclerview.adapter = WhoIsWatchingAdapter( selectCallBack = { account -> setAccount(account) diff --git a/app/src/main/res/layout/fragment_child_downloads.xml b/app/src/main/res/layout/fragment_child_downloads.xml index a3cc8ce8..9afaea0b 100644 --- a/app/src/main/res/layout/fragment_child_downloads.xml +++ b/app/src/main/res/layout/fragment_child_downloads.xml @@ -1,39 +1,40 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/download_child_root" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/primaryGrayBackground" + android:orientation="vertical" + tools:context=".ui.download.DownloadFragment"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@android:color/transparent"> + android:id="@+id/download_child_toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/primaryGrayBackground" + android:paddingTop="@dimen/navbar_height" + app:layout_scrollFlags="scroll|enterAlways" + app:navigationIconTint="?attr/iconColor" + app:titleTextColor="?attr/textColor" + tools:title="Overlord" /> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/primaryBlackBackground" + android:nextFocusLeft="@id/nav_rail_view" + android:nextFocusUp="@id/download_child_toolbar" + android:padding="10dp" + app:layout_behavior="@string/appbar_scrolling_view_behavior" + tools:listitem="@layout/download_child_episode" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_extensions.xml b/app/src/main/res/layout/fragment_extensions.xml index a550efa4..b3583539 100644 --- a/app/src/main/res/layout/fragment_extensions.xml +++ b/app/src/main/res/layout/fragment_extensions.xml @@ -64,6 +64,7 @@ android:focusable="true" android:foreground="@drawable/outline_drawable" android:nextFocusRight="@id/add_repo_button_imageview" + android:nextFocusUp="@id/repo_recycler_view" android:orientation="horizontal" android:padding="10dp" @@ -84,13 +85,13 @@ android:textColor="?attr/textColor" /> + android:elevation="0dp" + app:cardCornerRadius="@dimen/storage_radius" + app:cardElevation="0dp" + app:cardMaxElevation="0dp"> diff --git a/app/src/main/res/layout/fragment_plugins.xml b/app/src/main/res/layout/fragment_plugins.xml index c207b2c3..ee86f12b 100644 --- a/app/src/main/res/layout/fragment_plugins.xml +++ b/app/src/main/res/layout/fragment_plugins.xml @@ -25,18 +25,22 @@ app:titleTextColor="?attr/textColor" tools:title="Overlord" /> - + - diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml index 2fec04c6..1fde999c 100644 --- a/app/src/main/res/layout/fragment_result_tv.xml +++ b/app/src/main/res/layout/fragment_result_tv.xml @@ -248,6 +248,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit style="@style/ResultButtonTV" android:nextFocusRight="@id/result_description" + android:nextFocusUp="@id/result_play_movie" android:nextFocusDown="@id/result_play_series" android:text="@string/play_movie_button" android:visibility="visible" @@ -537,10 +538,10 @@ https://developer.android.com/design/ui/tv/samples/jet-fit + tools:visibility="gone"> --> + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingBottom="10dp"> - + @@ -116,7 +118,10 @@ android:background="?attr/primaryBlackBackground" android:descendantFocusability="afterDescendants" + android:nextFocusLeft="@id/nav_rail_view" + android:nextFocusUp="@id/tvtypes_chips" + android:nextFocusDown="@id/search_clear_call_history" android:visibility="gone" tools:listitem="@layout/homepage_parent" /> @@ -134,20 +139,24 @@ android:background="?attr/primaryBlackBackground" android:descendantFocusability="afterDescendants" android:nextFocusLeft="@id/nav_rail_view" - android:visibility="visible" + android:nextFocusUp="@id/tvtypes_chips" + android:nextFocusDown="@id/search_clear_call_history" + android:paddingBottom="50dp" + android:visibility="visible" tools:listitem="@layout/search_history_item" /> + android:layout_height="50dp" + android:layout_gravity="bottom" + android:layout_margin="0dp" + android:nextFocusUp="@id/search_history_recycler" + android:padding="0dp" + android:text="@string/clear_history" + app:cornerRadius="0dp" + app:icon="@drawable/delete_all" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search_tv.xml b/app/src/main/res/layout/fragment_search_tv.xml index 63c61393..4c4af404 100644 --- a/app/src/main/res/layout/fragment_search_tv.xml +++ b/app/src/main/res/layout/fragment_search_tv.xml @@ -11,11 +11,11 @@ tools:context=".ui.search.SearchFragment"> + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/navbar_width" + android:orientation="vertical" + android:paddingBottom="10dp"> - + @@ -118,38 +120,43 @@ android:background="?attr/primaryBlackBackground" android:descendantFocusability="afterDescendants" android:nextFocusLeft="@id/nav_rail_view" + android:nextFocusUp="@id/tvtypes_chips" + android:nextFocusDown="@id/search_clear_call_history" android:visibility="gone" tools:listitem="@layout/homepage_parent" /> + android:layout_height="match_parent" + android:background="?attr/primaryBlackBackground"> + android:layout_height="50dp" + android:layout_gravity="bottom" + android:layout_margin="0dp" + android:layout_marginStart="@dimen/navbar_width" + android:nextFocusUp="@id/search_history_recycler" + android:padding="0dp" + android:text="@string/clear_history" + app:cornerRadius="0dp" + app:icon="@drawable/delete_all" /> \ No newline at end of file diff --git a/app/src/main/res/layout/standard_toolbar.xml b/app/src/main/res/layout/standard_toolbar.xml index a28bdc80..bd1f251d 100644 --- a/app/src/main/res/layout/standard_toolbar.xml +++ b/app/src/main/res/layout/standard_toolbar.xml @@ -1,19 +1,20 @@ - + + android:id="@+id/settings_toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/primaryGrayBackground" + android:descendantFocusability="afterDescendants" + android:paddingTop="@dimen/navbar_height" + app:layout_scrollFlags="scroll|enterAlways" + app:navigationIconTint="?attr/iconColor" + app:titleTextColor="?attr/textColor" + tools:title="Overlord" /> \ No newline at end of file diff --git a/app/src/main/res/layout/tvtypes_chips.xml b/app/src/main/res/layout/tvtypes_chips.xml index 6b13546b..ee792602 100644 --- a/app/src/main/res/layout/tvtypes_chips.xml +++ b/app/src/main/res/layout/tvtypes_chips.xml @@ -6,6 +6,7 @@ android:paddingStart="8dp" android:paddingEnd="8dp" android:id="@+id/home_select_group" + android:descendantFocusability="afterDescendants" app:singleSelection="false" xmlns:android="http://schemas.android.com/apk/res/android"> diff --git a/app/src/main/res/layout/tvtypes_chips_scroll.xml b/app/src/main/res/layout/tvtypes_chips_scroll.xml index 66c7efda..8d006036 100644 --- a/app/src/main/res/layout/tvtypes_chips_scroll.xml +++ b/app/src/main/res/layout/tvtypes_chips_scroll.xml @@ -1,10 +1,13 @@ - + android:requiresFadingEdge="horizontal"> - + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 6e2a24f3..972a4caf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,8 +5,8 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:7.3.1") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20") + classpath("com.android.tools.build:gradle:8.0.2") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20") classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.5.0") // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index baa28c97..d4745142 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Fri Apr 30 17:11:15 CEST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME