migrated some items to viewbindings + removed Some<T>

This commit is contained in:
LagradOst 2023-07-13 23:18:37 +02:00
parent 927453d9fe
commit 05a0d3cd81
46 changed files with 1565 additions and 1264 deletions

View file

@ -28,6 +28,11 @@ android {
testOptions { testOptions {
unitTests.isReturnDefaultValues = true unitTests.isReturnDefaultValues = true
} }
viewBinding {
enable = true
}
signingConfigs { signingConfigs {
create("prerelease") { create("prerelease") {
if (prereleaseStoreFile != null) { if (prereleaseStoreFile != null) {

View file

@ -57,32 +57,6 @@ fun <T> LifecycleOwner.observeNullable(liveData: LiveData<T>, action: (t: T) ->
liveData.observe(this) { action(it) } liveData.observe(this) { action(it) }
} }
inline fun <reified T : Any> some(value: T?): Some<T> {
return if (value == null) {
Some.None
} else {
Some.Success(value)
}
}
sealed class Some<out T> {
data class Success<out T>(val value: T) : Some<T>()
object None : Some<Nothing>()
override fun toString(): String {
return when (this) {
is None -> "None"
is Success -> "Some(${value.toString()})"
}
}
}
sealed class ResourceSome<out T> {
data class Success<out T>(val value: T) : ResourceSome<T>()
object None : ResourceSome<Nothing>()
data class Loading(val data: Any? = null) : ResourceSome<Nothing>()
}
sealed class Resource<out T> { sealed class Resource<out T> {
data class Success<out T>(val value: T) : Resource<T>() data class Success<out T>(val value: T) : Resource<T>()
data class Failure( data class Failure(

View file

@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentChildDownloadsBinding
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
@ -15,13 +16,12 @@ import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.fragment_child_downloads.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class DownloadChildFragment : Fragment() { class DownloadChildFragment : Fragment() {
companion object { companion object {
fun newInstance(headerName: String, folder: String) : Bundle { fun newInstance(headerName: String, folder: String): Bundle {
return Bundle().apply { return Bundle().apply {
putString("folder", folder) putString("folder", folder)
putString("name", headerName) putString("name", headerName)
@ -30,13 +30,21 @@ class DownloadChildFragment : Fragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
(download_child_list?.adapter as DownloadChildAdapter?)?.killAdapter() (binding?.downloadChildList?.adapter as DownloadChildAdapter?)?.killAdapter()
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it } downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it }
binding = null
super.onDestroyView() super.onDestroyView()
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { var binding: FragmentChildDownloadsBinding? = null
return inflater.inflate(R.layout.fragment_child_downloads, container, false) override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val localBinding = FragmentChildDownloadsBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root//inflater.inflate(R.layout.fragment_child_downloads, container, false)
} }
private fun updateList(folder: String) = main { private fun updateList(folder: String) = main {
@ -50,14 +58,15 @@ class DownloadChildFragment : Fragment() {
?: return@mapNotNull null ?: return@mapNotNull null
VisualDownloadChildCached(info.fileLength, info.totalBytes, it) VisualDownloadChildCached(info.fileLength, info.totalBytes, it)
} }
}.sortedBy { it.data.episode + (it.data.season?: 0)*100000 } }.sortedBy { it.data.episode + (it.data.season ?: 0) * 100000 }
if (eps.isEmpty()) { if (eps.isEmpty()) {
activity?.onBackPressed() activity?.onBackPressed()
return@main return@main
} }
(download_child_list?.adapter as DownloadChildAdapter? ?: return@main).cardList = eps (binding?.downloadChildList?.adapter as DownloadChildAdapter? ?: return@main).cardList =
download_child_list?.adapter?.notifyDataSetChanged() eps
binding?.downloadChildList?.adapter?.notifyDataSetChanged()
} }
} }
@ -72,14 +81,17 @@ class DownloadChildFragment : Fragment() {
activity?.onBackPressed() // TODO FIX activity?.onBackPressed() // TODO FIX
return return
} }
context?.fixPaddingStatusbar(download_child_root) fixPaddingStatusbar(binding?.downloadChildRoot)
download_child_toolbar.title = name binding?.downloadChildToolbar?.apply {
download_child_toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24) title = name
download_child_toolbar.setNavigationOnClickListener { setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
activity?.onBackPressed() setNavigationOnClickListener {
activity?.onBackPressed()
}
} }
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
DownloadChildAdapter( DownloadChildAdapter(
ArrayList(), ArrayList(),
@ -88,7 +100,7 @@ class DownloadChildFragment : Fragment() {
} }
downloadDeleteEventListener = { id: Int -> downloadDeleteEventListener = { id: Int ->
val list = (download_child_list?.adapter as DownloadChildAdapter?)?.cardList val list = (binding?.downloadChildList?.adapter as DownloadChildAdapter?)?.cardList
if (list != null) { if (list != null) {
if (list.any { it.data.id == id }) { if (list.any { it.data.id == id }) {
updateList(folder) updateList(folder)
@ -98,8 +110,8 @@ class DownloadChildFragment : Fragment() {
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it } downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it }
download_child_list.adapter = adapter binding?.downloadChildList?.adapter = adapter
download_child_list.layoutManager = GridLayoutManager(context, 1) binding?.downloadChildList?.layoutManager = GridLayoutManager(context, 1)
updateList(folder) updateList(folder)
} }

View file

@ -34,10 +34,10 @@ import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.fragment_downloads.*
import kotlinx.android.synthetic.main.stream_input.*
import android.text.format.Formatter.formatShortFileSize import android.text.format.Formatter.formatShortFileSize
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import com.lagradost.cloudstream3.databinding.FragmentDownloadsBinding
import com.lagradost.cloudstream3.databinding.StreamInputBinding
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.ui.player.BasicLink import com.lagradost.cloudstream3.ui.player.BasicLink
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
@ -60,8 +60,8 @@ class DownloadFragment : Fragment() {
private fun setList(list: List<VisualDownloadHeaderCached>) { private fun setList(list: List<VisualDownloadHeaderCached>) {
main { main {
(download_list?.adapter as DownloadHeaderAdapter?)?.cardList = list (binding?.downloadList?.adapter as DownloadHeaderAdapter?)?.cardList = list
download_list?.adapter?.notifyDataSetChanged() binding?.downloadList?.adapter?.notifyDataSetChanged()
} }
} }
@ -70,10 +70,13 @@ class DownloadFragment : Fragment() {
VideoDownloadManager.downloadDeleteEvent -= downloadDeleteEventListener!! VideoDownloadManager.downloadDeleteEvent -= downloadDeleteEventListener!!
downloadDeleteEventListener = null downloadDeleteEventListener = null
} }
(download_list?.adapter as DownloadHeaderAdapter?)?.killAdapter() (binding?.downloadList?.adapter as DownloadHeaderAdapter?)?.killAdapter()
binding = null
super.onDestroyView() super.onDestroyView()
} }
var binding : FragmentDownloadsBinding? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -82,7 +85,9 @@ class DownloadFragment : Fragment() {
downloadsViewModel = downloadsViewModel =
ViewModelProvider(this)[DownloadViewModel::class.java] ViewModelProvider(this)[DownloadViewModel::class.java]
return inflater.inflate(R.layout.fragment_downloads, container, false) val localBinding = FragmentDownloadsBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root//inflater.inflate(R.layout.fragment_downloads, container, false)
} }
private var downloadDeleteEventListener: ((Int) -> Unit)? = null private var downloadDeleteEventListener: ((Int) -> Unit)? = null
@ -92,36 +97,40 @@ class DownloadFragment : Fragment() {
hideKeyboard() hideKeyboard()
observe(downloadsViewModel.noDownloadsText) { observe(downloadsViewModel.noDownloadsText) {
text_no_downloads.text = it binding?.textNoDownloads?.text = it
} }
observe(downloadsViewModel.headerCards) { observe(downloadsViewModel.headerCards) {
setList(it) setList(it)
download_loading.isVisible = false binding?.downloadLoading?.isVisible = false
} }
observe(downloadsViewModel.availableBytes) { observe(downloadsViewModel.availableBytes) {
download_free_txt?.text = binding?.downloadFreeTxt?.text =
getString(R.string.storage_size_format).format( getString(R.string.storage_size_format).format(
getString(R.string.free_storage), getString(R.string.free_storage),
formatShortFileSize(view.context, it) formatShortFileSize(view.context, it)
) )
download_free?.setLayoutWidth(it) binding?.downloadFree?.setLayoutWidth(it)
} }
observe(downloadsViewModel.usedBytes) { observe(downloadsViewModel.usedBytes) {
download_used_txt?.text = binding?.apply {
getString(R.string.storage_size_format).format( downloadUsedTxt.text =
getString(R.string.used_storage), getString(R.string.storage_size_format).format(
formatShortFileSize(view.context, it) getString(R.string.used_storage),
) formatShortFileSize(view.context, it)
download_used?.setLayoutWidth(it) )
download_storage_appbar?.isVisible = it > 0 downloadUsed.setLayoutWidth(it)
downloadStorageAppbar.isVisible = it > 0
}
} }
observe(downloadsViewModel.downloadBytes) { observe(downloadsViewModel.downloadBytes) {
download_app_txt?.text = binding?.apply {
getString(R.string.storage_size_format).format( downloadAppTxt.text =
getString(R.string.app_storage), getString(R.string.storage_size_format).format(
formatShortFileSize(view.context, it) getString(R.string.app_storage),
) formatShortFileSize(view.context, it)
download_app?.setLayoutWidth(it) )
downloadApp.setLayoutWidth(it)
}
} }
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
@ -164,7 +173,7 @@ class DownloadFragment : Fragment() {
) )
downloadDeleteEventListener = { id -> downloadDeleteEventListener = { id ->
val list = (download_list?.adapter as DownloadHeaderAdapter?)?.cardList val list = (binding?.downloadList?.adapter as DownloadHeaderAdapter?)?.cardList
if (list != null) { if (list != null) {
if (list.any { it.data.id == id }) { if (list.any { it.data.id == id }) {
context?.let { ctx -> context?.let { ctx ->
@ -177,31 +186,36 @@ class DownloadFragment : Fragment() {
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it } downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it }
download_list?.adapter = adapter binding?.downloadList?.apply {
download_list?.layoutManager = GridLayoutManager(context, 1) this.adapter = adapter
layoutManager = GridLayoutManager(context, 1)
}
// Should be visible in emulator layout // Should be visible in emulator layout
download_stream_button?.isGone = isTrueTvSettings() binding?.downloadStreamButton?.isGone = isTrueTvSettings()
download_stream_button?.setOnClickListener { binding?.downloadStreamButton?.setOnClickListener {
val dialog = val dialog =
Dialog(it.context ?: return@setOnClickListener, R.style.AlertDialogCustom) Dialog(it.context ?: return@setOnClickListener, R.style.AlertDialogCustom)
dialog.setContentView(R.layout.stream_input)
val binding = StreamInputBinding.inflate(dialog.layoutInflater)
dialog.setContentView(binding.root)
dialog.show() dialog.show()
// If user has clicked the switch do not interfere // If user has clicked the switch do not interfere
var preventAutoSwitching = false var preventAutoSwitching = false
dialog.hls_switch?.setOnClickListener { binding.hlsSwitch.setOnClickListener {
preventAutoSwitching = true preventAutoSwitching = true
} }
fun activateSwitchOnHls(text: String?) { fun activateSwitchOnHls(text: String?) {
dialog.hls_switch?.isChecked = normalSafeApiCall { binding.hlsSwitch.isChecked = normalSafeApiCall {
URI(text).path?.substringAfterLast(".")?.contains("m3u") URI(text).path?.substringAfterLast(".")?.contains("m3u")
} == true } == true
} }
dialog.stream_referer?.doOnTextChanged { text, _, _, _ -> binding.streamReferer.doOnTextChanged { text, _, _, _ ->
if (!preventAutoSwitching) if (!preventAutoSwitching)
activateSwitchOnHls(text?.toString()) activateSwitchOnHls(text?.toString())
} }
@ -210,16 +224,16 @@ class DownloadFragment : Fragment() {
0 0
)?.text?.toString()?.let { copy -> )?.text?.toString()?.let { copy ->
val fixedText = copy.trim() val fixedText = copy.trim()
dialog.stream_url?.setText(fixedText) binding.streamUrl.setText(fixedText)
activateSwitchOnHls(fixedText) activateSwitchOnHls(fixedText)
} }
dialog.apply_btt?.setOnClickListener { binding.applyBtt.setOnClickListener {
val url = dialog.stream_url.text?.toString() val url = binding.streamUrl.text?.toString()
if (url.isNullOrEmpty()) { if (url.isNullOrEmpty()) {
showToast(activity, R.string.error_invalid_url, Toast.LENGTH_SHORT) showToast(activity, R.string.error_invalid_url, Toast.LENGTH_SHORT)
} else { } else {
val referer = dialog.stream_referer.text?.toString() val referer = binding.streamReferer.text?.toString()
activity?.navigate( activity?.navigate(
R.id.global_to_navigation_player, R.id.global_to_navigation_player,
@ -228,7 +242,7 @@ class DownloadFragment : Fragment() {
listOf(BasicLink(url)), listOf(BasicLink(url)),
extract = true, extract = true,
referer = referer, referer = referer,
isM3u8 = dialog.hls_switch?.isChecked isM3u8 = binding.hlsSwitch.isChecked
) )
) )
) )
@ -237,22 +251,22 @@ class DownloadFragment : Fragment() {
} }
} }
dialog.cancel_btt?.setOnClickListener { binding.cancelBtt.setOnClickListener {
dialog.dismissSafe(activity) dialog.dismissSafe(activity)
} }
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
download_list?.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY -> binding?.downloadList?.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
val dy = scrollY - oldScrollY val dy = scrollY - oldScrollY
if (dy > 0) { //check for scroll down if (dy > 0) { //check for scroll down
download_stream_button?.shrink() // hide binding?.downloadStreamButton?.shrink() // hide
} else if (dy < -5) { } else if (dy < -5) {
download_stream_button?.extend() // show binding?.downloadStreamButton?.extend() // show
} }
} }
} }
downloadsViewModel.updateList(requireContext()) downloadsViewModel.updateList(requireContext())
context?.fixPaddingStatusbar(download_root) fixPaddingStatusbar(binding?.downloadRoot)
} }
} }

View file

@ -34,12 +34,15 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.MainActivity.Companion.bookmarksUpdatedEvent import com.lagradost.cloudstream3.MainActivity.Companion.bookmarksUpdatedEvent
import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent
import com.lagradost.cloudstream3.databinding.FragmentHomeBinding
import com.lagradost.cloudstream3.databinding.HomeEpisodesExpandedBinding
import com.lagradost.cloudstream3.databinding.HomeSelectMainpageBinding
import com.lagradost.cloudstream3.databinding.TvtypesChipsBinding
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
import com.lagradost.cloudstream3.ui.search.* import com.lagradost.cloudstream3.ui.search.*
@ -64,24 +67,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
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_change_api_loading
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_reload_connection_open_in_browser
import kotlinx.android.synthetic.main.fragment_home.home_reload_connectionerror
import kotlinx.android.synthetic.main.fragment_home.result_error_text
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.*
import kotlinx.android.synthetic.main.tvtypes_chips.view.*
import java.util.* import java.util.*
@ -125,22 +111,26 @@ class HomeFragment : Fragment() {
expand: HomeViewModel.ExpandableHomepageList, expand: HomeViewModel.ExpandableHomepageList,
deleteCallback: (() -> Unit)? = null, deleteCallback: (() -> Unit)? = null,
expandCallback: (suspend (String) -> HomeViewModel.ExpandableHomepageList?)? = null, expandCallback: (suspend (String) -> HomeViewModel.ExpandableHomepageList?)? = null,
dismissCallback : (() -> Unit), dismissCallback: (() -> Unit),
): BottomSheetDialog { ): BottomSheetDialog {
val context = this val context = this
val bottomSheetDialogBuilder = BottomSheetDialog(context) val bottomSheetDialogBuilder = BottomSheetDialog(context)
val binding: HomeEpisodesExpandedBinding = HomeEpisodesExpandedBinding.inflate(
bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded) bottomSheetDialogBuilder.layoutInflater,
val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!! null,
false
)
bottomSheetDialogBuilder.setContentView(binding.root)
//val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!!
//title.findViewTreeLifecycleOwner().lifecycle.addObserver() //title.findViewTreeLifecycleOwner().lifecycle.addObserver()
val item = expand.list val item = expand.list
title.text = item.name binding.homeExpandedText.text = item.name
val recycle = // val recycle =
bottomSheetDialogBuilder.findViewById<AutofitRecyclerView>(R.id.home_expanded_recycler)!! // bottomSheetDialogBuilder.findViewById<AutofitRecyclerView>(R.id.home_expanded_recycler)!!
val titleHolder = //val titleHolder =
bottomSheetDialogBuilder.findViewById<FrameLayout>(R.id.home_expanded_drag_down)!! // bottomSheetDialogBuilder.findViewById<FrameLayout>(R.id.home_expanded_drag_down)!!
// main { // main {
//(bottomSheetDialogBuilder.ownerActivity as androidx.fragment.app.FragmentActivity?)?.supportFragmentManager?.fragments?.lastOrNull()?.viewLifecycleOwner?.apply { //(bottomSheetDialogBuilder.ownerActivity as androidx.fragment.app.FragmentActivity?)?.supportFragmentManager?.fragments?.lastOrNull()?.viewLifecycleOwner?.apply {
@ -159,10 +149,10 @@ class HomeFragment : Fragment() {
// }) // })
//} //}
// } // }
val delete = bottomSheetDialogBuilder.home_expanded_delete //val delete = bottomSheetDialogBuilder.home_expanded_delete
delete.isGone = deleteCallback == null binding.homeExpandedDelete.isGone = deleteCallback == null
if (deleteCallback != null) { if (deleteCallback != null) {
delete.setOnClickListener { binding.homeExpandedDelete.setOnClickListener {
try { try {
val builder: AlertDialog.Builder = AlertDialog.Builder(context) val builder: AlertDialog.Builder = AlertDialog.Builder(context)
val dialogClickListener = val dialogClickListener =
@ -172,6 +162,7 @@ class HomeFragment : Fragment() {
deleteCallback.invoke() deleteCallback.invoke()
bottomSheetDialogBuilder.dismissSafe(this) bottomSheetDialogBuilder.dismissSafe(this)
} }
DialogInterface.BUTTON_NEGATIVE -> {} DialogInterface.BUTTON_NEGATIVE -> {}
} }
} }
@ -191,26 +182,27 @@ class HomeFragment : Fragment() {
} }
} }
} }
binding.homeExpandedDragDown.setOnClickListener {
titleHolder.setOnClickListener {
bottomSheetDialogBuilder.dismissSafe(this) bottomSheetDialogBuilder.dismissSafe(this)
} }
// Span settings // Span settings
recycle.spanCount = currentSpan binding.homeExpandedRecycler.spanCount = currentSpan
recycle.adapter = SearchAdapter(item.list.toMutableList(), recycle) { callback -> binding.homeExpandedRecycler.adapter =
handleSearchClickCallback(this, callback) SearchAdapter(item.list.toMutableList(), binding.homeExpandedRecycler) { callback ->
if (callback.action == SEARCH_ACTION_LOAD || callback.action == SEARCH_ACTION_PLAY_FILE) { handleSearchClickCallback(this, callback)
bottomSheetDialogBuilder.ownHide() // we hide here because we want to resume it later if (callback.action == SEARCH_ACTION_LOAD || callback.action == SEARCH_ACTION_PLAY_FILE) {
//bottomSheetDialogBuilder.dismissSafe(this) bottomSheetDialogBuilder.ownHide() // we hide here because we want to resume it later
//bottomSheetDialogBuilder.dismissSafe(this)
}
}.apply {
hasNext = expand.hasNext
} }
}.apply {
hasNext = expand.hasNext
}
recycle.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.homeExpandedRecycler.addOnScrollListener(object :
RecyclerView.OnScrollListener() {
var expandCount = 0 var expandCount = 0
val name = expand.list.name val name = expand.list.name
@ -238,7 +230,7 @@ class HomeFragment : Fragment() {
}) })
val spanListener = { span: Int -> val spanListener = { span: Int ->
recycle.spanCount = span binding.homeExpandedRecycler.spanCount = span
//(recycle.adapter as SearchAdapter).notifyDataSetChanged() //(recycle.adapter as SearchAdapter).notifyDataSetChanged()
} }
@ -280,19 +272,19 @@ class HomeFragment : Fragment() {
) )
} }
private fun getPairList(header: ChipGroup) = getPairList( private fun getPairList(header: TvtypesChipsBinding) = getPairList(
header.home_select_anime, header.homeSelectAnime,
header.home_select_cartoons, header.homeSelectCartoons,
header.home_select_tv_series, header.homeSelectTvSeries,
header.home_select_documentaries, header.homeSelectDocumentaries,
header.home_select_movies, header.homeSelectMovies,
header.home_select_asian, header.homeSelectAsian,
header.home_select_livestreams, header.homeSelectLivestreams,
header.home_select_nsfw, header.homeSelectNsfw,
header.home_select_others header.homeSelectOthers
) )
fun validateChips(header: ChipGroup?, validTypes: List<TvType>) { fun validateChips(header: TvtypesChipsBinding?, validTypes: List<TvType>) {
if (header == null) return if (header == null) return
val pairList = getPairList(header) val pairList = getPairList(header)
for ((button, types) in pairList) { for ((button, types) in pairList) {
@ -301,7 +293,7 @@ class HomeFragment : Fragment() {
} }
} }
fun updateChips(header: ChipGroup?, selectedTypes: List<TvType>) { fun updateChips(header: TvtypesChipsBinding?, selectedTypes: List<TvType>) {
if (header == null) return if (header == null) return
val pairList = getPairList(header) val pairList = getPairList(header)
for ((button, types) in pairList) { for ((button, types) in pairList) {
@ -311,7 +303,7 @@ class HomeFragment : Fragment() {
} }
fun bindChips( fun bindChips(
header: ChipGroup?, header: TvtypesChipsBinding?,
selectedTypes: List<TvType>, selectedTypes: List<TvType>,
validTypes: List<TvType>, validTypes: List<TvType>,
callback: (List<TvType>) -> Unit callback: (List<TvType>) -> Unit
@ -344,7 +336,13 @@ class HomeFragment : Fragment() {
BottomSheetDialog(this) BottomSheetDialog(this)
builder.behavior.state = BottomSheetBehavior.STATE_EXPANDED builder.behavior.state = BottomSheetBehavior.STATE_EXPANDED
builder.setContentView(R.layout.home_select_mainpage) val binding: HomeSelectMainpageBinding = HomeSelectMainpageBinding.inflate(
builder.layoutInflater,
null,
false
)
builder.setContentView(binding.root)
builder.show() builder.show()
builder.let { dialog -> builder.let { dialog ->
val isMultiLang = getApiProviderLangSettings().let { set -> val isMultiLang = getApiProviderLangSettings().let { set ->
@ -408,7 +406,7 @@ class HomeFragment : Fragment() {
} }
bindChips( bindChips(
dialog.home_select_group, binding.tvtypesChipsScroll.tvtypesChips,
preSelectedTypes, preSelectedTypes,
validAPIs.flatMap { it.supportedTypes }.distinct() validAPIs.flatMap { it.supportedTypes }.distinct()
) { list -> ) { list ->
@ -423,6 +421,9 @@ class HomeFragment : Fragment() {
private val homeViewModel: HomeViewModel by activityViewModels() private val homeViewModel: HomeViewModel by activityViewModels()
var binding: FragmentHomeBinding? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -433,11 +434,24 @@ class HomeFragment : Fragment() {
bottomSheetDialog?.ownShow() bottomSheetDialog?.ownShow()
val layout = val layout =
if (isTvSettings()) R.layout.fragment_home_tv else R.layout.fragment_home if (isTvSettings()) R.layout.fragment_home_tv else R.layout.fragment_home
return inflater.inflate(layout, container, false)
/* val binding = FragmentHomeTvBinding.inflate(layout, container, false)
binding.homeLoadingError
val binding2 = FragmentHomeBinding.inflate(layout, container, false)
binding2.homeLoadingError*/
val root = inflater.inflate(layout, container, false)
binding = FragmentHomeBinding.bind(root)
//val localBinding = FragmentHomeBinding.inflate(inflater)
//binding = localBinding
return root
//return inflater.inflate(layout, container, false)
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetDialog?.ownHide() bottomSheetDialog?.ownHide()
binding = null
super.onDestroyView() super.onDestroyView()
} }
@ -467,7 +481,7 @@ class HomeFragment : Fragment() {
fixGrid() fixGrid()
} }
fun bookmarksUpdated(_data : Boolean) { fun bookmarksUpdated(_data: Boolean) {
reloadStored() reloadStored()
} }
@ -525,14 +539,18 @@ class HomeFragment : Fragment() {
private var bottomSheetDialog: BottomSheetDialog? = null private var bottomSheetDialog: BottomSheetDialog? = null
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
fixGrid() fixGrid()
home_change_api_loading?.setOnClickListener(apiChangeClickListener) binding?.homeChangeApiLoading?.setOnClickListener(apiChangeClickListener)
home_api_fab?.setOnClickListener(apiChangeClickListener)
home_random?.setOnClickListener {
binding?.homeChangeApiLoading?.setOnClickListener(apiChangeClickListener)
binding?.homeApiFab?.setOnClickListener(apiChangeClickListener)
binding?.homeRandom?.setOnClickListener {
if (listHomepageItems.isNotEmpty()) { if (listHomepageItems.isNotEmpty()) {
activity.loadSearchResult(listHomepageItems.random()) activity.loadSearchResult(listHomepageItems.random())
} }
@ -542,91 +560,100 @@ class HomeFragment : Fragment() {
context?.let { context?.let {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(it) val settingsManager = PreferenceManager.getDefaultSharedPreferences(it)
toggleRandomButton = toggleRandomButton =
settingsManager.getBoolean(getString(R.string.random_button_key), false) settingsManager.getBoolean(
home_random?.visibility = View.GONE getString(R.string.random_button_key),
false
) && !isTvSettings()
binding?.homeRandom?.visibility = View.GONE
} }
observe(homeViewModel.preview) { preview -> observe(homeViewModel.preview) { preview ->
(home_master_recycler?.adapter as? HomeParentItemAdapterPreview?)?.setPreviewData( (binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setPreviewData(
preview preview
) )
} }
observe(homeViewModel.apiName) { apiName -> observe(homeViewModel.apiName) { apiName ->
currentApiName = apiName currentApiName = apiName
home_api_fab?.text = apiName binding?.homeApiFab?.text = apiName
(home_master_recycler?.adapter as? HomeParentItemAdapterPreview?)?.setApiName( (binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setApiName(
apiName apiName
) )
} }
observe(homeViewModel.page) { data -> observe(homeViewModel.page) { data ->
when (data) { binding?.apply {
is Resource.Success -> { when (data) {
home_loading_shimmer?.stopShimmer() is Resource.Success -> {
homeLoadingShimmer.stopShimmer()
val d = data.value val d = data.value
val mutableListOfResponse = mutableListOf<SearchResponse>() val mutableListOfResponse = mutableListOf<SearchResponse>()
listHomepageItems.clear() listHomepageItems.clear()
(home_master_recycler?.adapter as? ParentItemAdapter)?.updateList( (homeMasterRecycler.adapter as? ParentItemAdapter)?.updateList(
d.values.toMutableList(), d.values.toMutableList(),
home_master_recycler homeMasterRecycler
) )
home_loading?.isVisible = false homeLoading.isVisible = false
home_loading_error?.isVisible = false homeLoadingError.isVisible = false
home_master_recycler?.isVisible = true homeMasterRecycler.isVisible = true
//home_loaded?.isVisible = true //home_loaded?.isVisible = true
if (toggleRandomButton) { if (toggleRandomButton) {
//Flatten list //Flatten list
d.values.forEach { dlist -> d.values.forEach { dlist ->
mutableListOfResponse.addAll(dlist.list.list) mutableListOfResponse.addAll(dlist.list.list)
}
listHomepageItems.addAll(mutableListOfResponse.distinctBy { it.url })
homeRandom.isVisible = listHomepageItems.isNotEmpty()
} else {
homeRandom.isGone = true
} }
listHomepageItems.addAll(mutableListOfResponse.distinctBy { it.url })
home_random?.isVisible = listHomepageItems.isNotEmpty()
} else {
home_random?.isGone = true
} }
}
is Resource.Failure -> {
home_loading_shimmer?.stopShimmer()
result_error_text.text = data.errorString is Resource.Failure -> {
home_reload_connectionerror.setOnClickListener(apiChangeClickListener) homeLoadingShimmer.stopShimmer()
home_reload_connection_open_in_browser.setOnClickListener { view -> resultErrorText.text = data.errorString
val validAPIs = apis//.filter { api -> api.hasMainPage }
view.popupMenuNoIconsAndNoStringRes(validAPIs.mapIndexed { index, api -> homeReloadConnectionerror.setOnClickListener(apiChangeClickListener)
Pair(
index, homeReloadConnectionOpenInBrowser.setOnClickListener { view ->
api.name val validAPIs = apis//.filter { api -> api.hasMainPage }
)
}) { view.popupMenuNoIconsAndNoStringRes(validAPIs.mapIndexed { index, api ->
try { Pair(
val i = Intent(Intent.ACTION_VIEW) index,
i.data = Uri.parse(validAPIs[itemId].mainUrl) api.name
startActivity(i) )
} catch (e: Exception) { }) {
logError(e) try {
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse(validAPIs[itemId].mainUrl)
startActivity(i)
} catch (e: Exception) {
logError(e)
}
} }
} }
homeLoading.isVisible = false
homeLoadingError.isVisible = true
homeMasterRecycler.isVisible = false
//home_loaded?.isVisible = false
} }
home_loading?.isVisible = false is Resource.Loading -> {
home_loading_error?.isVisible = true (homeMasterRecycler.adapter as? ParentItemAdapter)?.updateList(listOf())
home_master_recycler?.isVisible = false homeLoadingShimmer.startShimmer()
//home_loaded?.isVisible = false homeLoading.isVisible = true
} homeLoadingError.isVisible = false
is Resource.Loading -> { homeMasterRecycler.isVisible = false
(home_master_recycler?.adapter as? ParentItemAdapter)?.updateList(listOf()) //home_loaded?.isVisible = false
home_loading_shimmer?.startShimmer() }
home_loading?.isVisible = true
home_loading_error?.isVisible = false
home_master_recycler?.isVisible = false
//home_loaded?.isVisible = false
} }
} }
} }
@ -638,19 +665,19 @@ class HomeFragment : Fragment() {
HOME_BOOKMARK_VALUE_LIST, HOME_BOOKMARK_VALUE_LIST,
availableWatchStatusTypes.first.map { it.internalId }.toIntArray() availableWatchStatusTypes.first.map { it.internalId }.toIntArray()
) )
(home_master_recycler?.adapter as? HomeParentItemAdapterPreview?)?.setAvailableWatchStatusTypes( (binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setAvailableWatchStatusTypes(
availableWatchStatusTypes availableWatchStatusTypes
) )
} }
observe(homeViewModel.bookmarks) { data -> observe(homeViewModel.bookmarks) { data ->
(home_master_recycler?.adapter as? HomeParentItemAdapterPreview?)?.setBookmarkData( (binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setBookmarkData(
data data
) )
} }
observe(homeViewModel.resumeWatching) { resumeWatching -> observe(homeViewModel.resumeWatching) { resumeWatching ->
(home_master_recycler?.adapter as? HomeParentItemAdapterPreview?)?.setResumeWatchingData( (binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setResumeWatchingData(
resumeWatching resumeWatching
) )
if (isTrueTvSettings()) { if (isTrueTvSettings()) {
@ -665,9 +692,9 @@ class HomeFragment : Fragment() {
//context?.fixPaddingStatusbarView(home_statusbar) //context?.fixPaddingStatusbarView(home_statusbar)
//context?.fixPaddingStatusbar(home_padding) //context?.fixPaddingStatusbar(home_padding)
context?.fixPaddingStatusbar(home_loading_statusbar) fixPaddingStatusbar(binding?.homeLoadingStatusbar)
home_master_recycler?.adapter = binding?.homeMasterRecycler?.adapter =
HomeParentItemAdapterPreview(mutableListOf(), { callback -> HomeParentItemAdapterPreview(mutableListOf(), { callback ->
homeHandleSearch(callback) homeHandleSearch(callback)
}, { item -> }, { item ->
@ -699,18 +726,22 @@ class HomeFragment : Fragment() {
reloadStored() reloadStored()
loadHomePage(false) loadHomePage(false)
home_master_recycler?.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding?.homeMasterRecycler?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy > 0) { //check for scroll down
home_api_fab?.shrink() // hide binding?.apply {
home_random?.shrink() if (dy > 0) { //check for scroll down
} else if (dy < -5) { homeApiFab.shrink() // hide
if (!isTvSettings()) { homeRandom.shrink()
home_api_fab?.extend() // show } else if (dy < -5) {
home_random?.extend() if (!isTvSettings()) {
homeApiFab.extend() // show
homeRandom.extend()
}
} }
} }
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
} }
}) })
@ -718,18 +749,20 @@ class HomeFragment : Fragment() {
// nice profile pic on homepage // nice profile pic on homepage
//home_profile_picture_holder?.isVisible = false //home_profile_picture_holder?.isVisible = false
// just in case // just in case
if (isTvSettings()) { binding?.apply {
home_api_fab?.isVisible = false if (isTvSettings()) {
if (isTrueTvSettings()) { homeApiFab.isVisible = false
home_change_api_loading?.isVisible = true if (isTrueTvSettings()) {
home_change_api_loading?.isFocusable = true homeChangeApiLoading.isVisible = true
home_change_api_loading?.isFocusableInTouchMode = true homeChangeApiLoading.isFocusable = true
homeChangeApiLoading.isFocusableInTouchMode = true
}
// home_bookmark_select?.isFocusable = true
// home_bookmark_select?.isFocusableInTouchMode = true
} else {
homeApiFab.isVisible = true
homeChangeApiLoading.isVisible = false
} }
// home_bookmark_select?.isFocusable = true
// home_bookmark_select?.isFocusableInTouchMode = true
} else {
home_api_fab?.isVisible = true
home_change_api_loading?.isVisible = false
} }
//TODO READD THIS //TODO READD THIS
/*for (syncApi in OAuth2Apis) { /*for (syncApi in OAuth2Apis) {

View file

@ -544,7 +544,7 @@ class HomeParentItemAdapterPreview(
} }
} }
itemView.home_search?.context?.fixPaddingStatusbar(itemView.home_search) fixPaddingStatusbar(itemView.home_search)
itemView.home_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { itemView.home_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
@ -575,7 +575,7 @@ class HomeParentItemAdapterPreview(
layoutParams = params layoutParams = params
} }
} else { } else {
itemView.home_none_padding?.context?.fixPaddingStatusbarView(itemView.home_none_padding) fixPaddingStatusbarView(itemView.home_none_padding)
} }
when (preview) { when (preview) {
is Resource.Success -> { is Resource.Success -> {

View file

@ -6,7 +6,6 @@ import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -14,6 +13,7 @@ import android.view.animation.AlphaAnimation
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.activityViewModels import androidx.fragment.app.activityViewModels
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.lagradost.cloudstream3.APIHolder import com.lagradost.cloudstream3.APIHolder
@ -22,6 +22,7 @@ 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.R import com.lagradost.cloudstream3.R
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.observe import com.lagradost.cloudstream3.mvvm.observe
@ -37,7 +38,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.reduceDragSensitivity
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import kotlinx.android.synthetic.main.fragment_library.*
import kotlin.math.abs import kotlin.math.abs
const val LIBRARY_FOLDER = "library_folder" const val LIBRARY_FOLDER = "library_folder"
@ -73,14 +73,25 @@ class LibraryFragment : Fragment() {
private val libraryViewModel: LibraryViewModel by activityViewModels() private val libraryViewModel: LibraryViewModel by activityViewModels()
var binding: FragmentLibraryBinding? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? { ): View {
return inflater.inflate(R.layout.fragment_library, container, false) val localBinding = FragmentLibraryBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root
//return inflater.inflate(R.layout.fragment_library, container, false)
}
override fun onDestroyView() {
binding = null
super.onDestroyView()
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
viewpager?.currentItem?.let { currentItem -> binding?.viewpager?.currentItem?.let { currentItem ->
outState.putInt(VIEWPAGER_ITEM_KEY, currentItem) outState.putInt(VIEWPAGER_ITEM_KEY, currentItem)
} }
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
@ -88,9 +99,9 @@ class LibraryFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(search_status_bar_padding) fixPaddingStatusbar(binding?.searchStatusBarPadding)
sort_fab?.setOnClickListener { binding?.sortFab?.setOnClickListener {
val methods = libraryViewModel.sortingMethods.map { val methods = libraryViewModel.sortingMethods.map {
txt(it.stringRes).asString(view.context) txt(it.stringRes).asString(view.context)
} }
@ -106,7 +117,7 @@ class LibraryFragment : Fragment() {
}) })
} }
main_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { binding?.mainSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean { override fun onQueryTextSubmit(query: String?): Boolean {
libraryViewModel.sort(ListSorting.Query, query) libraryViewModel.sort(ListSorting.Query, query)
return true return true
@ -129,7 +140,7 @@ class LibraryFragment : Fragment() {
libraryViewModel.reloadPages(false) libraryViewModel.reloadPages(false)
list_selector?.setOnClickListener { binding?.listSelector?.setOnClickListener {
val items = libraryViewModel.availableApiNames val items = libraryViewModel.availableApiNames
val currentItem = libraryViewModel.currentApiName.value val currentItem = libraryViewModel.currentApiName.value
@ -209,20 +220,22 @@ class LibraryFragment : Fragment() {
} }
} }
provider_selector?.setOnClickListener { binding?.providerSelector?.setOnClickListener {
val syncName = libraryViewModel.currentSyncApi?.syncIdName ?: return@setOnClickListener val syncName = libraryViewModel.currentSyncApi?.syncIdName ?: return@setOnClickListener
activity?.showPluginSelectionDialog(syncName.name, syncName) activity?.showPluginSelectionDialog(syncName.name, syncName)
} }
viewpager?.setPageTransformer(LibraryScrollTransformer()) binding?.viewpager?.setPageTransformer(LibraryScrollTransformer())
viewpager?.adapter = binding?.viewpager?.adapter =
viewpager.adapter ?: ViewpagerAdapter(mutableListOf(), { isScrollingDown: Boolean -> binding?.viewpager?.adapter ?: ViewpagerAdapter(
if (isScrollingDown) { mutableListOf(),
sort_fab?.shrink() { isScrollingDown: Boolean ->
} else { if (isScrollingDown) {
sort_fab?.extend() binding?.sortFab?.shrink()
} } else {
}) callback@{ searchClickCallback -> binding?.sortFab?.extend()
}
}) callback@{ searchClickCallback ->
// To prevent future accidents // To prevent future accidents
debugAssert({ debugAssert({
searchClickCallback.card !is SyncAPI.LibraryItem searchClickCallback.card !is SyncAPI.LibraryItem
@ -267,6 +280,7 @@ class LibraryFragment : Fragment() {
) )
} }
} }
LibraryOpenerType.None -> {} LibraryOpenerType.None -> {}
LibraryOpenerType.Provider -> LibraryOpenerType.Provider ->
savedSelection.providerData?.apiName?.let { apiName -> savedSelection.providerData?.apiName?.let { apiName ->
@ -275,8 +289,10 @@ class LibraryFragment : Fragment() {
apiName, apiName,
) )
} }
LibraryOpenerType.Browser -> LibraryOpenerType.Browser ->
openBrowser(searchClickCallback.card.url) openBrowser(searchClickCallback.card.url)
LibraryOpenerType.Search -> { LibraryOpenerType.Search -> {
QuickSearchFragment.pushSearch( QuickSearchFragment.pushSearch(
activity, activity,
@ -288,22 +304,28 @@ class LibraryFragment : Fragment() {
} }
} }
viewpager?.offscreenPageLimit = 2 binding?.apply {
viewpager?.reduceDragSensitivity() viewpager.offscreenPageLimit = 2
viewpager.reduceDragSensitivity()
}
val startLoading = Runnable { val startLoading = Runnable {
gridview?.numColumns = context?.getSpanCount() ?: 3 binding?.apply {
gridview?.adapter = gridview.numColumns = context?.getSpanCount() ?: 3
context?.let { LoadingPosterAdapter(it, 6 * 3) } gridview.adapter =
library_loading_overlay?.isVisible = true context?.let { LoadingPosterAdapter(it, 6 * 3) }
library_loading_shimmer?.startShimmer() libraryLoadingOverlay.isVisible = true
empty_list_textview?.isVisible = false libraryLoadingShimmer.startShimmer()
emptyListTextview.isVisible = false
}
} }
val stopLoading = Runnable { val stopLoading = Runnable {
gridview?.adapter = null binding?.apply {
library_loading_overlay?.isVisible = false gridview.adapter = null
library_loading_shimmer?.stopShimmer() libraryLoadingOverlay.isVisible = false
libraryLoadingShimmer.stopShimmer()
}
} }
val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
@ -314,65 +336,75 @@ class LibraryFragment : Fragment() {
handler.removeCallbacks(startLoading) handler.removeCallbacks(startLoading)
val pages = resource.value val pages = resource.value
val showNotice = pages.all { it.items.isEmpty() } val showNotice = pages.all { it.items.isEmpty() }
empty_list_textview?.isVisible = showNotice
if (showNotice) {
if (libraryViewModel.availableApiNames.size > 1) { binding?.apply {
empty_list_textview?.setText(R.string.empty_library_logged_in_message) emptyListTextview.isVisible = showNotice
} else { if (showNotice) {
empty_list_textview?.setText(R.string.empty_library_no_accounts_message) if (libraryViewModel.availableApiNames.size > 1) {
emptyListTextview.setText(R.string.empty_library_logged_in_message)
} else {
emptyListTextview.setText(R.string.empty_library_no_accounts_message)
}
} }
(viewpager.adapter as? ViewpagerAdapter)?.pages = pages
// Using notifyItemRangeChanged keeps the animations when sorting
viewpager.adapter?.notifyItemRangeChanged(
0,
viewpager.adapter?.itemCount ?: 0
)
// Only stop loading after 300ms to hide the fade effect the viewpager produces when updating
// Without this there would be a flashing effect:
// loading -> show old viewpager -> black screen -> show new viewpager
handler.postDelayed(stopLoading, 300)
savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos ->
if (currentPos < 0) return@let
viewpager.setCurrentItem(currentPos, false)
// Using remove() sets the key to 0 instead of removing it
savedInstanceState.putInt(VIEWPAGER_ITEM_KEY, -1)
}
// Since the animation to scroll multiple items is so much its better to just hide
// the viewpager a bit while the fastest animation is running
fun hideViewpager(distance: Int) {
if (distance < 3) return
val hideAnimation = AlphaAnimation(1f, 0f).apply {
duration = distance * 50L
fillAfter = true
}
val showAnimation = AlphaAnimation(0f, 1f).apply {
duration = distance * 50L
startOffset = distance * 100L
fillAfter = true
}
viewpager.startAnimation(hideAnimation)
viewpager.startAnimation(showAnimation)
}
TabLayoutMediator(
libraryTabLayout,
viewpager,
) { tab, position ->
tab.text = pages.getOrNull(position)?.title?.asStringNull(context)
tab.view.setOnClickListener {
val currentItem =
binding?.viewpager?.currentItem ?: return@setOnClickListener
val distance = abs(position - currentItem)
hideViewpager(distance)
}
}.attach()
} }
(viewpager.adapter as? ViewpagerAdapter)?.pages = pages
// Using notifyItemRangeChanged keeps the animations when sorting
viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0)
// Only stop loading after 300ms to hide the fade effect the viewpager produces when updating
// Without this there would be a flashing effect:
// loading -> show old viewpager -> black screen -> show new viewpager
handler.postDelayed(stopLoading, 300)
savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos ->
if (currentPos < 0) return@let
viewpager?.setCurrentItem(currentPos, false)
// Using remove() sets the key to 0 instead of removing it
savedInstanceState.putInt(VIEWPAGER_ITEM_KEY, -1)
}
// Since the animation to scroll multiple items is so much its better to just hide
// the viewpager a bit while the fastest animation is running
fun hideViewpager(distance: Int) {
if (distance < 3) return
val hideAnimation = AlphaAnimation(1f, 0f).apply {
duration = distance * 50L
fillAfter = true
}
val showAnimation = AlphaAnimation(0f, 1f).apply {
duration = distance * 50L
startOffset = distance * 100L
fillAfter = true
}
viewpager?.startAnimation(hideAnimation)
viewpager?.startAnimation(showAnimation)
}
TabLayoutMediator(
library_tab_layout,
viewpager,
) { tab, position ->
tab.text = pages.getOrNull(position)?.title?.asStringNull(context)
tab.view.setOnClickListener {
val currentItem = viewpager?.currentItem ?: return@setOnClickListener
val distance = abs(position - currentItem)
hideViewpager(distance)
}
}.attach()
} }
is Resource.Loading -> { is Resource.Loading -> {
// Only start loading after 200ms to prevent loading cached lists // Only start loading after 200ms to prevent loading cached lists
handler.postDelayed(startLoading, 200) handler.postDelayed(startLoading, 200)
} }
is Resource.Failure -> { is Resource.Failure -> {
stopLoading.run() stopLoading.run()
// No user indication it failed :( // No user indication it failed :(
@ -383,7 +415,7 @@ class LibraryFragment : Fragment() {
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
(viewpager.adapter as? ViewpagerAdapter)?.rebind() (binding?.viewpager?.adapter as? ViewpagerAdapter)?.rebind()
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
} }
} }

View file

@ -11,7 +11,6 @@ import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import kotlinx.coroutines.delay
enum class ListSorting(@StringRes val stringRes: Int) { enum class ListSorting(@StringRes val stringRes: Int) {
Query(R.string.none), Query(R.string.none),

View file

@ -3,23 +3,21 @@ package com.lagradost.cloudstream3.ui.library
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.AcraApplication import com.lagradost.cloudstream3.AcraApplication
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.SearchResultGridExpandedBinding
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.AutofitRecyclerView import com.lagradost.cloudstream3.ui.AutofitRecyclerView
import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.utils.AppUtils import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.android.synthetic.main.search_result_grid_expanded.view.*
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -32,8 +30,7 @@ class PageAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return LibraryItemViewHolder( return LibraryItemViewHolder(
LayoutInflater.from(parent.context) SearchResultGridExpandedBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.inflate(R.layout.search_result_grid_expanded, parent, false)
) )
} }
@ -57,8 +54,7 @@ class PageAdapter(
} }
} }
inner class LibraryItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class LibraryItemViewHolder(val binding : SearchResultGridExpandedBinding) : RecyclerView.ViewHolder(binding.root) {
val cardView: ImageView = itemView.imageView
private val compactView = false//itemView.context.getGridIsCompact() private val compactView = false//itemView.context.getGridIsCompact()
private val coverHeight: Int = private val coverHeight: Int =
@ -85,11 +81,11 @@ class PageAdapter(
val fg = val fg =
getDifferentColor(bg)//palette.getVibrantColor(ContextCompat.getColor(ctx,R.color.ratingColor)) getDifferentColor(bg)//palette.getVibrantColor(ContextCompat.getColor(ctx,R.color.ratingColor))
itemView.text_rating.apply { binding.textRating.apply {
setTextColor(ColorStateList.valueOf(fg)) setTextColor(ColorStateList.valueOf(fg))
} }
itemView.text_rating_holder?.backgroundTintList = ColorStateList.valueOf(bg) binding.textRatingHolder.backgroundTintList = ColorStateList.valueOf(bg)
itemView.watchProgress?.apply { binding.watchProgress.apply {
progressTintList = ColorStateList.valueOf(fg) progressTintList = ColorStateList.valueOf(fg)
progressBackgroundTintList = ColorStateList.valueOf(bg) progressBackgroundTintList = ColorStateList.valueOf(bg)
} }
@ -99,7 +95,7 @@ class PageAdapter(
// See searchAdaptor for this, it basically fixes the height // See searchAdaptor for this, it basically fixes the height
if (!compactView) { if (!compactView) {
cardView.apply { binding.imageView.apply {
layoutParams = FrameLayout.LayoutParams( layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
coverHeight coverHeight
@ -108,22 +104,22 @@ class PageAdapter(
} }
val showProgress = item.episodesCompleted != null && item.episodesTotal != null val showProgress = item.episodesCompleted != null && item.episodesTotal != null
itemView.watchProgress.isVisible = showProgress binding.watchProgress.isVisible = showProgress
if (showProgress) { if (showProgress) {
itemView.watchProgress.max = item.episodesTotal!! binding.watchProgress.max = item.episodesTotal!!
itemView.watchProgress.progress = item.episodesCompleted!! binding.watchProgress.progress = item.episodesCompleted!!
} }
itemView.imageText.text = item.name binding.imageText.text = item.name
val showRating = (item.personalRating ?: 0) != 0 val showRating = (item.personalRating ?: 0) != 0
itemView.text_rating_holder.isVisible = showRating binding.textRatingHolder.isVisible = showRating
if (showRating) { if (showRating) {
// We want to show 8.5 but not 8.0 hence the replace // We want to show 8.5 but not 8.0 hence the replace
val rating = ((item.personalRating ?: 0).toDouble() / 10).toString() val rating = ((item.personalRating ?: 0).toDouble() / 10).toString()
.replace(".0", "") .replace(".0", "")
itemView.text_rating.text = "$rating" binding.textRating.text = "$rating"
} }
} }
} }

View file

@ -2,16 +2,14 @@ package com.lagradost.cloudstream3.ui.library
import android.os.Build import android.os.Build
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.doOnAttach import androidx.core.view.doOnAttach
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnFlingListener import androidx.recyclerview.widget.RecyclerView.OnFlingListener
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.LibraryViewpagerPageBinding
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import kotlinx.android.synthetic.main.library_viewpager_page.view.*
class ViewpagerAdapter( class ViewpagerAdapter(
var pages: List<SyncAPI.Page>, var pages: List<SyncAPI.Page>,
@ -20,8 +18,7 @@ class ViewpagerAdapter(
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return PageViewHolder( return PageViewHolder(
LayoutInflater.from(parent.context) LibraryViewpagerPageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.inflate(R.layout.library_viewpager_page, parent, false)
) )
} }
@ -34,6 +31,7 @@ class ViewpagerAdapter(
} }
private val unbound = mutableSetOf<Int>() private val unbound = mutableSetOf<Int>()
/** /**
* Used to mark all pages for re-binding and forces all items to be refreshed * Used to mark all pages for re-binding and forces all items to be refreshed
* Without this the pages will still use the same adapters * Without this the pages will still use the same adapters
@ -43,44 +41,46 @@ class ViewpagerAdapter(
this.notifyItemRangeChanged(0, pages.size) this.notifyItemRangeChanged(0, pages.size)
} }
inner class PageViewHolder(private val itemViewTest: View) : inner class PageViewHolder(private val binding: LibraryViewpagerPageBinding) :
RecyclerView.ViewHolder(itemViewTest) { RecyclerView.ViewHolder(binding.root) {
fun bind(page: SyncAPI.Page, rebind: Boolean) { fun bind(page: SyncAPI.Page, rebind: Boolean) {
itemView.page_recyclerview?.spanCount = binding.pageRecyclerview.apply {
this@PageViewHolder.itemView.context.getSpanCount() ?: 3 spanCount =
this@PageViewHolder.itemView.context.getSpanCount() ?: 3
if (itemViewTest.page_recyclerview?.adapter == null || rebind) { if (adapter == null || rebind) {
// Only add the items after it has been attached since the items rely on ItemWidth // Only add the items after it has been attached since the items rely on ItemWidth
// Which is only determined after the recyclerview is attached. // Which is only determined after the recyclerview is attached.
// If this fails then item height becomes 0 when there is only one item // If this fails then item height becomes 0 when there is only one item
itemViewTest.page_recyclerview?.doOnAttach { doOnAttach {
itemViewTest.page_recyclerview?.adapter = PageAdapter( adapter = PageAdapter(
page.items.toMutableList(), page.items.toMutableList(),
itemViewTest.page_recyclerview, this,
clickCallback clickCallback
) )
}
} else {
(adapter as? PageAdapter)?.updateList(page.items)
scrollToPosition(0)
} }
} else {
(itemViewTest.page_recyclerview?.adapter as? PageAdapter)?.updateList(page.items)
itemViewTest.page_recyclerview?.scrollToPosition(0)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
itemViewTest.page_recyclerview.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
val diff = scrollY - oldScrollY val diff = scrollY - oldScrollY
if (diff == 0) return@setOnScrollChangeListener if (diff == 0) return@setOnScrollChangeListener
scrollCallback.invoke(diff > 0) scrollCallback.invoke(diff > 0)
} }
} else { } else {
itemViewTest.page_recyclerview.onFlingListener = object : OnFlingListener() { onFlingListener = object : OnFlingListener() {
override fun onFling(velocityX: Int, velocityY: Int): Boolean { override fun onFling(velocityX: Int, velocityY: Int): Boolean {
scrollCallback.invoke(velocityY > 0) scrollCallback.invoke(velocityY > 0)
return false return false
}
} }
} }
} }
} }
} }

View file

@ -21,6 +21,7 @@ import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.HomePageList import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.QuickSearchBinding
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
@ -37,7 +38,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import kotlinx.android.synthetic.main.quick_search.*
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
class QuickSearchFragment : Fragment() { class QuickSearchFragment : Fragment() {
@ -72,6 +72,8 @@ class QuickSearchFragment : Fragment() {
private var providers: Set<String>? = null private var providers: Set<String>? = null
private lateinit var searchViewModel: SearchViewModel private lateinit var searchViewModel: SearchViewModel
var binding: QuickSearchBinding? = null
private var bottomSheetDialog: BottomSheetDialog? = null private var bottomSheetDialog: BottomSheetDialog? = null
@ -79,13 +81,21 @@ class QuickSearchFragment : Fragment() {
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
activity?.window?.setSoftInputMode( activity?.window?.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
) )
searchViewModel = ViewModelProvider(this)[SearchViewModel::class.java] searchViewModel = ViewModelProvider(this)[SearchViewModel::class.java]
bottomSheetDialog?.ownShow() bottomSheetDialog?.ownShow()
return inflater.inflate(R.layout.quick_search, container, false) val localBinding = QuickSearchBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root
//return inflater.inflate(R.layout.quick_search, container, false)
}
override fun onDestroyView() {
binding = null
super.onDestroyView()
} }
override fun onDestroy() { override fun onDestroy() {
@ -111,7 +121,7 @@ class QuickSearchFragment : Fragment() {
activity?.getSpanCount()?.let { activity?.getSpanCount()?.let {
HomeFragment.currentSpan = it HomeFragment.currentSpan = it
} }
quick_search_autofit_results.spanCount = HomeFragment.currentSpan binding?.quickSearchAutofitResults?.spanCount = HomeFragment.currentSpan
HomeFragment.currentSpan = HomeFragment.currentSpan HomeFragment.currentSpan = HomeFragment.currentSpan
HomeFragment.configEvent.invoke(HomeFragment.currentSpan) HomeFragment.configEvent.invoke(HomeFragment.currentSpan)
} }
@ -123,7 +133,7 @@ class QuickSearchFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(quick_search_root) fixPaddingStatusbar(binding?.quickSearchRoot)
fixGrid() fixGrid()
arguments?.getStringArray(PROVIDER_KEY)?.let { arguments?.getStringArray(PROVIDER_KEY)?.let {
@ -136,21 +146,22 @@ class QuickSearchFragment : Fragment() {
} else false } else false
if (isSingleProvider) { if (isSingleProvider) {
quick_search_autofit_results.adapter = activity?.let { binding?.quickSearchAutofitResults?.apply {
SearchAdapter( adapter = SearchAdapter(
ArrayList(), ArrayList(),
quick_search_autofit_results, this,
) { callback -> ) { callback ->
SearchHelper.handleSearchClickCallback(activity, callback) SearchHelper.handleSearchClickCallback(activity, callback)
} }
} }
try { try {
quick_search?.queryHint = getString(R.string.search_hint_site).format(providers?.first()) binding?.quickSearch?.queryHint = getString(R.string.search_hint_site).format(providers?.first())
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
} }
} else { } else {
quick_search_master_recycler?.adapter = binding?.quickSearchMasterRecycler?.adapter =
ParentItemAdapter(mutableListOf(), { callback -> ParentItemAdapter(mutableListOf(), { callback ->
SearchHelper.handleSearchClickCallback(activity, callback) SearchHelper.handleSearchClickCallback(activity, callback)
//when (callback.action) { //when (callback.action) {
@ -164,18 +175,17 @@ class QuickSearchFragment : Fragment() {
bottomSheetDialog = null bottomSheetDialog = null
}) })
}) })
quick_search_master_recycler?.layoutManager = GridLayoutManager(context, 1) binding?.quickSearchMasterRecycler?.layoutManager = GridLayoutManager(context, 1)
} }
binding?.quickSearchAutofitResults?.isVisible = isSingleProvider
quick_search_autofit_results?.isVisible = isSingleProvider binding?.quickSearchMasterRecycler?.isGone = isSingleProvider
quick_search_master_recycler?.isGone = isSingleProvider
val listLock = ReentrantLock() val listLock = ReentrantLock()
observe(searchViewModel.currentSearch) { list -> observe(searchViewModel.currentSearch) { list ->
try { try {
// https://stackoverflow.com/questions/6866238/concurrent-modification-exception-adding-to-an-arraylist // https://stackoverflow.com/questions/6866238/concurrent-modification-exception-adding-to-an-arraylist
listLock.lock() listLock.lock()
(quick_search_master_recycler?.adapter as ParentItemAdapter?)?.apply { (binding?.quickSearchMasterRecycler?.adapter as ParentItemAdapter?)?.apply {
updateList(list.map { ongoing -> updateList(list.map { ongoing ->
val ongoingList = HomePageList( val ongoingList = HomePageList(
ongoing.apiName, ongoing.apiName,
@ -192,19 +202,18 @@ class QuickSearchFragment : Fragment() {
} }
val searchExitIcon = val searchExitIcon =
quick_search?.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn) binding?.quickSearch?.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
//val searchMagIcon = //val searchMagIcon =
// quick_search?.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon) // binding.quickSearch.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon)
//searchMagIcon?.scaleX = 0.65f //searchMagIcon?.scaleX = 0.65f
//searchMagIcon?.scaleY = 0.65f //searchMagIcon?.scaleY = 0.65f
binding?.quickSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
quick_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
if (search(context, query, false)) if (search(context, query, false))
UIHelper.hideKeyboard(quick_search) UIHelper.hideKeyboard(binding?.quickSearch)
return true return true
} }
@ -214,27 +223,26 @@ class QuickSearchFragment : Fragment() {
return true return true
} }
}) })
binding?.quickSearchLoadingBar?.alpha = 0f
quick_search_loading_bar.alpha = 0f
observe(searchViewModel.searchResponse) { observe(searchViewModel.searchResponse) {
when (it) { when (it) {
is Resource.Success -> { is Resource.Success -> {
it.value.let { data -> it.value.let { data ->
(quick_search_autofit_results?.adapter as? SearchAdapter)?.updateList( (binding?.quickSearchAutofitResults?.adapter as? SearchAdapter)?.updateList(
context?.filterSearchResultByFilmQuality(data) ?: data context?.filterSearchResultByFilmQuality(data) ?: data
) )
} }
searchExitIcon?.alpha = 1f searchExitIcon?.alpha = 1f
quick_search_loading_bar?.alpha = 0f binding?.quickSearchLoadingBar?.alpha = 0f
} }
is Resource.Failure -> { is Resource.Failure -> {
// Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show() // Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
searchExitIcon?.alpha = 1f searchExitIcon?.alpha = 1f
quick_search_loading_bar?.alpha = 0f binding?.quickSearchLoadingBar?.alpha = 0f
} }
is Resource.Loading -> { is Resource.Loading -> {
searchExitIcon?.alpha = 0f searchExitIcon?.alpha = 0f
quick_search_loading_bar?.alpha = 1f binding?.quickSearchLoadingBar?.alpha = 1f
} }
} }
} }
@ -246,13 +254,12 @@ class QuickSearchFragment : Fragment() {
// UIHelper.showInputMethod(view.findFocus()) // UIHelper.showInputMethod(view.findFocus())
// } // }
//} //}
binding?.quickSearchBack?.setOnClickListener {
quick_search_back.setOnClickListener {
activity?.popCurrentPage() activity?.popCurrentPage()
} }
arguments?.getString(AUTOSEARCH_KEY)?.let { arguments?.getString(AUTOSEARCH_KEY)?.let {
quick_search?.setQuery(it, true) binding?.quickSearch?.setQuery(it, true)
arguments?.remove(AUTOSEARCH_KEY) arguments?.remove(AUTOSEARCH_KEY)
} }
} }

View file

@ -1,20 +1,17 @@
package com.lagradost.cloudstream3.ui.result package com.lagradost.cloudstream3.ui.result
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.ActorData import com.lagradost.cloudstream3.ActorData
import com.lagradost.cloudstream3.ActorRole import com.lagradost.cloudstream3.ActorRole
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.CastItemBinding
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
import kotlinx.android.synthetic.main.cast_item.view.*
class ActorAdaptor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() { class ActorAdaptor : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
data class ActorMetaData( data class ActorMetaData(
var isInverted: Boolean, var isInverted: Boolean,
val actor: ActorData, val actor: ActorData,
@ -24,7 +21,7 @@ class ActorAdaptor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return CardViewHolder( return CardViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.cast_item, parent, false), CastItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
) )
} }
@ -68,15 +65,9 @@ class ActorAdaptor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private class CardViewHolder private class CardViewHolder
constructor( constructor(
itemView: View, val binding: CastItemBinding,
) : ) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(binding.root) {
private val actorImage: ImageView = itemView.actor_image
private val actorName: TextView = itemView.actor_name
private val actorExtra: TextView = itemView.actor_extra
private val voiceActorImage: ImageView = itemView.voice_actor_image
private val voiceActorImageHolder: View = itemView.voice_actor_image_holder
private val voiceActorName: TextView = itemView.voice_actor_name
fun bind(actor: ActorData, isInverted: Boolean, position: Int, callback: (Int) -> Unit) { fun bind(actor: ActorData, isInverted: Boolean, position: Int, callback: (Int) -> Unit) {
val (mainImg, vaImage) = if (!isInverted || actor.voiceActor?.image.isNullOrBlank()) { val (mainImg, vaImage) = if (!isInverted || actor.voiceActor?.image.isNullOrBlank()) {
@ -89,39 +80,43 @@ class ActorAdaptor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
callback(position) callback(position)
} }
actorImage.setImage(mainImg) binding.apply {
actorImage.setImage(mainImg)
actorName.text = actor.actor.name actorName.text = actor.actor.name
actor.role?.let { actor.role?.let {
actorExtra.context?.getString( actorExtra.context?.getString(
when (it) { when (it) {
ActorRole.Main -> { ActorRole.Main -> {
R.string.actor_main R.string.actor_main
} }
ActorRole.Supporting -> {
R.string.actor_supporting ActorRole.Supporting -> {
} R.string.actor_supporting
ActorRole.Background -> { }
R.string.actor_background
ActorRole.Background -> {
R.string.actor_background
}
} }
)?.let { text ->
actorExtra.isVisible = true
actorExtra.text = text
} }
)?.let { text -> } ?: actor.roleString?.let {
actorExtra.isVisible = true actorExtra.isVisible = true
actorExtra.text = text actorExtra.text = it
} ?: run {
actorExtra.isVisible = false
} }
} ?: actor.roleString?.let {
actorExtra.isVisible = true
actorExtra.text = it
} ?: run {
actorExtra.isVisible = false
}
if (actor.voiceActor == null) { if (actor.voiceActor == null) {
voiceActorImageHolder.isVisible = false voiceActorImageHolder.isVisible = false
voiceActorName.isVisible = false voiceActorName.isVisible = false
} else { } else {
voiceActorName.text = actor.voiceActor.name voiceActorName.text = actor.voiceActor.name
voiceActorImageHolder.isVisible = voiceActorImage.setImage(vaImage) voiceActorImageHolder.isVisible = voiceActorImage.setImage(vaImage)
}
} }
} }
} }

View file

@ -3,34 +3,25 @@ package com.lagradost.cloudstream3.ui.result
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.ResultEpisodeBinding
import com.lagradost.cloudstream3.databinding.ResultEpisodeLargeBinding
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
import com.lagradost.cloudstream3.ui.download.DownloadButtonViewHolder import com.lagradost.cloudstream3.ui.download.DownloadButtonViewHolder
import com.lagradost.cloudstream3.ui.download.DownloadClickEvent import com.lagradost.cloudstream3.ui.download.DownloadClickEvent
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.result_episode.view.*
import kotlinx.android.synthetic.main.result_episode.view.episode_text
import kotlinx.android.synthetic.main.result_episode_large.view.*
import kotlinx.android.synthetic.main.result_episode_large.view.episode_filler
import kotlinx.android.synthetic.main.result_episode_large.view.episode_progress
import kotlinx.android.synthetic.main.result_episode_large.view.result_episode_download
import kotlinx.android.synthetic.main.result_episode_large.view.result_episode_progress_downloaded
import java.util.* import java.util.*
const val ACTION_PLAY_EPISODE_IN_PLAYER = 1 const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
@ -144,26 +135,52 @@ class EpisodeAdapter(
diffResult.dispatchUpdatesTo(this) diffResult.dispatchUpdatesTo(this)
} }
var layout = R.layout.result_episode_both fun getItem(position: Int) : ResultEpisode {
return cardList[position]
}
override fun getItemViewType(position: Int): Int {
val item = getItem(position)
return if(item.poster.isNullOrBlank()) 0 else 1
}
// private val layout = R.layout.result_episode_both
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
/*val layout = if (cardList.filter { it.poster != null }.size >= cardList.size / 2) /*val layout = if (cardList.filter { it.poster != null }.size >= cardList.size / 2)
R.layout.result_episode_large R.layout.result_episode_large
else R.layout.result_episode*/ else R.layout.result_episode*/
return EpisodeCardViewHolder( return when(viewType) {
LayoutInflater.from(parent.context) 0 -> {
.inflate(layout, parent, false), EpisodeCardViewHolderSmall(
hasDownloadSupport, ResultEpisodeBinding.inflate(LayoutInflater.from(parent.context), parent, false),
clickCallback, hasDownloadSupport,
downloadClickCallback clickCallback,
) downloadClickCallback
)
}
1 -> {
EpisodeCardViewHolderLarge(
ResultEpisodeLargeBinding.inflate(LayoutInflater.from(parent.context), parent, false),
hasDownloadSupport,
clickCallback,
downloadClickCallback
)
}
else -> throw NotImplementedError()
}
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) { when (holder) {
is EpisodeCardViewHolder -> { is EpisodeCardViewHolderLarge -> {
holder.bind(cardList[position]) holder.bind(getItem(position))
mBoundViewHolders.add(holder)
}
is EpisodeCardViewHolderSmall -> {
holder.bind(getItem(position))
mBoundViewHolders.add(holder) mBoundViewHolders.add(holder)
} }
} }
@ -173,94 +190,81 @@ class EpisodeAdapter(
return cardList.size return cardList.size
} }
class EpisodeCardViewHolder class EpisodeCardViewHolderLarge
constructor( constructor(
itemView: View, val binding : ResultEpisodeLargeBinding,
private val hasDownloadSupport: Boolean, private val hasDownloadSupport: Boolean,
private val clickCallback: (EpisodeClickEvent) -> Unit, private val clickCallback: (EpisodeClickEvent) -> Unit,
private val downloadClickCallback: (DownloadClickEvent) -> Unit, private val downloadClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.ViewHolder(itemView), DownloadButtonViewHolder { ) : RecyclerView.ViewHolder(binding.root), DownloadButtonViewHolder {
override var downloadButton = EasyDownloadButton() override var downloadButton = EasyDownloadButton()
var episodeDownloadBar: ContentLoadingProgressBar? = null // TODO TV
var episodeDownloadImage: ImageView? = null
var localCard: ResultEpisode? = null var localCard: ResultEpisode? = null
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun bind(card: ResultEpisode) { fun bind(card: ResultEpisode) {
localCard = card localCard = card
binding.episodeLinHolder.layoutParams.apply {
width = if(isTvSettings()) ViewGroup.LayoutParams.WRAP_CONTENT else ViewGroup.LayoutParams.MATCH_PARENT
}
val isTrueTv = isTrueTvSettings() val isTrueTv = isTrueTvSettings()
val (parentView, otherView) = if (card.poster == null) { binding.apply {
itemView.episode_holder to itemView.episode_holder_large
} else {
itemView.episode_holder_large to itemView.episode_holder
}
parentView.isVisible = true
otherView.isVisible = false
val episodeText: TextView = parentView.episode_text val name =
val episodeFiller: MaterialButton? = parentView.episode_filler if (card.name == null) "${episodeText.context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}"
val episodeRating: TextView? = parentView.episode_rating episodeFiller.isVisible = card.isFiller == true
val episodeDescript: TextView? = parentView.episode_descript episodeText.text =
val episodeProgress: ContentLoadingProgressBar? = parentView.episode_progress name//if(card.isFiller == true) episodeText.context.getString(R.string.filler).format(name) else name
val episodePoster: ImageView? = parentView.episode_poster episodeText.isSelected = true // is needed for text repeating
episodeDownloadBar = if (card.videoWatchState == VideoWatchState.Watched) {
parentView.result_episode_progress_downloaded // This cannot be done in getDisplayPosition() as when you have not watched something
episodeDownloadImage = parentView.result_episode_download // the duration and position is 0
episodeProgress.max = 1
val name = episodeProgress.progress = 1
if (card.name == null) "${episodeText.context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}" episodeProgress.isVisible = true
episodeFiller?.isVisible = card.isFiller == true } else {
episodeText.text = val displayPos = card.getDisplayPosition()
name//if(card.isFiller == true) episodeText.context.getString(R.string.filler).format(name) else name episodeProgress.max = (card.duration / 1000).toInt()
episodeText.isSelected = true // is needed for text repeating episodeProgress.progress = (displayPos / 1000).toInt()
episodeProgress.isVisible = displayPos > 0L
if (card.videoWatchState == VideoWatchState.Watched) {
// This cannot be done in getDisplayPosition() as when you have not watched something
// the duration and position is 0
episodeProgress?.max = 1
episodeProgress?.progress = 1
episodeProgress?.isVisible = true
} else {
val displayPos = card.getDisplayPosition()
episodeProgress?.max = (card.duration / 1000).toInt()
episodeProgress?.progress = (displayPos / 1000).toInt()
episodeProgress?.isVisible = displayPos > 0L
}
episodePoster?.isVisible = episodePoster?.setImage(card.poster) == true
if (card.rating != null) {
episodeRating?.text = episodeRating?.context?.getString(R.string.rated_format)
?.format(card.rating.toFloat() / 10f)
} else {
episodeRating?.text = ""
}
episodeRating?.isGone = episodeRating?.text.isNullOrBlank()
episodeDescript?.apply {
text = card.description.html()
isGone = text.isNullOrBlank()
setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_DESCRIPTION, card))
}
}
if (!isTrueTv) {
episodePoster?.setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
} }
episodePoster?.setOnLongClickListener { episodePoster.isVisible = episodePoster.setImage(card.poster) == true
clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_TOAST, card))
return@setOnLongClickListener true if (card.rating != null) {
episodeRating.text = episodeRating.context?.getString(R.string.rated_format)
?.format(card.rating.toFloat() / 10f)
} else {
episodeRating.text = ""
}
episodeRating.isGone = episodeRating.text.isNullOrBlank()
episodeDescript.apply {
text = card.description.html()
isGone = text.isNullOrBlank()
setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_DESCRIPTION, card))
}
}
if (!isTrueTv) {
episodePoster.setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
}
episodePoster.setOnLongClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_TOAST, card))
return@setOnLongClickListener true
}
} }
} }
itemView.setOnClickListener { itemView.setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
} }
@ -276,8 +280,8 @@ class EpisodeAdapter(
return@setOnLongClickListener true return@setOnLongClickListener true
} }
episodeDownloadImage?.isVisible = hasDownloadSupport binding.resultEpisodeDownload.isVisible = hasDownloadSupport
episodeDownloadBar?.isVisible = hasDownloadSupport binding.resultEpisodeProgressDownloaded.isVisible = hasDownloadSupport
reattachDownloadButton() reattachDownloadButton()
} }
@ -285,9 +289,6 @@ class EpisodeAdapter(
downloadButton.dispose() downloadButton.dispose()
val card = localCard val card = localCard
if (hasDownloadSupport && card != null) { if (hasDownloadSupport && card != null) {
if (episodeDownloadBar == null ||
episodeDownloadImage == null
) return
val downloadInfo = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings( val downloadInfo = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
itemView.context, itemView.context,
card.id card.id
@ -296,8 +297,109 @@ class EpisodeAdapter(
downloadButton.setUpButton( downloadButton.setUpButton(
downloadInfo?.fileLength, downloadInfo?.fileLength,
downloadInfo?.totalBytes, downloadInfo?.totalBytes,
episodeDownloadBar ?: return, binding.resultEpisodeProgressDownloaded,
episodeDownloadImage ?: return, binding.resultEpisodeDownload,
null,
VideoDownloadHelper.DownloadEpisodeCached(
card.name,
card.poster,
card.episode,
card.season,
card.id,
card.parentId,
card.rating,
card.description,
System.currentTimeMillis(),
)
) {
if (it.action == DOWNLOAD_ACTION_DOWNLOAD) {
clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card))
} else {
downloadClickCallback.invoke(it)
}
}
}
}
}
class EpisodeCardViewHolderSmall
constructor(
val binding : ResultEpisodeBinding,
private val hasDownloadSupport: Boolean,
private val clickCallback: (EpisodeClickEvent) -> Unit,
private val downloadClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.ViewHolder(binding.root), DownloadButtonViewHolder {
override var downloadButton = EasyDownloadButton()
var localCard: ResultEpisode? = null
@SuppressLint("SetTextI18n")
fun bind(card: ResultEpisode) {
localCard = card
val isTrueTv = isTrueTvSettings()
binding.episodeHolder.layoutParams.apply {
width = if(isTvSettings()) ViewGroup.LayoutParams.WRAP_CONTENT else ViewGroup.LayoutParams.MATCH_PARENT
}
binding.apply {
val name =
if (card.name == null) "${episodeText.context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}"
episodeFiller.isVisible = card.isFiller == true
episodeText.text =
name//if(card.isFiller == true) episodeText.context.getString(R.string.filler).format(name) else name
episodeText.isSelected = true // is needed for text repeating
if (card.videoWatchState == VideoWatchState.Watched) {
// This cannot be done in getDisplayPosition() as when you have not watched something
// the duration and position is 0
episodeProgress.max = 1
episodeProgress.progress = 1
episodeProgress.isVisible = true
} else {
val displayPos = card.getDisplayPosition()
episodeProgress.max = (card.duration / 1000).toInt()
episodeProgress.progress = (displayPos / 1000).toInt()
episodeProgress.isVisible = displayPos > 0L
}
itemView.setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
}
if (isTrueTv) {
itemView.isFocusable = true
itemView.isFocusableInTouchMode = true
//itemView.touchscreenBlocksFocus = false
}
itemView.setOnLongClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
return@setOnLongClickListener true
}
binding.resultEpisodeDownload.isVisible = hasDownloadSupport
binding.resultEpisodeProgressDownloaded.isVisible = hasDownloadSupport
reattachDownloadButton()
}
}
override fun reattachDownloadButton() {
downloadButton.dispose()
val card = localCard
if (hasDownloadSupport && card != null) {
val downloadInfo = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
itemView.context,
card.id
)
downloadButton.setUpButton(
downloadInfo?.fileLength,
downloadInfo?.totalBytes,
binding.resultEpisodeProgressDownloaded,
binding.resultEpisodeDownload,
null, null,
VideoDownloadHelper.DownloadEpisodeCached( VideoDownloadHelper.DownloadEpisodeCached(
card.name, card.name,

View file

@ -1,11 +1,10 @@
package com.lagradost.cloudstream3.ui.result package com.lagradost.cloudstream3.ui.result
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.databinding.ResultMiniImageBinding
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
/* /*
@ -24,7 +23,6 @@ const val IMAGE_CLICK = 0
const val IMAGE_LONG_CLICK = 1 const val IMAGE_LONG_CLICK = 1
class ImageAdapter( class ImageAdapter(
val layout: Int,
val clickCallback: ((Int) -> Unit)? = null, val clickCallback: ((Int) -> Unit)? = null,
val nextFocusUp: Int? = null, val nextFocusUp: Int? = null,
val nextFocusDown: Int? = null, val nextFocusDown: Int? = null,
@ -34,7 +32,9 @@ class ImageAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ImageViewHolder( return ImageViewHolder(
LayoutInflater.from(parent.context).inflate(layout, parent, false) //result_mini_image
ResultMiniImageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
// LayoutInflater.from(parent.context).inflate(layout, parent, false)
) )
} }
@ -66,15 +66,15 @@ class ImageAdapter(
} }
class ImageViewHolder class ImageViewHolder
constructor(itemView: View) : constructor(val binding: ResultMiniImageBinding) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(binding.root) {
fun bind( fun bind(
img: Int, img: Int,
clickCallback: ((Int) -> Unit)?, clickCallback: ((Int) -> Unit)?,
nextFocusUp: Int?, nextFocusUp: Int?,
nextFocusDown: Int?, nextFocusDown: Int?,
) { ) {
(itemView as? ImageView?)?.apply { binding.root.apply {
setImageResource(img) setImageResource(img)
if (nextFocusDown != null) { if (nextFocusDown != null) {
this.nextFocusDownId = nextFocusDown this.nextFocusDownId = nextFocusDown

View file

@ -310,6 +310,7 @@ open class ResultFragment : ResultTrailerPlayer() {
result_finish_loading?.isVisible = false result_finish_loading?.isVisible = false
result_loading_error?.isVisible = false result_loading_error?.isVisible = false
} }
1 -> { 1 -> {
result_bookmark_fab?.isGone = true result_bookmark_fab?.isGone = true
result_loading?.isVisible = false result_loading?.isVisible = false
@ -317,6 +318,7 @@ open class ResultFragment : ResultTrailerPlayer() {
result_loading_error?.isVisible = true result_loading_error?.isVisible = true
result_reload_connection_open_in_browser?.isVisible = true result_reload_connection_open_in_browser?.isVisible = true
} }
2 -> { 2 -> {
result_bookmark_fab?.isGone = isTrueTvSettings() result_bookmark_fab?.isGone = isTrueTvSettings()
result_bookmark_fab?.extend() result_bookmark_fab?.extend()
@ -350,9 +352,9 @@ open class ResultFragment : ResultTrailerPlayer() {
viewModel.reloadEpisodes() viewModel.reloadEpisodes()
} }
open fun updateMovie(data: ResourceSome<Pair<UiText, ResultEpisode>>) { open fun updateMovie(data: Resource<Pair<UiText, ResultEpisode>>?) {
when (data) { when (data) {
is ResourceSome.Success -> { is Resource.Success -> {
data.value.let { (text, ep) -> data.value.let { (text, ep) ->
result_play_movie.setText(text) result_play_movie.setText(text)
result_play_movie?.setOnClickListener { result_play_movie?.setOnClickListener {
@ -410,6 +412,7 @@ open class ResultFragment : ResultTrailerPlayer() {
EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep) EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep)
) )
} }
else -> handleDownloadClick(activity, click) else -> handleDownloadClick(activity, click)
} }
} }
@ -417,6 +420,7 @@ open class ResultFragment : ResultTrailerPlayer() {
} }
} }
} }
else -> { else -> {
result_movie_progress_downloaded_holder?.isVisible = false result_movie_progress_downloaded_holder?.isVisible = false
result_play_movie?.isVisible = false result_play_movie?.isVisible = false
@ -424,17 +428,14 @@ open class ResultFragment : ResultTrailerPlayer() {
} }
} }
open fun updateEpisodes(episodes: ResourceSome<List<ResultEpisode>>) { open fun updateEpisodes(episodes: Resource<List<ResultEpisode>>?) {
when (episodes) { when (episodes) {
is ResourceSome.None -> { is Resource.Loading -> {
result_episode_loading?.isVisible = false
result_episodes?.isVisible = false
}
is ResourceSome.Loading -> {
result_episode_loading?.isVisible = true result_episode_loading?.isVisible = true
result_episodes?.isVisible = false result_episodes?.isVisible = false
} }
is ResourceSome.Success -> {
is Resource.Success -> {
result_episodes?.isVisible = true result_episodes?.isVisible = true
result_episode_loading?.isVisible = false result_episode_loading?.isVisible = false
@ -471,6 +472,11 @@ open class ResultFragment : ResultTrailerPlayer() {
result_episodes?.requestFocus() result_episodes?.requestFocus()
} }
} }
else -> {
result_episode_loading?.isVisible = false
result_episodes?.isVisible = false
}
} }
} }
@ -565,7 +571,7 @@ open class ResultFragment : ResultTrailerPlayer() {
context?.updateHasTrailers() context?.updateHasTrailers()
activity?.loadCache() activity?.loadCache()
activity?.fixPaddingStatusbar(result_top_bar) fixPaddingStatusbar(result_top_bar)
//activity?.fixPaddingStatusbar(result_barstatus) //activity?.fixPaddingStatusbar(result_barstatus)
/* val backParameter = result_back.layoutParams as FrameLayout.LayoutParams /* val backParameter = result_back.layoutParams as FrameLayout.LayoutParams
@ -588,7 +594,7 @@ open class ResultFragment : ResultTrailerPlayer() {
result_episodes?.adapter = result_episodes?.adapter =
EpisodeAdapter( EpisodeAdapter(
api?.hasDownloadSupport == true, api?.hasDownloadSupport == true && !isTvSettings(),
{ episodeClick -> { episodeClick ->
viewModel.handleAction(activity, episodeClick) viewModel.handleAction(activity, episodeClick)
}, },
@ -738,10 +744,12 @@ open class ResultFragment : ResultTrailerPlayer() {
viewModel.setMeta(d, syncModel.getSyncs()) viewModel.setMeta(d, syncModel.getSyncs())
} }
is Resource.Loading -> { is Resource.Loading -> {
result_sync_max_episodes?.text = result_sync_max_episodes?.text =
result_sync_max_episodes?.context?.getString(R.string.sync_total_episodes_none) result_sync_max_episodes?.context?.getString(R.string.sync_total_episodes_none)
} }
else -> {} else -> {}
} }
} }
@ -755,11 +763,13 @@ open class ResultFragment : ResultTrailerPlayer() {
result_sync_holder?.isVisible = false result_sync_holder?.isVisible = false
closed = true closed = true
} }
is Resource.Loading -> { is Resource.Loading -> {
result_sync_loading_shimmer?.startShimmer() result_sync_loading_shimmer?.startShimmer()
result_sync_loading_shimmer?.isVisible = true result_sync_loading_shimmer?.isVisible = true
result_sync_holder?.isVisible = false result_sync_holder?.isVisible = false
} }
is Resource.Success -> { is Resource.Success -> {
result_sync_loading_shimmer?.stopShimmer() result_sync_loading_shimmer?.stopShimmer()
result_sync_loading_shimmer?.isVisible = false result_sync_loading_shimmer?.isVisible = false
@ -789,6 +799,7 @@ open class ResultFragment : ResultTrailerPlayer() {
} }
} }
} }
null -> { null -> {
closed = false closed = false
} }
@ -796,58 +807,56 @@ open class ResultFragment : ResultTrailerPlayer() {
result_overlapping_panels?.setStartPanelLockState(if (closed) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED) result_overlapping_panels?.setStartPanelLockState(if (closed) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
} }
observe(viewModel.resumeWatching) { resume -> observeNullable(viewModel.resumeWatching) { resume ->
when (resume) { if (resume == null) {
is Some.Success -> { result_resume_parent?.isVisible = false
result_resume_parent?.isVisible = true return@observeNullable
val value = resume.value
value.progress?.let { progress ->
result_resume_series_title?.apply {
isVisible = !value.isMovie
text =
if (value.isMovie) null else activity?.getNameFull(
value.result.name,
value.result.episode,
value.result.season
)
}
result_resume_series_progress_text.setText(progress.progressLeft)
result_resume_series_progress?.apply {
isVisible = true
this.max = progress.maxProgress
this.progress = progress.progress
}
result_resume_progress_holder?.isVisible = true
} ?: run {
result_resume_progress_holder?.isVisible = false
result_resume_series_progress?.isVisible = false
result_resume_series_title?.isVisible = false
result_resume_series_progress_text?.isVisible = false
}
result_resume_series_button?.isVisible = !value.isMovie
result_resume_series_button_play?.isVisible = !value.isMovie
val click = View.OnClickListener {
viewModel.handleAction(
activity,
EpisodeClickEvent(
storedData?.playerAction ?: ACTION_PLAY_EPISODE_IN_PLAYER,
value.result
)
)
}
result_resume_series_button?.setOnClickListener(click)
result_resume_series_button_play?.setOnClickListener(click)
}
is Some.None -> {
result_resume_parent?.isVisible = false
}
} }
result_resume_parent?.isVisible = true
resume.progress?.let { progress ->
result_resume_series_title?.apply {
isVisible = !resume.isMovie
text =
if (resume.isMovie) null else activity?.getNameFull(
resume.result.name,
resume.result.episode,
resume.result.season
)
}
result_resume_series_progress_text?.setText(progress.progressLeft)
result_resume_series_progress?.apply {
isVisible = true
this.max = progress.maxProgress
this.progress = progress.progress
}
result_resume_progress_holder?.isVisible = true
} ?: run {
result_resume_progress_holder?.isVisible = false
result_resume_series_progress?.isVisible = false
result_resume_series_title?.isVisible = false
result_resume_series_progress_text?.isVisible = false
}
result_resume_series_button?.isVisible = !resume.isMovie
result_resume_series_button_play?.isVisible = !resume.isMovie
val click = View.OnClickListener {
viewModel.handleAction(
activity,
EpisodeClickEvent(
storedData?.playerAction ?: ACTION_PLAY_EPISODE_IN_PLAYER,
resume.result
)
)
}
result_resume_series_button?.setOnClickListener(click)
result_resume_series_button_play?.setOnClickListener(click)
} }
observe(viewModel.episodes) { episodes ->
observeNullable(viewModel.episodes) { episodes ->
updateEpisodes(episodes) updateEpisodes(episodes)
} }
@ -868,7 +877,7 @@ open class ResultFragment : ResultTrailerPlayer() {
setRecommendations(recommendations, null) setRecommendations(recommendations, null)
} }
observe(viewModel.movie) { data -> observeNullable(viewModel.movie) { data ->
updateMovie(data) updateMovie(data)
} }
@ -1046,10 +1055,12 @@ open class ResultFragment : ResultTrailerPlayer() {
}*/ }*/
//} //}
} }
is Resource.Failure -> { is Resource.Failure -> {
result_error_text.text = storedData?.url?.plus("\n") + data.errorString result_error_text.text = storedData?.url?.plus("\n") + data.errorString
updateVisStatus(1) updateVisStatus(1)
} }
is Resource.Loading -> { is Resource.Loading -> {
updateVisStatus(0) updateVisStatus(0)
} }

View file

@ -20,9 +20,9 @@ import com.google.android.gms.cast.framework.CastState
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.mvvm.Some
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
import com.lagradost.cloudstream3.ui.search.SearchAdapter import com.lagradost.cloudstream3.ui.search.SearchAdapter
import com.lagradost.cloudstream3.ui.search.SearchHelper import com.lagradost.cloudstream3.ui.search.SearchHelper
@ -212,7 +212,6 @@ class ResultFragmentPhone : ResultFragment() {
}*/ }*/
result_mini_sync?.adapter = ImageAdapter( result_mini_sync?.adapter = ImageAdapter(
R.layout.result_mini_image,
nextFocusDown = R.id.result_sync_set_score, nextFocusDown = R.id.result_sync_set_score,
clickCallback = { action -> clickCallback = { action ->
if (action == IMAGE_CLICK || action == IMAGE_LONG_CLICK) { if (action == IMAGE_CLICK || action == IMAGE_LONG_CLICK) {
@ -271,73 +270,68 @@ class ResultFragmentPhone : ResultFragment() {
} }
} }
observe(viewModel.episodesCountText) { count -> observeNullable(viewModel.episodesCountText) { count ->
result_episodes_text.setText(count) result_episodes_text.setText(count)
} }
observe(viewModel.selectPopup) { popup -> observeNullable(viewModel.selectPopup) { popup ->
when (popup) { if (popup == null) {
is Some.Success -> { popupDialog?.dismissSafe(activity)
popupDialog?.dismissSafe(activity) popupDialog = null
return@observeNullable
popupDialog = activity?.let { act ->
val pop = popup.value
val options = pop.getOptions(act)
val title = pop.getTitle(act)
act.showBottomDialogInstant(
options, title, {
popupDialog = null
pop.callback(null)
}, {
popupDialog = null
pop.callback(it)
}
)
}
}
is Some.None -> {
popupDialog?.dismissSafe(activity)
popupDialog = null
}
} }
popupDialog?.dismissSafe(activity)
popupDialog = activity?.let { act ->
val options = popup.getOptions(act)
val title = popup.getTitle(act)
act.showBottomDialogInstant(
options, title, {
popupDialog = null
popup.callback(null)
}, {
popupDialog = null
popup.callback(it)
}
)
}
} }
observe(viewModel.loadedLinks) { load -> observe(viewModel.loadedLinks) { load ->
when (load) { if (load == null) {
is Some.Success -> { loadingDialog?.dismissSafe(activity)
if (loadingDialog?.isShowing != true) { loadingDialog = null
loadingDialog?.dismissSafe(activity) return@observe
loadingDialog = null }
} if (loadingDialog?.isShowing != true) {
loadingDialog = loadingDialog ?: context?.let { ctx -> loadingDialog?.dismissSafe(activity)
val builder = loadingDialog = null
BottomSheetDialog(ctx) }
builder.setContentView(R.layout.bottom_loading) loadingDialog = loadingDialog ?: context?.let { ctx ->
builder.setOnDismissListener { val builder =
loadingDialog = null BottomSheetDialog(ctx)
viewModel.cancelLinks() builder.setContentView(R.layout.bottom_loading)
} builder.setOnDismissListener {
//builder.setOnCancelListener {
// it?.dismiss()
//}
builder.setCanceledOnTouchOutside(true)
builder.show()
builder
}
}
is Some.None -> {
loadingDialog?.dismissSafe(activity)
loadingDialog = null loadingDialog = null
viewModel.cancelLinks()
} }
//builder.setOnCancelListener {
// it?.dismiss()
//}
builder.setCanceledOnTouchOutside(true)
builder.show()
builder
} }
} }
observe(viewModel.selectedSeason) { text -> observeNullable(viewModel.selectedSeason) { text ->
result_season_button.setText(text) result_season_button.setText(text)
selectSeason = selectSeason =
(if (text is Some.Success) text.value else null)?.asStringNull(result_season_button?.context) text?.asStringNull(result_season_button?.context)
// If the season button is visible the result season button will be next focus down // If the season button is visible the result season button will be next focus down
if (result_season_button?.isVisible == true) if (result_season_button?.isVisible == true)
if (result_resume_parent?.isVisible == true) if (result_resume_parent?.isVisible == true)
@ -346,7 +340,7 @@ class ResultFragmentPhone : ResultFragment() {
// setFocusUpAndDown(result_bookmark_button, result_season_button) // setFocusUpAndDown(result_bookmark_button, result_season_button)
} }
observe(viewModel.selectedDubStatus) { status -> observeNullable(viewModel.selectedDubStatus) { status ->
result_dub_select?.setText(status) result_dub_select?.setText(status)
if (result_dub_select?.isVisible == true) if (result_dub_select?.isVisible == true)
@ -357,7 +351,7 @@ class ResultFragmentPhone : ResultFragment() {
// setFocusUpAndDown(result_bookmark_button, result_dub_select) // setFocusUpAndDown(result_bookmark_button, result_dub_select)
} }
} }
observe(viewModel.selectedRange) { range -> observeNullable(viewModel.selectedRange) { range ->
result_episode_select.setText(range) result_episode_select.setText(range)
// If Season button is invisible then the bookmark button next focus is episode select // If Season button is invisible then the bookmark button next focus is episode select

View file

@ -12,9 +12,9 @@ import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.mvvm.ResourceSome import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.Some
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.search.SearchAdapter import com.lagradost.cloudstream3.ui.search.SearchAdapter
@ -69,16 +69,16 @@ class ResultFragmentTv : ResultFragment() {
return focus == this.result_root return focus == this.result_root
} }
override fun updateEpisodes(episodes: ResourceSome<List<ResultEpisode>>) { override fun updateEpisodes(episodes: Resource<List<ResultEpisode>>?) {
super.updateEpisodes(episodes) super.updateEpisodes(episodes)
if (episodes is ResourceSome.Success && hasNoFocus()) { if (episodes is Resource.Success && hasNoFocus()) {
result_episodes?.requestFocus() result_episodes?.requestFocus()
} }
} }
override fun updateMovie(data: ResourceSome<Pair<UiText, ResultEpisode>>) { override fun updateMovie(data: Resource<Pair<UiText, ResultEpisode>>?) {
super.updateMovie(data) super.updateMovie(data)
if (data is ResourceSome.Success && hasNoFocus()) { if (data is Resource.Success && hasNoFocus()) {
result_play_movie?.requestFocus() result_play_movie?.requestFocus()
} }
} }
@ -130,9 +130,9 @@ class ResultFragmentTv : ResultFragment() {
LinearListLayout(result_episodes?.context).apply { LinearListLayout(result_episodes?.context).apply {
setHorizontal() setHorizontal()
} }
(result_episodes?.adapter as EpisodeAdapter?)?.apply { // (result_episodes?.adapter as EpisodeAdapter?)?.apply {
layout = R.layout.result_episode_both_tv // layout = R.layout.result_episode_both_tv
} // }
//result_episodes?.setMaxViewPoolSize(0, Int.MAX_VALUE) //result_episodes?.setMaxViewPoolSize(0, Int.MAX_VALUE)
result_season_selection.setAdapter() result_season_selection.setAdapter()
@ -140,37 +140,37 @@ class ResultFragmentTv : ResultFragment() {
result_dub_selection.setAdapter() result_dub_selection.setAdapter()
result_recommendations_filter_selection.setAdapter() result_recommendations_filter_selection.setAdapter()
observe(viewModel.selectPopup) { popup -> observeNullable(viewModel.selectPopup) { popup ->
when (popup) { if(popup == null) {
is Some.Success -> { popupDialog?.dismissSafe(activity)
popupDialog?.dismissSafe(activity) popupDialog = null
return@observeNullable
}
popupDialog = activity?.let { act -> popupDialog?.dismissSafe(activity)
val pop = popup.value
val options = pop.getOptions(act)
val title = pop.getTitle(act)
act.showBottomDialogInstant( popupDialog = activity?.let { act ->
options, title, { val options = popup.getOptions(act)
popupDialog = null val title = popup.getTitle(act)
pop.callback(null)
}, { act.showBottomDialogInstant(
popupDialog = null options, title, {
pop.callback(it) popupDialog = null
} popup.callback(null)
) }, {
popupDialog = null
popup.callback(it)
} }
} )
is Some.None -> {
popupDialog?.dismissSafe(activity)
popupDialog = null
}
} }
} }
observe(viewModel.loadedLinks) { load -> observeNullable(viewModel.loadedLinks) { load ->
when (load) { if(load == null) {
is Some.Success -> { loadingDialog?.dismissSafe(activity)
loadingDialog = null
return@observeNullable
}
if (loadingDialog?.isShowing != true) { if (loadingDialog?.isShowing != true) {
loadingDialog?.dismissSafe(activity) loadingDialog?.dismissSafe(activity)
loadingDialog = null loadingDialog = null
@ -189,16 +189,11 @@ class ResultFragmentTv : ResultFragment() {
builder.show() builder.show()
builder builder
} }
}
is Some.None -> {
loadingDialog?.dismissSafe(activity)
loadingDialog = null
}
}
} }
observe(viewModel.episodesCountText) { count -> observeNullable(viewModel.episodesCountText) { count ->
result_episodes_text.setText(count) result_episodes_text.setText(count)
} }

View file

@ -145,15 +145,18 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
minute minute
) )
} }
hours > 0 -> txt( hours > 0 -> txt(
R.string.next_episode_time_hour_format, R.string.next_episode_time_hour_format,
hours, hours,
minute minute
) )
minute > 0 -> txt( minute > 0 -> txt(
R.string.next_episode_time_min_format, R.string.next_episode_time_min_format,
minute minute
) )
else -> null else -> null
}?.also { }?.also {
nextAiringEpisode = txt(R.string.next_episode_format, airing.episode) nextAiringEpisode = txt(R.string.next_episode_format, airing.episode)
@ -305,6 +308,7 @@ fun SelectPopup.getOptions(context: Context): List<String> {
is SelectPopup.SelectArray -> { is SelectPopup.SelectArray -> {
this.options.map { it.first.asString(context) } this.options.map { it.first.asString(context) }
} }
is SelectPopup.SelectText -> options.map { it.asString(context) } is SelectPopup.SelectText -> options.map { it.asString(context) }
} }
} }
@ -352,17 +356,17 @@ class ResultViewModel2 : ViewModel() {
MutableLiveData(null) MutableLiveData(null)
val page: LiveData<Resource<ResultData>?> = _page val page: LiveData<Resource<ResultData>?> = _page
private val _episodes: MutableLiveData<ResourceSome<List<ResultEpisode>>> = private val _episodes: MutableLiveData<Resource<List<ResultEpisode>>?> =
MutableLiveData(ResourceSome.Loading()) MutableLiveData(Resource.Loading())
val episodes: LiveData<ResourceSome<List<ResultEpisode>>> = _episodes val episodes: LiveData<Resource<List<ResultEpisode>>?> = _episodes
private val _movie: MutableLiveData<ResourceSome<Pair<UiText, ResultEpisode>>> = private val _movie: MutableLiveData<Resource<Pair<UiText, ResultEpisode>>?> =
MutableLiveData(ResourceSome.None) MutableLiveData(null)
val movie: LiveData<ResourceSome<Pair<UiText, ResultEpisode>>> = _movie val movie: LiveData<Resource<Pair<UiText, ResultEpisode>>?> = _movie
private val _episodesCountText: MutableLiveData<Some<UiText>> = private val _episodesCountText: MutableLiveData<UiText?> =
MutableLiveData(Some.None) MutableLiveData(null)
val episodesCountText: LiveData<Some<UiText>> = _episodesCountText val episodesCountText: LiveData<UiText?> = _episodesCountText
private val _trailers: MutableLiveData<List<ExtractedTrailerData>> = private val _trailers: MutableLiveData<List<ExtractedTrailerData>> =
MutableLiveData(mutableListOf()) MutableLiveData(mutableListOf())
@ -384,16 +388,16 @@ class ResultViewModel2 : ViewModel() {
MutableLiveData(emptyList()) MutableLiveData(emptyList())
val recommendations: LiveData<List<SearchResponse>> = _recommendations val recommendations: LiveData<List<SearchResponse>> = _recommendations
private val _selectedRange: MutableLiveData<Some<UiText>> = private val _selectedRange: MutableLiveData<UiText?> =
MutableLiveData(Some.None) MutableLiveData(null)
val selectedRange: LiveData<Some<UiText>> = _selectedRange val selectedRange: LiveData<UiText?> = _selectedRange
private val _selectedSeason: MutableLiveData<Some<UiText>> = private val _selectedSeason: MutableLiveData<UiText?> =
MutableLiveData(Some.None) MutableLiveData(null)
val selectedSeason: LiveData<Some<UiText>> = _selectedSeason val selectedSeason: LiveData<UiText?> = _selectedSeason
private val _selectedDubStatus: MutableLiveData<Some<UiText>> = MutableLiveData(Some.None) private val _selectedDubStatus: MutableLiveData<UiText?> = MutableLiveData(null)
val selectedDubStatus: LiveData<Some<UiText>> = _selectedDubStatus val selectedDubStatus: LiveData<UiText?> = _selectedDubStatus
private val _selectedRangeIndex: MutableLiveData<Int> = private val _selectedRangeIndex: MutableLiveData<Int> =
MutableLiveData(-1) MutableLiveData(-1)
@ -406,12 +410,12 @@ class ResultViewModel2 : ViewModel() {
private val _selectedDubStatusIndex: MutableLiveData<Int> = MutableLiveData(-1) private val _selectedDubStatusIndex: MutableLiveData<Int> = MutableLiveData(-1)
val selectedDubStatusIndex: LiveData<Int> = _selectedDubStatusIndex val selectedDubStatusIndex: LiveData<Int> = _selectedDubStatusIndex
private val _loadedLinks: MutableLiveData<Some<LinkProgress>> = MutableLiveData(Some.None) private val _loadedLinks: MutableLiveData<LinkProgress?> = MutableLiveData(null)
val loadedLinks: LiveData<Some<LinkProgress>> = _loadedLinks val loadedLinks: LiveData<LinkProgress?> = _loadedLinks
private val _resumeWatching: MutableLiveData<Some<ResumeWatchingStatus>> = private val _resumeWatching: MutableLiveData<ResumeWatchingStatus?> =
MutableLiveData(Some.None) MutableLiveData(null)
val resumeWatching: LiveData<Some<ResumeWatchingStatus>> = _resumeWatching val resumeWatching: LiveData<ResumeWatchingStatus?> = _resumeWatching
private val _episodeSynopsis: MutableLiveData<String?> = MutableLiveData(null) private val _episodeSynopsis: MutableLiveData<String?> = MutableLiveData(null)
val episodeSynopsis: LiveData<String?> = _episodeSynopsis val episodeSynopsis: LiveData<String?> = _episodeSynopsis
@ -800,8 +804,8 @@ class ResultViewModel2 : ViewModel() {
private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData(WatchType.NONE) private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData(WatchType.NONE)
val watchStatus: LiveData<WatchType> get() = _watchStatus val watchStatus: LiveData<WatchType> get() = _watchStatus
private val _selectPopup: MutableLiveData<Some<SelectPopup>> = MutableLiveData(Some.None) private val _selectPopup: MutableLiveData<SelectPopup?> = MutableLiveData(null)
val selectPopup: LiveData<Some<SelectPopup>> get() = _selectPopup val selectPopup: LiveData<SelectPopup?> = _selectPopup
fun updateWatchStatus(status: WatchType) { fun updateWatchStatus(status: WatchType) {
@ -885,23 +889,22 @@ class ResultViewModel2 : ViewModel() {
} }
fun cancelLinks() { fun cancelLinks() {
println("called::cancelLinks")
currentLoadLinkJob?.cancel() currentLoadLinkJob?.cancel()
currentLoadLinkJob = null currentLoadLinkJob = null
_loadedLinks.postValue(Some.None) _loadedLinks.postValue(null)
} }
private fun postPopup(text: UiText, options: List<UiText>, callback: suspend (Int?) -> Unit) { private fun postPopup(text: UiText, options: List<UiText>, callback: suspend (Int?) -> Unit) {
_selectPopup.postValue( _selectPopup.postValue(
some(SelectPopup.SelectText( SelectPopup.SelectText(
text, text,
options options
) { value -> ) { value ->
viewModelScope.launchSafe { viewModelScope.launchSafe {
_selectPopup.postValue(Some.None) _selectPopup.postValue(null)
callback.invoke(value) callback.invoke(value)
} }
}) }
) )
} }
@ -912,15 +915,15 @@ class ResultViewModel2 : ViewModel() {
callback: suspend (Int?) -> Unit callback: suspend (Int?) -> Unit
) { ) {
_selectPopup.postValue( _selectPopup.postValue(
some(SelectPopup.SelectArray( SelectPopup.SelectArray(
text, text,
options, options,
) { value -> ) { value ->
viewModelScope.launchSafe { viewModelScope.launchSafe {
_selectPopup.value = Some.None _selectPopup.postValue(null)
callback.invoke(value) callback.invoke(value)
} }
}) }
) )
} }
@ -988,7 +991,7 @@ class ResultViewModel2 : ViewModel() {
val subs: MutableSet<SubtitleData> = mutableSetOf() val subs: MutableSet<SubtitleData> = mutableSetOf()
fun updatePage() { fun updatePage() {
if (isVisible && isActive) { if (isVisible && isActive) {
_loadedLinks.postValue(some(LinkProgress(links.size, subs.size))) _loadedLinks.postValue(LinkProgress(links.size, subs.size))
} }
} }
try { try {
@ -1005,7 +1008,7 @@ class ResultViewModel2 : ViewModel() {
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
} finally { } finally {
_loadedLinks.postValue(Some.None) _loadedLinks.postValue(null)
} }
return LinkLoadingResult(sortUrls(links), sortSubs(subs)) return LinkLoadingResult(sortUrls(links), sortSubs(subs))
@ -1233,6 +1236,7 @@ class ResultViewModel2 : ViewModel() {
) )
} }
} }
ACTION_CLICK_DEFAULT -> { ACTION_CLICK_DEFAULT -> {
activity?.let { ctx -> activity?.let { ctx ->
if (ctx.isConnectedToChromecast()) { if (ctx.isConnectedToChromecast()) {
@ -1249,6 +1253,7 @@ class ResultViewModel2 : ViewModel() {
} }
} }
} }
ACTION_SHOW_DESCRIPTION -> { ACTION_SHOW_DESCRIPTION -> {
_episodeSynopsis.postValue(click.data.description) _episodeSynopsis.postValue(click.data.description)
} }
@ -1286,9 +1291,11 @@ class ResultViewModel2 : ViewModel() {
) )
} }
} }
ACTION_SHOW_TOAST -> { ACTION_SHOW_TOAST -> {
showToast(activity, R.string.play_episode_toast, Toast.LENGTH_SHORT) showToast(activity, R.string.play_episode_toast, Toast.LENGTH_SHORT)
} }
ACTION_DOWNLOAD_EPISODE -> { ACTION_DOWNLOAD_EPISODE -> {
val response = currentResponse ?: return val response = currentResponse ?: return
downloadEpisode( downloadEpisode(
@ -1303,6 +1310,7 @@ class ResultViewModel2 : ViewModel() {
response.url response.url
) )
} }
ACTION_DOWNLOAD_MIRROR -> { ACTION_DOWNLOAD_MIRROR -> {
val response = currentResponse ?: return val response = currentResponse ?: return
acquireSingleLink( acquireSingleLink(
@ -1332,6 +1340,7 @@ class ResultViewModel2 : ViewModel() {
) )
} }
} }
ACTION_RELOAD_EPISODE -> { ACTION_RELOAD_EPISODE -> {
ioSafe { ioSafe {
loadLinks( loadLinks(
@ -1342,6 +1351,7 @@ class ResultViewModel2 : ViewModel() {
) )
} }
} }
ACTION_CHROME_CAST_MIRROR -> { ACTION_CHROME_CAST_MIRROR -> {
acquireSingleLink( acquireSingleLink(
click.data, click.data,
@ -1351,6 +1361,7 @@ class ResultViewModel2 : ViewModel() {
startChromecast(activity, click.data, result.links, result.subs, index) startChromecast(activity, click.data, result.links, result.subs, index)
} }
} }
ACTION_PLAY_EPISODE_IN_BROWSER -> acquireSingleLink( ACTION_PLAY_EPISODE_IN_BROWSER -> acquireSingleLink(
click.data, click.data,
isCasting = true, isCasting = true,
@ -1364,6 +1375,7 @@ class ResultViewModel2 : ViewModel() {
logError(e) logError(e)
} }
} }
ACTION_COPY_LINK -> { ACTION_COPY_LINK -> {
acquireSingleLink( acquireSingleLink(
click.data, click.data,
@ -1380,9 +1392,11 @@ class ResultViewModel2 : ViewModel() {
showToast(act, R.string.copy_link_toast, Toast.LENGTH_SHORT) showToast(act, R.string.copy_link_toast, Toast.LENGTH_SHORT)
} }
} }
ACTION_CHROME_CAST_EPISODE -> { ACTION_CHROME_CAST_EPISODE -> {
startChromecast(activity, click.data) startChromecast(activity, click.data)
} }
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> { ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
loadLinks(click.data, isVisible = true, isCasting = true) { links -> loadLinks(click.data, isVisible = true, isCasting = true) { links ->
if (links.links.isEmpty()) { if (links.links.isEmpty()) {
@ -1397,6 +1411,7 @@ class ResultViewModel2 : ViewModel() {
) )
} }
} }
ACTION_PLAY_EPISODE_IN_WEB_VIDEO -> acquireSingleLink( ACTION_PLAY_EPISODE_IN_WEB_VIDEO -> acquireSingleLink(
click.data, click.data,
isCasting = true, isCasting = true,
@ -1413,6 +1428,7 @@ class ResultViewModel2 : ViewModel() {
result.subs result.subs
) )
} }
ACTION_PLAY_EPISODE_IN_MPV -> acquireSingleLink( ACTION_PLAY_EPISODE_IN_MPV -> acquireSingleLink(
click.data, click.data,
isCasting = true, isCasting = true,
@ -1428,6 +1444,7 @@ class ResultViewModel2 : ViewModel() {
result.subs result.subs
) )
} }
ACTION_PLAY_EPISODE_IN_PLAYER -> { ACTION_PLAY_EPISODE_IN_PLAYER -> {
val data = currentResponse?.syncData?.toList() ?: emptyList() val data = currentResponse?.syncData?.toList() ?: emptyList()
val list = val list =
@ -1448,6 +1465,7 @@ class ResultViewModel2 : ViewModel() {
) )
) )
} }
ACTION_MARK_AS_WATCHED -> { ACTION_MARK_AS_WATCHED -> {
val isWatched = val isWatched =
DataStoreHelper.getVideoWatchState(click.data.id) == VideoWatchState.Watched DataStoreHelper.getVideoWatchState(click.data.id) == VideoWatchState.Watched
@ -1672,10 +1690,10 @@ class ResultViewModel2 : ViewModel() {
private fun postMovie() { private fun postMovie() {
val response = currentResponse val response = currentResponse
_episodes.postValue(ResourceSome.None) _episodes.postValue(null)
if (response == null) { if (response == null) {
_movie.postValue(ResourceSome.None) _movie.postValue(null)
return return
} }
@ -1692,11 +1710,11 @@ class ResultViewModel2 : ViewModel() {
} }
) )
val data = getMovie() val data = getMovie()
_episodes.postValue(ResourceSome.None) _episodes.postValue(null)
if (text == null || data == null) { if (text == null || data == null) {
_movie.postValue(ResourceSome.None) _movie.postValue(null)
} else { } else {
_movie.postValue(ResourceSome.Success(text to data)) _movie.postValue(Resource.Success(text to data))
} }
} }
@ -1705,14 +1723,14 @@ class ResultViewModel2 : ViewModel() {
postMovie() postMovie()
} else { } else {
_episodes.postValue( _episodes.postValue(
ResourceSome.Success( Resource.Success(
getEpisodes( getEpisodes(
currentIndex ?: return, currentIndex ?: return,
currentRange ?: return currentRange ?: return
) )
) )
) )
_movie.postValue(ResourceSome.None) _movie.postValue(null)
} }
postResume() postResume()
} }
@ -1755,14 +1773,14 @@ class ResultViewModel2 : ViewModel() {
val size = currentEpisodes[indexer]?.size val size = currentEpisodes[indexer]?.size
_episodesCountText.postValue( _episodesCountText.postValue(
some(
if (isMovie) null else if (isMovie) null else
txt( txt(
R.string.episode_format, R.string.episode_format,
size, size,
txt(if (size == 1) R.string.episode else R.string.episodes), txt(if (size == 1) R.string.episode else R.string.episodes),
) )
)
) )
_selectedSeasonIndex.postValue( _selectedSeasonIndex.postValue(
@ -1770,29 +1788,29 @@ class ResultViewModel2 : ViewModel() {
) )
_selectedSeason.postValue( _selectedSeason.postValue(
some(
if (isMovie || currentSeasons.size <= 1) null else
when (indexer.season) {
0 -> txt(R.string.no_season)
else -> {
val seasonNames = (currentResponse as? EpisodeResponse)?.seasonNames
val seasonData = seasonNames.getSeason(indexer.season)
// If displaySeason is null then only show the name! if (isMovie || currentSeasons.size <= 1) null else
if (seasonData?.name != null && seasonData.displaySeason == null) { when (indexer.season) {
txt(seasonData.name) 0 -> txt(R.string.no_season)
} else { else -> {
val suffix = seasonData?.name?.let { " $it" } ?: "" val seasonNames = (currentResponse as? EpisodeResponse)?.seasonNames
txt( val seasonData = seasonNames.getSeason(indexer.season)
R.string.season_format,
txt(R.string.season), // If displaySeason is null then only show the name!
seasonData?.displaySeason ?: indexer.season, if (seasonData?.name != null && seasonData.displaySeason == null) {
suffix txt(seasonData.name)
) } else {
} val suffix = seasonData?.name?.let { " $it" } ?: ""
txt(
R.string.season_format,
txt(R.string.season),
seasonData?.displaySeason ?: indexer.season,
suffix
)
} }
} }
) }
) )
_selectedRangeIndex.postValue( _selectedRangeIndex.postValue(
@ -1800,13 +1818,13 @@ class ResultViewModel2 : ViewModel() {
) )
_selectedRange.postValue( _selectedRange.postValue(
some(
if (isMovie) null else if ((currentRanges[indexer]?.size ?: 0) > 1) { if (isMovie) null else if ((currentRanges[indexer]?.size ?: 0) > 1) {
txt(R.string.episodes_range, range.startEpisode, range.endEpisode) txt(R.string.episodes_range, range.startEpisode, range.endEpisode)
} else { } else {
null null
} }
)
) )
_selectedDubStatusIndex.postValue( _selectedDubStatusIndex.postValue(
@ -1814,10 +1832,10 @@ class ResultViewModel2 : ViewModel() {
) )
_selectedDubStatus.postValue( _selectedDubStatus.postValue(
some(
if (isMovie || currentDubStatus.size <= 1) null else if (isMovie || currentDubStatus.size <= 1) null else
txt(indexer.dubStatus) txt(indexer.dubStatus)
)
) )
currentId?.let { id -> currentId?.let { id ->
@ -1851,7 +1869,7 @@ class ResultViewModel2 : ViewModel() {
} }
}*/ }*/
_episodes.postValue(ResourceSome.Success(ret)) _episodes.postValue(Resource.Success(ret))
} }
} }
@ -1869,7 +1887,7 @@ class ResultViewModel2 : ViewModel() {
} }
private suspend fun postEpisodes(loadResponse: LoadResponse, updateFillers: Boolean) { private suspend fun postEpisodes(loadResponse: LoadResponse, updateFillers: Boolean) {
_episodes.postValue(ResourceSome.Loading()) _episodes.postValue(Resource.Loading())
val mainId = loadResponse.getId() val mainId = loadResponse.getId()
currentId = mainId currentId = mainId
@ -1924,6 +1942,7 @@ class ResultViewModel2 : ViewModel() {
} }
episodes episodes
} }
is TvSeriesLoadResponse -> { is TvSeriesLoadResponse -> {
val episodes: MutableMap<EpisodeIndexer, MutableList<ResultEpisode>> = val episodes: MutableMap<EpisodeIndexer, MutableList<ResultEpisode>> =
mutableMapOf() mutableMapOf()
@ -1968,6 +1987,7 @@ class ResultViewModel2 : ViewModel() {
} }
episodes episodes
} }
is MovieLoadResponse -> { is MovieLoadResponse -> {
singleMap( singleMap(
buildResultEpisode( buildResultEpisode(
@ -1989,6 +2009,7 @@ class ResultViewModel2 : ViewModel() {
) )
) )
} }
is LiveStreamLoadResponse -> { is LiveStreamLoadResponse -> {
singleMap( singleMap(
buildResultEpisode( buildResultEpisode(
@ -2010,6 +2031,7 @@ class ResultViewModel2 : ViewModel() {
) )
) )
} }
is TorrentLoadResponse -> { is TorrentLoadResponse -> {
singleMap( singleMap(
buildResultEpisode( buildResultEpisode(
@ -2031,6 +2053,7 @@ class ResultViewModel2 : ViewModel() {
) )
) )
} }
else -> { else -> {
mapOf() mapOf()
} }
@ -2088,7 +2111,7 @@ class ResultViewModel2 : ViewModel() {
} }
fun postResume() { fun postResume() {
_resumeWatching.postValue(some(resume())) _resumeWatching.postValue(resume())
} }
private fun resume(): ResumeWatchingStatus? { private fun resume(): ResumeWatchingStatus? {
@ -2196,6 +2219,7 @@ class ResultViewModel2 : ViewModel() {
} }
} }
} }
START_ACTION_LOAD_EP -> { START_ACTION_LOAD_EP -> {
val all = currentEpisodes.values.flatten() val all = currentEpisodes.values.flatten()
val episode = val episode =
@ -2227,7 +2251,7 @@ class ResultViewModel2 : ViewModel() {
) = ) =
ioSafe { ioSafe {
_page.postValue(Resource.Loading(url)) _page.postValue(Resource.Loading(url))
_episodes.postValue(ResourceSome.Loading()) _episodes.postValue(Resource.Loading())
preferDubStatus = dubStatus preferDubStatus = dubStatus
currentShowFillers = showFillers currentShowFillers = showFillers
@ -2271,6 +2295,7 @@ class ResultViewModel2 : ViewModel() {
is Resource.Failure -> { is Resource.Failure -> {
_page.postValue(data) _page.postValue(data)
} }
is Resource.Success -> { is Resource.Success -> {
if (!isActive) return@ioSafe if (!isActive) return@ioSafe
val loadResponse = ioWork { val loadResponse = ioWork {
@ -2307,6 +2332,7 @@ class ResultViewModel2 : ViewModel() {
if (!isActive) return@ioSafe if (!isActive) return@ioSafe
handleAutoStart(activity, autostart) handleAutoStart(activity, autostart)
} }
is Resource.Loading -> { is Resource.Loading -> {
debugException { "Invalid load result" } debugException { "Invalid load result" }
} }

View file

@ -1,12 +1,11 @@
package com.lagradost.cloudstream3.ui.result package com.lagradost.cloudstream3.ui.result
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.ResultSelectionBinding
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
typealias SelectData = Pair<UiText?, Any> typealias SelectData = Pair<UiText?, Any>
@ -17,7 +16,9 @@ class SelectAdaptor(val callback: (Any) -> Unit) : RecyclerView.Adapter<Recycler
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return SelectViewHolder( return SelectViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.result_selection, parent, false), ResultSelectionBinding.inflate(LayoutInflater.from(parent.context), parent, false),
//LayoutInflater.from(parent.context).inflate(R.layout.result_selection, parent, false),
) )
} }
@ -73,10 +74,10 @@ class SelectAdaptor(val callback: (Any) -> Unit) : RecyclerView.Adapter<Recycler
private class SelectViewHolder private class SelectViewHolder
constructor( constructor(
itemView: View, binding: ResultSelectionBinding,
) : ) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(binding.root) {
private val item: MaterialButton = itemView as MaterialButton private val item: MaterialButton = binding.root
fun update(isSelected: Boolean) { fun update(isSelected: Boolean) {
item.isSelected = isSelected item.isSelected = isSelected

View file

@ -8,7 +8,6 @@ import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.lagradost.cloudstream3.mvvm.Some
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
@ -162,11 +161,3 @@ fun TextView?.setTextHtml(text: UiText?) {
this.text = str.html() this.text = str.html()
} }
} }
fun TextView?.setTextHtml(text: Some<UiText>?) {
setTextHtml(if (text is Some.Success) text.value else null)
}
fun TextView?.setText(text: Some<UiText>?) {
setText(if (text is Some.Success) text.value else null)
}

View file

@ -78,7 +78,7 @@ class SearchAdapter(
resView: AutofitRecyclerView resView: AutofitRecyclerView
) : ) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(itemView) {
val cardView: ImageView = itemView.imageView private val cardView: ImageView = itemView.imageView
private val compactView = false//itemView.context.getGridIsCompact() private val compactView = false//itemView.context.getGridIsCompact()
private val coverHeight: Int = private val coverHeight: Int =

View file

@ -33,6 +33,8 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.databinding.FragmentSearchBinding
import com.lagradost.cloudstream3.databinding.HomeSelectMainpageBinding
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
@ -56,8 +58,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import kotlinx.android.synthetic.main.fragment_search.*
import kotlinx.android.synthetic.main.tvtypes_chips.*
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
const val SEARCH_PREF_TAGS = "search_pref_tags" const val SEARCH_PREF_TAGS = "search_pref_tags"
@ -89,6 +89,7 @@ class SearchFragment : Fragment() {
private val searchViewModel: SearchViewModel by activityViewModels() private val searchViewModel: SearchViewModel by activityViewModels()
private var bottomSheetDialog: BottomSheetDialog? = null private var bottomSheetDialog: BottomSheetDialog? = null
var binding: FragmentSearchBinding? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -99,18 +100,20 @@ class SearchFragment : Fragment() {
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
) )
bottomSheetDialog?.ownShow() bottomSheetDialog?.ownShow()
return inflater.inflate(
if (isTvSettings()) R.layout.fragment_search_tv else R.layout.fragment_search, val layout = if (isTvSettings()) R.layout.fragment_search_tv else R.layout.fragment_search
container,
false val root = inflater.inflate(layout, container, false)
) binding = FragmentSearchBinding.bind(root)
return root
} }
private fun fixGrid() { private fun fixGrid() {
activity?.getSpanCount()?.let { activity?.getSpanCount()?.let {
currentSpan = it currentSpan = it
} }
search_autofit_results.spanCount = currentSpan binding?.searchAutofitResults?.spanCount = currentSpan
currentSpan = currentSpan currentSpan = currentSpan
HomeFragment.configEvent.invoke(currentSpan) HomeFragment.configEvent.invoke(currentSpan)
} }
@ -123,6 +126,7 @@ class SearchFragment : Fragment() {
override fun onDestroyView() { override fun onDestroyView() {
hideKeyboard() hideKeyboard()
bottomSheetDialog?.ownHide() bottomSheetDialog?.ownHide()
binding = null
super.onDestroyView() super.onDestroyView()
} }
@ -181,7 +185,7 @@ class SearchFragment : Fragment() {
searchViewModel.reloadRepos() searchViewModel.reloadRepos()
context?.filterProviderByPreferredMedia()?.let { validAPIs -> context?.filterProviderByPreferredMedia()?.let { validAPIs ->
bindChips( bindChips(
home_select_group, binding?.tvtypesChipsScroll?.tvtypesChips,
selectedSearchTypes, selectedSearchTypes,
validAPIs.flatMap { api -> api.supportedTypes }.distinct() validAPIs.flatMap { api -> api.supportedTypes }.distinct()
) { list -> ) { list ->
@ -189,7 +193,7 @@ class SearchFragment : Fragment() {
setKey(SEARCH_PREF_TAGS, selectedSearchTypes) setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
selectedSearchTypes.clear() selectedSearchTypes.clear()
selectedSearchTypes.addAll(list) selectedSearchTypes.addAll(list)
search(main_search?.query?.toString()) search(binding?.mainSearch?.query?.toString())
} }
} }
} }
@ -199,24 +203,27 @@ class SearchFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(searchRoot) fixPaddingStatusbar(binding?.searchRoot)
fixGrid() fixGrid()
reloadRepos() reloadRepos()
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = activity?.let { binding?.apply {
SearchAdapter( val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? =
ArrayList(), SearchAdapter(
search_autofit_results, ArrayList(),
) { callback -> searchAutofitResults,
SearchHelper.handleSearchClickCallback(activity, callback) ) { callback ->
} SearchHelper.handleSearchClickCallback(activity, callback)
}
searchAutofitResults.adapter = adapter
searchLoadingBar.alpha = 0f
} }
search_autofit_results.adapter = adapter
search_loading_bar.alpha = 0f
val searchExitIcon = val searchExitIcon =
main_search.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn) binding?.mainSearch?.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
// val searchMagIcon = // val searchMagIcon =
// main_search.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon) // main_search.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon)
//searchMagIcon.scaleX = 0.65f //searchMagIcon.scaleX = 0.65f
@ -230,7 +237,7 @@ class SearchFragment : Fragment() {
)!!.toMutableSet() )!!.toMutableSet()
} }
search_filter.setOnClickListener { searchView -> binding?.searchFilter?.setOnClickListener { searchView ->
searchView?.context?.let { ctx -> searchView?.context?.let { ctx ->
val validAPIs = ctx.filterProviderByPreferredMedia(hasHomePageIsRequired = false) val validAPIs = ctx.filterProviderByPreferredMedia(hasHomePageIsRequired = false)
var currentValidApis = listOf<MainAPI>() var currentValidApis = listOf<MainAPI>()
@ -241,7 +248,13 @@ class SearchFragment : Fragment() {
BottomSheetDialog(ctx) BottomSheetDialog(ctx)
builder.behavior.state = BottomSheetBehavior.STATE_EXPANDED builder.behavior.state = BottomSheetBehavior.STATE_EXPANDED
builder.setContentView(R.layout.home_select_mainpage)
val binding: HomeSelectMainpageBinding = HomeSelectMainpageBinding.inflate(
builder.layoutInflater,
null,
false
)
builder.setContentView(binding.root)
builder.show() builder.show()
builder.let { dialog -> builder.let { dialog ->
val isMultiLang = ctx.getApiProviderLangSettings().let { set -> val isMultiLang = ctx.getApiProviderLangSettings().let { set ->
@ -303,7 +316,7 @@ class SearchFragment : Fragment() {
?: mutableListOf(TvType.Movie, TvType.TvSeries) ?: mutableListOf(TvType.Movie, TvType.TvSeries)
bindChips( bindChips(
dialog.home_select_group, binding.tvtypesChipsScroll.tvtypesChips,
selectedSearchTypes, selectedSearchTypes,
TvType.values().toList() TvType.values().toList()
) { list -> ) { list ->
@ -343,15 +356,15 @@ class SearchFragment : Fragment() {
?: mutableListOf(TvType.Movie, TvType.TvSeries) ?: mutableListOf(TvType.Movie, TvType.TvSeries)
if (isTrueTvSettings()) { if (isTrueTvSettings()) {
search_filter.isFocusable = true binding?.searchFilter?.isFocusable = true
search_filter.isFocusableInTouchMode = true binding?.searchFilter?.isFocusableInTouchMode = true
} }
main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener { binding?.mainSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
search(query) search(query)
main_search?.let { binding?.mainSearch?.let {
hideKeyboard(it) hideKeyboard(it)
} }
@ -365,17 +378,17 @@ class SearchFragment : Fragment() {
searchViewModel.clearSearch() searchViewModel.clearSearch()
searchViewModel.updateHistory() searchViewModel.updateHistory()
} }
binding?.apply {
search_history_holder?.isVisible = showHistory searchHistoryHolder.isVisible = showHistory
searchMasterRecycler.isVisible = !showHistory && isAdvancedSearch
search_master_recycler?.isVisible = !showHistory && isAdvancedSearch searchAutofitResults.isVisible = !showHistory && !isAdvancedSearch
search_autofit_results?.isVisible = !showHistory && !isAdvancedSearch }
return true return true
} }
}) })
search_clear_call_history?.setOnClickListener { binding?.searchClearCallHistory?.setOnClickListener {
activity?.let { ctx -> activity?.let { ctx ->
val builder: AlertDialog.Builder = AlertDialog.Builder(ctx) val builder: AlertDialog.Builder = AlertDialog.Builder(ctx)
val dialogClickListener = val dialogClickListener =
@ -409,8 +422,8 @@ class SearchFragment : Fragment() {
} }
observe(searchViewModel.currentHistory) { list -> observe(searchViewModel.currentHistory) { list ->
search_clear_call_history?.isVisible = list.isNotEmpty() binding?.searchClearCallHistory?.isVisible = list.isNotEmpty()
(search_history_recycler.adapter as? SearchHistoryAdaptor?)?.updateList(list) (binding?.searchHistoryRecycler?.adapter as? SearchHistoryAdaptor?)?.updateList(list)
} }
searchViewModel.updateHistory() searchViewModel.updateHistory()
@ -420,20 +433,20 @@ class SearchFragment : Fragment() {
is Resource.Success -> { is Resource.Success -> {
it.value.let { data -> it.value.let { data ->
if (data.isNotEmpty()) { if (data.isNotEmpty()) {
(search_autofit_results?.adapter as? SearchAdapter)?.updateList(data) (binding?.searchAutofitResults?.adapter as? SearchAdapter)?.updateList(data)
} }
} }
searchExitIcon.alpha = 1f searchExitIcon?.alpha = 1f
search_loading_bar.alpha = 0f binding?.searchLoadingBar?.alpha = 0f
} }
is Resource.Failure -> { is Resource.Failure -> {
// Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show() // Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
searchExitIcon.alpha = 1f searchExitIcon?.alpha = 1f
search_loading_bar.alpha = 0f binding?.searchLoadingBar?.alpha = 0f
} }
is Resource.Loading -> { is Resource.Loading -> {
searchExitIcon.alpha = 0f searchExitIcon?.alpha = 0f
search_loading_bar.alpha = 1f binding?.searchLoadingBar?.alpha = 1f
} }
} }
} }
@ -443,7 +456,7 @@ class SearchFragment : Fragment() {
try { try {
// https://stackoverflow.com/questions/6866238/concurrent-modification-exception-adding-to-an-arraylist // https://stackoverflow.com/questions/6866238/concurrent-modification-exception-adding-to-an-arraylist
listLock.lock() listLock.lock()
(search_master_recycler?.adapter as ParentItemAdapter?)?.apply { (binding?.searchMasterRecycler?.adapter as ParentItemAdapter?)?.apply {
val newItems = list.map { ongoing -> val newItems = list.map { ongoing ->
val dataList = val dataList =
if (ongoing.data is Resource.Success) ongoing.data.value else ArrayList() if (ongoing.data is Resource.Success) ongoing.data.value else ArrayList()
@ -490,8 +503,8 @@ class SearchFragment : Fragment() {
SEARCH_HISTORY_OPEN -> { SEARCH_HISTORY_OPEN -> {
searchViewModel.clearSearch() searchViewModel.clearSearch()
if (searchItem.type.isNotEmpty()) if (searchItem.type.isNotEmpty())
updateChips(home_select_group, searchItem.type.toMutableList()) updateChips(binding?.tvtypesChipsScroll?.tvtypesChips, searchItem.type.toMutableList())
main_search?.setQuery(searchItem.searchText, true) binding?.mainSearch?.setQuery(searchItem.searchText, true)
} }
SEARCH_HISTORY_REMOVE -> { SEARCH_HISTORY_REMOVE -> {
removeKey(SEARCH_HISTORY_KEY, searchItem.key) removeKey(SEARCH_HISTORY_KEY, searchItem.key)
@ -503,20 +516,23 @@ class SearchFragment : Fragment() {
} }
} }
search_history_recycler?.adapter = historyAdapter binding?.apply {
search_history_recycler?.layoutManager = GridLayoutManager(context, 1) searchHistoryRecycler.adapter = historyAdapter
searchHistoryRecycler.layoutManager = GridLayoutManager(context, 1)
search_master_recycler?.adapter = masterAdapter searchMasterRecycler.adapter = masterAdapter
search_master_recycler?.layoutManager = GridLayoutManager(context, 1) searchMasterRecycler.layoutManager = GridLayoutManager(context, 1)
// Automatically search the specified query, this allows the app search to launch from intent // Automatically search the specified query, this allows the app search to launch from intent
arguments?.getString(SEARCH_QUERY)?.let { query -> arguments?.getString(SEARCH_QUERY)?.let { query ->
if (query.isBlank()) return@let if (query.isBlank()) return@let
main_search?.setQuery(query, true) mainSearch.setQuery(query, true)
// Clear the query as to not make it request the same query every time the page is opened // Clear the query as to not make it request the same query every time the page is opened
arguments?.putString(SEARCH_QUERY, null) arguments?.putString(SEARCH_QUERY, null)
}
} }
// SubtitlesFragment.push(activity) // SubtitlesFragment.push(activity)
//searchViewModel.search("iron man") //searchViewModel.search("iron man")
//(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") //(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")

View file

@ -10,7 +10,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.TvType
import kotlinx.android.synthetic.main.search_history_item.view.* import com.lagradost.cloudstream3.databinding.AccountSingleBinding
import com.lagradost.cloudstream3.databinding.SearchHistoryItemBinding
data class SearchHistoryItem( data class SearchHistoryItem(
@JsonProperty("searchedAt") val searchedAt: Long, @JsonProperty("searchedAt") val searchedAt: Long,
@ -34,8 +35,7 @@ class SearchHistoryAdaptor(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return CardViewHolder( return CardViewHolder(
LayoutInflater.from(parent.context) SearchHistoryItemBinding.inflate(LayoutInflater.from(parent.context), parent, false),
.inflate(R.layout.search_history_item, parent, false),
clickCallback, clickCallback,
) )
} }
@ -65,22 +65,24 @@ class SearchHistoryAdaptor(
class CardViewHolder class CardViewHolder
constructor( constructor(
itemView: View, val binding: SearchHistoryItemBinding,
private val clickCallback: (SearchHistoryCallback) -> Unit, private val clickCallback: (SearchHistoryCallback) -> Unit,
) : ) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(binding.root) {
private val removeButton: ImageView = itemView.home_history_remove // private val removeButton: ImageView = itemView.home_history_remove
private val openButton: View = itemView.home_history_tab // private val openButton: View = itemView.home_history_tab
private val title: TextView = itemView.home_history_title // private val title: TextView = itemView.home_history_title
fun bind(card: SearchHistoryItem) { fun bind(card: SearchHistoryItem) {
title.text = card.searchText binding.apply {
homeHistoryTitle.text = card.searchText
removeButton.setOnClickListener { homeHistoryRemove.setOnClickListener {
clickCallback.invoke(SearchHistoryCallback(card, SEARCH_HISTORY_REMOVE)) clickCallback.invoke(SearchHistoryCallback(card, SEARCH_HISTORY_REMOVE))
} }
openButton.setOnClickListener { homeHistoryTab.setOnClickListener {
clickCallback.invoke(SearchHistoryCallback(card, SEARCH_HISTORY_OPEN)) clickCallback.invoke(SearchHistoryCallback(card, SEARCH_HISTORY_OPEN))
}
} }
} }
} }

View file

@ -1,7 +1,6 @@
package com.lagradost.cloudstream3.ui.search package com.lagradost.cloudstream3.ui.search
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.ProgressBar import android.widget.ProgressBar
@ -10,14 +9,29 @@ import androidx.cardview.widget.CardView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.palette.graphics.Palette import androidx.palette.graphics.Palette
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.AnimeSearchResponse
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.LiveSearchResponse
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchQuality
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.isMovieType
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
import kotlinx.android.synthetic.main.home_result_grid.view.* import kotlinx.android.synthetic.main.home_result_grid.view.background_card
import kotlinx.android.synthetic.main.home_result_grid.view.imageText
import kotlinx.android.synthetic.main.home_result_grid.view.imageView
import kotlinx.android.synthetic.main.home_result_grid.view.search_item_download_play
import kotlinx.android.synthetic.main.home_result_grid.view.text_flag
import kotlinx.android.synthetic.main.home_result_grid.view.text_is_dub
import kotlinx.android.synthetic.main.home_result_grid.view.text_is_sub
import kotlinx.android.synthetic.main.home_result_grid.view.text_quality
import kotlinx.android.synthetic.main.home_result_grid.view.title_shadow
import kotlinx.android.synthetic.main.home_result_grid.view.watchProgress
object SearchResultBuilder { object SearchResultBuilder {
private val showCache: MutableMap<String, Boolean> = mutableMapOf() private val showCache: MutableMap<String, Boolean> = mutableMapOf()

View file

@ -3,11 +3,10 @@ package com.lagradost.cloudstream3.ui.settings
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.AccountSingleBinding
import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
@ -15,14 +14,15 @@ class AccountClickCallback(val action: Int, val view: View, val card: AuthAPI.Lo
class AccountAdapter( class AccountAdapter(
val cardList: List<AuthAPI.LoginInfo>, val cardList: List<AuthAPI.LoginInfo>,
val layout: Int = R.layout.account_single,
private val clickCallback: (AccountClickCallback) -> Unit private val clickCallback: (AccountClickCallback) -> Unit
) : ) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return CardViewHolder( return CardViewHolder(
LayoutInflater.from(parent.context).inflate(layout, parent, false), clickCallback AccountSingleBinding.inflate(LayoutInflater.from(parent.context), parent, false), //LayoutInflater.from(parent.context).inflate(layout, parent, false),
clickCallback
) )
} }
@ -43,18 +43,18 @@ class AccountAdapter(
} }
class CardViewHolder class CardViewHolder
constructor(itemView: View, private val clickCallback: (AccountClickCallback) -> Unit) : constructor(val binding: AccountSingleBinding, private val clickCallback: (AccountClickCallback) -> Unit) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(binding.root) {
private val pfp: ImageView = itemView.findViewById(R.id.account_profile_picture)!! // private val pfp: ImageView = itemView.findViewById(R.id.account_profile_picture)!!
private val accountName: TextView = itemView.findViewById(R.id.account_name)!! // private val accountName: TextView = itemView.findViewById(R.id.account_name)!!
fun bind(card: AuthAPI.LoginInfo) { fun bind(card: AuthAPI.LoginInfo) {
// just in case name is null account index will show, should never happened // just in case name is null account index will show, should never happened
accountName.text = card.name ?: "%s %d".format( binding.accountName.text = card.name ?: "%s %d".format(
accountName.context.getString(R.string.account), binding.accountName.context.getString(R.string.account),
card.accountIndex card.accountIndex
) )
pfp.isVisible = pfp.setImage(card.profilePicture) binding.accountProfilePicture.isVisible = binding.accountProfilePicture.setImage(card.profilePicture)
itemView.setOnClickListener { itemView.setOnClickListener {
clickCallback.invoke(AccountClickCallback(0, itemView, card)) clickCallback.invoke(AccountClickCallback(0, itemView, card))

View file

@ -96,7 +96,7 @@ class SettingsAccount : PreferenceFragmentCompat() {
} }
} }
api.accountIndex = ogIndex api.accountIndex = ogIndex
val adapter = AccountAdapter(items, R.layout.account_single) { val adapter = AccountAdapter(items) {
dialog?.dismissSafe(activity) dialog?.dismissSafe(activity)
api.changeAccount(it.card.accountIndex) api.changeAccount(it.card.accountIndex)
} }

View file

@ -62,7 +62,7 @@ class SettingsFragment : Fragment() {
activity?.onBackPressed() activity?.onBackPressed()
} }
} }
context.fixPaddingStatusbar(settings_toolbar) fixPaddingStatusbar(settings_toolbar)
} }
fun Fragment?.setUpToolbar(@StringRes title: Int) { fun Fragment?.setUpToolbar(@StringRes title: Int) {
@ -74,7 +74,7 @@ class SettingsFragment : Fragment() {
activity?.onBackPressed() activity?.onBackPressed()
} }
} }
context.fixPaddingStatusbar(settings_toolbar) fixPaddingStatusbar(settings_toolbar)
} }
fun getFolderSize(dir: File): Long { fun getFolderSize(dir: File): Long {

View file

@ -18,8 +18,8 @@ import androidx.navigation.fragment.findNavController
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.Some
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.plugins.RepositoryManager import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.setText
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
@ -97,6 +97,7 @@ class ExtensionsFragment : Fragment() {
extensionViewModel.loadRepositories() extensionViewModel.loadRepositories()
} }
} }
DialogInterface.BUTTON_NEGATIVE -> {} DialogInterface.BUTTON_NEGATIVE -> {}
} }
} }
@ -138,29 +139,26 @@ class ExtensionsFragment : Fragment() {
// } // }
// } // }
observe(extensionViewModel.pluginStats) { observeNullable(extensionViewModel.pluginStats) { value ->
when (it) { if (value == null) {
is Some.Success -> { plugin_storage_appbar?.isVisible = false
val value = it.value
plugin_storage_appbar?.isVisible = true return@observeNullable
if (value.total == 0) {
plugin_download?.setLayoutWidth(1)
plugin_disabled?.setLayoutWidth(0)
plugin_not_downloaded?.setLayoutWidth(0)
} else {
plugin_download?.setLayoutWidth(value.downloaded)
plugin_disabled?.setLayoutWidth(value.disabled)
plugin_not_downloaded?.setLayoutWidth(value.notDownloaded)
}
plugin_not_downloaded_txt.setText(value.notDownloadedText)
plugin_disabled_txt.setText(value.disabledText)
plugin_download_txt.setText(value.downloadedText)
}
is Some.None -> {
plugin_storage_appbar?.isVisible = false
}
} }
plugin_storage_appbar?.isVisible = true
if (value.total == 0) {
plugin_download?.setLayoutWidth(1)
plugin_disabled?.setLayoutWidth(0)
plugin_not_downloaded?.setLayoutWidth(0)
} else {
plugin_download?.setLayoutWidth(value.downloaded)
plugin_disabled?.setLayoutWidth(value.disabled)
plugin_not_downloaded?.setLayoutWidth(value.notDownloaded)
}
plugin_not_downloaded_txt.setText(value.notDownloadedText)
plugin_disabled_txt.setText(value.disabledText)
plugin_download_txt.setText(value.downloadedText)
} }
plugin_storage_appbar?.setOnClickListener { plugin_storage_appbar?.setOnClickListener {

View file

@ -7,7 +7,6 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.amap
import com.lagradost.cloudstream3.mvvm.Some
import com.lagradost.cloudstream3.mvvm.debugAssert import com.lagradost.cloudstream3.mvvm.debugAssert
import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.plugins.PluginManager.getPluginsOnline import com.lagradost.cloudstream3.plugins.PluginManager.getPluginsOnline
@ -40,8 +39,8 @@ class ExtensionsViewModel : ViewModel() {
private val _repositories = MutableLiveData<Array<RepositoryData>>() private val _repositories = MutableLiveData<Array<RepositoryData>>()
val repositories: LiveData<Array<RepositoryData>> = _repositories val repositories: LiveData<Array<RepositoryData>> = _repositories
private val _pluginStats: MutableLiveData<Some<PluginStats>> = MutableLiveData(Some.None) private val _pluginStats: MutableLiveData<PluginStats?> = MutableLiveData(null)
val pluginStats: LiveData<Some<PluginStats>> = _pluginStats val pluginStats: LiveData<PluginStats?> = _pluginStats
//TODO CACHE GET REQUESTS //TODO CACHE GET REQUESTS
// DO not use viewModelScope.launchSafe, it will ANR on slow internet // DO not use viewModelScope.launchSafe, it will ANR on slow internet
@ -78,7 +77,7 @@ class ExtensionsViewModel : ViewModel() {
debugAssert({ stats.downloaded + stats.notDownloaded + stats.disabled != stats.total }) { debugAssert({ stats.downloaded + stats.notDownloaded + stats.disabled != stats.total }) {
"downloaded(${stats.downloaded}) + notDownloaded(${stats.notDownloaded}) + disabled(${stats.disabled}) != total(${stats.total})" "downloaded(${stats.downloaded}) + notDownloaded(${stats.notDownloaded}) + disabled(${stats.disabled}) != total(${stats.total})"
} }
_pluginStats.postValue(Some.Success(stats)) _pluginStats.postValue(stats)
} }
private fun repos() = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY) private fun repos() = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)

View file

@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.AllLanguagesName
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.databinding.FragmentPluginsBinding
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
@ -20,9 +21,6 @@ import com.lagradost.cloudstream3.ui.settings.appLanguages
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.android.synthetic.main.fragment_plugins.*
import kotlinx.android.synthetic.main.tvtypes_chips.*
import kotlinx.android.synthetic.main.tvtypes_chips_scroll.*
const val PLUGINS_BUNDLE_NAME = "name" const val PLUGINS_BUNDLE_NAME = "name"
const val PLUGINS_BUNDLE_URL = "url" const val PLUGINS_BUNDLE_URL = "url"
@ -33,11 +31,19 @@ class PluginsFragment : Fragment() {
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle?, savedInstanceState: Bundle?,
): View? { ): View {
return inflater.inflate(R.layout.fragment_plugins, container, false) val localBinding = FragmentPluginsBinding.inflate(inflater,container,false)
binding = localBinding
return localBinding.root//inflater.inflate(R.layout.fragment_plugins, container, false)
}
override fun onDestroyView() {
binding = null
super.onDestroyView()
} }
private val pluginViewModel: PluginsViewModel by activityViewModels() private val pluginViewModel: PluginsViewModel by activityViewModels()
var binding: FragmentPluginsBinding? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -66,8 +72,8 @@ class PluginsFragment : Fragment() {
} }
setUpToolbar(name) setUpToolbar(name)
binding?.settingsToolbar?.apply {
settings_toolbar?.setOnMenuItemClickListener { menuItem -> setOnMenuItemClickListener { menuItem ->
when (menuItem?.itemId) { when (menuItem?.itemId) {
R.id.download_all -> { R.id.download_all -> {
PluginsViewModel.downloadAll(activity, url, pluginViewModel) PluginsViewModel.downloadAll(activity, url, pluginViewModel)
@ -99,67 +105,69 @@ class PluginsFragment : Fragment() {
} }
val searchView = val searchView =
settings_toolbar?.menu?.findItem(R.id.search_button)?.actionView as? SearchView menu?.findItem(R.id.search_button)?.actionView as? SearchView
// Don't go back if active query // Don't go back if active query
settings_toolbar?.setNavigationOnClickListener { setNavigationOnClickListener {
if (searchView?.isIconified == false) { if (searchView?.isIconified == false) {
searchView.isIconified = true searchView.isIconified = true
} else { } else {
activity?.onBackPressed() activity?.onBackPressed()
} }
} }
searchView?.setOnQueryTextFocusChangeListener { _, hasFocus ->
if (!hasFocus) pluginViewModel.search(null)
}
searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
pluginViewModel.search(query)
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
pluginViewModel.search(newText)
return true
}
})
}
// searchView?.onActionViewCollapsed = { // searchView?.onActionViewCollapsed = {
// pluginViewModel.search(null) // pluginViewModel.search(null)
// } // }
// Because onActionViewCollapsed doesn't wanna work we need this workaround :( // Because onActionViewCollapsed doesn't wanna work we need this workaround :(
searchView?.setOnQueryTextFocusChangeListener { _, hasFocus ->
if (!hasFocus) pluginViewModel.search(null)
}
searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
pluginViewModel.search(query)
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
pluginViewModel.search(newText)
return true
}
})
plugin_recycler_view?.adapter =
binding?.pluginRecyclerView?.adapter =
PluginAdapter { PluginAdapter {
pluginViewModel.handlePluginAction(activity, url, it, isLocal) pluginViewModel.handlePluginAction(activity, url, it, isLocal)
} }
if (isTvSettings()) { if (isTvSettings()) {
// Scrolling down does not reveal the whole RecyclerView on TV, add to bypass that. // Scrolling down does not reveal the whole RecyclerView on TV, add to bypass that.
plugin_recycler_view?.setPadding(0, 0, 0, 200.toPx) binding?.pluginRecyclerView?.setPadding(0, 0, 0, 200.toPx)
} }
observe(pluginViewModel.filteredPlugins) { (scrollToTop, list) -> observe(pluginViewModel.filteredPlugins) { (scrollToTop, list) ->
(plugin_recycler_view?.adapter as? PluginAdapter)?.updateList(list) (binding?.pluginRecyclerView?.adapter as? PluginAdapter)?.updateList(list)
if (scrollToTop) if (scrollToTop)
plugin_recycler_view?.scrollToPosition(0) binding?.pluginRecyclerView?.scrollToPosition(0)
} }
if (isLocal) { if (isLocal) {
// No download button and no categories on local // No download button and no categories on local
settings_toolbar?.menu?.findItem(R.id.download_all)?.isVisible = false binding?.settingsToolbar?.menu?.findItem(R.id.download_all)?.isVisible = false
settings_toolbar?.menu?.findItem(R.id.lang_filter)?.isVisible = false binding?.settingsToolbar?.menu?.findItem(R.id.lang_filter)?.isVisible = false
pluginViewModel.updatePluginListLocal() pluginViewModel.updatePluginListLocal()
tv_types_scroll_view?.isVisible = false
binding?.tvtypesChipsScroll?.root?.isVisible = false
} else { } else {
pluginViewModel.updatePluginList(context, url) pluginViewModel.updatePluginList(context, url)
tv_types_scroll_view?.isVisible = true binding?.tvtypesChipsScroll?.root?.isVisible = true
bindChips(home_select_group, emptyList(), TvType.values().toList()) { list -> bindChips(binding?.tvtypesChipsScroll?.tvtypesChips, emptyList(), TvType.values().toList()) { list ->
pluginViewModel.tvTypes.clear() pluginViewModel.tvTypes.clear()
pluginViewModel.tvTypes.addAll(list.map { it.name }) pluginViewModel.tvTypes.addAll(list.map { it.name })
pluginViewModel.updateFilteredPlugins() pluginViewModel.updateFilteredPlugins()

View file

@ -8,21 +8,16 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AllLanguagesName
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentSetupExtensionsBinding
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.plugins.RepositoryManager import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel
import com.lagradost.cloudstream3.ui.settings.extensions.RepoAdapter import com.lagradost.cloudstream3.ui.settings.extensions.RepoAdapter
import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_extensions.blank_repo_screen
import kotlinx.android.synthetic.main.fragment_extensions.repo_recycler_view
import kotlinx.android.synthetic.main.fragment_setup_media.next_btt
import kotlinx.android.synthetic.main.fragment_setup_media.prev_btt
import kotlinx.android.synthetic.main.fragment_setup_media.setup_root
class SetupFragmentExtensions : Fragment() { class SetupFragmentExtensions : Fragment() {
@ -39,13 +34,24 @@ class SetupFragmentExtensions : Fragment() {
} }
} }
var binding: FragmentSetupExtensionsBinding? = null
override fun onDestroyView() {
binding = null
super.onDestroyView()
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
return inflater.inflate(R.layout.fragment_setup_extensions, container, false) val localBinding = FragmentSetupExtensionsBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root
//return inflater.inflate(R.layout.fragment_setup_extensions, container, false)
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
afterRepositoryLoadedEvent += ::setRepositories afterRepositoryLoadedEvent += ::setRepositories
@ -60,12 +66,12 @@ class SetupFragmentExtensions : Fragment() {
main { main {
val repositories = RepositoryManager.getRepositories() + PREBUILT_REPOSITORIES val repositories = RepositoryManager.getRepositories() + PREBUILT_REPOSITORIES
val hasRepos = repositories.isNotEmpty() val hasRepos = repositories.isNotEmpty()
repo_recycler_view?.isVisible = hasRepos binding?.repoRecyclerView?.isVisible = hasRepos
blank_repo_screen?.isVisible = !hasRepos binding?.blankRepoScreen?.isVisible = !hasRepos
// view_public_repositories_button?.isVisible = hasRepos // view_public_repositories_button?.isVisible = hasRepos
if (hasRepos) { if (hasRepos) {
repo_recycler_view?.adapter = RepoAdapter(true, {}, { binding?.repoRecyclerView?.adapter = RepoAdapter(true, {}, {
PluginsViewModel.downloadAll(activity, it.url, null) PluginsViewModel.downloadAll(activity, it.url, null)
}).apply { updateList(repositories) } }).apply { updateList(repositories) }
} }
@ -80,39 +86,40 @@ class SetupFragmentExtensions : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(setup_root) fixPaddingStatusbar(binding?.setupRoot)
val isSetup = arguments?.getBoolean(SETUP_EXTENSION_BUNDLE_IS_SETUP) ?: false val isSetup = arguments?.getBoolean(SETUP_EXTENSION_BUNDLE_IS_SETUP) ?: false
// view_public_repositories_button?.setOnClickListener { // view_public_repositories_button?.setOnClickListener {
// openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this) // openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
// } // }
with(context) { normalSafeApiCall {
if (this == null) return // val ctx = context ?: return@normalSafeApiCall
setRepositories() setRepositories()
binding?.apply {
if (!isSetup) {
nextBtt.setText(R.string.setup_done)
}
prevBtt.isVisible = isSetup
if (!isSetup) { nextBtt.setOnClickListener {
next_btt.setText(R.string.setup_done) // Continue setup
} if (isSetup)
prev_btt?.isVisible = isSetup if (
// If any available languages
apis.distinctBy { it.lang }.size > 1
) {
findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_provider_languages)
} else {
findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_media)
}
else
findNavController().navigate(R.id.navigation_home)
}
next_btt?.setOnClickListener { prevBtt.setOnClickListener {
// Continue setup findNavController().navigate(R.id.navigation_setup_language)
if (isSetup) }
if (
// If any available languages
apis.distinctBy { it.lang }.size > 1
) {
findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_provider_languages)
} else {
findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_media)
}
else
findNavController().navigate(R.id.navigation_home)
}
prev_btt?.setOnClickListener {
findNavController().navigate(R.id.navigation_setup_language)
} }
} }
} }

View file

@ -13,40 +13,49 @@ import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.BuildConfig import com.lagradost.cloudstream3.BuildConfig
import com.lagradost.cloudstream3.CommonActivity import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentSetupLanguageBinding
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.settings.appLanguages import com.lagradost.cloudstream3.ui.settings.appLanguages
import com.lagradost.cloudstream3.ui.settings.getCurrentLocale import com.lagradost.cloudstream3.ui.settings.getCurrentLocale
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_setup_language.*
import kotlinx.android.synthetic.main.fragment_setup_media.listview1
import kotlinx.android.synthetic.main.fragment_setup_media.next_btt
const val HAS_DONE_SETUP_KEY = "HAS_DONE_SETUP" const val HAS_DONE_SETUP_KEY = "HAS_DONE_SETUP"
class SetupFragmentLanguage : Fragment() { class SetupFragmentLanguage : Fragment() {
var binding: FragmentSetupLanguageBinding? = null
override fun onDestroyView() {
binding = null
super.onDestroyView()
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
// Inflate the layout for this fragment val localBinding = FragmentSetupLanguageBinding.inflate(inflater, container, false)
return inflater.inflate(R.layout.fragment_setup_language, container, false) binding = localBinding
return localBinding.root
//return inflater.inflate(R.layout.fragment_setup_language, container, false)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(setup_root)
// We don't want a crash for all users // We don't want a crash for all users
normalSafeApiCall { normalSafeApiCall {
with(context) { fixPaddingStatusbar(binding?.setupRoot)
if (this == null) return@normalSafeApiCall
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val arrayAdapter = val ctx = context ?: return@normalSafeApiCall
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice) val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val arrayAdapter =
ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
binding?.apply {
// Icons may crash on some weird android versions? // Icons may crash on some weird android versions?
normalSafeApiCall { normalSafeApiCall {
val drawable = when { val drawable = when {
@ -54,10 +63,10 @@ class SetupFragmentLanguage : Fragment() {
BuildConfig.BUILD_TYPE == "prerelease" -> R.drawable.cloud_2_gradient_beta BuildConfig.BUILD_TYPE == "prerelease" -> R.drawable.cloud_2_gradient_beta
else -> R.drawable.cloud_2_gradient else -> R.drawable.cloud_2_gradient
} }
app_icon_image?.setImageDrawable(ContextCompat.getDrawable(this, drawable)) appIconImage.setImageDrawable(ContextCompat.getDrawable(ctx, drawable))
} }
val current = getCurrentLocale(this) val current = getCurrentLocale(ctx)
val languageCodes = appLanguages.map { it.third } val languageCodes = appLanguages.map { it.third }
val languageNames = appLanguages.map { (emoji, name, iso) -> val languageNames = appLanguages.map { (emoji, name, iso) ->
val flag = emoji.ifBlank { SubtitleHelper.getFlagFromIso(iso) ?: "ERROR" } val flag = emoji.ifBlank { SubtitleHelper.getFlagFromIso(iso) ?: "ERROR" }
@ -66,18 +75,19 @@ class SetupFragmentLanguage : Fragment() {
val index = languageCodes.indexOf(current) val index = languageCodes.indexOf(current)
arrayAdapter.addAll(languageNames) arrayAdapter.addAll(languageNames)
listview1?.adapter = arrayAdapter listview1.adapter = arrayAdapter
listview1?.choiceMode = AbsListView.CHOICE_MODE_SINGLE listview1.choiceMode = AbsListView.CHOICE_MODE_SINGLE
listview1?.setItemChecked(index, true) listview1.setItemChecked(index, true)
listview1?.setOnItemClickListener { _, _, position, _ -> listview1.setOnItemClickListener { _, _, position, _ ->
val code = languageCodes[position] val code = languageCodes[position]
CommonActivity.setLocale(activity, code) CommonActivity.setLocale(activity, code)
settingsManager.edit().putString(getString(R.string.locale_key), code).apply() settingsManager.edit().putString(getString(R.string.locale_key), code)
.apply()
activity?.recreate() activity?.recreate()
} }
next_btt?.setOnClickListener { nextBtt.setOnClickListener {
// If no plugins go to plugins page // If no plugins go to plugins page
val nextDestination = if ( val nextDestination = if (
PluginManager.getPluginsOnline().isEmpty() PluginManager.getPluginsOnline().isEmpty()
@ -92,10 +102,11 @@ class SetupFragmentLanguage : Fragment() {
) )
} }
skip_btt?.setOnClickListener { skipBtt.setOnClickListener {
findNavController().navigate(R.id.navigation_home) findNavController().navigate(R.id.navigation_home)
} }
} }
} }
} }

View file

@ -10,30 +10,39 @@ import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentSetupLayoutBinding
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_setup_layout.*
import kotlinx.android.synthetic.main.fragment_setup_media.listview1
import kotlinx.android.synthetic.main.fragment_setup_media.next_btt
import kotlinx.android.synthetic.main.fragment_setup_media.prev_btt
import kotlinx.android.synthetic.main.fragment_setup_media.setup_root
import org.acra.ACRA import org.acra.ACRA
class SetupFragmentLayout : Fragment() { class SetupFragmentLayout : Fragment() {
var binding: FragmentSetupLayoutBinding? = null
override fun onDestroyView() {
binding = null
super.onDestroyView()
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
return inflater.inflate(R.layout.fragment_setup_layout, container, false) val localBinding = FragmentSetupLayoutBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root
//return inflater.inflate(R.layout.fragment_setup_layout, container, false)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(setup_root) fixPaddingStatusbar(binding?.setupRoot)
with(context) { normalSafeApiCall {
if (this == null) return val ctx = context ?: return@normalSafeApiCall
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val prefNames = resources.getStringArray(R.array.app_layout) val prefNames = resources.getStringArray(R.array.app_layout)
val prefValues = resources.getIntArray(R.array.app_layout_values) val prefValues = resources.getIntArray(R.array.app_layout_values)
@ -42,48 +51,48 @@ class SetupFragmentLayout : Fragment() {
settingsManager.getInt(getString(R.string.app_layout_key), -1) settingsManager.getInt(getString(R.string.app_layout_key), -1)
val arrayAdapter = val arrayAdapter =
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice) ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
arrayAdapter.addAll(prefNames.toList()) arrayAdapter.addAll(prefNames.toList())
listview1?.adapter = arrayAdapter binding?.apply {
listview1?.choiceMode = AbsListView.CHOICE_MODE_SINGLE listview1.adapter = arrayAdapter
listview1?.setItemChecked( listview1.choiceMode = AbsListView.CHOICE_MODE_SINGLE
prefValues.indexOf(currentLayout), true listview1.setItemChecked(
) prefValues.indexOf(currentLayout), true
listview1?.setOnItemClickListener { _, _, position, _ ->
settingsManager.edit()
.putInt(getString(R.string.app_layout_key), prefValues[position])
.apply()
activity?.recreate()
}
acra_switch?.setOnCheckedChangeListener { _, enableCrashReporting ->
// Use same pref as in settings
settingsManager.edit().putBoolean(ACRA.PREF_DISABLE_ACRA, !enableCrashReporting)
.apply()
val text =
if (enableCrashReporting) R.string.bug_report_settings_off else R.string.bug_report_settings_on
crash_reporting_text?.text = getText(text)
}
val enableCrashReporting = !settingsManager.getBoolean(ACRA.PREF_DISABLE_ACRA, true)
acra_switch.isChecked = enableCrashReporting
crash_reporting_text.text =
getText(
if (enableCrashReporting) R.string.bug_report_settings_off else R.string.bug_report_settings_on
) )
listview1.setOnItemClickListener { _, _, position, _ ->
settingsManager.edit()
.putInt(getString(R.string.app_layout_key), prefValues[position])
.apply()
activity?.recreate()
}
acraSwitch.setOnCheckedChangeListener { _, enableCrashReporting ->
// Use same pref as in settings
settingsManager.edit().putBoolean(ACRA.PREF_DISABLE_ACRA, !enableCrashReporting)
.apply()
val text =
if (enableCrashReporting) R.string.bug_report_settings_off else R.string.bug_report_settings_on
crashReportingText.text = getText(text)
}
next_btt?.setOnClickListener { val enableCrashReporting = !settingsManager.getBoolean(ACRA.PREF_DISABLE_ACRA, true)
findNavController().navigate(R.id.navigation_home)
}
prev_btt?.setOnClickListener { acraSwitch.isChecked = enableCrashReporting
findNavController().popBackStack() crashReportingText.text =
getText(
if (enableCrashReporting) R.string.bug_report_settings_off else R.string.bug_report_settings_on
)
nextBtt.setOnClickListener {
findNavController().navigate(R.id.navigation_home)
}
prevBtt.setOnClickListener {
findNavController().popBackStack()
}
} }
} }
} }
} }

View file

@ -10,72 +10,85 @@ import androidx.core.util.forEach
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.databinding.FragmentSetupMediaBinding
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_setup_media.* import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
class SetupFragmentMedia : Fragment() { class SetupFragmentMedia : Fragment() {
var binding: FragmentSetupMediaBinding? = null
override fun onDestroyView() {
binding = null
super.onDestroyView()
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
return inflater.inflate(R.layout.fragment_setup_media, container, false) val localBinding = FragmentSetupMediaBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root
//return inflater.inflate(R.layout.fragment_setup_media, container, false)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(setup_root) normalSafeApiCall {
fixPaddingStatusbar(binding?.setupRoot)
with(context) { val ctx = context ?: return@normalSafeApiCall
if (this == null) return val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val arrayAdapter = val arrayAdapter =
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice) ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
val names = enumValues<TvType>().sorted().map { it.name } val names = enumValues<TvType>().sorted().map { it.name }
val selected = mutableListOf<Int>() val selected = mutableListOf<Int>()
arrayAdapter.addAll(names) arrayAdapter.addAll(names)
listview1?.let { binding?.apply {
it.adapter = arrayAdapter listview1.let {
it.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE it.adapter = arrayAdapter
it.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
it.setOnItemClickListener { _, _, _, _ -> it.setOnItemClickListener { _, _, _, _ ->
it.checkedItemPositions?.forEach { key, value -> it.checkedItemPositions?.forEach { key, value ->
if (value) { if (value) {
selected.add(key) selected.add(key)
} else { } else {
selected.remove(key) selected.remove(key)
}
} }
val prefValues = selected.mapNotNull { pos ->
val item =
it.getItemAtPosition(pos)?.toString() ?: return@mapNotNull null
val itemVal = TvType.valueOf(item)
itemVal.ordinal.toString()
}.toSet()
settingsManager.edit()
.putStringSet(getString(R.string.prefer_media_type_key), prefValues)
.apply()
// Regenerate set homepage
removeKey(USER_SELECTED_HOMEPAGE_API)
} }
val prefValues = selected.mapNotNull { pos ->
val item = it.getItemAtPosition(pos)?.toString() ?: return@mapNotNull null
val itemVal = TvType.valueOf(item)
itemVal.ordinal.toString()
}.toSet()
settingsManager.edit()
.putStringSet(getString(R.string.prefer_media_type_key), prefValues)
.apply()
// Regenerate set homepage
removeKey(USER_SELECTED_HOMEPAGE_API)
} }
}
next_btt?.setOnClickListener { nextBtt.setOnClickListener {
findNavController().navigate(R.id.navigation_setup_media_to_navigation_setup_layout) findNavController().navigate(R.id.navigation_setup_media_to_navigation_setup_layout)
} }
prev_btt?.setOnClickListener { prevBtt.setOnClickListener {
findNavController().popBackStack() findNavController().popBackStack()
}
} }
} }
} }
} }

View file

@ -14,31 +14,43 @@ import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.AllLanguagesName
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentSetupProviderLanguagesBinding
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_setup_media.*
class SetupFragmentProviderLanguage : Fragment() { class SetupFragmentProviderLanguage : Fragment() {
var binding: FragmentSetupProviderLanguagesBinding? = null
override fun onDestroyView() {
binding = null
super.onDestroyView()
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
// Inflate the layout for this fragment val localBinding = FragmentSetupProviderLanguagesBinding.inflate(inflater, container, false)
return inflater.inflate(R.layout.fragment_setup_provider_languages, container, false) binding = localBinding
return localBinding.root
//return inflater.inflate(R.layout.fragment_setup_provider_languages, container, false)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(setup_root) fixPaddingStatusbar(binding?.setupRoot)
with(context) { normalSafeApiCall {
if (this == null) return val ctx = context ?: return@normalSafeApiCall
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val arrayAdapter = val arrayAdapter =
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice) ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
val current = this.getApiProviderLangSettings() val current = ctx.getApiProviderLangSettings()
val langs = APIHolder.apis.map { it.lang }.toSet() val langs = APIHolder.apis.map { it.lang }.toSet()
.sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) } + AllLanguagesName .sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) } + AllLanguagesName
@ -56,31 +68,31 @@ class SetupFragmentProviderLanguage : Fragment() {
} }
arrayAdapter.addAll(languageNames) arrayAdapter.addAll(languageNames)
binding?.apply {
listview1?.adapter = arrayAdapter listview1.adapter = arrayAdapter
listview1?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE listview1.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
currentList.forEach { currentList.forEach {
listview1.setItemChecked(it, true) listview1.setItemChecked(it, true)
} }
listview1?.setOnItemClickListener { _, _, _, _ -> listview1.setOnItemClickListener { _, _, _, _ ->
val currentLanguages = mutableListOf<String>() val currentLanguages = mutableListOf<String>()
listview1?.checkedItemPositions?.forEach { key, value -> listview1.checkedItemPositions?.forEach { key, value ->
if (value) currentLanguages.add(langs[key]) if (value) currentLanguages.add(langs[key])
} }
settingsManager.edit().putStringSet( settingsManager.edit().putStringSet(
this.getString(R.string.provider_lang_key), ctx.getString(R.string.provider_lang_key),
currentLanguages.toSet() currentLanguages.toSet()
).apply() ).apply()
} }
next_btt?.setOnClickListener { nextBtt.setOnClickListener {
findNavController().navigate(R.id.navigation_setup_provider_languages_to_navigation_setup_media) findNavController().navigate(R.id.navigation_setup_provider_languages_to_navigation_setup_media)
} }
prev_btt?.setOnClickListener { prevBtt.setOnClickListener {
findNavController().popBackStack() findNavController().popBackStack()
} } }
} }
} }

