weakrefrence activity for cleaner code + HomeParentItemAdapterPreview viewmodel + more stuff in viewmodel

This commit is contained in:
LagradOst 2023-07-17 03:32:41 +02:00
parent afadf121f4
commit 4d6e64adb6
29 changed files with 378 additions and 326 deletions

View file

@ -34,9 +34,18 @@ import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission
import com.lagradost.cloudstream3.utils.UIHelper.shouldShowPIPMode
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import org.schabi.newpipe.extractor.NewPipe
import java.lang.ref.WeakReference
import java.util.*
object CommonActivity {
private var _activity: WeakReference<Activity>? = null
var activity
get() = _activity?.get()
private set(value) {
_activity = WeakReference(value)
}
@MainThread
fun Activity?.getCastSession(): CastSession? {
return (this as MainActivity?)?.mSessionManager?.currentCastSession
@ -56,6 +65,30 @@ object CommonActivity {
var currentToast: Toast? = null
fun showToast(@StringRes message: Int, duration: Int? = null) {
val act = activity ?: return
act.runOnUiThread {
showToast(act, act.getString(message), duration)
}
}
fun showToast(message: String?, duration: Int? = null) {
val act = activity ?: return
act.runOnUiThread {
showToast(act, message, duration)
}
}
fun showToast(message: UiText?, duration: Int? = null) {
val act = activity ?: return
if (message == null) return
act.runOnUiThread {
showToast(act, message.asString(act), duration)
}
}
@MainThread
fun showToast(act: Activity?, text: UiText, duration: Int) {
if (act == null) return
text.asStringNull(act)?.let {
@ -140,6 +173,7 @@ object CommonActivity {
fun init(act: ComponentActivity?) {
if (act == null) return
activity = act
//https://stackoverflow.com/questions/52594181/how-to-know-if-user-has-disabled-picture-in-picture-feature-permission
//https://developer.android.com/guide/topics/ui/picture-in-picture
canShowPipMode =
@ -222,6 +256,7 @@ object CommonActivity {
"AmoledLight" -> R.style.AmoledModeLight
"Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
R.style.MonetMode else R.style.AppTheme
else -> R.style.AppTheme
}
@ -244,8 +279,10 @@ object CommonActivity {
"Pink" -> R.style.OverlayPrimaryColorPink
"Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
R.style.OverlayPrimaryColorMonet else R.style.OverlayPrimaryColorNormal
"Monet2" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
R.style.OverlayPrimaryColorMonetTwo else R.style.OverlayPrimaryColorNormal
else -> R.style.OverlayPrimaryColorNormal
}
act.theme.applyStyle(currentTheme, true)
@ -271,12 +308,15 @@ object CommonActivity {
FocusDirection.Left -> {
view.nextFocusLeftId
}
FocusDirection.Up -> {
view.nextFocusUpId
}
FocusDirection.Right -> {
view.nextFocusRightId
}
FocusDirection.Down -> {
view.nextFocusDownId
}
@ -328,30 +368,39 @@ object CommonActivity {
KeyEvent.KEYCODE_FORWARD, KeyEvent.KEYCODE_D, KeyEvent.KEYCODE_MEDIA_SKIP_FORWARD, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD -> {
PlayerEventType.SeekForward
}
KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD, KeyEvent.KEYCODE_MEDIA_REWIND -> {
PlayerEventType.SeekBack
}
KeyEvent.KEYCODE_MEDIA_NEXT, KeyEvent.KEYCODE_BUTTON_R1, KeyEvent.KEYCODE_N -> {
PlayerEventType.NextEpisode
}
KeyEvent.KEYCODE_MEDIA_PREVIOUS, KeyEvent.KEYCODE_BUTTON_L1, KeyEvent.KEYCODE_B -> {
PlayerEventType.PrevEpisode
}
KeyEvent.KEYCODE_MEDIA_PAUSE -> {
PlayerEventType.Pause
}
KeyEvent.KEYCODE_MEDIA_PLAY, KeyEvent.KEYCODE_BUTTON_START -> {
PlayerEventType.Play
}
KeyEvent.KEYCODE_L, KeyEvent.KEYCODE_NUMPAD_7, KeyEvent.KEYCODE_7 -> {
PlayerEventType.Lock
}
KeyEvent.KEYCODE_H, KeyEvent.KEYCODE_MENU -> {
PlayerEventType.ToggleHide
}
KeyEvent.KEYCODE_M, KeyEvent.KEYCODE_VOLUME_MUTE -> {
PlayerEventType.ToggleMute
}
KeyEvent.KEYCODE_S, KeyEvent.KEYCODE_NUMPAD_9, KeyEvent.KEYCODE_9 -> {
PlayerEventType.ShowMirrors
}
@ -359,21 +408,27 @@ object CommonActivity {
KeyEvent.KEYCODE_O, KeyEvent.KEYCODE_NUMPAD_8, KeyEvent.KEYCODE_8 -> {
PlayerEventType.SearchSubtitlesOnline
}
KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_NUMPAD_3, KeyEvent.KEYCODE_3 -> {
PlayerEventType.ShowSpeed
}
KeyEvent.KEYCODE_R, KeyEvent.KEYCODE_NUMPAD_0, KeyEvent.KEYCODE_0 -> {
PlayerEventType.Resize
}
KeyEvent.KEYCODE_C, KeyEvent.KEYCODE_NUMPAD_4, KeyEvent.KEYCODE_4 -> {
PlayerEventType.SkipOp
}
KeyEvent.KEYCODE_V, KeyEvent.KEYCODE_NUMPAD_5, KeyEvent.KEYCODE_5 -> {
PlayerEventType.SkipCurrentChapter
}
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_NUMPAD_ENTER, KeyEvent.KEYCODE_ENTER -> { // space is not captured due to navigation
PlayerEventType.PlayPauseToggle
}
else -> null
}?.let { playerEvent ->
playerEventListener?.invoke(playerEvent)
@ -398,16 +453,19 @@ object CommonActivity {
act.currentFocus,
FocusDirection.Left
)
KeyEvent.KEYCODE_DPAD_RIGHT -> getNextFocus(
act,
act.currentFocus,
FocusDirection.Right
)
KeyEvent.KEYCODE_DPAD_UP -> getNextFocus(
act,
act.currentFocus,
FocusDirection.Up
)
KeyEvent.KEYCODE_DPAD_DOWN -> getNextFocus(
act,
act.currentFocus,

View file

@ -309,7 +309,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
this@with.runOnUiThread {
try {
showToast(
this@with,
getString(if (isSuccessful) R.string.authenticated_user else R.string.authenticated_user_fail).format(
api.name
)
@ -785,7 +784,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
newLocalBinding
}
} catch (t: Throwable) {
showToast(this, txt(R.string.unable_to_inflate, t.message ?: ""), Toast.LENGTH_LONG)
showToast(txt(R.string.unable_to_inflate, t.message ?: ""), Toast.LENGTH_LONG)
null
}
@ -817,7 +816,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
if (PluginManager.checkSafeModeFile()) {
normalSafeApiCall {
showToast(this, R.string.safe_mode_file, Toast.LENGTH_LONG)
showToast( R.string.safe_mode_file, Toast.LENGTH_LONG)
}
} else if (lastError == null) {
ioSafe {
@ -876,7 +875,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
}
when (resource) {
is Resource.Failure -> {
showToast(this, R.string.error)
showToast(R.string.error)
viewModel.clear()
hidePreviewPopupDialog()
}

View file

@ -5,6 +5,7 @@ import android.content.DialogInterface
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
@ -19,7 +20,7 @@ import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager
object DownloadButtonSetup {
fun handleDownloadClick(activity: Activity?, click: DownloadClickEvent) {
fun handleDownloadClick(click: DownloadClickEvent) {
val id = click.data.id
if (click.data !is VideoDownloadHelper.DownloadEpisodeCached) return
when (click.action) {
@ -89,9 +90,9 @@ object DownloadButtonSetup {
)?.fileLength
?: 0
if (length > 0) {
showToast(act, R.string.delete, Toast.LENGTH_LONG)
showToast(R.string.delete, Toast.LENGTH_LONG)
} else {
showToast(act, R.string.download, Toast.LENGTH_LONG)
showToast(R.string.download, Toast.LENGTH_LONG)
}
}
}

View file

@ -95,7 +95,7 @@ class DownloadChildFragment : Fragment() {
DownloadChildAdapter(
ArrayList(),
) { click ->
handleDownloadClick(activity, click)
handleDownloadClick(click)
}
downloadDeleteEventListener = { id: Int ->

View file

@ -162,7 +162,7 @@ class DownloadFragment : Fragment() {
},
{ downloadClickEvent ->
if (downloadClickEvent.data !is VideoDownloadHelper.DownloadEpisodeCached) return@DownloadHeaderAdapter
handleDownloadClick(activity, downloadClickEvent)
handleDownloadClick(downloadClickEvent)
if (downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) {
context?.let { ctx ->
downloadsViewModel.updateList(ctx)
@ -230,7 +230,7 @@ class DownloadFragment : Fragment() {
binding.applyBtt.setOnClickListener {
val url = binding.streamUrl.text?.toString()
if (url.isNullOrEmpty()) {
showToast(activity, R.string.error_invalid_url, Toast.LENGTH_SHORT)
showToast(R.string.error_invalid_url, Toast.LENGTH_SHORT)
} else {
val referer = binding.streamReferer.text?.toString()

View file

@ -42,6 +42,7 @@ import com.lagradost.cloudstream3.databinding.TvtypesChipsBinding
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
import com.lagradost.cloudstream3.ui.WatchType
@ -194,7 +195,7 @@ class HomeFragment : Fragment() {
binding.homeExpandedRecycler.adapter =
SearchAdapter(item.list.toMutableList(), binding.homeExpandedRecycler) { callback ->
handleSearchClickCallback(this, callback)
handleSearchClickCallback(callback)
if (callback.action == SEARCH_ACTION_LOAD || callback.action == SEARCH_ACTION_PLAY_FILE) {
bottomSheetDialogBuilder.ownHide() // we hide here because we want to resume it later
//bottomSheetDialogBuilder.dismissSafe(this)
@ -441,7 +442,7 @@ class HomeFragment : Fragment() {
binding = try {
FragmentHomeBinding.bind(root)
} catch (t: Throwable) {
showToast(activity, txt(R.string.unable_to_inflate, t.message ?: ""), Toast.LENGTH_LONG)
showToast(txt(R.string.unable_to_inflate, t.message ?: ""), Toast.LENGTH_LONG)
logError(t)
null
}
@ -481,59 +482,6 @@ class HomeFragment : Fragment() {
fixGrid()
}
fun bookmarksUpdated(_data: Boolean) {
reloadStored()
}
override fun onResume() {
super.onResume()
reloadStored()
bookmarksUpdatedEvent += ::bookmarksUpdated
afterPluginsLoadedEvent += ::afterPluginsLoaded
mainPluginsLoadedEvent += ::afterMainPluginsLoaded
}
override fun onStop() {
bookmarksUpdatedEvent -= ::bookmarksUpdated
afterPluginsLoadedEvent -= ::afterPluginsLoaded
mainPluginsLoadedEvent -= ::afterMainPluginsLoaded
super.onStop()
}
private fun reloadStored() {
homeViewModel.loadResumeWatching()
val list = EnumSet.noneOf(WatchType::class.java)
getKey<IntArray>(HOME_BOOKMARK_VALUE_LIST)?.map { WatchType.fromInternalId(it) }?.let {
list.addAll(it)
}
homeViewModel.loadStoredData(list)
}
private fun afterMainPluginsLoaded(unused: Boolean = false) {
loadHomePage(false)
}
private fun afterPluginsLoaded(forceReload: Boolean) {
loadHomePage(forceReload)
}
private fun loadHomePage(forceReload: Boolean) {
val apiName = context?.getKey<String>(USER_SELECTED_HOMEPAGE_API)
if (homeViewModel.apiName.value != apiName || apiName == null || forceReload) {
//println("Caught home: " + homeViewModel.apiName.value + " at " + apiName)
homeViewModel.loadAndCancel(apiName, forceReload)
}
}
private fun homeHandleSearch(callback: SearchClickCallback) {
if (callback.action == SEARCH_ACTION_FOCUSED) {
//focusCallback(callback.card)
} else {
handleSearchClickCallback(activity, callback)
}
}
private var currentApiName: String? = null
private var toggleRandomButton = false
@ -546,8 +494,6 @@ class HomeFragment : Fragment() {
fixGrid()
binding?.homeChangeApiLoading?.setOnClickListener(apiChangeClickListener)
binding?.homeChangeApiLoading?.setOnClickListener(apiChangeClickListener)
binding?.homeApiFab?.setOnClickListener(apiChangeClickListener)
binding?.homeRandom?.setOnClickListener {
@ -567,18 +513,9 @@ class HomeFragment : Fragment() {
binding?.homeRandom?.visibility = View.GONE
}
observe(homeViewModel.preview) { preview ->
(binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setPreviewData(
preview
)
}
observe(homeViewModel.apiName) { apiName ->
currentApiName = apiName
binding?.homeApiFab?.text = apiName
(binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setApiName(
apiName
)
}
observe(homeViewModel.page) { data ->
@ -659,73 +596,38 @@ class HomeFragment : Fragment() {
}
observe(homeViewModel.availableWatchStatusTypes) { availableWatchStatusTypes ->
context?.setKey(
HOME_BOOKMARK_VALUE_LIST,
availableWatchStatusTypes.first.map { it.internalId }.toIntArray()
)
(binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setAvailableWatchStatusTypes(
availableWatchStatusTypes
)
}
observe(homeViewModel.bookmarks) { data ->
(binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setBookmarkData(
data
)
}
observe(homeViewModel.resumeWatching) { resumeWatching ->
(binding?.homeMasterRecycler?.adapter as? HomeParentItemAdapterPreview?)?.setResumeWatchingData(
resumeWatching
)
if (isTrueTvSettings()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ioSafe {
activity?.addProgramsToContinueWatching(resumeWatching.mapNotNull { it as? DataStoreHelper.ResumeWatchingResult })
}
}
}
}
//context?.fixPaddingStatusbarView(home_statusbar)
//context?.fixPaddingStatusbar(home_padding)
fixPaddingStatusbar(binding?.homeLoadingStatusbar)
binding?.homeMasterRecycler?.adapter =
HomeParentItemAdapterPreview(mutableListOf(), { callback ->
homeHandleSearch(callback)
}, { item ->
observeNullable(homeViewModel.popup) { item ->
if (item == null) {
bottomSheetDialog?.dismissSafe()
bottomSheetDialog = null
return@observeNullable
}
// don't recreate
if (bottomSheetDialog != null) {
return@observeNullable
}
bottomSheetDialog = activity?.loadHomepageList(item, expandCallback = {
homeViewModel.expandAndReturn(it)
}, dismissCallback = {
homeViewModel.popup(null)
bottomSheetDialog = null
})
}, { name ->
homeViewModel.expand(name)
}, { load ->
activity?.loadResult(load.response.url, load.response.apiName, load.action)
}, {
homeViewModel.loadMoreHomeScrollResponses()
}, {
apiChangeClickListener.onClick(it)
}, reloadStored = {
reloadStored()
}, loadStoredData = {
homeViewModel.loadStoredData(it)
}, { (isQuickSearch, text) ->
if (!isQuickSearch) {
QuickSearchFragment.pushSearch(
activity,
text,
currentApiName?.let { arrayOf(it) })
}
})
reloadStored()
loadHomePage(false)
binding?.homeMasterRecycler?.adapter =
HomeParentItemAdapterPreview(
mutableListOf(),
homeViewModel
)
homeViewModel.reloadStored()
//loadHomePage(false)
binding?.homeMasterRecycler?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {

View file

@ -26,6 +26,7 @@ class LoadClickCallback(
open class ParentItemAdapter(
private var items: MutableList<HomeViewModel.ExpandableHomepageList>,
//private val viewModel: HomeViewModel,
private val clickCallback: (SearchClickCallback) -> Unit,
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
private val expandCallback: ((String) -> Unit)? = null,
@ -153,6 +154,7 @@ open class ParentItemAdapter(
class ParentViewHolder
constructor(
val binding: HomepageParentBinding,
// val viewModel: HomeViewModel,
private val clickCallback: (SearchClickCallback) -> Unit,
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
private val expandCallback: ((String) -> Unit)? = null,

View file

@ -8,7 +8,9 @@ import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipDrawable
@ -18,8 +20,13 @@ import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.databinding.FragmentHomeHeadBinding
import com.lagradost.cloudstream3.databinding.FragmentHomeHeadTvBinding
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.debugException
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage
import com.lagradost.cloudstream3.ui.result.ResultViewModel2
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
@ -61,102 +68,51 @@ import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_type_on_ho
import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_type_watching_btt
import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_watch_child_recyclerview
import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_watch_holder
import kotlinx.android.synthetic.main.toast.view.*
class HomeParentItemAdapterPreview(
items: MutableList<HomeViewModel.ExpandableHomepageList>,
val clickCallback: (SearchClickCallback) -> Unit,
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit,
expandCallback: ((String) -> Unit)? = null,
private val loadCallback: (LoadClickCallback) -> Unit,
private val loadMoreCallback: (() -> Unit),
private val changeHomePageCallback: ((View) -> Unit),
private val reloadStored: (() -> Unit),
private val loadStoredData: ((Set<WatchType>) -> Unit),
private val searchQueryCallback: ((Pair<Boolean, String>) -> Unit)
) : ParentItemAdapter(items, clickCallback, moreInfoClickCallback, expandCallback) {
private var previewData: Resource<Pair<Boolean, List<LoadResponse>>> = Resource.Loading()
private var resumeWatchingData: List<SearchResponse> = listOf()
private var bookmarkData: Pair<Boolean, List<SearchResponse>> =
false to listOf()
private var apiName: String = "NONE"
private val viewModel: HomeViewModel,
) : ParentItemAdapter(items, clickCallback = {
viewModel.click(it)
}, moreInfoClickCallback = {
viewModel.popup(it)
}, expandCallback = {
viewModel.expand(it)
}) {
val headItems = 1
private var availableWatchStatusTypes: Pair<Set<WatchType>, Set<WatchType>> =
setOf<WatchType>() to setOf()
fun setAvailableWatchStatusTypes(data: Pair<Set<WatchType>, Set<WatchType>>) {
availableWatchStatusTypes = data
holder?.setAvailableWatchStatusTypes(data)
}
companion object {
private const val VIEW_TYPE_HEADER = 2
private const val VIEW_TYPE_ITEM = 1
}
fun setResumeWatchingData(resumeWatching: List<SearchResponse>) {
resumeWatchingData = resumeWatching
holder?.updateResume(resumeWatchingData)
}
fun setPreviewData(preview: Resource<Pair<Boolean, List<LoadResponse>>>) {
previewData = preview
holder?.updatePreview(preview)
}
fun setApiName(name: String) {
apiName = name
holder?.updateApiName(name)
}
fun setBookmarkData(data: Pair<Boolean, List<SearchResponse>>) {
bookmarkData = data
holder?.updateBookmarks(data)
}
override fun getItemViewType(position: Int) = when (position) {
0 -> VIEW_TYPE_HEADER
else -> VIEW_TYPE_ITEM
}
var holder: HeaderViewHolder? = null
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is HeaderViewHolder -> {
holder.updatePreview(previewData)
holder.updateResume(resumeWatchingData)
holder.updateBookmarks(bookmarkData)
holder.setAvailableWatchStatusTypes(availableWatchStatusTypes)
holder.updateApiName(apiName)
}
else -> super.onBindViewHolder(holder, position - 1)
is HeaderViewHolder -> {}
else -> super.onBindViewHolder(holder, position - headItems)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
println("onCreateViewHolder $viewType")
return when (viewType) {
VIEW_TYPE_HEADER -> HeaderViewHolder(
LayoutInflater.from(parent.context).inflate(
if (isTvSettings()) R.layout.fragment_home_head_tv else R.layout.fragment_home_head,
VIEW_TYPE_HEADER -> {
val inflater = LayoutInflater.from(parent.context)
val binding = if (isTvSettings()) FragmentHomeHeadTvBinding.inflate(
inflater,
parent,
false
),
loadCallback,
loadMoreCallback,
changeHomePageCallback,
clickCallback,
reloadStored,
loadStoredData,
searchQueryCallback,
moreInfoClickCallback
).also {
this.holder = it
) else FragmentHomeHeadBinding.inflate(inflater, parent, false)
HeaderViewHolder(
binding,
viewModel,
)
}
VIEW_TYPE_ITEM -> super.onCreateViewHolder(parent, viewType)
else -> error("Unhandled viewType=$viewType")
}
@ -167,7 +123,7 @@ class HomeParentItemAdapterPreview(
}
override fun getItemId(position: Int): Long {
if (position == 0) return previewData.hashCode().toLong()
if (position == 0) return 0//previewData.hashCode().toLong()
return super.getItemId(position - headItems)
}
@ -176,6 +132,7 @@ class HomeParentItemAdapterPreview(
is HeaderViewHolder -> {
holder.onViewDetachedFromWindow()
}
else -> super.onViewDetachedFromWindow(holder)
}
}
@ -185,6 +142,7 @@ class HomeParentItemAdapterPreview(
is HeaderViewHolder -> {
holder.onViewAttachedToWindow()
}
else -> super.onViewAttachedToWindow(holder)
}
}
@ -192,16 +150,9 @@ class HomeParentItemAdapterPreview(
class HeaderViewHolder
constructor(
itemView: View,
private val clickCallback: ((LoadClickCallback) -> Unit)?,
private val loadMoreCallback: (() -> Unit),
private val changeHomePageCallback: ((View) -> Unit),
private val searchClickCallback: (SearchClickCallback) -> Unit,
private val reloadStored: () -> Unit,
private val loadStoredData: ((Set<WatchType>) -> Unit),
private val searchQueryCallback: ((Pair<Boolean, String>) -> Unit),
private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit
) : RecyclerView.ViewHolder(itemView) {
val binding: ViewBinding,
val viewModel: HomeViewModel,
) : RecyclerView.ViewHolder(binding.root) {
private var previewAdapter: HomeScrollAdapter? = null
private val previewViewpager: ViewPager2? = itemView.home_preview_viewpager
private val previewHeader: FrameLayout? = itemView.home_preview
@ -214,8 +165,8 @@ class HomeParentItemAdapterPreview(
previewAdapter?.apply {
if (position >= itemCount - 1 && hasMoreItems) {
hasMoreItems = false // dont make two requests
loadMoreCallback()
hasMoreItems = false // don't make two requests
viewModel.loadMoreHomeScrollResponses()
//homeViewModel.loadMoreHomeScrollResponses()
}
}
@ -261,7 +212,7 @@ class HomeParentItemAdapterPreview(
// itemView.home_preview_title?.text = name
itemView.home_preview_play?.setOnClickListener { view ->
clickCallback?.invoke(
viewModel.click(
LoadClickCallback(
START_ACTION_RESUME_LATEST,
view,
@ -271,13 +222,13 @@ class HomeParentItemAdapterPreview(
)
}
itemView.home_preview_info?.setOnClickListener { view ->
clickCallback?.invoke(
viewModel.click(
LoadClickCallback(0, view, position, this)
)
}
itemView.home_preview_play_btt?.setOnClickListener { view ->
clickCallback?.invoke(
viewModel.click(
LoadClickCallback(
START_ACTION_RESUME_LATEST,
view,
@ -298,7 +249,7 @@ class HomeParentItemAdapterPreview(
itemView.home_preview_info_btt?.setOnClickListener { view ->
clickCallback?.invoke(
viewModel.click(
LoadClickCallback(0, view, position, this)
)
}
@ -362,7 +313,7 @@ class HomeParentItemAdapterPreview(
this,
newValue
)
reloadStored()
viewModel.reloadStored()
}
}
}
@ -384,6 +335,24 @@ class HomeParentItemAdapterPreview(
fun onViewAttachedToWindow() {
previewViewpager?.registerOnPageChangeCallback(previewCallback)
binding.root.findViewTreeLifecycleOwner()?.apply {
observe(viewModel.preview) {
updatePreview(it)
}
observe(viewModel.apiName) {
updateApiName(it)
}
observe(viewModel.resumeWatching) {
updateResume(it)
}
observe(viewModel.bookmarks) {
updateBookmarks(it)
}
observe(viewModel.availableWatchStatusTypes) {
setAvailableWatchStatusTypes(it)
}
} ?: debugException { "Expected findViewTreeLifecycleOwner" }
}
private val toggleList = listOf(
@ -395,11 +364,17 @@ class HomeParentItemAdapterPreview(
)
init {
itemView.home_preview_change_api?.setOnClickListener { view ->
changeHomePageCallback(view)
if (binding is FragmentHomeHeadTvBinding) {
binding.homePreviewChangeApi.setOnClickListener { view ->
view.context.selectHomepage(viewModel.repo?.name) { api ->
viewModel.loadAndCancel(api)
}
}
binding.homePreviewChangeApi2.setOnClickListener { view ->
view.context.selectHomepage(viewModel.repo?.name) { api ->
viewModel.loadAndCancel(api)
}
}
itemView.home_preview_change_api2?.setOnClickListener { view ->
changeHomePageCallback(view)
}
previewViewpager?.apply {
@ -421,7 +396,7 @@ class HomeParentItemAdapterPreview(
nextFocusDown = itemView.nextFocusDownId
) { callback ->
if (callback.action != SEARCH_ACTION_SHOW_METADATA) {
searchClickCallback(callback)
viewModel.click(callback)
return@HomeChildItemAdapter
}
callback.view.context?.getActivity()?.showOptionSelectStringRes(
@ -440,7 +415,7 @@ class HomeParentItemAdapterPreview(
when (actionId + if (isTv) 0 else 1) {
// play
0 -> {
searchClickCallback.invoke(
viewModel.click(
SearchClickCallback(
START_ACTION_RESUME_LATEST,
callback.view,
@ -448,11 +423,11 @@ class HomeParentItemAdapterPreview(
callback.card
)
)
reloadStored()
viewModel.reloadStored()
}
//info
1 -> {
searchClickCallback(
viewModel.click(
SearchClickCallback(
SEARCH_ACTION_LOAD,
callback.view,
@ -461,14 +436,14 @@ class HomeParentItemAdapterPreview(
)
)
reloadStored()
viewModel.reloadStored()
}
// remove
2 -> {
val card = callback.card
if (card is DataStoreHelper.ResumeWatchingResult) {
DataStoreHelper.removeLastWatched(card.parentId)
reloadStored()
viewModel.reloadStored()
}
}
}
@ -483,7 +458,7 @@ class HomeParentItemAdapterPreview(
nextFocusDown = itemView.nextFocusDownId
) { callback ->
if (callback.action != SEARCH_ACTION_SHOW_METADATA) {
searchClickCallback(callback)
viewModel.click(callback)
return@HomeChildItemAdapter
}
callback.view.context?.getActivity()?.showOptionSelectStringRes(
@ -501,7 +476,7 @@ class HomeParentItemAdapterPreview(
) { (isTv, actionId) ->
when (actionId + if (isTv) 0 else 1) { // play
0 -> {
searchClickCallback.invoke(
viewModel.click(
SearchClickCallback(
START_ACTION_RESUME_LATEST,
callback.view,
@ -509,10 +484,11 @@ class HomeParentItemAdapterPreview(
callback.card
)
)
reloadStored()
viewModel.reloadStored()
}
1 -> { // info
searchClickCallback(
viewModel.click(
SearchClickCallback(
SEARCH_ACTION_LOAD,
callback.view,
@ -521,14 +497,15 @@ class HomeParentItemAdapterPreview(
)
)
reloadStored()
viewModel.reloadStored()
}
2 -> { // remove
DataStoreHelper.setResultWatchState(
callback.card.id,
WatchType.NONE.internalId
)
reloadStored()
viewModel.reloadStored()
}
}
}
@ -540,7 +517,7 @@ class HomeParentItemAdapterPreview(
chip?.isChecked = false
chip?.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
loadStoredData(
viewModel.loadStoredData(
setOf(watch)
// If we filter all buttons then two can be checked at the same time
// Revert this if you want to go back to multi selection
@ -549,7 +526,7 @@ class HomeParentItemAdapterPreview(
}
// Else if all are unchecked -> Do not load data
else if (toggleList.all { it.first?.isChecked != true }) {
loadStoredData(emptySet())
viewModel.loadStoredData(emptySet())
}
}
}
@ -558,25 +535,26 @@ class HomeParentItemAdapterPreview(
itemView.home_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
searchQueryCallback.invoke(false to query)
viewModel.queryTextSubmit(query)
//QuickSearchFragment.pushSearch(activity, query, currentApiName?.let { arrayOf(it) }
return true
}
override fun onQueryTextChange(newText: String): Boolean {
searchQueryCallback.invoke(true to newText)
viewModel.queryTextChange(newText)
//searchViewModel.quickSearch(newText)
return true
}
})
}
fun updateApiName(name: String) {
private fun updateApiName(name: String) {
itemView.home_preview_change_api2?.text = name
itemView.home_preview_change_api?.text = name
}
fun updatePreview(preview: Resource<Pair<Boolean, List<LoadResponse>>>) {
private fun updatePreview(preview: Resource<Pair<Boolean, List<LoadResponse>>>) {
itemView.home_preview_change_api2?.isGone = preview is Resource.Success
if (preview is Resource.Success) {
itemView.home_none_padding?.apply {
@ -604,6 +582,7 @@ class HomeParentItemAdapterPreview(
previewHeader?.isVisible = true
}
}
else -> {
previewAdapter?.setItems(listOf(), false)
previewViewpager?.setCurrentItem(0, false)
@ -614,13 +593,13 @@ class HomeParentItemAdapterPreview(
//previewViewpager?.postInvalidate()
}
fun updateResume(resumeWatching: List<SearchResponse>) {
private fun updateResume(resumeWatching: List<SearchResponse>) {
resumeHolder?.isVisible = resumeWatching.isNotEmpty()
resumeAdapter?.updateList(resumeWatching)
if (!isTvSettings()) {
itemView.home_watch_parent_item_title?.setOnClickListener {
moreInfoClickCallback.invoke(
viewModel.popup(
HomeViewModel.ExpandableHomepageList(
HomePageList(
itemView.home_watch_parent_item_title?.text.toString(),
@ -633,9 +612,10 @@ class HomeParentItemAdapterPreview(
}
}
fun updateBookmarks(data: Pair<Boolean, List<SearchResponse>>) {
bookmarkHolder?.isVisible = data.first
bookmarkAdapter?.updateList(data.second)
private fun updateBookmarks(data: Pair<Boolean, List<SearchResponse>>) {
val (visible, list) = data
bookmarkHolder?.isVisible = visible
bookmarkAdapter?.updateList(list)
if (!isTvSettings()) {
itemView.home_bookmark_parent_item_title?.setOnClickListener {
val items = toggleList.mapNotNull { it.first }.filter { it.isChecked }
@ -643,11 +623,11 @@ class HomeParentItemAdapterPreview(
val textSum = items
.mapNotNull { it.text }.joinToString()
moreInfoClickCallback.invoke(
viewModel.popup(
HomeViewModel.ExpandableHomepageList(
HomePageList(
textSum,
data.second,
list,
false
), 1, false
)
@ -656,11 +636,12 @@ class HomeParentItemAdapterPreview(
}
}
fun setAvailableWatchStatusTypes(availableWatchStatusTypes: Pair<Set<WatchType>, Set<WatchType>>) {
private fun setAvailableWatchStatusTypes(availableWatchStatusTypes: Pair<Set<WatchType>, Set<WatchType>>) {
val (visible, checked) = availableWatchStatusTypes
for ((chip, watch) in toggleList) {
chip?.apply {
isVisible = availableWatchStatusTypes.second.contains(watch)
isChecked = availableWatchStatusTypes.first.contains(watch)
isVisible = visible.contains(watch)
isChecked = checked.contains(watch)
}
}
}

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.ui.home
import android.os.Build
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@ -13,13 +14,24 @@ import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_FOCUSED
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import com.lagradost.cloudstream3.ui.search.SearchHelper
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.addProgramsToContinueWatching
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
@ -72,7 +84,7 @@ class HomeViewModel : ViewModel() {
}
}
private var repo: APIRepository? = null
var repo: APIRepository? = null
private val _apiName = MutableLiveData<String>()
val apiName: LiveData<String> = _apiName
@ -101,8 +113,14 @@ class HomeViewModel : ViewModel() {
val resumeWatching: LiveData<List<SearchResponse>> = _resumeWatching
val preview: LiveData<Resource<Pair<Boolean, List<LoadResponse>>>> = _preview
fun loadResumeWatching() = viewModelScope.launchSafe {
private fun loadResumeWatching() = viewModelScope.launchSafe {
val resumeWatchingResult = getResumeWatching()
if (isTrueTvSettings() && resumeWatchingResult != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ioSafe {
// this WILL crash on non tvs, so keep this inside a try catch
activity?.addProgramsToContinueWatching(resumeWatchingResult)
}
}
resumeWatchingResult?.let {
_resumeWatching.postValue(it)
}
@ -128,6 +146,10 @@ class HomeViewModel : ViewModel() {
currentWatchTypes.remove(WatchType.NONE)
if (currentWatchTypes.size <= 0) {
setKey(
HOME_BOOKMARK_VALUE_LIST,
intArrayOf()
)
_availableWatchStatusTypes.postValue(setOf<WatchType>() to setOf())
_bookmarks.postValue(Pair(false, ArrayList()))
return@launchSafe
@ -135,7 +157,10 @@ class HomeViewModel : ViewModel() {
val watchPrefNotNull = preferredWatchStatus ?: EnumSet.of(currentWatchTypes.first())
//if (currentWatchTypes.any { watchPrefNotNull.contains(it) }) watchPrefNotNull else listOf(currentWatchTypes.first())
setKey(
HOME_BOOKMARK_VALUE_LIST,
watchPrefNotNull.map { it.internalId }.toIntArray()
)
_availableWatchStatusTypes.postValue(
Pair(
watchPrefNotNull,
@ -337,14 +362,80 @@ class HomeViewModel : ViewModel() {
logError(e)
}
}
is Resource.Failure -> {
_page.postValue(data!!)
_preview.postValue(data!!)
}
else -> Unit
}
}
fun click(callback: SearchClickCallback) {
if (callback.action == SEARCH_ACTION_FOCUSED) {
//focusCallback(callback.card)
} else {
SearchHelper.handleSearchClickCallback(callback)
}
}
private val _popup = MutableLiveData<ExpandableHomepageList?>(null)
val popup: LiveData<ExpandableHomepageList?> = _popup
fun popup(list: ExpandableHomepageList?) {
_popup.postValue(list)
}
private fun bookmarksUpdated(unused: Boolean) {
reloadStored()
}
private fun afterPluginsLoaded(forceReload: Boolean) {
loadAndCancel(getKey(USER_SELECTED_HOMEPAGE_API), forceReload)
}
private fun afterMainPluginsLoaded(unused: Boolean = false) {
loadAndCancel(getKey(USER_SELECTED_HOMEPAGE_API), false)
}
init {
MainActivity.bookmarksUpdatedEvent += ::bookmarksUpdated
MainActivity.afterPluginsLoadedEvent += ::afterPluginsLoaded
MainActivity.mainPluginsLoadedEvent += ::afterMainPluginsLoaded
}
override fun onCleared() {
MainActivity.bookmarksUpdatedEvent -= ::bookmarksUpdated
MainActivity.afterPluginsLoadedEvent -= ::afterPluginsLoaded
MainActivity.mainPluginsLoadedEvent -= ::afterMainPluginsLoaded
super.onCleared()
}
fun queryTextSubmit(query: String) {
QuickSearchFragment.pushSearch(
query,
repo?.name?.let { arrayOf(it) })
}
fun queryTextChange(newText: String) {
// do nothing
}
fun reloadStored() {
loadResumeWatching()
val list = EnumSet.noneOf(WatchType::class.java)
getKey<IntArray>(HOME_BOOKMARK_VALUE_LIST)?.map { WatchType.fromInternalId(it) }?.let {
list.addAll(it)
}
loadStoredData(list)
}
fun click(load: LoadClickCallback) {
loadResult(load.response.url, load.response.apiName, load.action)
}
fun loadAndCancel(preferredApiName: String?, forceReload: Boolean = true) =
viewModelScope.launchSafe {
// Since plugins are loaded in stages this function can get called multiple times.

View file

@ -243,14 +243,12 @@ abstract class AbstractPlayerFragment(
fun showToast(message: String, gotoNext: Boolean = false) {
if (gotoNext && hasNextMirror()) {
showToast(
activity,
message,
Toast.LENGTH_SHORT
)
nextMirror()
} else {
showToast(
activity,
context?.getString(R.string.no_links_found_toast) + "\n" + message,
Toast.LENGTH_LONG
)
@ -461,7 +459,7 @@ abstract class AbstractPlayerFragment(
player_view?.resizeMode = type
if (showToast)
showToast(activity, resize.nameRes, Toast.LENGTH_SHORT)
showToast(resize.nameRes, Toast.LENGTH_SHORT)
}
override fun onStop() {

View file

@ -509,7 +509,6 @@ class GeneratorPlayer : FullScreenPlayer() {
selectSourceDialog?.dismissSafe()
showToast(
activity,
String.format(ctx.getString(R.string.player_loaded_subtitles), subtitleData.name),
Toast.LENGTH_LONG
)
@ -889,7 +888,7 @@ class GeneratorPlayer : FullScreenPlayer() {
}
private fun noLinksFound() {
showToast(activity, R.string.no_links_found_toast, Toast.LENGTH_SHORT)
showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT)
activity?.popCurrentPage()
}
@ -1357,7 +1356,7 @@ class GeneratorPlayer : FullScreenPlayer() {
}
is Resource.Failure -> {
showToast(activity, it.errorString, Toast.LENGTH_LONG)
showToast(it.errorString, Toast.LENGTH_LONG)
startPlayer()
}
}

View file

@ -19,6 +19,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.QuickSearchBinding
@ -45,6 +46,13 @@ class QuickSearchFragment : Fragment() {
const val AUTOSEARCH_KEY = "autosearch"
const val PROVIDER_KEY = "providers"
fun pushSearch(
autoSearch: String? = null,
providers: Array<String>? = null
) {
pushSearch(activity, autoSearch, providers)
}
fun pushSearch(
activity: Activity?,
autoSearch: String? = null,
@ -151,19 +159,20 @@ class QuickSearchFragment : Fragment() {
ArrayList(),
this,
) { callback ->
SearchHelper.handleSearchClickCallback(activity, callback)
SearchHelper.handleSearchClickCallback(callback)
}
}
try {
binding?.quickSearch?.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) {
logError(e)
}
} else {
binding?.quickSearchMasterRecycler?.adapter =
ParentItemAdapter(mutableListOf(), { callback ->
SearchHelper.handleSearchClickCallback(activity, callback)
SearchHelper.handleSearchClickCallback(callback)
//when (callback.action) {
//SEARCH_ACTION_LOAD -> {
// clickCallback?.invoke(callback)
@ -235,11 +244,13 @@ class QuickSearchFragment : Fragment() {
searchExitIcon?.alpha = 1f
binding?.quickSearchLoadingBar?.alpha = 0f
}
is Resource.Failure -> {
// Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
searchExitIcon?.alpha = 1f
binding?.quickSearchLoadingBar?.alpha = 0f
}
is Resource.Loading -> {
searchExitIcon?.alpha = 0f
binding?.quickSearchLoadingBar?.alpha = 1f

View file

@ -388,7 +388,7 @@ open class ResultFragment : ResultTrailerPlayer() {
)
}
else -> handleDownloadClick(activity, click)
else -> handleDownloadClick(click)
}
}
}
@ -575,7 +575,7 @@ open class ResultFragment : ResultTrailerPlayer() {
viewModel.handleAction(activity, episodeClick)
},
{ downloadClickEvent ->
handleDownloadClick(activity, downloadClickEvent)
handleDownloadClick(downloadClickEvent)
}
)
@ -939,7 +939,7 @@ open class ResultFragment : ResultTrailerPlayer() {
val name = (viewModel.page.value as? Resource.Success)?.value?.title
?: txt(R.string.no_data).asStringNull(context) ?: ""
showToast(activity, txt(message, name), Toast.LENGTH_SHORT)
showToast(txt(message, name), Toast.LENGTH_SHORT)
}
result_open_in_browser?.isVisible = d.url.startsWith("http")

View file

@ -187,7 +187,7 @@ class ResultFragmentPhone : ResultFragment() {
ArrayList(),
result_recommendations,
) { callback ->
SearchHelper.handleSearchClickCallback(activity, callback)
SearchHelper.handleSearchClickCallback(callback)
}
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
@ -246,7 +246,6 @@ class ResultFragmentPhone : ResultFragment() {
if (!chromecastSupport) {
media_route_button?.setOnClickListener {
CommonActivity.showToast(
activity,
R.string.no_chromecast_support_toast,
Toast.LENGTH_LONG
)

View file

@ -226,7 +226,7 @@ class ResultFragmentTv : ResultFragment() {
ArrayList(),
result_recommendations,
) { callback ->
SearchHelper.handleSearchClickCallback(activity, callback)
SearchHelper.handleSearchClickCallback(callback)
}
}
}

View file

@ -748,7 +748,6 @@ class ResultViewModel2 : ViewModel() {
if (currentLinks.isEmpty()) {
main {
showToast(
activity,
R.string.no_links_found_toast,
Toast.LENGTH_SHORT
)
@ -757,7 +756,6 @@ class ResultViewModel2 : ViewModel() {
} else {
main {
showToast(
activity,
R.string.download_started,
Toast.LENGTH_SHORT
)
@ -1030,9 +1028,9 @@ class ResultViewModel2 : ViewModel() {
logError(t)
main {
if (t is ActivityNotFoundException) {
showToast(activity, txt(R.string.app_not_found_error), Toast.LENGTH_LONG)
showToast(txt(R.string.app_not_found_error), Toast.LENGTH_LONG)
} else {
showToast(activity, t.toString(), Toast.LENGTH_LONG)
showToast(t.toString(), Toast.LENGTH_LONG)
}
}
}
@ -1285,7 +1283,6 @@ class ResultViewModel2 : ViewModel() {
)
)
showToast(
activity,
R.string.download_started,
Toast.LENGTH_SHORT
)
@ -1293,7 +1290,7 @@ class ResultViewModel2 : ViewModel() {
}
ACTION_SHOW_TOAST -> {
showToast(activity, R.string.play_episode_toast, Toast.LENGTH_SHORT)
showToast(R.string.play_episode_toast, Toast.LENGTH_SHORT)
}
ACTION_DOWNLOAD_EPISODE -> {
@ -1334,7 +1331,6 @@ class ResultViewModel2 : ViewModel() {
)
}
showToast(
activity,
R.string.download_started,
Toast.LENGTH_SHORT
)
@ -1389,7 +1385,7 @@ class ResultViewModel2 : ViewModel() {
val link = result.links[index]
val clip = ClipData.newPlainText(link.name, link.url)
serviceClipboard.setPrimaryClip(clip)
showToast(act, R.string.copy_link_toast, Toast.LENGTH_SHORT)
showToast(R.string.copy_link_toast, Toast.LENGTH_SHORT)
}
}
@ -1400,7 +1396,7 @@ class ResultViewModel2 : ViewModel() {
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
loadLinks(click.data, isVisible = true, isCasting = true) { links ->
if (links.links.isEmpty()) {
showToast(activity, R.string.no_links_found_toast, Toast.LENGTH_SHORT)
showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT)
return@loadLinks
}

View file

@ -214,7 +214,7 @@ class SearchFragment : Fragment() {
ArrayList(),
searchAutofitResults,
) { callback ->
SearchHelper.handleSearchClickCallback(activity, callback)
SearchHelper.handleSearchClickCallback(callback)
}
@ -491,7 +491,7 @@ class SearchFragment : Fragment() {
val masterAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
ParentItemAdapter(mutableListOf(), { callback ->
SearchHelper.handleSearchClickCallback(activity, callback)
SearchHelper.handleSearchClickCallback(callback)
}, { item ->
bottomSheetDialog = activity?.loadHomepageList(item, dismissCallback = {
bottomSheetDialog = null

View file

@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.ui.search
import android.app.Activity
import android.widget.Toast
import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainActivity
import com.lagradost.cloudstream3.R
@ -15,21 +16,21 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
object SearchHelper {
fun handleSearchClickCallback(activity: Activity?, callback: SearchClickCallback) {
fun handleSearchClickCallback(callback: SearchClickCallback) {
val card = callback.card
when (callback.action) {
SEARCH_ACTION_LOAD -> {
activity.loadSearchResult(card)
loadSearchResult(card)
}
SEARCH_ACTION_PLAY_FILE -> {
if (card is DataStoreHelper.ResumeWatchingResult) {
val id = card.id
if(id == null) {
showToast(activity, R.string.error_invalid_id, Toast.LENGTH_SHORT)
showToast(R.string.error_invalid_id, Toast.LENGTH_SHORT)
} else {
if (card.isFromDownload) {
handleDownloadClick(
activity, DownloadClickEvent(
DownloadClickEvent(
DOWNLOAD_ACTION_PLAY_FILE,
VideoDownloadHelper.DownloadEpisodeCached(
card.name,
@ -45,12 +46,11 @@ object SearchHelper {
)
)
} else {
activity.loadSearchResult(card, START_ACTION_LOAD_EP, id)
loadSearchResult(card, START_ACTION_LOAD_EP, id)
}
}
} else {
handleSearchClickCallback(
activity,
SearchClickCallback(SEARCH_ACTION_LOAD, callback.view, -1, callback.card)
)
}
@ -60,10 +60,10 @@ object SearchHelper {
(activity as? MainActivity?)?.apply {
loadPopup(callback.card)
} ?: kotlin.run {
showToast(activity, callback.card.name, Toast.LENGTH_SHORT)
showToast(callback.card.name, Toast.LENGTH_SHORT)
}
} else {
showToast(activity, callback.card.name, Toast.LENGTH_SHORT)
showToast(callback.card.name, Toast.LENGTH_SHORT)
}
}
}

View file

@ -216,7 +216,6 @@ class SettingsAccount : PreferenceFragmentCompat() {
activity.runOnUiThread {
try {
showToast(
activity,
activity.getString(if (isSuccessful) R.string.authenticated_user else R.string.authenticated_user_fail)
.format(
api.name

View file

@ -213,7 +213,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
val lang = binding.siteLangInput.text?.toString()
val realLang = if (lang.isNullOrBlank()) provider.lang else lang
if (url.isNullOrBlank() || name.isNullOrBlank() || realLang.length != 2) {
showToast(activity, R.string.error_invalid_data, Toast.LENGTH_SHORT)
showToast(R.string.error_invalid_data, Toast.LENGTH_SHORT)
return@setOnClickListener
}

View file

@ -95,7 +95,7 @@ class SettingsUpdates : PreferenceFragmentCompat() {
serviceClipboard.setPrimaryClip(clip)
dialog.dismissSafe(activity)
} catch (e: TransactionTooLargeException) {
showToast(activity, R.string.clipboard_too_large)
showToast(R.string.clipboard_too_large)
}
}
binding.clearBtt.setOnClickListener {
@ -158,7 +158,6 @@ class SettingsUpdates : PreferenceFragmentCompat() {
if (activity?.runAutoUpdate(false) == false) {
activity?.runOnUiThread {
showToast(
activity,
R.string.no_update_found,
Toast.LENGTH_SHORT
)

View file

@ -211,7 +211,7 @@ class ExtensionsFragment : Fragment() {
?.let { it1 -> RepositoryManager.parseRepoUrl(it1) }
if (url.isNullOrBlank()) {
main {
showToast(activity, R.string.error_invalid_data, Toast.LENGTH_SHORT)
showToast(R.string.error_invalid_data, Toast.LENGTH_SHORT)
}
} else {
val fixedName = if (!name.isNullOrBlank()) name

View file

@ -86,7 +86,6 @@ class PluginsViewModel : ViewModel() {
}.also { list ->
main {
showToast(
activity,
if (list.isEmpty()) {
txt(
R.string.batch_download_nothing_to_download_format,
@ -113,7 +112,6 @@ class PluginsViewModel : ViewModel() {
}.main { list ->
if (list.any { it }) {
showToast(
activity,
txt(
R.string.batch_download_finish_format,
list.count { it },
@ -123,7 +121,7 @@ class PluginsViewModel : ViewModel() {
)
viewModel?.updatePluginListPrivate(activity, repositoryUrl)
} else if (list.isNotEmpty()) {
showToast(activity, R.string.download_failed, Toast.LENGTH_SHORT)
showToast(R.string.download_failed, Toast.LENGTH_SHORT)
}
}
}
@ -166,9 +164,9 @@ class PluginsViewModel : ViewModel() {
runOnMainThread {
if (success)
showToast(activity, message, Toast.LENGTH_SHORT)
showToast(message, Toast.LENGTH_SHORT)
else
showToast(activity, R.string.error, Toast.LENGTH_SHORT)
showToast(R.string.error, Toast.LENGTH_SHORT)
}
if (success)

View file

@ -194,7 +194,7 @@ class ChromecastSubtitlesFragment : Fragment() {
this.setOnLongClickListener {
it.context.setColor(id, null)
showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
showToast(R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
return@setOnLongClickListener true
}
}
@ -252,7 +252,7 @@ class ChromecastSubtitlesFragment : Fragment() {
binding?.subsEdgeType?.setOnLongClickListener {
state.edgeType = defaultState.edgeType
it.context.updateState()
showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
showToast(R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
return@setOnLongClickListener true
}
@ -293,7 +293,7 @@ class ChromecastSubtitlesFragment : Fragment() {
binding?.subsFontSize?.setOnLongClickListener { _ ->
state.fontScale = defaultState.fontScale
//textView.context.updateState() // font size not changed
showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
showToast(R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
return@setOnLongClickListener true
}

View file

@ -272,7 +272,7 @@ class SubtitlesFragment : Fragment() {
this.setOnLongClickListener {
it.context.setColor(id, null)
showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
showToast(R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
return@setOnLongClickListener true
}
}
@ -322,7 +322,7 @@ class SubtitlesFragment : Fragment() {
subsSubtitleElevation.setOnLongClickListener {
state.elevation = DEF_SUBS_ELEVATION
it.context.updateState()
showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
showToast(R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
return@setOnLongClickListener true
}
@ -367,7 +367,7 @@ class SubtitlesFragment : Fragment() {
subsEdgeType.setOnLongClickListener {
state.edgeType = CaptionStyleCompat.EDGE_TYPE_OUTLINE
it.context.updateState()
showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
showToast(R.string.subs_default_reset_toast, Toast.LENGTH_SHORT)
return@setOnLongClickListener true
}

View file

@ -47,6 +47,7 @@ import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.wrappers.Wrappers
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
import com.lagradost.cloudstream3.mvvm.logError
@ -198,7 +199,11 @@ object AppUtils {
animation.start()
}
fun Context.createNotificationChannel(channelId: String, channelName: String, description: String) {
fun Context.createNotificationChannel(
channelId: String,
channelName: String,
description: String
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel =
@ -288,6 +293,7 @@ object AppUtils {
// https://github.com/googlearchive/leanback-homescreen-channels/blob/master/app/src/main/java/com/google/android/tvhomescreenchannels/SampleTvProvider.java
@SuppressLint("RestrictedApi")
@Throws
@WorkerThread
suspend fun Context.addProgramsToContinueWatching(data: List<DataStoreHelper.ResumeWatchingResult>) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
@ -369,7 +375,6 @@ object AppUtils {
)
main {
showToast(
this@loadRepository,
getString(R.string.player_loaded_subtitles, repo.name),
Toast.LENGTH_LONG
)
@ -577,6 +582,15 @@ object AppUtils {
}
}
fun loadResult(
url: String,
apiName: String,
startAction: Int = 0,
startValue: Int = 0
) {
(activity as FragmentActivity?)?.loadResult(url, apiName, startAction, startValue)
}
fun FragmentActivity.loadResult(
url: String,
apiName: String,
@ -592,6 +606,14 @@ object AppUtils {
}
}
fun loadSearchResult(
card: SearchResponse,
startAction: Int = 0,
startValue: Int? = null,
) {
activity?.loadSearchResult(card, startAction, startValue)
}
fun Activity?.loadSearchResult(
card: SearchResponse,
startAction: Int = 0,

View file

@ -149,7 +149,7 @@ object BackupUtils {
fun FragmentActivity.backup() {
try {
if (!checkWrite()) {
showToast(this, getString(R.string.backup_failed), Toast.LENGTH_LONG)
showToast(getString(R.string.backup_failed), Toast.LENGTH_LONG)
requestRW()
return
}
@ -201,7 +201,6 @@ object BackupUtils {
printStream.close()
showToast(
this,
R.string.backup_success,
Toast.LENGTH_LONG
)
@ -209,7 +208,6 @@ object BackupUtils {
logError(e)
try {
showToast(
this,
getString(R.string.backup_failed_error_format).format(e.toString()),
Toast.LENGTH_LONG
)
@ -243,7 +241,6 @@ object BackupUtils {
logError(e)
main { // smth can fail in .format
showToast(
activity,
getString(R.string.restore_failed_format).format(e.toString())
)
}
@ -270,7 +267,7 @@ object BackupUtils {
)
)
} catch (e: Exception) {
showToast(this, e.message)
showToast(e.message)
logError(e)
}
}

View file

@ -300,7 +300,7 @@ class InAppUpdater {
// Forcefully start any delayed installations
if (ApkInstaller.delayedInstaller?.startInstallation() == true) return@setPositiveButton
showToast(context, R.string.download_started, Toast.LENGTH_LONG)
showToast(R.string.download_started, Toast.LENGTH_LONG)
// Check if the setting hasn't been changed
if (settingsManager.getInt(
@ -335,7 +335,6 @@ class InAppUpdater {
if (!downloadUpdate(update.updateURL))
runOnUiThread {
showToast(
context,
R.string.download_failed,
Toast.LENGTH_LONG
)

View file

@ -46,6 +46,7 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions.bitmapTransform
import com.bumptech.glide.request.target.Target
import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.result.UiImage
@ -554,7 +555,7 @@ object UIHelper {
}
fun Dialog?.dismissSafe() {
if (this?.isShowing == true) {
if (this?.isShowing == true && activity?.isFinishing != true) {
this.dismiss()
}
}