diff --git a/app/build.gradle b/app/build.gradle index 67ad34e8..9a85f3b2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -89,7 +89,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.2' implementation 'androidx.navigation:navigation-fragment-ktx:2.4.0-beta02' implementation 'androidx.navigation:navigation-ui-ktx:2.4.0-beta02' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0' @@ -114,6 +114,8 @@ dependencies { implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' + implementation "androidx.leanback:leanback-paging:1.1.0-alpha09" + // Exoplayer implementation 'com.google.android.exoplayer:exoplayer:2.15.1' implementation 'com.google.android.exoplayer:extension-cast:2.15.1' diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 8f0136b9..677eeaa6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -51,6 +51,8 @@ import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.UIHelper.shouldShowPIPMode import com.lagradost.cloudstream3.utils.UIHelper.toPx import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.activity_main.cast_mini_controller_holder +import kotlinx.android.synthetic.main.activity_main_tv.* import kotlinx.android.synthetic.main.fragment_result.* import java.util.* import kotlin.concurrent.thread @@ -445,8 +447,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { .setPopExitAnim(R.anim.nav_pop_exit) .setPopUpTo(navController.graph.startDestination, false) .build()*/ - nav_view.setupWithNavController(navController) - + nav_view?.setupWithNavController(navController) + nav_rail_view?.setupWithNavController(navController) navController.addOnDestinationChangedListener { _, destination, _ -> this.hideKeyboard() // nav_view.hideKeyboard() @@ -462,13 +464,16 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { cast_mini_controller_holder?.isVisible = !listOf(R.id.navigation_results, R.id.navigation_player).contains(destination.id) - nav_view.isVisible = listOf( + val isNavVisible = listOf( R.id.navigation_home, R.id.navigation_search, R.id.navigation_downloads, R.id.navigation_settings, R.id.navigation_download_child ).contains(destination.id) + + nav_view?.isVisible = isNavVisible + nav_rail_view?.isVisible = isNavVisible } /*nav_view.setOnNavigationItemSelectedListener { item -> @@ -489,7 +494,9 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { true }*/ - nav_view.itemRippleColor = ColorStateList.valueOf(getResourceColor(R.attr.colorPrimary, 0.1f)) + val rippleColor = ColorStateList.valueOf(getResourceColor(R.attr.colorPrimary, 0.1f)) + nav_view?.itemRippleColor = rippleColor + nav_rail_view?.itemRippleColor = rippleColor if (!checkWrite()) { requestRW() 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 89d573be..0fbc8156 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 @@ -18,7 +18,7 @@ class HomeChildItemAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return CardViewHolder( - LayoutInflater.from(parent.context).inflate(layout, parent, false), clickCallback + LayoutInflater.from(parent.context).inflate(layout, parent, false), clickCallback, itemCount ) } @@ -39,11 +39,19 @@ class HomeChildItemAdapter( } class CardViewHolder - constructor(itemView: View, private val clickCallback: (SearchClickCallback) -> Unit) : + constructor(itemView: View, private val clickCallback: (SearchClickCallback) -> Unit, val itemCount: Int) : RecyclerView.ViewHolder(itemView) { fun bind(card: SearchResponse, index: Int) { - SearchResultBuilder.bind(clickCallback, card, itemView) + + // TV focus fixing + val nextFocusBehavior = when(index){ + 0 -> true + itemCount - 1 -> false + else -> null + } + + SearchResultBuilder.bind(clickCallback, card, itemView, nextFocusBehavior) itemView.tag = index //val ani = ScaleAnimation(0.9f, 1.0f, 0.9f, 1f) //ani.fillAfter = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt index 78d6169f..32c7be64 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt @@ -14,6 +14,7 @@ import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD import com.lagradost.cloudstream3.ui.download.DownloadButtonViewHolder import com.lagradost.cloudstream3.ui.download.DownloadClickEvent import com.lagradost.cloudstream3.ui.download.EasyDownloadButton +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadManager @@ -183,6 +184,12 @@ class EpisodeAdapter( clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) } + if (episodeHolder.context.isTvSettings()) { + episodeHolder.isFocusable = true + episodeHolder.isFocusableInTouchMode = true + episodeHolder.touchscreenBlocksFocus = false + } + episodeHolder.setOnLongClickListener { clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card)) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt index 8af9f63b..90d2e611 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt @@ -69,7 +69,7 @@ class SearchAdapter( } } - SearchResultBuilder.bind(clickCallback, card, itemView) + SearchResultBuilder.bind(clickCallback, card, itemView,) } } } 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 799d6603..f85a98b0 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 @@ -29,6 +29,7 @@ import com.lagradost.cloudstream3.ui.APIRepository.Companion.typesActive import com.lagradost.cloudstream3.ui.home.HomeFragment import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList import com.lagradost.cloudstream3.ui.home.ParentItemAdapter +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.SEARCH_PROVIDER_TOGGLE @@ -295,6 +296,11 @@ class SearchFragment : Fragment() { } } + if(context?.isTvSettings() == true) { + search_filter.isFocusable = true + search_filter.isFocusableInTouchMode = true + } + main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { context?.let { ctx -> diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt index 2ba9d1df..efc45a14 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt @@ -6,6 +6,7 @@ import android.widget.ProgressBar import android.widget.TextView import androidx.cardview.widget.CardView import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.getNameFull import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual @@ -13,10 +14,15 @@ import com.lagradost.cloudstream3.utils.UIHelper.setImage import kotlinx.android.synthetic.main.home_result_grid.view.* object SearchResultBuilder { + /** + * @param nextFocusBehavior True if first, False if last, Null if between. + * Used to prevent escaping the adapter horizontally (focus wise). + */ fun bind( clickCallback: (SearchClickCallback) -> Unit, card: SearchResponse, - itemView: View + itemView: View, + nextFocusBehavior: Boolean? = null ) { val cardView: ImageView = itemView.imageView val cardText: TextView? = itemView.imageText @@ -51,6 +57,21 @@ object SearchResultBuilder { ) } + when (nextFocusBehavior) { + true -> bg.nextFocusLeftId = bg.id + false -> bg.nextFocusRightId = bg.id + null -> { + bg.nextFocusRightId = -1 + bg.nextFocusLeftId = -1 + } + } + + if (bg.context.isTvSettings()) { + bg.isFocusable = true + bg.isFocusableInTouchMode = true + bg.touchscreenBlocksFocus = false + } + bg.setOnLongClickListener { clickCallback.invoke(SearchClickCallback(SEARCH_ACTION_SHOW_METADATA, it, card)) return@setOnLongClickListener true diff --git a/app/src/main/res/layout/activity_main_tv.xml b/app/src/main/res/layout/activity_main_tv.xml index 6c396c0c..3713e35a 100644 --- a/app/src/main/res/layout/activity_main_tv.xml +++ b/app/src/main/res/layout/activity_main_tv.xml @@ -11,34 +11,31 @@ - + app:menu="@menu/bottom_nav_menu"> + + +