View file

@ -23,6 +23,7 @@ import com.lagradost.cloudstream3.CommonActivity.onColorSelectedEvent
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.ChromecastSubtitleSettingsBinding
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.Event import com.lagradost.cloudstream3.utils.Event
@ -31,7 +32,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI
import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import kotlinx.android.synthetic.main.subtitle_settings.*
const val CHROME_SUBTITLE_KEY = "chome_subtitle_settings" const val CHROME_SUBTITLE_KEY = "chome_subtitle_settings"
@ -137,12 +137,21 @@ class ChromecastSubtitlesFragment : Fragment() {
//subtitle_text?.setStyle(fromSaveToStyle(state)) //subtitle_text?.setStyle(fromSaveToStyle(state))
} }
var binding : ChromecastSubtitleSettingsBinding? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle?, savedInstanceState: Bundle?,
): View? { ): View {
return inflater.inflate(R.layout.chromecast_subtitle_settings, container, false) val localBinding = ChromecastSubtitleSettingsBinding.inflate(inflater, container, false)
binding = localBinding
return localBinding.root//inflater.inflate(R.layout.chromecast_subtitle_settings, container, false)
}
override fun onDestroyView() {
binding = null
super.onDestroyView()
} }
private lateinit var state: SaveChromeCaptionStyle private lateinit var state: SaveChromeCaptionStyle
@ -159,7 +168,7 @@ class ChromecastSubtitlesFragment : Fragment() {
onColorSelectedEvent += ::onColorSelected onColorSelectedEvent += ::onColorSelected
onDialogDismissedEvent += ::onDialogDismissed onDialogDismissedEvent += ::onDialogDismissed
context?.fixPaddingStatusbar(subs_root) fixPaddingStatusbar(binding?.subsRoot)
state = getCurrentSavedStyle() state = getCurrentSavedStyle()
context?.updateState() context?.updateState()
@ -190,17 +199,20 @@ class ChromecastSubtitlesFragment : Fragment() {
} }
} }
subs_text_color.setup(0) binding?.apply {
subs_outline_color.setup(1) subsTextColor.setup(0)
subs_background_color.setup(2) subsOutlineColor.setup(1)
subsBackgroundColor.setup(2)
}
val dismissCallback = { val dismissCallback = {
if (hide) if (hide)
activity?.hideSystemUI() activity?.hideSystemUI()
} }
subs_edge_type.setFocusableInTv() binding?.subsEdgeType?.setFocusableInTv()
subs_edge_type.setOnClickListener { textView -> binding?.subsEdgeType?.setOnClickListener { textView ->
val edgeTypes = listOf( val edgeTypes = listOf(
Pair( Pair(
EDGE_TYPE_NONE, EDGE_TYPE_NONE,
@ -237,15 +249,15 @@ class ChromecastSubtitlesFragment : Fragment() {
} }
} }
subs_edge_type.setOnLongClickListener { binding?.subsEdgeType?.setOnLongClickListener {
state.edgeType = defaultState.edgeType state.edgeType = defaultState.edgeType
it.context.updateState() it.context.updateState()
showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT) showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
return@setOnLongClickListener true return@setOnLongClickListener true
} }
subs_font_size.setFocusableInTv() binding?.subsFontSize?.setFocusableInTv()
subs_font_size.setOnClickListener { textView -> binding?.subsFontSize?.setOnClickListener { textView ->
val fontSizes = listOf( val fontSizes = listOf(
Pair(0.75f, "75%"), Pair(0.75f, "75%"),
Pair(0.80f, "80%"), Pair(0.80f, "80%"),
@ -278,24 +290,26 @@ class ChromecastSubtitlesFragment : Fragment() {
} }
} }
subs_font_size.setOnLongClickListener { _ -> binding?.subsFontSize?.setOnLongClickListener { _ ->
state.fontScale = defaultState.fontScale state.fontScale = defaultState.fontScale
//textView.context.updateState() // font size not changed //textView.context.updateState() // font size not changed
showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT) showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
return@setOnLongClickListener true return@setOnLongClickListener true
} }
subs_font.setFocusableInTv()
subs_font.setOnClickListener { textView ->
binding?.subsFont?.setFocusableInTv()
binding?.subsFont?.setOnClickListener { textView ->
val fontTypes = listOf( val fontTypes = listOf(
Pair(null, textView.context.getString(R.string.normal)), null to textView.context.getString(R.string.normal),
Pair("Droid Sans", "Droid Sans"), "Droid Sans" to "Droid Sans",
Pair("Droid Sans Mono", "Droid Sans Mono"), "Droid Sans Mono" to "Droid Sans Mono",
Pair("Droid Serif Regular", "Droid Serif Regular"), "Droid Serif Regular" to "Droid Serif Regular",
Pair("Cutive Mono", "Cutive Mono"), "Cutive Mono" to "Cutive Mono",
Pair("Short Stack", "Short Stack"), "Short Stack" to "Short Stack",
Pair("Quintessential", "Quintessential"), "Quintessential" to "Quintessential",
Pair("Alegreya Sans SC", "Alegreya Sans SC"), "Alegreya Sans SC" to "Alegreya Sans SC",
) )
//showBottomDialog //showBottomDialog
@ -310,35 +324,35 @@ class ChromecastSubtitlesFragment : Fragment() {
textView.context.updateState() textView.context.updateState()
} }
} }
binding?.subsFont?.setOnLongClickListener { textView ->
subs_font.setOnLongClickListener { textView ->
state.fontFamily = defaultState.fontFamily state.fontFamily = defaultState.fontFamily
textView.context.updateState() textView.context.updateState()
showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT) showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
return@setOnLongClickListener true return@setOnLongClickListener true
} }
cancel_btt.setOnClickListener { binding?.cancelBtt?.setOnClickListener {
activity?.popCurrentPage() activity?.popCurrentPage()
} }
apply_btt.setOnClickListener { binding?.applyBtt?.setOnClickListener {
it.context.saveStyle(state) it.context.saveStyle(state)
applyStyleEvent.invoke(state) applyStyleEvent.invoke(state)
//it.context.fromSaveToStyle(state) //it.context.fromSaveToStyle(state)
activity?.popCurrentPage() activity?.popCurrentPage()
} }
binding?.subtitleText?.apply {
subtitle_text.setCues( setCues(
listOf( listOf(
Cue.Builder() Cue.Builder()
.setTextSize( .setTextSize(
getPixels(TypedValue.COMPLEX_UNIT_SP, 25.0f).toFloat(), getPixels(TypedValue.COMPLEX_UNIT_SP, 25.0f).toFloat(),
Cue.TEXT_SIZE_TYPE_ABSOLUTE Cue.TEXT_SIZE_TYPE_ABSOLUTE
) )
.setText(subtitle_text.context.getString(R.string.subtitles_example_text)) .setText(context.getString(R.string.subtitles_example_text))
.build() .build()
)
) )
) }
} }
} }

