bookmarks working

This commit is contained in:
LagradOst 2021-07-31 01:41:54 +02:00
parent a467060486
commit 5523d6539a
14 changed files with 233 additions and 84 deletions

View file

@ -13,8 +13,8 @@ android {
applicationId "com.lagradost.cloudstream3" applicationId "com.lagradost.cloudstream3"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode 8 versionCode 9
versionName "1.1.6" versionName "1.1.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -56,7 +56,7 @@ object APIHolder {
} }
fun LoadResponse.getId(): Int { fun LoadResponse.getId(): Int {
return url.replace(getApiFromName(apiName).mainUrl, "").hashCode() return url.replace(getApiFromName(apiName).mainUrl, "").replace("/", "").hashCode()
} }
fun Activity.getApiSettings(): HashSet<String> { fun Activity.getApiSettings(): HashSet<String> {
@ -86,7 +86,7 @@ abstract class MainAPI {
open val hasMainPage = false open val hasMainPage = false
open val hasQuickSearch = false open val hasQuickSearch = false
open fun getMainPage() : HomePageResponse? { open fun getMainPage(): HomePageResponse? {
return null return null
} }
@ -189,6 +189,7 @@ interface SearchResponse {
val type: TvType val type: TvType
val posterUrl: String? val posterUrl: String?
val year: Int? val year: Int?
val id: Int?
} }
data class AnimeSearchResponse( data class AnimeSearchResponse(
@ -204,6 +205,7 @@ data class AnimeSearchResponse(
val dubStatus: EnumSet<DubStatus>?, val dubStatus: EnumSet<DubStatus>?,
val dubEpisodes: Int?, val dubEpisodes: Int?,
val subEpisodes: Int?, val subEpisodes: Int?,
override val id: Int? = null,
) : SearchResponse ) : SearchResponse
data class MovieSearchResponse( data class MovieSearchResponse(
@ -214,6 +216,7 @@ data class MovieSearchResponse(
override val posterUrl: String?, override val posterUrl: String?,
override val year: Int?, override val year: Int?,
override val id: Int? = null,
) : SearchResponse ) : SearchResponse
data class TvSeriesSearchResponse( data class TvSeriesSearchResponse(
@ -225,6 +228,7 @@ data class TvSeriesSearchResponse(
override val posterUrl: String?, override val posterUrl: String?,
override val year: Int?, override val year: Int?,
val episodes: Int?, val episodes: Int?,
override val id: Int? = null,
) : SearchResponse ) : SearchResponse
interface LoadResponse { interface LoadResponse {

View file

@ -13,7 +13,6 @@ import androidx.navigation.NavOptions
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor
@ -31,6 +30,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
import com.lagradost.cloudstream3.utils.Event
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_result.* import kotlinx.android.synthetic.main.fragment_result.*
@ -59,6 +59,8 @@ class MainActivity : AppCompatActivity() {
var isInPlayer: Boolean = false var isInPlayer: Boolean = false
var canShowPipMode: Boolean = false var canShowPipMode: Boolean = false
var isInPIPMode: Boolean = false var isInPIPMode: Boolean = false
val backEvent = Event<Boolean>()
lateinit var navOptions: NavOptions lateinit var navOptions: NavOptions
} }
@ -110,8 +112,10 @@ class MainActivity : AppCompatActivity() {
.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit) .setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit)
.remove(currentFragment) .remove(currentFragment)
.commitAllowingStateLoss() .commitAllowingStateLoss()
backEvent.invoke(true)
return true return true
} }
backEvent.invoke(false)
return false return false
} }

View file

@ -10,11 +10,14 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import kotlinx.android.synthetic.main.home_result_grid.view.* import kotlinx.android.synthetic.main.home_result_grid.view.*
class HomeChildItemAdapter( class HomeChildItemAdapter(
var cardList: List<Any>, var cardList: List<SearchResponse>,
private val clickCallback: (SearchResponse) -> Unit private val clickCallback: (SearchClickCallback) -> Unit
) : ) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
@ -38,7 +41,7 @@ class HomeChildItemAdapter(
} }
class CardViewHolder class CardViewHolder
constructor(itemView: View, private val clickCallback: (SearchResponse) -> Unit) : constructor(itemView: View, private val clickCallback: (SearchClickCallback) -> Unit) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(itemView) {
val cardView: ImageView = itemView.imageView val cardView: ImageView = itemView.imageView
private val cardText: TextView = itemView.imageText private val cardText: TextView = itemView.imageText
@ -52,50 +55,51 @@ class HomeChildItemAdapter(
//val imageTextProvider: TextView? = itemView.imageTextProvider //val imageTextProvider: TextView? = itemView.imageTextProvider
private val bg: CardView = itemView.backgroundCard private val bg: CardView = itemView.backgroundCard
fun bind(card: Any) { fun bind(card: SearchResponse) {
if (card is SearchResponse) { // GENERIC textType?.text = when (card.type) {
TvType.Anime -> "Anime"
TvType.Movie -> "Movie"
TvType.ONA -> "ONA"
TvType.TvSeries -> "TV"
}
// search_result_lang?.visibility = View.GONE
textType?.text = when (card.type) { textIsDub?.visibility = View.GONE
TvType.Anime -> "Anime" textIsSub?.visibility = View.GONE
TvType.Movie -> "Movie"
TvType.ONA -> "ONA"
TvType.TvSeries -> "TV"
}
// search_result_lang?.visibility = View.GONE
textIsDub?.visibility = View.GONE cardText.text = card.name
textIsSub?.visibility = View.GONE
cardText.text = card.name //imageTextProvider.text = card.apiName
if (!card.posterUrl.isNullOrEmpty()) {
//imageTextProvider.text = card.apiName val glideUrl =
if (!card.posterUrl.isNullOrEmpty()) { GlideUrl(card.posterUrl)
val glideUrl = Glide.with(cardView.context)
GlideUrl(card.posterUrl) .load(glideUrl)
.into(cardView)
Glide.with(cardView.context) }
.load(glideUrl)
.into(cardView)
} bg.setOnClickListener {
clickCallback.invoke(SearchClickCallback(SEARCH_ACTION_LOAD, it, card))
}
bg.setOnClickListener { bg.setOnLongClickListener {
clickCallback.invoke(card) clickCallback.invoke(SearchClickCallback(SEARCH_ACTION_SHOW_METADATA, it, card))
// (activity as AppCompatActivity).loadResult(card.url, card.slug, card.apiName) return@setOnLongClickListener true
} }
when (card) { when (card) {
is AnimeSearchResponse -> { is AnimeSearchResponse -> {
if (card.dubStatus?.size == 1) { if (card.dubStatus?.size == 1) {
//search_result_lang?.visibility = View.VISIBLE //search_result_lang?.visibility = View.VISIBLE
if (card.dubStatus.contains(DubStatus.Dubbed)) { if (card.dubStatus.contains(DubStatus.Dubbed)) {
textIsDub?.visibility = View.VISIBLE textIsDub?.visibility = View.VISIBLE
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.dubColor)) //search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.dubColor))
} else if (card.dubStatus.contains(DubStatus.Subbed)) { } else if (card.dubStatus.contains(DubStatus.Subbed)) {
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.subColor)) //search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.subColor))
textIsSub?.visibility = View.VISIBLE textIsSub?.visibility = View.VISIBLE
}
} }
} }
} }

View file

@ -17,26 +17,33 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.AnimeSearchResponse import com.lagradost.cloudstream3.MainActivity.Companion.backEvent
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.AutofitRecyclerView import com.lagradost.cloudstream3.ui.AutofitRecyclerView
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
import com.lagradost.cloudstream3.ui.search.SearchAdapter import com.lagradost.cloudstream3.ui.search.SearchAdapter
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
import com.lagradost.cloudstream3.utils.Event import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.HOMEPAGE_API import com.lagradost.cloudstream3.utils.HOMEPAGE_API
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
import kotlinx.android.synthetic.main.fragment_home.* import kotlinx.android.synthetic.main.fragment_home.*
const val HOME_BOOKMARK_VALUE = "home_bookmarked_last"
class HomeFragment : Fragment() { class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel private lateinit var homeViewModel: HomeViewModel
@ -142,6 +149,28 @@ class HomeFragment : Fragment() {
fixGrid() fixGrid()
} }
override fun onResume() {
backEvent += ::handleBack
super.onResume()
}
override fun onStop() {
backEvent -= ::handleBack
super.onStop()
}
private fun reloadStored() {
context?.let { ctx ->
homeViewModel.loadStoredData(ctx, WatchType.fromInternalId(ctx.getKey(HOME_BOOKMARK_VALUE)))
}
}
private fun handleBack(poppedFragment: Boolean) {
if (poppedFragment) {
reloadStored()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
fixGrid() fixGrid()
@ -225,9 +254,7 @@ class HomeFragment : Fragment() {
} }
} }
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf(), { card -> fun loadHomepageList(item: HomePageList) {
activity.loadSearchResult(card)
}, { item ->
val bottomSheetDialogBuilder = BottomSheetDialog(view.context) val bottomSheetDialogBuilder = BottomSheetDialog(view.context)
bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded) bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded)
val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!! val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!!
@ -242,9 +269,11 @@ class HomeFragment : Fragment() {
// Span settings // Span settings
recycle.spanCount = currentSpan recycle.spanCount = currentSpan
recycle.adapter = SearchAdapter(item.list, recycle) { card -> recycle.adapter = SearchAdapter(item.list, recycle) { callback ->
bottomSheetDialogBuilder.dismiss() handleSearchClickCallback(activity, callback)
activity.loadSearchResult(card) if (callback.action == SEARCH_ACTION_LOAD) {
bottomSheetDialogBuilder.dismiss()
}
} }
val spanListener = { span: Int -> val spanListener = { span: Int ->
@ -261,13 +290,66 @@ class HomeFragment : Fragment() {
(recycle.adapter as SearchAdapter).notifyDataSetChanged() (recycle.adapter as SearchAdapter).notifyDataSetChanged()
bottomSheetDialogBuilder.show() bottomSheetDialogBuilder.show()
}
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf(), { callback ->
handleSearchClickCallback(activity, callback)
}, { item ->
loadHomepageList(item)
}) })
observe(homeViewModel.availableWatchStatusTypes) { availableWatchStatusTypes ->
context?.setKey(HOME_BOOKMARK_VALUE, availableWatchStatusTypes.first.internalId)
home_bookmark_select?.setOnClickListener {
it.popupMenuNoIcons(availableWatchStatusTypes.second.map { type ->
Pair(
type.internalId,
type.stringRes
)
}) {
homeViewModel.loadStoredData(it.context, WatchType.fromInternalId(this.itemId))
}
}
home_bookmarked_parent_item_title?.text = getString(availableWatchStatusTypes.first.stringRes)
}
observe(homeViewModel.bookmarks) { bookmarks ->
home_bookmarked_holder.visibility = if (bookmarks.isNotEmpty()) View.VISIBLE else View.GONE
(home_bookmarked_child_recyclerview?.adapter as HomeChildItemAdapter?)?.cardList = bookmarks
home_bookmarked_child_recyclerview?.adapter?.notifyDataSetChanged()
home_bookmarked_child_more_info.setOnClickListener {
loadHomepageList(
HomePageList(
home_bookmarked_parent_item_title?.text?.toString() ?: getString(R.string.error_bookmarks_text),
bookmarks
)
)
}
}
home_bookmarked_child_recyclerview.adapter = HomeChildItemAdapter(ArrayList()) { callback ->
if (callback.action == SEARCH_ACTION_SHOW_METADATA) {
val id = callback.card.id
if (id != null) {
callback.view.popupMenuNoIcons(listOf(Pair(0, R.string.action_remove_from_bookmarks))) {
if (itemId == 0) {
activity?.setResultWatchState(id, WatchType.NONE.internalId)
reloadStored()
}
}
}
} else {
handleSearchClickCallback(activity, callback)
}
}
context?.fixPaddingStatusbar(home_root) context?.fixPaddingStatusbar(home_root)
home_master_recycler.adapter = adapter home_master_recycler.adapter = adapter
home_master_recycler.layoutManager = GridLayoutManager(context, 1) home_master_recycler.layoutManager = GridLayoutManager(context, 1)
reloadStored()
homeViewModel.load(context?.getKey<String>(HOMEPAGE_API)) homeViewModel.load(context?.getKey<String>(HOMEPAGE_API))
} }
} }

