Initial implementation for Library on TV

This commit is contained in:
KingLucius 2023-10-07 14:21:39 +03:00
parent f14557fe6a
commit a4a96c514b
5 changed files with 282 additions and 23 deletions

View file

@ -542,9 +542,9 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
navRailView.isVisible = isNavVisible && landscape navRailView.isVisible = isNavVisible && landscape
// Hide library on TV since it is not supported yet :( // Hide library on TV since it is not supported yet :(
val isTrueTv = isTrueTvSettings() //val isTrueTv = isTrueTvSettings()
navView.menu.findItem(R.id.navigation_library)?.isVisible = !isTrueTv //navView.menu.findItem(R.id.navigation_library)?.isVisible = !isTrueTv
navRailView.menu.findItem(R.id.navigation_library)?.isVisible = !isTrueTv //navRailView.menu.findItem(R.id.navigation_library)?.isVisible = !isTrueTv
// Hide downloads on TV // Hide downloads on TV
//navView.menu.findItem(R.id.navigation_downloads)?.isVisible = !isTrueTv //navView.menu.findItem(R.id.navigation_downloads)?.isVisible = !isTrueTv
@ -904,6 +904,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
val visible = val visible =
newFocus != null && newFocus.measuredHeight > 0 && newFocus.measuredWidth > 0 && newFocus.isShown && newFocus.tag != "tv_no_focus_tag" newFocus != null && newFocus.measuredHeight > 0 && newFocus.measuredWidth > 0 && newFocus.isShown && newFocus.tag != "tv_no_focus_tag"
focusOutline.isVisible = visible focusOutline.isVisible = visible
//Todo Clean this
Log.d("King","${newFocus?.tag}:${newFocus.toString()}")
if (newFocus != null) { if (newFocus != null) {
lastFocus = WeakReference(newFocus) lastFocus = WeakReference(newFocus)

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.ui.library package com.lagradost.cloudstream3.ui.library
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
@ -10,22 +11,27 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.AlphaAnimation import android.view.animation.AlphaAnimation
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.lagradost.cloudstream3.APIHolder import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.APIHolder.allProviders import com.lagradost.cloudstream3.APIHolder.allProviders
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentLibraryBinding import com.lagradost.cloudstream3.databinding.FragmentLibraryBinding
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.debugAssert import com.lagradost.cloudstream3.mvvm.debugAssert
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName import com.lagradost.cloudstream3.syncproviders.SyncIdName
@ -33,6 +39,7 @@ import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD 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.SEARCH_ACTION_SHOW_METADATA
import com.lagradost.cloudstream3.ui.settings.SettingsFragment
import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import com.lagradost.cloudstream3.utils.AppUtils.reduceDragSensitivity import com.lagradost.cloudstream3.utils.AppUtils.reduceDragSensitivity
@ -80,9 +87,21 @@ class LibraryFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View { ): View {
val localBinding = FragmentLibraryBinding.inflate(inflater, container, false) val layout =
binding = localBinding if (SettingsFragment.isTvSettings()) R.layout.fragment_library_tv else R.layout.fragment_library
return localBinding.root val root = inflater.inflate(layout, container, false)
binding = try {
FragmentLibraryBinding.bind(root)
} catch (t: Throwable) {
CommonActivity.showToast(
txt(R.string.unable_to_inflate, t.message ?: ""),
Toast.LENGTH_LONG
)
logError(t)
null
}
return root
//return inflater.inflate(R.layout.fragment_library, container, false) //return inflater.inflate(R.layout.fragment_library, container, false)
} }
@ -99,25 +118,15 @@ class LibraryFragment : Fragment() {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
} }
@SuppressLint("ResourceType", "CutPasteId")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
fixPaddingStatusbar(binding?.searchStatusBarPadding) fixPaddingStatusbar(binding?.searchStatusBarPadding)
binding?.sortFab?.setOnClickListener { binding?.sortFab?.setOnClickListener(sortChangeClickListener)
val methods = libraryViewModel.sortingMethods.map { binding?.librarySort?.setOnClickListener(sortChangeClickListener)
txt(it.stringRes).asString(view.context)
}
activity?.showBottomDialog(methods, binding?.libraryRoot?.findViewById<TextView>(R.id.search_src_text)?.tag = "tv_no_focus_tag"
libraryViewModel.sortingMethods.indexOf(libraryViewModel.currentSortingMethod),
txt(R.string.sort_by).asString(view.context),
false,
{},
{
val method = libraryViewModel.sortingMethods[it]
libraryViewModel.sort(method)
})
}
binding?.mainSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { binding?.mainSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean { override fun onQueryTextSubmit(query: String?): Boolean {
@ -266,7 +275,10 @@ class LibraryFragment : Fragment() {
// selects the whole list opener // selects the whole list opener
val savedListSelection = val savedListSelection =
getKey<LibraryOpener>("$currentAccount/$LIBRARY_FOLDER", syncName.name) getKey<LibraryOpener>("$currentAccount/$LIBRARY_FOLDER", syncName.name)
val savedSelection = getKey<LibraryOpener>("$currentAccount/$LIBRARY_FOLDER", syncId).takeIf { val savedSelection = getKey<LibraryOpener>(
"$currentAccount/$LIBRARY_FOLDER",
syncId
).takeIf {
it?.openType != LibraryOpenerType.Default it?.openType != LibraryOpenerType.Default
} ?: savedListSelection } ?: savedListSelection
@ -354,6 +366,9 @@ class LibraryFragment : Fragment() {
} }
(viewpager.adapter as? ViewpagerAdapter)?.pages = pages (viewpager.adapter as? ViewpagerAdapter)?.pages = pages
//fix focus on the viewpager itself
(viewpager.getChildAt(0) as RecyclerView).tag = "tv_no_focus_tag"
// Using notifyItemRangeChanged keeps the animations when sorting // Using notifyItemRangeChanged keeps the animations when sorting
viewpager.adapter?.notifyItemRangeChanged( viewpager.adapter?.notifyItemRangeChanged(
0, 0,
@ -396,6 +411,11 @@ class LibraryFragment : Fragment() {
viewpager, viewpager,
) { tab, position -> ) { tab, position ->
tab.text = pages.getOrNull(position)?.title?.asStringNull(context) tab.text = pages.getOrNull(position)?.title?.asStringNull(context)
tab.view.tag = "tv_no_focus_tag"
tab.view.nextFocusUpId = R.id.search_result_root
/*tab.view.setOnFocusChangeListener { view, b ->
Log.d("King", libraryTabLayout.focusedChild)
}*/
tab.view.setOnClickListener { tab.view.setOnClickListener {
val currentItem = val currentItem =
binding?.viewpager?.currentItem ?: return@setOnClickListener binding?.viewpager?.currentItem ?: return@setOnClickListener
@ -429,6 +449,22 @@ class LibraryFragment : Fragment() {
(binding?.viewpager?.adapter as? ViewpagerAdapter)?.rebind() (binding?.viewpager?.adapter as? ViewpagerAdapter)?.rebind()
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
} }
private val sortChangeClickListener = View.OnClickListener { view ->
val methods = libraryViewModel.sortingMethods.map {
txt(it.stringRes).asString(view.context)
}
activity?.showBottomDialog(methods,
libraryViewModel.sortingMethods.indexOf(libraryViewModel.currentSortingMethod),
txt(R.string.sort_by).asString(view.context),
false,
{},
{
val method = libraryViewModel.sortingMethods[it]
libraryViewModel.sort(method)
})
}
} }
class MenuSearchView(context: Context) : SearchView(context) { class MenuSearchView(context: Context) : SearchView(context) {

View file

@ -41,6 +41,20 @@
android:src="@drawable/ic_baseline_extension_24" android:src="@drawable/ic_baseline_extension_24"
app:tint="?attr/textColor" /> app:tint="?attr/textColor" />
<ImageView
android:id="@+id/library_sort"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_gravity="end|center_vertical"
android:layout_marginStart="10dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/change_providers_img_des"
android:padding="5dp"
android:visibility="gone"
tools:visibility="visible"
android:src="@drawable/ic_baseline_sort_24"
app:tint="?attr/textColor" />
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="40dp" android:layout_height="40dp"
@ -160,8 +174,7 @@
android:layout_height="40dp" android:layout_height="40dp"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:background="?attr/primaryGrayBackground" android:background="?attr/primaryGrayBackground"
android:descendantFocusability="blocksDescendants" android:focusable="true"
android:focusable="false"
android:paddingHorizontal="5dp" android:paddingHorizontal="5dp"
app:layout_scrollFlags="noScroll" app:layout_scrollFlags="noScroll"
app:tabGravity="center" app:tabGravity="center"

View file

@ -0,0 +1,206 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/library_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/empty_list_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="30dp"
android:gravity="center"
android:visibility="gone"
tools:visibility="visible" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryGrayBackground">
<LinearLayout
android:id="@+id/search_status_bar_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/provider_selector"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="end|center_vertical"
android:layout_marginStart="10dp"
android:padding="2dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/change_providers_img_des"
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusRight="@id/library_sort"
android:nextFocusDown="@id/search_result_root"
android:tag="@string/tv_no_focus_tag"
android:src="@drawable/ic_baseline_extension_24"
app:tint="?attr/textColor" />
<ImageView
android:id="@+id/library_sort"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="end|center_vertical"
android:layout_marginStart="10dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/change_providers_img_des"
android:nextFocusLeft="@id/provider_selector"
android:nextFocusRight="@id/list_selector"
android:nextFocusDown="@id/search_result_root"
android:tag="@string/tv_no_focus_tag"
android:src="@drawable/ic_baseline_sort_24"
app:tint="?attr/textColor" />
<ImageView
android:id="@+id/list_selector"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="end|center_vertical"
android:padding="1dp"
android:layout_marginStart="10dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/change_providers_img_des"
android:nextFocusLeft="@id/library_sort"
android:nextFocusRight="@id/main_search"
android:nextFocusDown="@id/search_result_root"
android:tag="@string/tv_no_focus_tag"
android:src="@drawable/ic_baseline_filter_list_24"
app:tint="?attr/textColor" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="10dp"
android:background="@drawable/search_background"
android:visibility="visible"
android:nextFocusDown="@id/search_result_root"
app:layout_scrollFlags="scroll|enterAlways">
<androidx.appcompat.widget.SearchView
android:id="@+id/main_search"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="end"
android:iconifiedByDefault="false"
android:imeOptions="actionSearch"
android:inputType="text"
android:focusable="true"
android:tag="tv_no_focus_tag"
android:nextFocusLeft="@id/list_selector"
android:nextFocusDown="@id/search_result_root"
android:paddingStart="-10dp"
app:iconifiedByDefault="false"
app:queryBackground="@color/transparent"
app:queryHint="@string/search_hint"
app:searchIcon="@drawable/search_icon"
tools:ignore="RtlSymmetry">
</androidx.appcompat.widget.SearchView>
</FrameLayout>
</LinearLayout>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="40dp"
android:focusable="false"
android:tag="@string/tv_no_focus_tag"
tools:listitem="@layout/library_viewpager_page" />
<LinearLayout
android:id="@+id/library_loading_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="false"
android:tag="@string/tv_no_focus_tag"
android:background="?attr/primaryBlackBackground"
android:visibility="gone"
tools:visibility="visible">
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/library_loading_shimmer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="2dp"
app:shimmer_auto_start="true"
app:shimmer_base_alpha="0.2"
android:focusable="false"
android:tag="@string/tv_no_focus_tag"
app:shimmer_duration="@integer/loading_time"
app:shimmer_highlight_alpha="0.3">
<GridView
android:id="@+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:gravity="center"
android:horizontalSpacing="10dp"
android:numColumns="3"
android:paddingBottom="120dp"
android:focusable="false"
android:tag="@string/tv_no_focus_tag"
android:verticalSpacing="10dp"
tools:listitem="@layout/loading_poster_dynamic" />
</com.facebook.shimmer.ShimmerFrameLayout>
</LinearLayout>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="40dp">
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/sort_fab"
style="@style/ExtendedFloatingActionButton"
android:text="@string/sort"
android:textColor="?attr/textColor"
android:visibility="gone"
tools:visibility="visible"
app:icon="@drawable/ic_baseline_sort_24"
tools:ignore="ContentDescription" />
</FrameLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/library_tab_layout"
style="@style/Theme.Widget.Tabs"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="bottom"
android:nextFocusUp="@id/search_result_root"
android:background="?attr/primaryGrayBackground"
android:nextFocusLeft="@id/nav_rail_view"
android:paddingHorizontal="5dp"
app:layout_scrollFlags="noScroll"
app:tabGravity="center"
app:tabIndicator="@drawable/indicator_background"
app:tabIndicatorColor="?attr/white"
app:tabIndicatorGravity="center"
app:tabIndicatorHeight="30dp"
app:tabMode="scrollable"
app:tabSelectedTextColor="?attr/primaryBlackBackground"
app:tabTextAppearance="@style/TabNoCaps"
app:tabTextColor="?attr/textColor" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -5,5 +5,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:focusable="false"
android:tag="tv_no_focus_tag"
tools:listitem="@layout/home_result_grid_expanded" /> tools:listitem="@layout/home_result_grid_expanded" />