View file

@ -238,7 +238,7 @@ class SubtitlesFragment : Fragment() {
context?.getExternalFilesDir(null)?.absolutePath.toString() + "/Fonts" context?.getExternalFilesDir(null)?.absolutePath.toString() + "/Fonts"
) )
context?.fixPaddingStatusbar(subs_root) fixPaddingStatusbar(subs_root)
state = getCurrentSavedStyle() state = getCurrentSavedStyle()
context?.updateState() context?.updateState()

View file

@ -397,21 +397,22 @@ object UIHelper {
return result return result
} }
fun Context?.fixPaddingStatusbar(v: View?) { fun fixPaddingStatusbar(v: View?) {
if (v == null || this == null) return if (v == null) return
val ctx = v.context ?: return
v.setPadding( v.setPadding(
v.paddingLeft, v.paddingLeft,
v.paddingTop + getStatusBarHeight(), v.paddingTop + ctx.getStatusBarHeight(),
v.paddingRight, v.paddingRight,
v.paddingBottom v.paddingBottom
) )
} }
fun Context.fixPaddingStatusbarView(v: View?) { fun fixPaddingStatusbarView(v: View?) {
if (v == null) return if (v == null) return
val ctx = v.context ?: return
val params = v.layoutParams val params = v.layoutParams
params.height = getStatusBarHeight() params.height = ctx.getStatusBarHeight()
v.layoutParams = params v.layoutParams = params
} }