View file

@ -9,11 +9,12 @@ import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.HomePageList import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import kotlinx.android.synthetic.main.homepage_parent.view.* import kotlinx.android.synthetic.main.homepage_parent.view.*
class ParentItemAdapter( class ParentItemAdapter(
var items: List<HomePageList>, var items: List<HomePageList>,
private val clickCallback: (SearchResponse) -> Unit, private val clickCallback: (SearchClickCallback) -> Unit,
private val moreInfoClickCallback: (HomePageList) -> Unit, private val moreInfoClickCallback: (HomePageList) -> Unit,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, i: Int): ParentViewHolder { override fun onCreateViewHolder(parent: ViewGroup, i: Int): ParentViewHolder {
@ -38,7 +39,7 @@ class ParentItemAdapter(
class ParentViewHolder class ParentViewHolder
constructor( constructor(
itemView: View, itemView: View,
private val clickCallback: (SearchResponse) -> Unit, private val clickCallback: (SearchClickCallback) -> Unit,
private val moreInfoClickCallback: (HomePageList) -> Unit private val moreInfoClickCallback: (HomePageList) -> Unit
) : ) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(itemView) {

View file

@ -33,15 +33,17 @@ class HomeViewModel : ViewModel() {
return APIRepository(apis.first { it.hasMainPage }) return APIRepository(apis.first { it.hasMainPage })
} }
private val availableWatchStatusTypes = MutableLiveData<Pair<WatchType, List<WatchType>>>() private val _availableWatchStatusTypes = MutableLiveData<Pair<WatchType, List<WatchType>>>()
private val bookmarks = MutableLiveData<List<SearchResponse>>() val availableWatchStatusTypes: LiveData<Pair<WatchType, List<WatchType>>> = _availableWatchStatusTypes
private val _bookmarks = MutableLiveData<List<SearchResponse>>()
val bookmarks: LiveData<List<SearchResponse>> = _bookmarks
fun loadStoredData(context: Context, preferredWatchStatus: WatchType?) = viewModelScope.launch { fun loadStoredData(context: Context, preferredWatchStatus: WatchType?) = viewModelScope.launch {
val watchStatusIds = withContext(Dispatchers.IO) { val watchStatusIds = withContext(Dispatchers.IO) {
context.getAllWatchStateIds().map { id -> context.getAllWatchStateIds().map { id ->
Pair(id, context.getResultWatchState(id)) Pair(id, context.getResultWatchState(id))
} }
} }.distinctBy { it.first }
val length = WatchType.values().size val length = WatchType.values().size
val currentWatchTypes = HashSet<WatchType>() val currentWatchTypes = HashSet<WatchType>()
@ -52,21 +54,28 @@ class HomeViewModel : ViewModel() {
} }
} }
currentWatchTypes.remove(WatchType.NONE)
if (currentWatchTypes.size <= 0) { if (currentWatchTypes.size <= 0) {
bookmarks.postValue(ArrayList()) _bookmarks.postValue(ArrayList())
return@launch return@launch
} }
val watchStatus = preferredWatchStatus ?: currentWatchTypes.first() val watchPrefNotNull = preferredWatchStatus ?: currentWatchTypes.first()
availableWatchStatusTypes.postValue( val watchStatus =
if (currentWatchTypes.contains(watchPrefNotNull)) watchPrefNotNull else currentWatchTypes.first()
_availableWatchStatusTypes.postValue(
Pair( Pair(
watchStatus, watchStatus,
currentWatchTypes.sortedBy { it.internalId }.toList() currentWatchTypes.sortedBy { it.internalId }.toList()
) )
) )
val list = withContext(Dispatchers.IO) { val list = withContext(Dispatchers.IO) {
watchStatusIds.map { context.getBookmarkedData(it.first) } watchStatusIds.filter { it.second == watchStatus }
.mapNotNull { context.getBookmarkedData(it.first) }
.sortedBy { -it.latestUpdatedTime }
} }
_bookmarks.postValue(list)
} }
fun load(api: MainAPI?) = viewModelScope.launch { fun load(api: MainAPI?) = viewModelScope.launch {

View file

@ -117,7 +117,7 @@ class EpisodeAdapter(
class EpisodeCardViewHolder class EpisodeCardViewHolder
constructor( constructor(
itemView: View, itemView: View,
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(itemView), DownloadButtonViewHolder {
@ -129,8 +129,8 @@ class EpisodeAdapter(
private val episodeProgress: ContentLoadingProgressBar? = itemView.episode_progress private val episodeProgress: ContentLoadingProgressBar? = itemView.episode_progress
private val episodePoster: ImageView? = itemView.episode_poster private val episodePoster: ImageView? = itemView.episode_poster
val episodeDownloadBar: ContentLoadingProgressBar = itemView.result_episode_progress_downloaded private val episodeDownloadBar: ContentLoadingProgressBar = itemView.result_episode_progress_downloaded
val episodeDownloadImage: ImageView = itemView.result_episode_download private val episodeDownloadImage: ImageView = itemView.result_episode_download
private val episodeHolder = itemView.episode_holder private val episodeHolder = itemView.episode_holder

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.ui.search package com.lagradost.cloudstream3.ui.search
import android.app.Activity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -24,12 +25,16 @@ import kotlinx.android.synthetic.main.search_result_compact.view.imageView
import kotlinx.android.synthetic.main.search_result_grid.view.* import kotlinx.android.synthetic.main.search_result_grid.view.*
import kotlin.math.roundToInt import kotlin.math.roundToInt
const val SEARCH_ACTION_LOAD = 0
const val SEARCH_ACTION_SHOW_METADATA = 1
class SearchClickCallback(val action: Int, val view: View, val card: SearchResponse)
class SearchAdapter( class SearchAdapter(
var cardList: List<SearchResponse>, var cardList: List<SearchResponse>,
private val resView: AutofitRecyclerView, private val resView: AutofitRecyclerView,
private val clickCallback: (SearchResponse) -> Unit, private val clickCallback: (SearchClickCallback) -> 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 {
val layout = parent.context.getGridFormatId() val layout = parent.context.getGridFormatId()
@ -54,7 +59,11 @@ class SearchAdapter(
} }
class CardViewHolder class CardViewHolder
constructor(itemView: View, private val clickCallback: (SearchResponse) -> Unit, resView: AutofitRecyclerView) : constructor(
itemView: View,
private val clickCallback: (SearchClickCallback) -> Unit,
resView: AutofitRecyclerView
) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(itemView) {
val cardView: ImageView = itemView.imageView val cardView: ImageView = itemView.imageView
private val cardText: TextView = itemView.imageText private val cardText: TextView = itemView.imageText
@ -105,7 +114,12 @@ class SearchAdapter(
} }
bg.setOnClickListener { bg.setOnClickListener {
clickCallback.invoke(card) clickCallback.invoke(SearchClickCallback(SEARCH_ACTION_LOAD, it, card))
}
bg.setOnLongClickListener {
clickCallback.invoke(SearchClickCallback(SEARCH_ACTION_SHOW_METADATA, it, card))
return@setOnLongClickListener true
} }
when (card) { when (card) {
@ -121,7 +135,6 @@ class SearchAdapter(
} }
} }
} }
} }
} }
} }

View file

@ -9,7 +9,6 @@ import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -24,11 +23,9 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import kotlinx.android.synthetic.main.fragment_search.* import kotlinx.android.synthetic.main.fragment_search.*
class SearchFragment : Fragment() { class SearchFragment : Fragment() {
private lateinit var searchViewModel: SearchViewModel private lateinit var searchViewModel: SearchViewModel
override fun onCreateView( override fun onCreateView(
@ -77,8 +74,8 @@ class SearchFragment : Fragment() {
SearchAdapter( SearchAdapter(
ArrayList(), ArrayList(),
cardSpace, cardSpace,
) { card -> ) { callback ->
activity.loadSearchResult(card) SearchHelper.handleSearchClickCallback(activity, callback)
} }
} }

View file

@ -0,0 +1,21 @@
package com.lagradost.cloudstream3.ui.search
import android.app.Activity
import android.widget.Toast
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
object SearchHelper {
fun handleSearchClickCallback(activity: Activity?, callback: SearchClickCallback) {
val card = callback.card
when (callback.action) {
SEARCH_ACTION_LOAD -> {
activity.loadSearchResult(card)
}
SEARCH_ACTION_SHOW_METADATA -> {
activity?.let { act ->
Toast.makeText(act, callback.card.name, Toast.LENGTH_SHORT).show()
}
}
}
}
}

View file

@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
const val VIDEO_POS_DUR = "video_pos_dur" const val VIDEO_POS_DUR = "video_pos_dur"
@ -27,9 +28,9 @@ object DataStoreHelper {
} }
data class BookmarkedData( data class BookmarkedData(
val parentId: Int, override val id: Int?,
val bookmarkedTime : Long, val bookmarkedTime: Long,
val latestUpdatedTime : Long, val latestUpdatedTime: Long,
override val name: String, override val name: String,
override val url: String, override val url: String,
override val apiName: String, override val apiName: String,
@ -42,17 +43,19 @@ object DataStoreHelper {
fun Context.getAllWatchStateIds(): List<Int> { fun Context.getAllWatchStateIds(): List<Int> {
val folder = "$currentAccount/$RESULT_WATCH_STATE" val folder = "$currentAccount/$RESULT_WATCH_STATE"
return getKeys(folder).mapNotNull { it.removePrefix(folder).toIntOrNull() } return getKeys(folder).mapNotNull {
it.removePrefix("$folder/").toIntOrNull()
}
} }
fun Context.setBookmarkedData(id: Int?, data: BookmarkedData) { fun Context.setBookmarkedData(id: Int?, data: BookmarkedData) {
if (id == null) return if (id == null) return
setKey("$currentAccount/$RESULT_WATCH_STATE", id.toString(), data) setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data)
} }
fun Context.getBookmarkedData(id: Int?): BookmarkedData? { fun Context.getBookmarkedData(id: Int?): BookmarkedData? {
if (id == null) return null if (id == null) return null
return getKey("$currentAccount/$RESULT_WATCH_STATE", id.toString()) return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
} }
fun Context.setViewPos(id: Int?, pos: Long, dur: Long) { fun Context.setViewPos(id: Int?, pos: Long, dur: Long) {
@ -66,7 +69,13 @@ object DataStoreHelper {
fun Context.setResultWatchState(id: Int?, status: Int) { fun Context.setResultWatchState(id: Int?, status: Int) {
if (id == null) return if (id == null) return
setKey("$currentAccount/$RESULT_WATCH_STATE", id.toString(), status) val folder = "$currentAccount/$RESULT_WATCH_STATE"
if (status == WatchType.NONE.internalId) {
removeKey(folder, id.toString())
removeKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
} else {
setKey(folder, id.toString(), status)
}
} }
fun Context.getResultWatchState(id: Int): WatchType { fun Context.getResultWatchState(id: Int): WatchType {

View file

@ -207,6 +207,8 @@
<LinearLayout <LinearLayout
android:id="@+id/home_bookmarked_holder" android:id="@+id/home_bookmarked_holder"
android:orientation="vertical" android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@ -217,6 +219,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<ImageView <ImageView
android:id="@+id/home_bookmark_select"
android:src="@drawable/ic_baseline_filter_list_24" android:src="@drawable/ic_baseline_filter_list_24"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
@ -232,7 +235,7 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:textSize="18sp" android:textSize="18sp"
android:textStyle="bold" android:textStyle="bold"
android:text="Bookmarked" tools:text="Bookmarked"
/> />
<ImageView <ImageView
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"

View file

@ -64,4 +64,6 @@
<string name="background_blur">Background blur</string> <string name="background_blur">Background blur</string>
<string name="background_shadow">Background Shadow</string> <string name="background_shadow">Background Shadow</string>
<string name="filter_bookmarks">Filter Bookmarks</string> <string name="filter_bookmarks">Filter Bookmarks</string>
<string name="error_bookmarks_text">Bookmarks</string>
<string name="action_remove_from_bookmarks">Remove</string>
</resources> </resources>