diff --git a/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt index 2a3e13e5..81753f6b 100644 --- a/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt @@ -138,7 +138,7 @@ class ExampleInstrumentedTest { } break } - if(!validResults) { + if (!validResults) { System.err.println("Api ${api.name} did not load on any") } @@ -183,7 +183,9 @@ class ExampleInstrumentedTest { getAllProviders().amap { api -> if (api.hasMainPage) { try { - val homepage = api.getMainPage() + val f = api.mainPage.first() + val homepage = + api.getMainPage(1, MainPageRequest(f.name, f.data, f.horizontalImages)) when { homepage == null -> { System.err.println("Homepage provider ${api.name} did not correctly load homepage!") @@ -192,7 +194,7 @@ class ExampleInstrumentedTest { System.err.println("Homepage provider ${api.name} does not contain any items!") } homepage.items.any { it.list.isEmpty() } -> { - System.err.println ("Homepage provider ${api.name} does not have any items on result!") + System.err.println("Homepage provider ${api.name} does not have any items on result!") } } } catch (e: Exception) { @@ -231,7 +233,7 @@ class ExampleInstrumentedTest { invalidProvider.add(Pair(api, e)) } } - if(invalidProvider.isEmpty()) { + if (invalidProvider.isEmpty()) { println("No Invalid providers! :D") } else { println("Invalid providers are: ") diff --git a/app/src/main/java/com/lagradost/cloudstream3/HeaderDecorationBindingAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/HeaderDecorationBindingAdapter.kt new file mode 100644 index 00000000..045a7963 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/HeaderDecorationBindingAdapter.kt @@ -0,0 +1,11 @@ +package com.lagradost.cloudstream3 + +import android.view.LayoutInflater +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.ui.HeaderViewDecoration + +fun setHeaderDecoration(view: RecyclerView, @LayoutRes headerViewRes: Int) { + val headerView = LayoutInflater.from(view.context).inflate(headerViewRes, null) + view.addItemDecoration(HeaderViewDecoration(headerView)) +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 9a591580..1551467b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -10,12 +10,18 @@ import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.WindowManager +import android.widget.FrameLayout +import android.widget.LinearLayout import androidx.activity.result.ActivityResultLauncher import androidx.annotation.IdRes import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible +import androidx.core.view.marginRight +import androidx.core.view.setMargins import androidx.fragment.app.FragmentActivity +import androidx.fragment.app.FragmentContainerView import androidx.navigation.NavController import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hierarchy @@ -66,6 +72,7 @@ import com.lagradost.cloudstream3.ui.search.SearchResultBuilder import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv import com.lagradost.cloudstream3.ui.settings.SettingsGeneral import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions @@ -89,6 +96,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.requestRW +import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.USER_PROVIDER_API import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.nicehttp.Requests @@ -379,6 +387,27 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { R.id.navigation_settings_plugins, ).contains(destination.id) + + val dontPush = listOf( + R.id.navigation_home, + R.id.navigation_search, + R.id.navigation_results_phone, + R.id.navigation_results_tv, + R.id.navigation_player, + ).contains(destination.id) + + nav_host_fragment?.apply { + val params = layoutParams as ConstraintLayout.LayoutParams + + params.setMargins( + if (!dontPush && isTvSettings()) resources.getDimensionPixelSize(R.dimen.navbar_width) else 0, + params.topMargin, + params.rightMargin, + params.bottomMargin + ) + layoutParams = params + } + val landscape = when (resources.configuration.orientation) { Configuration.ORIENTATION_LANDSCAPE -> { true @@ -619,7 +648,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) - + updateTv() if (isTvSettings()) { setContentView(R.layout.activity_main_tv) } else { @@ -743,7 +772,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { nav_view?.setupWithNavController(navController) val nav_rail = findViewById(R.id.nav_rail_view) nav_rail?.setupWithNavController(navController) + if (isTvSettings()) { + nav_rail?.background?.alpha = 200 + } else { + nav_rail?.background?.alpha = 255 + } nav_rail?.setOnItemSelectedListener { item -> onNavDestinationSelected( item, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/HeaderViewDecoration.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/HeaderViewDecoration.kt new file mode 100644 index 00000000..40c03012 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/HeaderViewDecoration.kt @@ -0,0 +1,42 @@ +package com.lagradost.cloudstream3.ui + +import android.graphics.Canvas +import android.graphics.Rect +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +class HeaderViewDecoration(private val customView: View) : RecyclerView.ItemDecoration() { + override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + super.onDraw(c, parent, state) + customView.layout(parent.left, 0, parent.right, customView.measuredHeight) + for (i in 0 until parent.childCount) { + val view = parent.getChildAt(i) + if (parent.getChildAdapterPosition(view) == 0) { + c.save() + val height = customView.measuredHeight + val top = view.top - height + c.translate(0f, top.toFloat()) + customView.draw(c) + c.restore() + break + } + } + } + + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + if (parent.getChildAdapterPosition(view) == 0) { + customView.measure( + View.MeasureSpec.makeMeasureSpec(parent.measuredWidth, View.MeasureSpec.AT_MOST), + View.MeasureSpec.makeMeasureSpec(parent.measuredHeight, View.MeasureSpec.AT_MOST) + ) + outRect.set(0, customView.measuredHeight, 0, 0) + } else { + outRect.setEmpty() + } + } +} \ No newline at end of file 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 8e2ca6af..2f08e065 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 @@ -31,6 +31,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.button.MaterialButton import com.google.android.material.chip.Chip +import com.google.android.material.chip.ChipDrawable import com.google.android.material.chip.ChipGroup import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.apis @@ -84,31 +85,21 @@ import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImageBlur import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.cloudstream3.widget.CenterZoomLayoutManager +import kotlinx.android.synthetic.main.activity_main_tv.* import kotlinx.android.synthetic.main.fragment_home.* import kotlinx.android.synthetic.main.fragment_home.home_api_fab -import kotlinx.android.synthetic.main.fragment_home.home_bookmarked_child_recyclerview -import kotlinx.android.synthetic.main.fragment_home.home_bookmarked_holder import kotlinx.android.synthetic.main.fragment_home.home_change_api_loading -import kotlinx.android.synthetic.main.fragment_home.home_loaded import kotlinx.android.synthetic.main.fragment_home.home_loading import kotlinx.android.synthetic.main.fragment_home.home_loading_error import kotlinx.android.synthetic.main.fragment_home.home_loading_shimmer import kotlinx.android.synthetic.main.fragment_home.home_loading_statusbar import kotlinx.android.synthetic.main.fragment_home.home_master_recycler -import kotlinx.android.synthetic.main.fragment_home.home_plan_to_watch_btt -import kotlinx.android.synthetic.main.fragment_home.home_provider_meta_info -import kotlinx.android.synthetic.main.fragment_home.home_provider_name import kotlinx.android.synthetic.main.fragment_home.home_reload_connection_open_in_browser import kotlinx.android.synthetic.main.fragment_home.home_reload_connectionerror -import kotlinx.android.synthetic.main.fragment_home.home_type_completed_btt -import kotlinx.android.synthetic.main.fragment_home.home_type_dropped_btt -import kotlinx.android.synthetic.main.fragment_home.home_type_on_hold_btt -import kotlinx.android.synthetic.main.fragment_home.home_type_watching_btt -import kotlinx.android.synthetic.main.fragment_home.home_watch_child_recyclerview -import kotlinx.android.synthetic.main.fragment_home.home_watch_holder -import kotlinx.android.synthetic.main.fragment_home.home_watch_parent_item_title import kotlinx.android.synthetic.main.fragment_home.result_error_text +import kotlinx.android.synthetic.main.fragment_home_head_tv.* import kotlinx.android.synthetic.main.fragment_home_tv.* +import kotlinx.android.synthetic.main.fragment_result.* import kotlinx.android.synthetic.main.fragment_search.* import kotlinx.android.synthetic.main.home_episodes_expanded.* import kotlinx.android.synthetic.main.tvtypes_chips.* @@ -440,10 +431,6 @@ class HomeFragment : Fragment() { return inflater.inflate(layout, container, false) } - private fun toggleMainVisibility(visible: Boolean) { - home_main_poster_recyclerview?.isVisible = visible - } - @SuppressLint("NotifyDataSetChanged") // we need to notify to change poster private fun fixGrid() { activity?.getSpanCount()?.let { @@ -467,7 +454,7 @@ class HomeFragment : Fragment() { override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) - (home_preview_viewpager?.adapter as? HomeScrollAdapter)?.notifyDataSetChanged() + //(home_preview_viewpager?.adapter as? HomeScrollAdapter)?.notifyDataSetChanged() fixGrid() } @@ -517,8 +504,9 @@ class HomeFragment : Fragment() { }*/ private fun focusCallback(card: SearchResponse) { - home_focus_text?.text = card.name - home_blur_poster?.setImageBlur(card.posterUrl, 50) + // home_focus_text?.text = card.name + // home_blur_poster?.setImageBlur(card.posterUrl, 50, headers = card.posterHeaders) + // home_bg_poster?.setImage(card.posterUrl,headers =card.posterHeaders) } private fun homeHandleSearch(callback: SearchClickCallback) { @@ -537,8 +525,8 @@ class HomeFragment : Fragment() { super.onViewCreated(view, savedInstanceState) fixGrid() - home_change_api?.setOnClickListener(apiChangeClickListener) home_change_api_loading?.setOnClickListener(apiChangeClickListener) + home_preview_change_api?.setOnClickListener(apiChangeClickListener) home_api_fab?.setOnClickListener(apiChangeClickListener) home_random?.setOnClickListener { if (listHomepageItems.isNotEmpty()) { @@ -557,208 +545,135 @@ class HomeFragment : Fragment() { observe(homeViewModel.preview) { preview -> // Always reset the padding, otherwise the will move lower and lower // home_fix_padding?.setPadding(0, 0, 0, 0) - home_fix_padding?.let { v -> - val params = v.layoutParams - params.height = 0 - v.layoutParams = params - } + //home_fix_padding?.let { v -> + // val params = v.layoutParams + // params.height = 0 + // v.layoutParams = params + //} - when (preview) { - is Resource.Success -> { - home_preview?.isVisible = true - (home_preview_viewpager?.adapter as? HomeScrollAdapter)?.apply { - if (!setItems(preview.value.second, preview.value.first)) { - home_preview_viewpager?.setCurrentItem(0, false) - } - // home_preview_viewpager?.setCurrentItem(1000, false) - } + (home_master_recycler?.adapter as? HomeParentItemAdapterPreview?)?.setPreviewData( + preview + ) - //.also { - //home_preview_viewpager?.adapter = - //} - } - else -> { - (home_preview_viewpager?.adapter as? HomeScrollAdapter)?.setItems( - listOf(), - false - ) - home_preview?.isVisible = false - context?.fixPaddingStatusbarView(home_fix_padding) - } - } + //when (preview) { + // is Resource.Success -> { + // home_preview?.isVisible = true + // (home_preview_viewpager?.adapter as? HomeScrollAdapter)?.apply { + // if (!setItems(preview.value.second, preview.value.first)) { + // home_preview_viewpager?.setCurrentItem(0, false) + // } + // // home_preview_viewpager?.setCurrentItem(1000, false) + // } +// + // //.also { + // //home_preview_viewpager?.adapter = + // //} + // } + // else -> { + // (home_preview_viewpager?.adapter as? HomeScrollAdapter)?.setItems( + // listOf(), + // false + // ) + // home_preview?.isVisible = false + // context?.fixPaddingStatusbarView(home_fix_padding) + // } + //} } - val searchText = - home_search?.findViewById(androidx.appcompat.R.id.search_src_text) - searchText?.context?.getResourceColor(R.attr.white)?.let { color -> - searchText.setTextColor(color) - searchText.setHintTextColor(color) - } - - home_preview_viewpager?.apply { - setPageTransformer(HomeScrollTransformer()) - val callback: OnPageChangeCallback = object : OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - - // home_search?.isIconified = true - //home_search?.isVisible = true - //home_search?.clearFocus() - - (home_preview_viewpager?.adapter as? HomeScrollAdapter)?.apply { - if (position >= itemCount - 1 && hasMoreItems) { - hasMoreItems = false // dont make two requests - homeViewModel.loadMoreHomeScrollResponses() - } - - getItem(position) - ?.apply { - home_preview_title_holder?.let { parent -> - TransitionManager.beginDelayedTransition(parent, ChangeBounds()) - } - - // home_preview_tags?.text = tags?.joinToString(" • ") ?: "" - // home_preview_tags?.isGone = tags.isNullOrEmpty() - // home_preview_image?.setImage(posterUrl, posterHeaders) - // home_preview_title?.text = name - - home_preview_play?.setOnClickListener { - activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST) - //activity.loadSearchResult(url, START_ACTION_RESUME_LATEST) - } - home_preview_info?.setOnClickListener { - activity?.loadResult(url, apiName) - //activity.loadSearchResult(random) - } - // very ugly code, but I dont care - val watchType = DataStoreHelper.getResultWatchState(this.getId()) - home_preview_bookmark?.setText(watchType.stringRes) - home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds( - null, - getDrawable(home_preview_bookmark.context, watchType.iconRes), - null, - null - ) - home_preview_bookmark?.setOnClickListener { fab -> - activity?.showBottomDialog( - WatchType.values() - .map { fab.context.getString(it.stringRes) } - .toList(), - DataStoreHelper.getResultWatchState(this.getId()).ordinal, - fab.context.getString(R.string.action_add_to_bookmarks), - showApply = false, - {}) { - val newValue = WatchType.values()[it] - home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds( - null, - getDrawable( - home_preview_bookmark.context, - newValue.iconRes - ), - null, - null - ) - home_preview_bookmark?.setText(newValue.stringRes) - - updateWatchStatus(this, newValue) - reloadStored() - } - } - - } - } - } - } - registerOnPageChangeCallback(callback) - adapter = HomeScrollAdapter() - } + //val searchText = + // home_search?.findViewById(androidx.appcompat.R.id.search_src_text) + //searchText?.context?.getResourceColor(R.attr.white)?.let { color -> + // searchText.setTextColor(color) + // searchText.setHintTextColor(color) + //} observe(homeViewModel.apiName) { apiName -> currentApiName = apiName // setKey(USER_SELECTED_HOMEPAGE_API, apiName) home_api_fab?.text = apiName - home_provider_name?.text = apiName - try { - home_search?.queryHint = getString(R.string.search_hint_site).format(apiName) - } catch (e: Exception) { - logError(e) - } - home_provider_meta_info?.isVisible = false + //home_provider_name?.text = apiName + // try { + // home_search?.queryHint = getString(R.string.search_hint_site).format(apiName) + // } catch (e: Exception) { + // logError(e) + // } + //home_provider_meta_info?.isVisible = false - getApiFromNameNull(apiName)?.let { currentApi -> - val typeChoices = listOf( - Pair(R.string.movies, listOf(TvType.Movie)), - Pair(R.string.tv_series, listOf(TvType.TvSeries)), - Pair(R.string.documentaries, listOf(TvType.Documentary)), - Pair(R.string.cartoons, listOf(TvType.Cartoon)), - Pair(R.string.anime, listOf(TvType.Anime, TvType.OVA, TvType.AnimeMovie)), - Pair(R.string.torrent, listOf(TvType.Torrent)), - Pair(R.string.asian_drama, listOf(TvType.AsianDrama)), - ).filter { item -> currentApi.supportedTypes.any { type -> item.second.contains(type) } } - home_provider_meta_info?.text = - typeChoices.joinToString(separator = ", ") { getString(it.first) } - home_provider_meta_info?.isVisible = true - } + //getApiFromNameNull(apiName)?.let { currentApi -> + // val typeChoices = listOf( + // Pair(R.string.movies, listOf(TvType.Movie)), + // Pair(R.string.tv_series, listOf(TvType.TvSeries)), + // Pair(R.string.documentaries, listOf(TvType.Documentary)), + // Pair(R.string.cartoons, listOf(TvType.Cartoon)), + // Pair(R.string.anime, listOf(TvType.Anime, TvType.OVA, TvType.AnimeMovie)), + // Pair(R.string.torrent, listOf(TvType.Torrent)), + // Pair(R.string.asian_drama, listOf(TvType.AsianDrama)), + // ).filter { item -> currentApi.supportedTypes.any { type -> item.second.contains(type) } } + // home_provider_meta_info?.text = + // typeChoices.joinToString(separator = ", ") { getString(it.first) } + // home_provider_meta_info?.isVisible = true + //} } - home_main_poster_recyclerview?.adapter = - HomeChildItemAdapter( - mutableListOf(), - R.layout.home_result_big_grid, - nextFocusUp = home_main_poster_recyclerview?.nextFocusUpId, - nextFocusDown = home_main_poster_recyclerview?.nextFocusDownId - ) { callback -> - homeHandleSearch(callback) - } - home_main_poster_recyclerview?.setLinearListLayout() - observe(homeViewModel.randomItems) { items -> - if (items.isNullOrEmpty()) { - toggleMainVisibility(false) - } else { - val tempAdapter = home_main_poster_recyclerview?.adapter as? HomeChildItemAdapter? - // no need to reload if it has the same data - if (tempAdapter != null && tempAdapter.cardList == items) { - toggleMainVisibility(true) - return@observe - } + //home_main_poster_recyclerview?.adapter = + // HomeChildItemAdapter( + // mutableListOf(), + // R.layout.home_result_big_grid, + // nextFocusUp = home_main_poster_recyclerview?.nextFocusUpId, + // nextFocusDown = home_main_poster_recyclerview?.nextFocusDownId + // ) { callback -> + // homeHandleSearch(callback) + // } + //home_main_poster_recyclerview?.setLinearListLayout() + //observe(homeViewModel.randomItems) { items -> + // if (items.isNullOrEmpty()) { + // toggleMainVisibility(false) + // } else { + // val tempAdapter = home_main_poster_recyclerview?.adapter as? HomeChildItemAdapter? + // // no need to reload if it has the same data + // if (tempAdapter != null && tempAdapter.cardList == items) { + // toggleMainVisibility(true) + // return@observe + // } +// + // val randomSize = items.size + // tempAdapter?.updateList(items) + // if (!isTvSettings()) { + // home_main_poster_recyclerview?.post { + // (home_main_poster_recyclerview?.layoutManager as CenterZoomLayoutManager?)?.let { manager -> + // manager.updateSize(forceUpdate = true) + // if (randomSize > 2) { + // manager.scrollToPosition(randomSize / 2) + // manager.snap { dx -> + // home_main_poster_recyclerview?.post { + // // this is the best I can do, fuck android for not including instant scroll + // home_main_poster_recyclerview?.smoothScrollBy(dx, 0) + // } + // } + // } + // } + // } + // } else { + // items.firstOrNull()?.let { + // focusCallback(it) + // } + // } + // toggleMainVisibility(true) + // } + //} - val randomSize = items.size - tempAdapter?.updateList(items) - if (!isTvSettings()) { - home_main_poster_recyclerview?.post { - (home_main_poster_recyclerview?.layoutManager as CenterZoomLayoutManager?)?.let { manager -> - manager.updateSize(forceUpdate = true) - if (randomSize > 2) { - manager.scrollToPosition(randomSize / 2) - manager.snap { dx -> - home_main_poster_recyclerview?.post { - // this is the best I can do, fuck android for not including instant scroll - home_main_poster_recyclerview?.smoothScrollBy(dx, 0) - } - } - } - } - } - } else { - items.firstOrNull()?.let { - focusCallback(it) - } - } - toggleMainVisibility(true) - } - } - - home_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String): Boolean { - QuickSearchFragment.pushSearch(activity, query, currentApiName?.let { arrayOf(it) }) - - return true - } - - override fun onQueryTextChange(newText: String): Boolean { - //searchViewModel.quickSearch(newText) - return true - } - }) + //home_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + // override fun onQueryTextSubmit(query: String): Boolean { + // QuickSearchFragment.pushSearch(activity, query, currentApiName?.let { arrayOf(it) }) +// + // return true + // } +// + // override fun onQueryTextChange(newText: String): Boolean { + // //searchViewModel.quickSearch(newText) + // return true + // } + //}) observe(homeViewModel.page) { data -> when (data) { @@ -769,7 +684,6 @@ class HomeFragment : Fragment() { val mutableListOfResponse = mutableListOf() listHomepageItems.clear() - // println("ITEMCOUNT: ${d.values.size} ${home_master_recycler?.adapter?.itemCount}") (home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList( d.values.toMutableList(), home_master_recycler @@ -777,7 +691,7 @@ class HomeFragment : Fragment() { home_loading?.isVisible = false home_loading_error?.isVisible = false - home_loaded?.isVisible = true + //home_loaded?.isVisible = true if (toggleRandomButton) { //Flatten list d.values.forEach { dlist -> @@ -817,80 +731,29 @@ class HomeFragment : Fragment() { home_loading?.isVisible = false home_loading_error?.isVisible = true - home_loaded?.isVisible = false + //home_loaded?.isVisible = false } is Resource.Loading -> { (home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList(listOf()) home_loading_shimmer?.startShimmer() home_loading?.isVisible = true home_loading_error?.isVisible = false - home_loaded?.isVisible = false + //home_loaded?.isVisible = false } } } - val toggleList = listOf( - Pair(home_type_watching_btt, WatchType.WATCHING), - Pair(home_type_completed_btt, WatchType.COMPLETED), - Pair(home_type_dropped_btt, WatchType.DROPPED), - Pair(home_type_on_hold_btt, WatchType.ONHOLD), - Pair(home_plan_to_watch_btt, WatchType.PLANTOWATCH), - ) - val currentSet = getKey(HOME_BOOKMARK_VALUE_LIST) - ?.map { WatchType.fromInternalId(it) }?.toSet() ?: emptySet() - for ((chip, watch) in toggleList) { - chip.isChecked = currentSet.contains(watch) - chip?.setOnCheckedChangeListener { _, isChecked -> - if (isChecked) { - homeViewModel.loadStoredData( - setOf(watch) - // If we filter all buttons then two can be checked at the same time - // Revert this if you want to go back to multi selection -// toggleList.filter { it.first?.isChecked == true }.map { it.second }.toSet() - ) - } - // Else if all are unchecked -> Do not load data - else if (toggleList.all { it.first?.isChecked != true }) { - homeViewModel.loadStoredData(emptySet()) - } - } - /*chip?.setOnClickListener { - - - homeViewModel.loadStoredData(EnumSet.of(watch)) - } - - chip?.setOnLongClickListener { itemView -> - val list = EnumSet.noneOf(WatchType::class.java) - itemView.context.getKey(HOME_BOOKMARK_VALUE_LIST) - ?.map { WatchType.fromInternalId(it) }?.let { - list.addAll(it) - } - - if (list.contains(watch)) { - list.remove(watch) - } else { - list.add(watch) - } - homeViewModel.loadStoredData(list) - return@setOnLongClickListener true - }*/ - } observe(homeViewModel.availableWatchStatusTypes) { availableWatchStatusTypes -> context?.setKey( HOME_BOOKMARK_VALUE_LIST, availableWatchStatusTypes.first.map { it.internalId }.toIntArray() ) + (home_master_recycler?.adapter as? HomeParentItemAdapterPreview?)?.setAvailableWatchStatusTypes( + availableWatchStatusTypes + ) - for (item in toggleList) { - val watch = item.second - item.first?.apply { - isVisible = availableWatchStatusTypes.second.contains(watch) - isSelected = availableWatchStatusTypes.first.contains(watch) - } - } /*home_bookmark_select?.setOnClickListener { it.popupMenuNoIcons(availableWatchStatusTypes.second.map { type -> @@ -905,32 +768,40 @@ class HomeFragment : Fragment() { home_bookmarked_parent_item_title?.text = getString(availableWatchStatusTypes.first.stringRes)*/ } - observe(homeViewModel.bookmarks) { (isVis, bookmarks) -> - home_bookmarked_holder.isVisible = isVis - - (home_bookmarked_child_recyclerview?.adapter as? HomeChildItemAdapter?)?.updateList( - bookmarks + //TODO REMAKE + observe(homeViewModel.bookmarks) { data -> + (home_master_recycler?.adapter as? HomeParentItemAdapterPreview?)?.setBookmarkData( + data ) - home_bookmarked_child_more_info?.setOnClickListener { - activity?.loadHomepageList( - HomePageList( - getString(R.string.error_bookmarks_text), //home_bookmarked_parent_item_title?.text?.toString() ?: getString(R.string.error_bookmarks_text), - bookmarks - ) - ) { - deleteAllBookmarkedData() - homeViewModel.loadStoredData(null) - } - } + //(home_bookmarked_child_recyclerview?.adapter as? HomeChildItemAdapter?)?.updateList( + // bookmarks + //) +// + // home_bookmarked_child_more_info?.setOnClickListener { + // activity?.loadHomepageList( + // HomePageList( + // getString(R.string.error_bookmarks_text), //home_bookmarked_parent_item_title?.text?.toString() ?: getString(R.string.error_bookmarks_text), + // bookmarks + // ) + // ) { + // deleteAllBookmarkedData() + // homeViewModel.loadStoredData(null) + // } + // } } observe(homeViewModel.resumeWatching) { resumeWatching -> - home_watch_holder?.isVisible = resumeWatching.isNotEmpty() - (home_watch_child_recyclerview?.adapter as? HomeChildItemAdapter?)?.updateList( + (home_master_recycler?.adapter as? HomeParentItemAdapterPreview?)?.setResumeWatchingData( resumeWatching ) + + //home_watch_holder?.isVisible = resumeWatching.isNotEmpty() + //(home_watch_child_recyclerview?.adapter as? HomeChildItemAdapter?)?.updateList( + // resumeWatching + //) + if (isTrueTvSettings()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ioSafe { @@ -939,21 +810,21 @@ class HomeFragment : Fragment() { } } - home_watch_child_more_info?.setOnClickListener { - activity?.loadHomepageList( - HomePageList( - home_watch_parent_item_title?.text?.toString() - ?: getString(R.string.continue_watching), - resumeWatching - ) - ) { - deleteAllResumeStateIds() - homeViewModel.loadResumeWatching() - } - } + //home_watch_child_more_info?.setOnClickListener { + // activity?.loadHomepageList( + // HomePageList( + // home_watch_parent_item_title?.text?.toString() + // ?: getString(R.string.continue_watching), + // resumeWatching + // ) + // ) { + // deleteAllResumeStateIds() + // homeViewModel.loadResumeWatching() + // } + //} } - home_bookmarked_child_recyclerview.adapter = HomeChildItemAdapter( + home_bookmarked_child_recyclerview?.adapter = HomeChildItemAdapter( ArrayList(), nextFocusUp = home_bookmarked_child_recyclerview?.nextFocusUpId, nextFocusDown = home_bookmarked_child_recyclerview?.nextFocusDownId @@ -995,26 +866,15 @@ class HomeFragment : Fragment() { reloadStored() } - if (isTv) { - when (actionId) { - 0 -> { - play() - } - 1 -> { - info() - } - 2 -> { - remove() - } + when (actionId) { + 0 -> { + play() } - } else { - when (actionId) { - 0 -> { - info() - } - 1 -> { - remove() - } + 1 -> { + info() + } + 2 -> { + remove() } } } @@ -1022,8 +882,8 @@ class HomeFragment : Fragment() { homeHandleSearch(callback) } } - home_watch_child_recyclerview.setLinearListLayout() - home_bookmarked_child_recyclerview.setLinearListLayout() + home_watch_child_recyclerview?.setLinearListLayout() + home_bookmarked_child_recyclerview?.setLinearListLayout() home_watch_child_recyclerview?.adapter = HomeChildItemAdapter( ArrayList(), @@ -1100,11 +960,11 @@ class HomeFragment : Fragment() { } //context?.fixPaddingStatusbarView(home_statusbar) - context?.fixPaddingStatusbar(home_padding) + //context?.fixPaddingStatusbar(home_padding) context?.fixPaddingStatusbar(home_loading_statusbar) - home_master_recycler.adapter = - ParentItemAdapter(mutableListOf(), { callback -> + home_master_recycler?.adapter = + HomeParentItemAdapterPreview(mutableListOf(), { callback -> homeHandleSearch(callback) }, { item -> activity?.loadHomepageList(item, expandCallback = { @@ -1112,53 +972,68 @@ class HomeFragment : Fragment() { }) }, { name -> homeViewModel.expand(name) + }, { load -> + activity?.loadResult(load.response.url, load.response.apiName, load.action) + }, { + homeViewModel.loadMoreHomeScrollResponses() + }, { + apiChangeClickListener.onClick(it) + }, reloadStored = { + reloadStored() + }, loadStoredData = { + homeViewModel.loadStoredData(it) + }, { (isQuickSearch, text) -> + if (!isQuickSearch) { + QuickSearchFragment.pushSearch( + activity, + text, + currentApiName?.let { arrayOf(it) }) + } }) - home_master_recycler.setLinearListLayout() - home_master_recycler?.setMaxViewPoolSize(0, Int.MAX_VALUE) - home_master_recycler.layoutManager = object : LinearLayoutManager(context) { - override fun supportsPredictiveItemAnimations(): Boolean { - return false - } - } // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() } + //home_master_recycler.setLinearListLayout() + + //home_master_recycler?.setMaxViewPoolSize(0, 4) + //home_master_recycler.layoutManager = object : LinearLayoutManager(context) { + // override fun supportsPredictiveItemAnimations(): Boolean { + // return false + // } + //} // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() } reloadStored() loadHomePage(false) - home_loaded.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { v, _, scrollY, _, oldScrollY -> - val dy = scrollY - oldScrollY - if (dy > 0) { //check for scroll down - home_api_fab?.shrink() // hide - home_random?.shrink() - } else if (dy < -5) { - if (!isTvSettings()) { - home_api_fab?.extend() // show - home_random?.extend() - } + /* + home_loaded?.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { v, _, scrollY, _, oldScrollY -> + val dy = scrollY - oldScrollY + if (dy > 0) { //check for scroll down + home_api_fab?.shrink() // hide + home_random?.shrink() + } else if (dy < -5) { + if (!isTvSettings()) { + home_api_fab?.extend() // show + home_random?.extend() } - }) - + } + }) +*/ // nice profile pic on homepage - home_profile_picture_holder?.isVisible = false + //home_profile_picture_holder?.isVisible = false // just in case if (isTvSettings()) { home_api_fab?.isVisible = false - home_change_api?.isVisible = true if (isTrueTvSettings()) { home_change_api_loading?.isVisible = true home_change_api_loading?.isFocusable = true home_change_api_loading?.isFocusableInTouchMode = true - home_change_api?.isFocusable = true - home_change_api?.isFocusableInTouchMode = true } // home_bookmark_select?.isFocusable = true // home_bookmark_select?.isFocusableInTouchMode = true } else { home_api_fab?.isVisible = true - home_change_api?.isVisible = false home_change_api_loading?.isVisible = false } - for (syncApi in OAuth2Apis) { + /*for (syncApi in OAuth2Apis) { val login = syncApi.loginInfo() val pic = login?.profilePicture if (home_profile_picture?.setImage( @@ -1169,6 +1044,6 @@ class HomeFragment : Fragment() { home_profile_picture_holder?.isVisible = true break } - } + }*/ } } 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 23ab81ce..f85d5a08 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 @@ -4,34 +4,70 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import android.widget.LinearLayout import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.core.view.isGone +import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListUpdateCallback import androidx.recyclerview.widget.RecyclerView +import androidx.transition.ChangeBounds +import androidx.transition.TransitionManager +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.chip.Chip +import com.google.android.material.chip.ChipDrawable +import com.lagradost.cloudstream3.APIHolder.getId +import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity import com.lagradost.cloudstream3.HomePageList +import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.SearchResponse +import com.lagradost.cloudstream3.mvvm.Resource +import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.result.LinearListLayout +import com.lagradost.cloudstream3.ui.result.ResultViewModel2 +import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.setLinearListLayout +import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD 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.utils.AppUtils.isRecyclerScrollable +import com.lagradost.cloudstream3.utils.AppUtils.loadResult +import com.lagradost.cloudstream3.utils.DataStoreHelper +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView +import kotlinx.android.synthetic.main.activity_main_tv.* +import kotlinx.android.synthetic.main.activity_main_tv.view.* +import kotlinx.android.synthetic.main.fragment_home.* +import kotlinx.android.synthetic.main.fragment_home.view.* +import kotlinx.android.synthetic.main.fragment_home_head_tv.* +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.* +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_preview +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_preview_viewpager import kotlinx.android.synthetic.main.homepage_parent.view.* +class LoadClickCallback( + val action: Int = 0, + val view: View, + val position: Int, + val response: LoadResponse +) -class ParentItemAdapter( +open class ParentItemAdapter( private var items: MutableList, private val clickCallback: (SearchClickCallback) -> Unit, private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit, private val expandCallback: ((String) -> Unit)? = null, ) : RecyclerView.Adapter() { - - override fun onCreateViewHolder(parent: ViewGroup, i: Int): ParentViewHolder { - //println("onCreateViewHolder $i") - val layout = - if (isTvSettings()) R.layout.homepage_parent_tv else R.layout.homepage_parent + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return ParentViewHolder( - LayoutInflater.from(parent.context).inflate(layout, parent, false), + LayoutInflater.from(parent.context).inflate( + if (isTvSettings()) R.layout.homepage_parent_tv else R.layout.homepage_parent, + parent, + false + ), clickCallback, moreInfoClickCallback, expandCallback @@ -39,8 +75,6 @@ class ParentItemAdapter( } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - //println("onBindViewHolder $position") - when (holder) { is ParentViewHolder -> { holder.bind(items[position]) @@ -84,7 +118,10 @@ class ParentItemAdapter( val mAdapter = this diffResult.dispatchUpdatesTo(object : ListUpdateCallback { override fun onInserted(position: Int, count: Int) { - mAdapter.notifyItemRangeInserted(position, count) + mAdapter.notifyItemRangeChanged( + position, + count + )//notifyItemRangeInserted(position, count) } override fun onRemoved(position: Int, count: Int) { @@ -95,23 +132,32 @@ class ParentItemAdapter( mAdapter.notifyItemMoved(fromPosition, toPosition) } - override fun onChanged(position: Int, count: Int, payload: Any?) { + override fun onChanged(_position: Int, count: Int, payload: Any?) { + val delta = if (this@ParentItemAdapter is HomeParentItemAdapterPreview) { + headItems + } else { + 0 + } + val position = _position + delta + // I know kinda messy, what this does is using the update or bind instead of onCreateViewHolder -> bind recyclerView?.apply { // this loops every viewHolder in the recycle view and checks the position to see if it is within the update range val missingUpdates = (position until (position + count)).toMutableSet() for (i in 0 until itemCount) { - val viewHolder = getChildViewHolder(getChildAt(i)) - val absolutePosition = viewHolder.absoluteAdapterPosition + val child = getChildAt(i) ?: continue + val viewHolder = getChildViewHolder(child) ?: continue + if (viewHolder !is ParentViewHolder) continue + + val absolutePosition = viewHolder.bindingAdapterPosition if (absolutePosition >= position && absolutePosition < position + count) { - val expand = items.getOrNull(absolutePosition) ?: continue - if (viewHolder is ParentViewHolder) { - missingUpdates -= absolutePosition - if (viewHolder.title.text == expand.list.name) { - viewHolder.update(expand) - } else { - viewHolder.bind(expand) - } + val expand = items.getOrNull(absolutePosition - delta) ?: continue + missingUpdates -= absolutePosition + //println("Updating ${viewHolder.title.text} ($absolutePosition $position) -> ${expand.list.name}") + if (viewHolder.title.text == expand.list.name) { + viewHolder.update(expand) + } else { + viewHolder.bind(expand) } } } @@ -120,7 +166,8 @@ class ParentItemAdapter( for (i in missingUpdates) { mAdapter.notifyItemChanged(i, payload) } - } ?: run { // in case we don't have a nice + } ?: run { + // in case we don't have a nice mAdapter.notifyItemRangeChanged(position, count, payload) } } 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 new file mode 100644 index 00000000..c84b861a --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt @@ -0,0 +1,581 @@ +package com.lagradost.cloudstream3.ui.home + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.appcompat.widget.SearchView +import androidx.core.content.ContextCompat +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.chip.Chip +import com.google.android.material.chip.ChipDrawable +import com.lagradost.cloudstream3.APIHolder.getId +import com.lagradost.cloudstream3.AcraApplication +import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity +import com.lagradost.cloudstream3.LoadResponse +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.SearchResponse +import com.lagradost.cloudstream3.mvvm.Resource +import com.lagradost.cloudstream3.ui.WatchType +import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment +import com.lagradost.cloudstream3.ui.result.ResultViewModel2 +import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST +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.search.SearchHelper +import com.lagradost.cloudstream3.ui.settings.SettingsFragment +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult +import com.lagradost.cloudstream3.utils.DataStoreHelper +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes +import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar +import com.lagradost.cloudstream3.utils.UIHelper.setImage +import kotlinx.android.synthetic.main.activity_main_tv.view.* +import kotlinx.android.synthetic.main.fragment_home_head.view.* +import kotlinx.android.synthetic.main.fragment_home_head.view.home_bookmarked_child_recyclerview +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.* +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_bookmarked_holder +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_plan_to_watch_btt +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_preview +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_preview_viewpager +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_type_completed_btt +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_type_dropped_btt +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_type_on_hold_btt +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_type_watching_btt +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_watch_child_recyclerview +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_watch_holder +import kotlinx.android.synthetic.main.fragment_setup_media.* +import java.util.ArrayList + +class HomeParentItemAdapterPreview( + items: MutableList, + val clickCallback: (SearchClickCallback) -> Unit, + moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit, + expandCallback: ((String) -> Unit)? = null, + private val loadCallback: (LoadClickCallback) -> Unit, + private val loadMoreCallback: (() -> Unit), + private val changeHomePageCallback: ((View) -> Unit), + private val reloadStored: (() -> Unit), + private val loadStoredData: ((Set) -> Unit), + private val searchQueryCallback: ((Pair) -> Unit) +) : ParentItemAdapter(items, clickCallback, moreInfoClickCallback, expandCallback) { + private var previewData: Resource>> = Resource.Loading() + private var resumeWatchingData: List = listOf() + private var bookmarkData: Pair> = + false to listOf() + + val headItems = 1 + + private var availableWatchStatusTypes: Pair, Set> = + setOf() to setOf() + + fun setAvailableWatchStatusTypes(data: Pair, Set>) { + availableWatchStatusTypes = data + holder?.setAvailableWatchStatusTypes(data) + } + + companion object { + private const val VIEW_TYPE_HEADER = 4815 + private const val VIEW_TYPE_ITEM = 1623 + } + + fun setResumeWatchingData(resumeWatching: List) { + resumeWatchingData = resumeWatching + holder?.updateResume(resumeWatchingData) + } + + fun setPreviewData(preview: Resource>>) { + previewData = preview + holder?.updatePreview(preview) + //notifyItemChanged(0) + } + + fun setBookmarkData(data: Pair>) { + bookmarkData = data + holder?.updateBookmarks(data) + } + + override fun getItemViewType(position: Int) = when (position) { + 0 -> VIEW_TYPE_HEADER + else -> VIEW_TYPE_ITEM + } + + var holder: HeaderViewHolder? = null + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is HeaderViewHolder -> { + holder.updatePreview(previewData) + holder.updateResume(resumeWatchingData) + holder.updateBookmarks(bookmarkData) + holder.setAvailableWatchStatusTypes(availableWatchStatusTypes) + } + else -> super.onBindViewHolder(holder, position - 1) + } + } + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + VIEW_TYPE_HEADER -> HeaderViewHolder( + LayoutInflater.from(parent.context).inflate( + if (isTvSettings()) R.layout.fragment_home_head_tv else R.layout.fragment_home_head, + parent, + false + ), + loadCallback, + loadMoreCallback, + changeHomePageCallback, + clickCallback, + reloadStored, + loadStoredData, + searchQueryCallback + ).also { + this.holder = it + } + VIEW_TYPE_ITEM -> super.onCreateViewHolder(parent, viewType) + else -> error("Unhandled viewType=$viewType") + } + } + + override fun getItemCount(): Int { + return super.getItemCount() + headItems + } + + override fun getItemId(position: Int): Long { + if (position == 0) return previewData.hashCode().toLong() + return super.getItemId(position - headItems) + } + + override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { + when (holder) { + is HeaderViewHolder -> { + holder.onViewDetachedFromWindow() + } + else -> super.onViewDetachedFromWindow(holder) + } + } + + override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) { + when (holder) { + is HeaderViewHolder -> { + holder.onViewAttachedToWindow() + } + else -> super.onViewAttachedToWindow(holder) + } + } + + + class HeaderViewHolder + constructor( + itemView: View, + private val clickCallback: ((LoadClickCallback) -> Unit)?, + private val loadMoreCallback: (() -> Unit), + private val changeHomePageCallback: ((View) -> Unit), + private val searchClickCallback: (SearchClickCallback) -> Unit, + private val reloadStored: () -> Unit, + private val loadStoredData: ((Set) -> Unit), + private val searchQueryCallback: ((Pair) -> Unit) + ) : RecyclerView.ViewHolder(itemView) { + private var previewAdapter: HomeScrollAdapter? = null + private val previewViewpager: ViewPager2? = itemView.home_preview_viewpager + private val previewHeader: LinearLayout? = itemView.home_preview + private val previewCallback: ViewPager2.OnPageChangeCallback = + object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + // home_search?.isIconified = true + //home_search?.isVisible = true + //home_search?.clearFocus() + + previewAdapter?.apply { + if (position >= itemCount - 1 && hasMoreItems) { + hasMoreItems = false // dont make two requests + loadMoreCallback() + //homeViewModel.loadMoreHomeScrollResponses() + } + } + previewAdapter?.getItem(position) + ?.apply { + //itemView.home_preview_title_holder?.let { parent -> + // TransitionManager.beginDelayedTransition( + // parent, + // ChangeBounds() + // ) + //} + itemView.home_preview_description?.isGone = + this.plot.isNullOrBlank() + itemView.home_preview_description?.text = + this.plot ?: "" + itemView.home_preview_text?.text = this.name + itemView.home_preview_change_api?.text = apiName + itemView.home_preview_change_api?.setOnClickListener { view -> + changeHomePageCallback(view) + } + itemView.home_preview_tags?.apply { + removeAllViews() + tags?.forEach { tag -> + val chip = Chip(context) + val chipDrawable = + ChipDrawable.createFromAttributes( + context, + null, + 0, + R.style.ChipFilledSemiTransparent + ) + chip.setChipDrawable(chipDrawable) + chip.text = tag + chip.isChecked = false + chip.isCheckable = false + chip.isFocusable = false + chip.isClickable = false + addView(chip) + } + } + itemView.home_preview_tags?.isGone = + tags.isNullOrEmpty() + itemView.home_preview_image?.setImage( + posterUrl, + posterHeaders + ) + // itemView.home_preview_title?.text = name + + itemView.home_preview_play?.setOnClickListener { view -> + clickCallback?.invoke( + LoadClickCallback( + START_ACTION_RESUME_LATEST, + view, + position, + this + ) + ) + } + itemView.home_preview_info?.setOnClickListener { view -> + clickCallback?.invoke( + LoadClickCallback(0, view, position, this) + ) + } + + itemView.home_preview_play_btt?.setOnClickListener { view -> + clickCallback?.invoke( + LoadClickCallback( + START_ACTION_RESUME_LATEST, + view, + position, + this + ) + ) + } + itemView.home_preview_info_btt?.setOnClickListener { view -> + clickCallback?.invoke( + LoadClickCallback(0, view, position, this) + ) + } + itemView.home_preview_hidden_next_focus?.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + previewViewpager?.apply { + setCurrentItem(currentItem + 1, true) + } + itemView.home_preview_info_btt?.requestFocus() + } + } + itemView.home_preview_hidden_prev_focus?.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + previewViewpager?.apply { + if (currentItem <= 0) { + nav_rail_view?.menu?.getItem(0)?.actionView?.requestFocus() + } else { + setCurrentItem(currentItem - 1, true) + itemView.home_preview_play_btt?.requestFocus() + } + } + } + } + // very ugly code, but I dont care + val watchType = + DataStoreHelper.getResultWatchState(this.getId()) + itemView.home_preview_bookmark?.setText(watchType.stringRes) + itemView.home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds( + null, + ContextCompat.getDrawable( + itemView.home_preview_bookmark.context, + watchType.iconRes + ), + null, + null + ) + itemView.home_preview_bookmark?.setOnClickListener { fab -> + fab.context.getActivity()?.showBottomDialog( + WatchType.values() + .map { fab.context.getString(it.stringRes) } + .toList(), + DataStoreHelper.getResultWatchState(this.getId()).ordinal, + fab.context.getString(R.string.action_add_to_bookmarks), + showApply = false, + {}) { + val newValue = WatchType.values()[it] + itemView.home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds( + null, + ContextCompat.getDrawable( + itemView.home_preview_bookmark.context, + newValue.iconRes + ), + null, + null + ) + itemView.home_preview_bookmark?.setText(newValue.stringRes) + + ResultViewModel2.updateWatchStatus( + this, + newValue + ) + reloadStored() + } + } + } + } + } + + private var resumeAdapter: HomeChildItemAdapter? = null + private var resumeHolder: View? = itemView.home_watch_holder + private var resumeRecyclerView: RecyclerView? = itemView.home_watch_child_recyclerview + + private var bookmarkHolder: View? = itemView.home_bookmarked_holder + private var bookmarkAdapter: HomeChildItemAdapter? = null + private var bookmarkRecyclerView: RecyclerView? = + itemView.home_bookmarked_child_recyclerview + + fun onViewDetachedFromWindow() { + previewViewpager?.unregisterOnPageChangeCallback(previewCallback) + } + + fun onViewAttachedToWindow() { + previewViewpager?.registerOnPageChangeCallback(previewCallback) + } + + private val toggleList = listOf( + Pair(itemView.home_type_watching_btt, WatchType.WATCHING), + Pair(itemView.home_type_completed_btt, WatchType.COMPLETED), + Pair(itemView.home_type_dropped_btt, WatchType.DROPPED), + Pair(itemView.home_type_on_hold_btt, WatchType.ONHOLD), + Pair(itemView.home_plan_to_watch_btt, WatchType.PLANTOWATCH), + ) + + init { + previewViewpager?.apply { + if (!isTvSettings()) + setPageTransformer(HomeScrollTransformer()) + else + setPageTransformer(null) + + if (adapter == null) + adapter = HomeScrollAdapter( + if (isTvSettings()) R.layout.home_scroll_view_tv else R.layout.home_scroll_view, + if (isTvSettings()) true else null + ) + } + previewAdapter = previewViewpager?.adapter as? HomeScrollAdapter? + previewViewpager?.registerOnPageChangeCallback(previewCallback) + + if (resumeAdapter == null) { + resumeRecyclerView?.adapter = HomeChildItemAdapter( + ArrayList(), + nextFocusUp = itemView.nextFocusUpId, + nextFocusDown = itemView.nextFocusDownId + ) { callback -> + if (callback.action != SEARCH_ACTION_SHOW_METADATA) { + searchClickCallback(callback) + return@HomeChildItemAdapter + } + callback.view.context?.getActivity()?.showOptionSelectStringRes( + callback.view, + callback.card.posterUrl, + listOf( + R.string.action_open_watching, + R.string.action_remove_watching + ), + listOf( + R.string.action_open_play, + R.string.action_open_watching, + R.string.action_remove_watching + ) + ) { (isTv, actionId) -> + when (actionId + if (isTv) 0 else 1) { + // play + 0 -> { + searchClickCallback.invoke( + SearchClickCallback( + START_ACTION_RESUME_LATEST, + callback.view, + -1, + callback.card + ) + ) + reloadStored() + } + //info + 1 -> { + searchClickCallback( + SearchClickCallback( + SEARCH_ACTION_LOAD, + callback.view, + -1, + callback.card + ) + ) + + reloadStored() + } + // remove + 2 -> { + val card = callback.card + if (card is DataStoreHelper.ResumeWatchingResult) { + DataStoreHelper.removeLastWatched(card.parentId) + reloadStored() + } + } + } + } + } + } + resumeAdapter = resumeRecyclerView?.adapter as? HomeChildItemAdapter + if (bookmarkAdapter == null) { + bookmarkRecyclerView?.adapter = HomeChildItemAdapter( + ArrayList(), + nextFocusUp = itemView.nextFocusUpId, + nextFocusDown = itemView.nextFocusDownId + ) { callback -> + if (callback.action != SEARCH_ACTION_SHOW_METADATA) { + searchClickCallback(callback) + return@HomeChildItemAdapter + } + callback.view.context?.getActivity()?.showOptionSelectStringRes( + callback.view, + callback.card.posterUrl, + listOf( + R.string.action_open_watching, + R.string.action_remove_from_bookmarks, + ), + listOf( + R.string.action_open_play, + R.string.action_open_watching, + R.string.action_remove_from_bookmarks + ) + ) { (isTv, actionId) -> + when (actionId + if (isTv) 0 else 1) { // play + 0 -> { + searchClickCallback.invoke( + SearchClickCallback( + START_ACTION_RESUME_LATEST, + callback.view, + -1, + callback.card + ) + ) + reloadStored() + } + 1 -> { // info + searchClickCallback( + SearchClickCallback( + SEARCH_ACTION_LOAD, + callback.view, + -1, + callback.card + ) + ) + + reloadStored() + } + 2 -> { // remove + DataStoreHelper.setResultWatchState( + callback.card.id, + WatchType.NONE.internalId + ) + reloadStored() + } + } + } + } + } + bookmarkAdapter = bookmarkRecyclerView?.adapter as? HomeChildItemAdapter + + for ((chip, watch) in toggleList) { + chip?.isChecked = false + chip?.setOnCheckedChangeListener { _, isChecked -> + if (isChecked) { + loadStoredData( + setOf(watch) + // If we filter all buttons then two can be checked at the same time + // Revert this if you want to go back to multi selection +// toggleList.filter { it.first?.isChecked == true }.map { it.second }.toSet() + ) + } + // Else if all are unchecked -> Do not load data + else if (toggleList.all { it.first?.isChecked != true }) { + loadStoredData(emptySet()) + } + } + } + + itemView.home_search?.context?.fixPaddingStatusbar(itemView.home_search) + + itemView.home_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + searchQueryCallback.invoke(false to query) + //QuickSearchFragment.pushSearch(activity, query, currentApiName?.let { arrayOf(it) } + return true + } + + override fun onQueryTextChange(newText: String): Boolean { + searchQueryCallback.invoke(true to newText) + //searchViewModel.quickSearch(newText) + return true + } + }) + } + + fun updatePreview(preview: Resource>>) { + when (preview) { + is Resource.Success -> { + previewHeader?.isVisible = true + if (true != previewAdapter?.setItems( + preview.value.second, + preview.value.first + ) + ) { + previewViewpager?.setCurrentItem(0, false) + } + } + else -> { + previewHeader?.isVisible = false + + previewAdapter?.setItems(listOf(), false) + previewViewpager?.setCurrentItem(0, false) + } + } + } + + fun updateResume(resumeWatching: List) { + resumeHolder?.isVisible = resumeWatching.isNotEmpty() + resumeAdapter?.updateList(resumeWatching) + } + + fun updateBookmarks(data: Pair>) { + bookmarkHolder?.isVisible = true // data.first + bookmarkAdapter?.updateList(data.second) + } + + fun setAvailableWatchStatusTypes(availableWatchStatusTypes: Pair, Set>) { + for ((chip, watch) in toggleList) { + chip?.apply { + isVisible = availableWatchStatusTypes.second.contains(watch) + isChecked = availableWatchStatusTypes.first.contains(watch) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt index 7ed074dc..f296e53d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt @@ -4,16 +4,22 @@ import android.content.res.Configuration import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.LayoutRes import androidx.core.view.isGone import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.utils.UIHelper.setImage +import kotlinx.android.synthetic.main.fragment_home_head_tv.* +import kotlinx.android.synthetic.main.fragment_home_head_tv.view.* import kotlinx.android.synthetic.main.home_scroll_view.view.* -class HomeScrollAdapter : RecyclerView.Adapter() { +class HomeScrollAdapter( + @LayoutRes val layout: Int = R.layout.home_scroll_view, + private val forceHorizontalPosters: Boolean? = null +) : RecyclerView.Adapter() { private var items: MutableList = mutableListOf() var hasMoreItems: Boolean = false @@ -40,7 +46,8 @@ class HomeScrollAdapter : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return CardViewHolder( - LayoutInflater.from(parent.context).inflate(R.layout.home_scroll_view, parent, false), + LayoutInflater.from(parent.context).inflate(layout, parent, false), + forceHorizontalPosters ) } @@ -55,13 +62,15 @@ class HomeScrollAdapter : RecyclerView.Adapter() { class CardViewHolder constructor( itemView: View, + private val forceHorizontalPosters: Boolean? = null ) : RecyclerView.ViewHolder(itemView) { fun bind(card: LoadResponse) { card.apply { val isHorizontal = - itemView.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + (forceHorizontalPosters == true) || ((forceHorizontalPosters != false) && itemView.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) + val posterUrl = if (isHorizontal) backgroundPosterUrl ?: posterUrl else posterUrl ?: backgroundPosterUrl itemView.home_scroll_preview_tags?.text = tags?.joinToString(" • ") ?: "" 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 3bb196e2..58c01603 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 @@ -128,6 +128,7 @@ class HomeViewModel : ViewModel() { currentWatchTypes.remove(WatchType.NONE) if (currentWatchTypes.size <= 0) { + _availableWatchStatusTypes.postValue(setOf() to setOf()) _bookmarks.postValue(Pair(false, ArrayList())) return@launchSafe } 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 3efaeff8..6e7e341f 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 @@ -921,8 +921,6 @@ open class ResultFragment : ResultTrailerPlayer() { } - result_tag?.removeAllViews() - d.comingSoon.let { soon -> result_coming_soon?.isVisible = soon result_data_holder?.isGone = soon @@ -931,6 +929,7 @@ open class ResultFragment : ResultTrailerPlayer() { val tags = d.tags result_tag_holder?.isVisible = tags.isNotEmpty() result_tag?.apply { + removeAllViews() tags.forEach { tag -> val chip = Chip(context) val chipDrawable = ChipDrawable.createFromAttributes( 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 c2523931..ddf559fc 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 @@ -20,17 +20,23 @@ const val SEARCH_ACTION_SHOW_METADATA = 1 const val SEARCH_ACTION_PLAY_FILE = 2 const val SEARCH_ACTION_FOCUSED = 4 -class SearchClickCallback(val action: Int, val view: View, val position : Int, val card: SearchResponse) +class SearchClickCallback( + val action: Int, + val view: View, + val position: Int, + val card: SearchResponse +) class SearchAdapter( private val cardList: MutableList, private val resView: AutofitRecyclerView, private val clickCallback: (SearchClickCallback) -> Unit, ) : RecyclerView.Adapter() { - var hasNext : Boolean = false + var hasNext: Boolean = false override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val layout = if(parent.context.IsBottomLayout()) R.layout.search_result_grid_expanded else R.layout.search_result_grid + val layout = + if (parent.context.IsBottomLayout()) R.layout.search_result_grid_expanded else R.layout.search_result_grid return CardViewHolder( LayoutInflater.from(parent.context).inflate(layout, parent, false), clickCallback, @@ -71,7 +77,8 @@ class SearchAdapter( val cardView: ImageView = itemView.imageView private val compactView = false//itemView.context.getGridIsCompact() - private val coverHeight: Int = if (compactView) 80.toPx else (resView.itemWidth / 0.68).roundToInt() + private val coverHeight: Int = + if (compactView) 80.toPx else (resView.itemWidth / 0.68).roundToInt() fun bind(card: SearchResponse, position: Int) { if (!compactView) { @@ -88,7 +95,10 @@ class SearchAdapter( } } -class SearchResponseDiffCallback(private val oldList: List, private val newList: List) : +class SearchResponseDiffCallback( + private val oldList: List, + private val newList: List +) : DiffUtil.Callback() { override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = oldList[oldItemPosition].name == newList[newItemPosition].name 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 4e59e6a0..9c6b62e8 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 @@ -44,6 +44,7 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageLis import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.updateChips import com.lagradost.cloudstream3.ui.home.ParentItemAdapter import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey @@ -93,7 +94,11 @@ class SearchFragment : Fragment() { activity?.window?.setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE ) - return inflater.inflate(R.layout.fragment_search, container, false) + return inflater.inflate( + if (isTvSettings()) R.layout.fragment_search_tv else R.layout.fragment_search, + container, + false + ) } private fun fixGrid() { 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 b3bce446..2dc6846c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt @@ -99,7 +99,8 @@ object SingleSelectionHelper { val textView = dialog.text1//.findViewById(R.id.text1)!! val applyButton = dialog.apply_btt//.findViewById(R.id.apply_btt) val cancelButton = dialog.cancel_btt//findViewById(R.id.cancel_btt) - val applyHolder = dialog.apply_btt_holder//.findViewById(R.id.apply_btt_holder) + val applyHolder = + dialog.apply_btt_holder//.findViewById(R.id.apply_btt_holder) applyHolder?.isVisible = realShowApply if (!realShowApply) { @@ -249,6 +250,17 @@ object SingleSelectionHelper { ) } + fun showBottomDialog( + items: List, + selectedIndex: Int, + name: String, + showApply: Boolean, + dismissCallback: () -> Unit, + callback: (Int) -> Unit, + ) { + + } + /** Only for a low amount of items */ fun Activity?.showBottomDialog( items: List, diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_left_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_left_24.xml index ff93af1f..916c761c 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_left_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_left_24.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_right_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_right_24.xml index fc07eaae..ea924625 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_right_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_right_24.xml @@ -1,5 +1,11 @@ - - + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4905d454..ad29d22a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -14,10 +14,11 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/homeRoot" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|keyboard|navigation" + android:paddingTop="0dp"> + android:layout_width="match_parent" + android:layout_height="match_parent"> + android:id="@+id/nav_host_fragment" + android:name="androidx.navigation.fragment.NavHostFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:defaultNavHost="true" + app:layout_constraintBottom_toTopOf="@id/cast_mini_controller_holder" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:navGraph="@navigation/mobile_navigation" /> + app:labelVisibilityMode="unlabeled" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:menu="@menu/bottom_nav_menu" + app:menuGravity="center"> + android:id="@+id/cast_mini_controller_holder" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/nav_rail_view" + tools:layout_height="100dp"> + android:id="@+id/cast_mini_controller" + class="com.lagradost.cloudstream3.ui.MyMiniControllerFragment" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + app:castControlButtons="@array/cast_mini_controller_control_buttons" + app:customCastBackgroundColor="?attr/primaryGrayBackground" + tools:ignore="FragmentTagUsage" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 7f8e2c01..eb38e262 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -153,435 +153,14 @@ android:textColor="?attr/textColor" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_height="wrap_content" + android:descendantFocusability="afterDescendants" + android:nextFocusLeft="@id/nav_rail_view" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/homepage_parent" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home_head_tv.xml b/app/src/main/res/layout/fragment_home_head_tv.xml new file mode 100644 index 00000000..5992ac91 --- /dev/null +++ b/app/src/main/res/layout/fragment_home_head_tv.xml @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home_tv.xml b/app/src/main/res/layout/fragment_home_tv.xml index 5b092927..ebcd3e9f 100644 --- a/app/src/main/res/layout/fragment_home_tv.xml +++ b/app/src/main/res/layout/fragment_home_tv.xml @@ -1,87 +1,88 @@ + android:layout_height="match_parent" + tools:context=".ui.home.HomeFragment"> + android:id="@+id/home_loading" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginStart="@dimen/navbar_width" + android:visibility="gone" + tools:visibility="gone"> + android:layout_width="50dp" + android:layout_height="50dp" + android:layout_gravity="center" + android:visibility="gone" + tools:visibility="gone" /> + android:id="@+id/home_loading_shimmer" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:layout_marginTop="15dp" + android:orientation="vertical" + android:paddingTop="40dp" + app:shimmer_auto_start="true" + app:shimmer_base_alpha="0.2" + app:shimmer_duration="@integer/loading_time" + app:shimmer_highlight_alpha="0.3"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + android:layout_width="125dp" + android:layout_height="200dp" + android:layout_gravity="center" + android:layout_margin="@dimen/loading_margin" + android:background="@color/grayShimmer" + android:translationX="-164dp" + app:cardCornerRadius="@dimen/loading_radius" /> + android:layout_width="148dp" + android:layout_height="234dp" + android:layout_gravity="center" + android:layout_margin="@dimen/loading_margin" + android:background="@color/grayShimmer" + app:cardCornerRadius="@dimen/loading_radius" /> + android:layout_width="125dp" + android:layout_height="200dp" + android:layout_gravity="center" + android:layout_margin="@dimen/loading_margin" + android:background="@color/grayShimmer" + android:translationX="164dp" + app:cardCornerRadius="@dimen/loading_radius" /> + android:layout_marginEnd="@dimen/result_padding" + android:orientation="vertical"> @@ -93,355 +94,82 @@ + android:id="@+id/home_loading_statusbar" + android:layout_width="match_parent" + android:layout_height="70dp"> + android:layout_margin="10dp" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/home_change_provider_img_des" + android:src="@drawable/ic_baseline_keyboard_arrow_down_24" /> + + + android:layout_margin="5dp" + android:minWidth="200dp" + android:text="@string/reload_error" + app:icon="@drawable/ic_baseline_autorenew_24" /> - - + android:layout_gravity="center" + android:layout_margin="5dp" + android:minWidth="200dp" + android:text="@string/result_open_in_browser" + app:icon="@drawable/ic_baseline_public_24" /> + android:id="@+id/result_error_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_margin="5dp" + android:gravity="center" + android:textColor="?attr/textColor" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + android:id="@+id/home_api_fab" + style="@style/ExtendedFloatingActionButton" + android:visibility="gone" + app:icon="@drawable/ic_baseline_filter_list_24" + tools:ignore="ContentDescription" + tools:visibility="visible" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index f88fc173..8b9b9e36 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -516,6 +516,7 @@ android:visibility="gone" /> diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml index f6786196..a29dc192 100644 --- a/app/src/main/res/layout/fragment_result_tv.xml +++ b/app/src/main/res/layout/fragment_result_tv.xml @@ -385,6 +385,7 @@ diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index 6df5837f..bed08570 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -10,79 +10,86 @@ android:orientation="vertical" tools:context=".ui.search.SearchFragment"> - + android:layout_height="wrap_content"> + android:layout_height="40dp" + android:layout_margin="10dp" + android:background="@drawable/search_background" + android:visibility="visible"> - - android:iconifiedByDefault="false" - android:imeOptions="actionSearch" + + + + + + + + + + + + - - - - - - - - + android:src="@drawable/ic_baseline_tune_24" + app:tint="?attr/textColor" /> - - - - + + + - + android:background="?attr/primaryBlackBackground" + android:descendantFocusability="afterDescendants" + android:nextFocusLeft="@id/nav_rail_view" + android:visibility="visible" + android:paddingBottom="50dp" + tools:listitem="@layout/search_history_item" /> - + \ 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 new file mode 100644 index 00000000..0a85a471 --- /dev/null +++ b/app/src/main/res/layout/fragment_search_tv.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/home_scroll_view_tv.xml b/app/src/main/res/layout/home_scroll_view_tv.xml new file mode 100644 index 00000000..e0da8197 --- /dev/null +++ b/app/src/main/res/layout/home_scroll_view_tv.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/homepage_parent_tv.xml b/app/src/main/res/layout/homepage_parent_tv.xml index bfbee465..f9adf1c1 100644 --- a/app/src/main/res/layout/homepage_parent_tv.xml +++ b/app/src/main/res/layout/homepage_parent_tv.xml @@ -1,32 +1,34 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - + android:layout_marginStart="@dimen/navbar_width" + android:id="@+id/home_parent_item_title" + android:padding="12dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/WatchHeaderText" + android:layout_marginEnd="0dp" + tools:text="Trending" /> + android:descendantFocusability="afterDescendants" + + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + android:id="@+id/home_child_recyclerview" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:listitem="@layout/home_result_grid" /> \ 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 33ca7762..6b13546b 100644 --- a/app/src/main/res/layout/tvtypes_chips.xml +++ b/app/src/main/res/layout/tvtypes_chips.xml @@ -1,5 +1,6 @@ #161616 #e9eaee + #1AFFFFFF #9ba0a4 #DCDCDC diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index dac8b9cd..567b6d35 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -16,4 +16,5 @@ 2000 3dp + 62dp \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 6926bb47..b14cd189 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -88,6 +88,16 @@ @font/google_sans @color/chip_color_text @font/google_sans + 0dp + + +