View file

@ -172,4 +172,15 @@
app:icon="@drawable/ic_baseline_filter_list_24" app:icon="@drawable/ic_baseline_filter_list_24"
tools:ignore="ContentDescription" tools:ignore="ContentDescription"
tools:visibility="visible" /> tools:visibility="visible" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/home_random"
style="@style/ExtendedFloatingActionButton"
android:layout_gravity="bottom|start"
android:text="@string/home_random"
android:textColor="?attr/textColor"
android:visibility="gone"
app:icon="@drawable/ic_baseline_play_arrow_24"
tools:ignore="ContentDescription"
tools:visibility="visible" />
</FrameLayout> </FrameLayout>

View file

@ -25,7 +25,7 @@
app:titleTextColor="?attr/textColor" app:titleTextColor="?attr/textColor"
tools:title="Overlord" /> tools:title="Overlord" />
<include layout="@layout/tvtypes_chips_scroll" /> <include layout="@layout/tvtypes_chips_scroll" android:id="@+id/tvtypes_chips_scroll" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView

View file

@ -88,7 +88,7 @@
app:tint="?attr/textColor" /> app:tint="?attr/textColor" />
</FrameLayout> </FrameLayout>
<include layout="@layout/tvtypes_chips_scroll" /> <include layout="@layout/tvtypes_chips_scroll" android:id="@+id/tvtypes_chips_scroll" />
</LinearLayout> </LinearLayout>

