mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	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" | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 30 | ||||
|         versionCode 8 | ||||
|         versionName "1.1.6" | ||||
|         versionCode 9 | ||||
|         versionName "1.1.7" | ||||
| 
 | ||||
|         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ object APIHolder { | |||
|     } | ||||
| 
 | ||||
|     fun LoadResponse.getId(): Int { | ||||
|         return url.replace(getApiFromName(apiName).mainUrl, "").hashCode() | ||||
|         return url.replace(getApiFromName(apiName).mainUrl, "").replace("/", "").hashCode() | ||||
|     } | ||||
| 
 | ||||
|     fun Activity.getApiSettings(): HashSet<String> { | ||||
|  | @ -86,7 +86,7 @@ abstract class MainAPI { | |||
|     open val hasMainPage = false | ||||
|     open val hasQuickSearch = false | ||||
| 
 | ||||
|     open fun getMainPage() : HomePageResponse? { | ||||
|     open fun getMainPage(): HomePageResponse? { | ||||
|         return null | ||||
|     } | ||||
| 
 | ||||
|  | @ -189,6 +189,7 @@ interface SearchResponse { | |||
|     val type: TvType | ||||
|     val posterUrl: String? | ||||
|     val year: Int? | ||||
|     val id: Int? | ||||
| } | ||||
| 
 | ||||
| data class AnimeSearchResponse( | ||||
|  | @ -204,6 +205,7 @@ data class AnimeSearchResponse( | |||
|     val dubStatus: EnumSet<DubStatus>?, | ||||
|     val dubEpisodes: Int?, | ||||
|     val subEpisodes: Int?, | ||||
|     override val id: Int? = null, | ||||
| ) : SearchResponse | ||||
| 
 | ||||
| data class MovieSearchResponse( | ||||
|  | @ -214,6 +216,7 @@ data class MovieSearchResponse( | |||
| 
 | ||||
|     override val posterUrl: String?, | ||||
|     override val year: Int?, | ||||
|     override val id: Int? = null, | ||||
| ) : SearchResponse | ||||
| 
 | ||||
| data class TvSeriesSearchResponse( | ||||
|  | @ -225,6 +228,7 @@ data class TvSeriesSearchResponse( | |||
|     override val posterUrl: String?, | ||||
|     override val year: Int?, | ||||
|     val episodes: Int?, | ||||
|     override val id: Int? = null, | ||||
| ) : SearchResponse | ||||
| 
 | ||||
| interface LoadResponse { | ||||
|  |  | |||
|  | @ -13,7 +13,6 @@ import androidx.navigation.NavOptions | |||
| import androidx.navigation.findNavController | ||||
| import androidx.navigation.fragment.NavHostFragment | ||||
| 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.utils.UIHelper.checkWrite | ||||
| 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.removeKey | ||||
| 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.fragment_result.* | ||||
| 
 | ||||
|  | @ -59,6 +59,8 @@ class MainActivity : AppCompatActivity() { | |||
|         var isInPlayer: Boolean = false | ||||
|         var canShowPipMode: Boolean = false | ||||
|         var isInPIPMode: Boolean = false | ||||
| 
 | ||||
|         val backEvent = Event<Boolean>() | ||||
|         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) | ||||
|                 .remove(currentFragment) | ||||
|                 .commitAllowingStateLoss() | ||||
|             backEvent.invoke(true) | ||||
|             return true | ||||
|         } | ||||
|         backEvent.invoke(false) | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,11 +10,14 @@ import androidx.recyclerview.widget.RecyclerView | |||
| import com.bumptech.glide.Glide | ||||
| import com.bumptech.glide.load.model.GlideUrl | ||||
| 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.* | ||||
| 
 | ||||
| class HomeChildItemAdapter( | ||||
|     var cardList: List<Any>, | ||||
|     private val clickCallback: (SearchResponse) -> Unit | ||||
|     var cardList: List<SearchResponse>, | ||||
|     private val clickCallback: (SearchClickCallback) -> Unit | ||||
| ) : | ||||
|     RecyclerView.Adapter<RecyclerView.ViewHolder>() { | ||||
| 
 | ||||
|  | @ -38,7 +41,7 @@ class HomeChildItemAdapter( | |||
|     } | ||||
| 
 | ||||
|     class CardViewHolder | ||||
|     constructor(itemView: View, private val clickCallback: (SearchResponse) -> Unit) : | ||||
|     constructor(itemView: View, private val clickCallback: (SearchClickCallback) -> Unit) : | ||||
|         RecyclerView.ViewHolder(itemView) { | ||||
|         val cardView: ImageView = itemView.imageView | ||||
|         private val cardText: TextView = itemView.imageText | ||||
|  | @ -52,50 +55,51 @@ class HomeChildItemAdapter( | |||
|         //val imageTextProvider: TextView? = itemView.imageTextProvider | ||||
|         private val bg: CardView = itemView.backgroundCard | ||||
| 
 | ||||
|         fun bind(card: Any) { | ||||
|             if (card is SearchResponse) { // GENERIC | ||||
|         fun bind(card: SearchResponse) { | ||||
|             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) { | ||||
|                     TvType.Anime -> "Anime" | ||||
|                     TvType.Movie -> "Movie" | ||||
|                     TvType.ONA -> "ONA" | ||||
|                     TvType.TvSeries -> "TV" | ||||
|                 } | ||||
|                 // search_result_lang?.visibility = View.GONE | ||||
|             textIsDub?.visibility = View.GONE | ||||
|             textIsSub?.visibility = View.GONE | ||||
| 
 | ||||
|                 textIsDub?.visibility = View.GONE | ||||
|                 textIsSub?.visibility = View.GONE | ||||
|             cardText.text = card.name | ||||
| 
 | ||||
|                 cardText.text = card.name | ||||
|             //imageTextProvider.text = card.apiName | ||||
|             if (!card.posterUrl.isNullOrEmpty()) { | ||||
| 
 | ||||
|                 //imageTextProvider.text = card.apiName | ||||
|                 if (!card.posterUrl.isNullOrEmpty()) { | ||||
|                 val glideUrl = | ||||
|                     GlideUrl(card.posterUrl) | ||||
| 
 | ||||
|                     val glideUrl = | ||||
|                         GlideUrl(card.posterUrl) | ||||
|                 Glide.with(cardView.context) | ||||
|                     .load(glideUrl) | ||||
|                     .into(cardView) | ||||
| 
 | ||||
|                     Glide.with(cardView.context) | ||||
|                         .load(glideUrl) | ||||
|                         .into(cardView) | ||||
|             } | ||||
| 
 | ||||
|                 } | ||||
|             bg.setOnClickListener { | ||||
|                 clickCallback.invoke(SearchClickCallback(SEARCH_ACTION_LOAD, it, card)) | ||||
|             } | ||||
| 
 | ||||
|                 bg.setOnClickListener { | ||||
|                     clickCallback.invoke(card) | ||||
|                    // (activity as AppCompatActivity).loadResult(card.url, card.slug, card.apiName) | ||||
|                 } | ||||
|             bg.setOnLongClickListener { | ||||
|                 clickCallback.invoke(SearchClickCallback(SEARCH_ACTION_SHOW_METADATA, it, card)) | ||||
|                 return@setOnLongClickListener true | ||||
|             } | ||||
| 
 | ||||
|                 when (card) { | ||||
|                     is AnimeSearchResponse -> { | ||||
|                         if (card.dubStatus?.size == 1) { | ||||
|                             //search_result_lang?.visibility = View.VISIBLE | ||||
|                             if (card.dubStatus.contains(DubStatus.Dubbed)) { | ||||
|                                 textIsDub?.visibility = View.VISIBLE | ||||
|                                 //search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.dubColor)) | ||||
|                             } else if (card.dubStatus.contains(DubStatus.Subbed)) { | ||||
|                                 //search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.subColor)) | ||||
|                                 textIsSub?.visibility = View.VISIBLE | ||||
|                             } | ||||
|             when (card) { | ||||
|                 is AnimeSearchResponse -> { | ||||
|                     if (card.dubStatus?.size == 1) { | ||||
|                         //search_result_lang?.visibility = View.VISIBLE | ||||
|                         if (card.dubStatus.contains(DubStatus.Dubbed)) { | ||||
|                             textIsDub?.visibility = View.VISIBLE | ||||
|                             //search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.dubColor)) | ||||
|                         } else if (card.dubStatus.contains(DubStatus.Subbed)) { | ||||
|                             //search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.subColor)) | ||||
|                             textIsSub?.visibility = View.VISIBLE | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  |  | |||
|  | @ -17,26 +17,33 @@ import androidx.recyclerview.widget.RecyclerView | |||
| import com.bumptech.glide.Glide | ||||
| import com.bumptech.glide.load.model.GlideUrl | ||||
| import com.google.android.material.bottomsheet.BottomSheetDialog | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.APIHolder.apis | ||||
| import com.lagradost.cloudstream3.AnimeSearchResponse | ||||
| import com.lagradost.cloudstream3.HomePageResponse | ||||
| import com.lagradost.cloudstream3.R | ||||
| import com.lagradost.cloudstream3.SearchResponse | ||||
| import com.lagradost.cloudstream3.MainActivity.Companion.backEvent | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes | ||||
| import com.lagradost.cloudstream3.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.observe | ||||
| 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.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.SearchHelper.handleSearchClickCallback | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult | ||||
| 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.DataStoreHelper.setResultWatchState | ||||
| import com.lagradost.cloudstream3.utils.Event | ||||
| import com.lagradost.cloudstream3.utils.HOMEPAGE_API | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons | ||||
| import kotlinx.android.synthetic.main.fragment_home.* | ||||
| 
 | ||||
| const val HOME_BOOKMARK_VALUE = "home_bookmarked_last" | ||||
| 
 | ||||
| class HomeFragment : Fragment() { | ||||
|     private lateinit var homeViewModel: HomeViewModel | ||||
| 
 | ||||
|  | @ -142,6 +149,28 @@ class HomeFragment : Fragment() { | |||
|         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?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|         fixGrid() | ||||
|  | @ -225,9 +254,7 @@ class HomeFragment : Fragment() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf(), { card -> | ||||
|             activity.loadSearchResult(card) | ||||
|         }, { item -> | ||||
|         fun loadHomepageList(item: HomePageList) { | ||||
|             val bottomSheetDialogBuilder = BottomSheetDialog(view.context) | ||||
|             bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded) | ||||
|             val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!! | ||||
|  | @ -242,9 +269,11 @@ class HomeFragment : Fragment() { | |||
|             // Span settings | ||||
|             recycle.spanCount = currentSpan | ||||
| 
 | ||||
|             recycle.adapter = SearchAdapter(item.list, recycle) { card -> | ||||
|                 bottomSheetDialogBuilder.dismiss() | ||||
|                 activity.loadSearchResult(card) | ||||
|             recycle.adapter = SearchAdapter(item.list, recycle) { callback -> | ||||
|                 handleSearchClickCallback(activity, callback) | ||||
|                 if (callback.action == SEARCH_ACTION_LOAD) { | ||||
|                     bottomSheetDialogBuilder.dismiss() | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             val spanListener = { span: Int -> | ||||
|  | @ -261,13 +290,66 @@ class HomeFragment : Fragment() { | |||
|             (recycle.adapter as SearchAdapter).notifyDataSetChanged() | ||||
| 
 | ||||
|             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) | ||||
| 
 | ||||
|         home_master_recycler.adapter = adapter | ||||
|         home_master_recycler.layoutManager = GridLayoutManager(context, 1) | ||||
| 
 | ||||
|         reloadStored() | ||||
|         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.R | ||||
| import com.lagradost.cloudstream3.SearchResponse | ||||
| import com.lagradost.cloudstream3.ui.search.SearchClickCallback | ||||
| import kotlinx.android.synthetic.main.homepage_parent.view.* | ||||
| 
 | ||||
| class ParentItemAdapter( | ||||
|     var items: List<HomePageList>, | ||||
|     private val clickCallback: (SearchResponse) -> Unit, | ||||
|     private val clickCallback: (SearchClickCallback) -> Unit, | ||||
|     private val moreInfoClickCallback: (HomePageList) -> Unit, | ||||
| ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { | ||||
|     override fun onCreateViewHolder(parent: ViewGroup, i: Int): ParentViewHolder { | ||||
|  | @ -38,7 +39,7 @@ class ParentItemAdapter( | |||
|     class ParentViewHolder | ||||
|     constructor( | ||||
|         itemView: View, | ||||
|         private val clickCallback: (SearchResponse) -> Unit, | ||||
|         private val clickCallback: (SearchClickCallback) -> Unit, | ||||
|         private val moreInfoClickCallback: (HomePageList) -> Unit | ||||
|     ) : | ||||
|         RecyclerView.ViewHolder(itemView) { | ||||
|  |  | |||
|  | @ -33,15 +33,17 @@ class HomeViewModel : ViewModel() { | |||
|         return APIRepository(apis.first { it.hasMainPage }) | ||||
|     } | ||||
| 
 | ||||
|     private val availableWatchStatusTypes = MutableLiveData<Pair<WatchType, List<WatchType>>>() | ||||
|     private val bookmarks = MutableLiveData<List<SearchResponse>>() | ||||
|     private val _availableWatchStatusTypes = MutableLiveData<Pair<WatchType, List<WatchType>>>() | ||||
|     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 { | ||||
|         val watchStatusIds = withContext(Dispatchers.IO) { | ||||
|             context.getAllWatchStateIds().map { id -> | ||||
|                 Pair(id, context.getResultWatchState(id)) | ||||
|             } | ||||
|         } | ||||
|         }.distinctBy { it.first } | ||||
|         val length = WatchType.values().size | ||||
|         val currentWatchTypes = HashSet<WatchType>() | ||||
| 
 | ||||
|  | @ -52,21 +54,28 @@ class HomeViewModel : ViewModel() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         currentWatchTypes.remove(WatchType.NONE) | ||||
| 
 | ||||
|         if (currentWatchTypes.size <= 0) { | ||||
|             bookmarks.postValue(ArrayList()) | ||||
|             _bookmarks.postValue(ArrayList()) | ||||
|             return@launch | ||||
|         } | ||||
| 
 | ||||
|         val watchStatus = preferredWatchStatus ?: currentWatchTypes.first() | ||||
|         availableWatchStatusTypes.postValue( | ||||
|         val watchPrefNotNull = preferredWatchStatus ?: currentWatchTypes.first() | ||||
|         val watchStatus = | ||||
|             if (currentWatchTypes.contains(watchPrefNotNull)) watchPrefNotNull else currentWatchTypes.first() | ||||
|         _availableWatchStatusTypes.postValue( | ||||
|             Pair( | ||||
|                 watchStatus, | ||||
|                 currentWatchTypes.sortedBy { it.internalId }.toList() | ||||
|             ) | ||||
|         ) | ||||
|         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 { | ||||
|  |  | |||
|  | @ -117,7 +117,7 @@ class EpisodeAdapter( | |||
|     class EpisodeCardViewHolder | ||||
|     constructor( | ||||
|         itemView: View, | ||||
|         val hasDownloadSupport: Boolean, | ||||
|         private val hasDownloadSupport: Boolean, | ||||
|         private val clickCallback: (EpisodeClickEvent) -> Unit, | ||||
|         private val downloadClickCallback: (DownloadClickEvent) -> Unit, | ||||
|     ) : RecyclerView.ViewHolder(itemView), DownloadButtonViewHolder { | ||||
|  | @ -129,8 +129,8 @@ class EpisodeAdapter( | |||
|         private val episodeProgress: ContentLoadingProgressBar? = itemView.episode_progress | ||||
|         private val episodePoster: ImageView? = itemView.episode_poster | ||||
| 
 | ||||
|         val episodeDownloadBar: ContentLoadingProgressBar = itemView.result_episode_progress_downloaded | ||||
|         val episodeDownloadImage: ImageView = itemView.result_episode_download | ||||
|         private val episodeDownloadBar: ContentLoadingProgressBar = itemView.result_episode_progress_downloaded | ||||
|         private val episodeDownloadImage: ImageView = itemView.result_episode_download | ||||
| 
 | ||||
|         private val episodeHolder = itemView.episode_holder | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| package com.lagradost.cloudstream3.ui.search | ||||
| 
 | ||||
| import android.app.Activity | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| 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 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( | ||||
|     var cardList: List<SearchResponse>, | ||||
|     private val resView: AutofitRecyclerView, | ||||
|     private val clickCallback: (SearchResponse) -> Unit, | ||||
| ) : | ||||
|     RecyclerView.Adapter<RecyclerView.ViewHolder>() { | ||||
|     private val clickCallback: (SearchClickCallback) -> Unit, | ||||
| ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { | ||||
| 
 | ||||
|     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { | ||||
|         val layout = parent.context.getGridFormatId() | ||||
|  | @ -54,7 +59,11 @@ class SearchAdapter( | |||
|     } | ||||
| 
 | ||||
|     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) { | ||||
|         val cardView: ImageView = itemView.imageView | ||||
|         private val cardText: TextView = itemView.imageText | ||||
|  | @ -105,7 +114,12 @@ class SearchAdapter( | |||
|             } | ||||
| 
 | ||||
|             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) { | ||||
|  | @ -121,7 +135,6 @@ class SearchAdapter( | |||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -9,7 +9,6 @@ import android.view.ViewGroup | |||
| import android.view.WindowManager | ||||
| import android.view.inputmethod.InputMethodManager | ||||
| import android.widget.ImageView | ||||
| import android.widget.Toast | ||||
| import androidx.appcompat.app.AlertDialog | ||||
| import androidx.appcompat.widget.SearchView | ||||
| 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.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.observe | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult | ||||
| import kotlinx.android.synthetic.main.fragment_search.* | ||||
| 
 | ||||
| class SearchFragment : Fragment() { | ||||
| 
 | ||||
|     private lateinit var searchViewModel: SearchViewModel | ||||
| 
 | ||||
|     override fun onCreateView( | ||||
|  | @ -77,8 +74,8 @@ class SearchFragment : Fragment() { | |||
|             SearchAdapter( | ||||
|                 ArrayList(), | ||||
|                 cardSpace, | ||||
|             ) { card -> | ||||
|                 activity.loadSearchResult(card) | ||||
|             ) { callback -> | ||||
|                 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.utils.DataStore.getKey | ||||
| import com.lagradost.cloudstream3.utils.DataStore.getKeys | ||||
| import com.lagradost.cloudstream3.utils.DataStore.removeKey | ||||
| import com.lagradost.cloudstream3.utils.DataStore.setKey | ||||
| 
 | ||||
| const val VIDEO_POS_DUR = "video_pos_dur" | ||||
|  | @ -27,9 +28,9 @@ object DataStoreHelper { | |||
|     } | ||||
| 
 | ||||
|     data class BookmarkedData( | ||||
|         val parentId: Int, | ||||
|         val bookmarkedTime : Long, | ||||
|         val latestUpdatedTime : Long, | ||||
|         override val id: Int?, | ||||
|         val bookmarkedTime: Long, | ||||
|         val latestUpdatedTime: Long, | ||||
|         override val name: String, | ||||
|         override val url: String, | ||||
|         override val apiName: String, | ||||
|  | @ -42,17 +43,19 @@ object DataStoreHelper { | |||
| 
 | ||||
|     fun Context.getAllWatchStateIds(): List<Int> { | ||||
|         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) { | ||||
|         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? { | ||||
|         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) { | ||||
|  | @ -66,7 +69,13 @@ object DataStoreHelper { | |||
| 
 | ||||
|     fun Context.setResultWatchState(id: Int?, status: Int) { | ||||
|         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 { | ||||
|  |  | |||
|  | @ -207,6 +207,8 @@ | |||
|             <LinearLayout | ||||
|                     android:id="@+id/home_bookmarked_holder" | ||||
|                     android:orientation="vertical" | ||||
|                     android:visibility="gone" | ||||
|                     tools:visibility="visible" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_height="wrap_content"> | ||||
| 
 | ||||
|  | @ -217,6 +219,7 @@ | |||
|                         android:layout_width="match_parent" | ||||
|                         android:layout_height="wrap_content"> | ||||
|                     <ImageView | ||||
|                             android:id="@+id/home_bookmark_select" | ||||
|                             android:src="@drawable/ic_baseline_filter_list_24" | ||||
|                             android:layout_width="24dp" | ||||
|                             android:layout_height="24dp" | ||||
|  | @ -232,7 +235,7 @@ | |||
|                             android:gravity="center_vertical" | ||||
|                             android:textSize="18sp" | ||||
|                             android:textStyle="bold" | ||||
|                             android:text="Bookmarked" | ||||
|                             tools:text="Bookmarked" | ||||
|                     /> | ||||
|                     <ImageView | ||||
|                             android:layout_marginEnd="5dp" | ||||
|  |  | |||
|  | @ -64,4 +64,6 @@ | |||
|     <string name="background_blur">Background blur</string> | ||||
|     <string name="background_shadow">Background Shadow</string> | ||||
|     <string name="filter_bookmarks">Filter Bookmarks</string> | ||||
|     <string name="error_bookmarks_text">Bookmarks</string> | ||||
|     <string name="action_remove_from_bookmarks">Remove</string> | ||||
| </resources> | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue