forked from recloudstream/cloudstream
bookmarks working
This commit is contained in:
parent
a467060486
commit
5523d6539a
14 changed files with 233 additions and 84 deletions
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
Loading…
Reference in a new issue