View file

@ -89,7 +89,7 @@
app:tint="?attr/textColor" /> app:tint="?attr/textColor" />
</FrameLayout> </FrameLayout>
<include layout="@layout/tvtypes_chips_scroll" /> <include layout="@layout/tvtypes_chips_scroll" android:id="@+id/tvtypes_chips_scroll" />
</LinearLayout> </LinearLayout>

View file

@ -26,7 +26,7 @@
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp"> android:layout_height="60dp">
<include layout="@layout/tvtypes_chips_scroll" /> <include layout="@layout/tvtypes_chips_scroll" android:id="@+id/tvtypes_chips_scroll" />
<LinearLayout <LinearLayout
android:id="@+id/apply_btt_holder" android:id="@+id/apply_btt_holder"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -1,6 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.lagradost.cloudstream3.ui.AutofitRecyclerView xmlns:android="http://schemas.android.com/apk/res/android" <com.lagradost.cloudstream3.ui.AutofitRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/page_recyclerview" android:id="@+id/page_recyclerview"

View file

@ -1,151 +1,152 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:nextFocusRight="@id/result_episode_download" android:id="@+id/episode_holder_large"
android:id="@+id/episode_holder_large" android:layout_width="wrap_content"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_marginBottom="10dp"
app:cardCornerRadius="@dimen/rounded_image_radius" android:nextFocusRight="@id/result_episode_download"
app:cardBackgroundColor="?attr/boxItemBackground" app:cardBackgroundColor="?attr/boxItemBackground"
android:layout_marginBottom="10dp"> app:cardCornerRadius="@dimen/rounded_image_radius">
<LinearLayout <LinearLayout
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:layout_width="wrap_content"
android:padding="10dp" android:layout_height="wrap_content"
android:orientation="vertical" android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="match_parent" android:orientation="vertical"
android:layout_height="wrap_content"> android:padding="10dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:id="@+id/episode_lin_holder"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:orientation="horizontal">
<!--app:cardCornerRadius="@dimen/roundedImageRadius"--> <!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:layout_width="126dp" android:layout_width="126dp"
android:layout_height="72dp" android:layout_height="72dp"
android:foreground="@drawable/outline_drawable"> android:foreground="@drawable/outline_drawable">
<ImageView <ImageView
android:nextFocusRight="@id/result_episode_download" android:id="@+id/episode_poster"
android:id="@+id/episode_poster" android:layout_width="match_parent"
tools:src="@drawable/example_poster" android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:contentDescription="@string/episode_poster_img_des"
android:scaleType="centerCrop" android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="match_parent" android:nextFocusRight="@id/result_episode_download"
android:layout_height="match_parent" android:scaleType="centerCrop"
android:contentDescription="@string/episode_poster_img_des" /> tools:src="@drawable/example_poster" />
<ImageView <ImageView
android:src="@drawable/play_button" android:layout_width="36dp"
android:layout_gravity="center" android:layout_height="36dp"
android:layout_width="36dp" android:layout_gravity="center"
android:layout_height="36dp" android:contentDescription="@string/play_episode"
android:contentDescription="@string/play_episode" /> android:src="@drawable/play_button" />
<androidx.core.widget.ContentLoadingProgressBar <androidx.core.widget.ContentLoadingProgressBar
android:layout_marginBottom="-1.5dp" android:id="@+id/episode_progress"
android:id="@+id/episode_progress" style="@android:style/Widget.Material.ProgressBar.Horizontal"
android:progressTint="?attr/colorPrimary" android:layout_width="match_parent"
android:progressBackgroundTint="?attr/colorPrimary" android:layout_height="5dp"
style="@android:style/Widget.Material.ProgressBar.Horizontal" android:layout_gravity="bottom"
android:layout_width="match_parent" android:layout_marginBottom="-1.5dp"
tools:progress="50" android:progressBackgroundTint="?attr/colorPrimary"
android:layout_gravity="bottom" android:progressTint="?attr/colorPrimary"
android:layout_height="5dp" /> tools:progress="50" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<LinearLayout <LinearLayout
android:layout_marginStart="15dp" android:layout_width="match_parent"
android:orientation="vertical" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_width="match_parent" android:layout_marginStart="15dp"
android:layout_marginEnd="50dp" android:layout_marginEnd="50dp"
android:layout_height="wrap_content"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:orientation="horizontal" android:layout_width="wrap_content"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_height="wrap_content"> android:orientation="horizontal">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:layout_gravity="start" android:id="@+id/episode_filler"
style="@style/SmallBlackButton" style="@style/SmallBlackButton"
android:layout_marginEnd="10dp" android:layout_gravity="start"
android:text="@string/filler" android:layout_marginEnd="10dp"
android:id="@+id/episode_filler" /> android:text="@string/filler" />
<TextView <TextView
android:layout_gravity="center_vertical" android:id="@+id/episode_text"
android:id="@+id/episode_text" android:layout_width="wrap_content"
tools:text="1. Jobless" android:layout_height="wrap_content"
android:textStyle="bold" android:layout_gravity="center_vertical"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:layout_width="wrap_content" android:textStyle="bold"
android:layout_height="wrap_content" /> tools:text="1. Jobless" />
</LinearLayout> </LinearLayout>
<TextView <TextView
android:id="@+id/episode_rating" android:id="@+id/episode_rating"
tools:text="Rated: 8.8" android:layout_width="wrap_content"
android:textColor="?attr/grayTextColor" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:textColor="?attr/grayTextColor"
android:layout_height="wrap_content" /> tools:text="Rated: 8.8" />
</LinearLayout> </LinearLayout>
<FrameLayout <FrameLayout
android:layout_marginStart="-50dp" android:layout_width="wrap_content"
android:layout_gravity="end" android:layout_height="match_parent"
android:layout_width="wrap_content" android:layout_gravity="end"
android:layout_height="match_parent"> android:layout_marginStart="-50dp">
<androidx.core.widget.ContentLoadingProgressBar <androidx.core.widget.ContentLoadingProgressBar
android:layout_marginEnd="10dp" android:id="@+id/result_episode_progress_downloaded"
android:layout_marginStart="10dp" style="?android:attr/progressBarStyleHorizontal"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:id="@+id/result_episode_progress_downloaded" android:layout_gravity="end|center_vertical"
android:indeterminate="false" android:layout_margin="5dp"
android:progressDrawable="@drawable/circular_progress_bar" android:layout_marginStart="10dp"
android:background="@drawable/circle_shape" android:layout_marginEnd="10dp"
style="?android:attr/progressBarStyleHorizontal" android:background="@drawable/circle_shape"
android:max="100" android:indeterminate="false"
android:layout_margin="5dp" android:max="100"
android:layout_gravity="end|center_vertical" android:progress="0"
android:progress="0" android:progressDrawable="@drawable/circular_progress_bar"
android:visibility="visible" /> android:visibility="visible" />
<ImageView <ImageView
android:nextFocusLeft="@id/episode_poster" android:id="@+id/result_episode_download"
android:id="@+id/result_episode_download" android:layout_width="50dp"
android:visibility="visible" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_gravity="center_vertical"
android:layout_gravity="center_vertical" android:background="?selectableItemBackgroundBorderless"
android:padding="10dp" android:contentDescription="@string/download"
android:layout_width="50dp" android:nextFocusLeft="@id/episode_poster"
android:background="?selectableItemBackgroundBorderless" android:padding="10dp"
android:src="@drawable/ic_baseline_play_arrow_24" android:src="@drawable/ic_baseline_play_arrow_24"
android:contentDescription="@string/download" android:visibility="visible"
app:tint="?attr/white" /> app:tint="?attr/white" />
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>
<TextView <TextView
android:maxLines="4" android:id="@+id/episode_descript"
android:ellipsize="end" android:layout_width="match_parent"
android:paddingTop="10dp" android:layout_height="wrap_content"
android:paddingBottom="10dp" android:ellipsize="end"
android:id="@+id/episode_descript" android:maxLines="4"
android:textColor="?attr/grayTextColor" android:paddingTop="10dp"
tools:text="Jon and Daenerys arrive in Winterfell and are met with skepticism. Sam learns about the fate of his family. Cersei gives Euron the reward he aims for. Theon follows his heart. Jon and Daenerys arrive in Winterfell and are met with skepticism. Sam learns about the fate of his family. Cersei gives Euron the reward he aims for. Theon follows his heart." android:paddingBottom="10dp"
android:layout_width="match_parent" android:textColor="?attr/grayTextColor"
android:layout_height="wrap_content" /> tools:text="Jon and Daenerys arrive in Winterfell and are met with skepticism. Sam learns about the fate of his family. Cersei gives Euron the reward he aims for. Theon follows his heart. Jon and Daenerys arrive in Winterfell and are met with skepticism. Sam learns about the fate of his family. Cersei gives Euron the reward he aims for. Theon follows his heart." />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View file

@ -6,5 +6,5 @@
android:requiresFadingEdge="horizontal" android:requiresFadingEdge="horizontal"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android">
<include layout="@layout/tvtypes_chips" /> <include layout="@layout/tvtypes_chips" android:id="@+id/tvtypes_chips" />
</HorizontalScrollView> </HorizontalScrollView>