From 2e2c9cb8532e79fae896e6377bb6eca7565ba41b Mon Sep 17 00:00:00 2001 From: AdityaGaikwad Date: Wed, 17 Jul 2024 21:02:20 +0530 Subject: [PATCH] MVVM for web series activity new fragment for WebShowFragment load more api integration for episodes and teasers caching data in same viewmodel --- .../com/woka/home/fragments/Home1Fragment.kt | 15 +- .../com/woka/home/fragments/Home2Fragment.kt | 14 +- .../com/woka/home/fragments/MyListFragment.kt | 14 +- .../java/com/woka/home/views/HomeActivity.kt | 8 +- .../java/com/woka/webseries/Repository.kt | 74 ++- .../adapters/WebSeriesShowAdapter.kt | 17 +- .../com/woka/webseries/models/AgeRangeData.kt | 6 +- .../com/woka/webseries/models/CategoryData.kt | 6 +- .../webseries/models/ContentMoreDetail.kt | 6 +- .../com/woka/webseries/models/GenderData.kt | 6 +- .../com/woka/webseries/models/ShowData.kt | 7 +- .../models/episodedata/EpisodeResponseData.kt | 2 +- .../models/seasondata/SeasonDataResponse.kt | 2 +- .../com/woka/webseries/viewmodel/ViewModel.kt | 130 +++- .../woka/webseries/views/SeasonActivity.kt | 3 +- .../woka/webseries/views/SeriesActivity.kt | 3 - .../woka/webseries/views/WebSeriesActivity.kt | 3 +- .../views/fragments/WebSeriesFragment.kt | 64 +- .../views/fragments/WebShowFragment.kt | 552 +++++++++++++++++ app/src/main/res/layout/fragment_web_show.xml | 562 ++++++++++++++++++ app/src/main/res/values/colors.xml | 1 + 21 files changed, 1408 insertions(+), 87 deletions(-) create mode 100644 app/src/main/java/com/woka/webseries/views/fragments/WebShowFragment.kt create mode 100644 app/src/main/res/layout/fragment_web_show.xml diff --git a/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt b/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt index 98d74c1..cff2105 100644 --- a/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt +++ b/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt @@ -10,6 +10,7 @@ import android.content.Intent.ACTION_TIME_TICK import android.content.IntentFilter import android.os.Bundle import android.util.DisplayMetrics +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -30,9 +31,11 @@ import com.woka.userdata.userDataModels.UserDataResponse import com.woka.networking.ApiResult import com.woka.players.views.LiveStreamPlayerActivity import com.woka.userPreference.UserType +import com.woka.utils.TAG import com.woka.utils.hide import com.woka.utils.scaleAnimate import com.woka.utils.show +import com.woka.utils.toast import com.woka.webseries.views.SeriesActivity import com.woka.webseries.views.WebSeriesActivity import com.woka.wokagames.views.GamesActivity @@ -441,6 +444,16 @@ class Home1Fragment : Fragment(), Listener { } companion object { - fun newInstance() = Home1Fragment() + private var instance: Home1Fragment? = null + private val any = Any() + fun getInstance(): Home1Fragment{ + return synchronized(any){ + if (instance == null){ + instance = Home1Fragment() + } + + return@synchronized instance!! + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/woka/home/fragments/Home2Fragment.kt b/app/src/main/java/com/woka/home/fragments/Home2Fragment.kt index 2dca361..f384111 100644 --- a/app/src/main/java/com/woka/home/fragments/Home2Fragment.kt +++ b/app/src/main/java/com/woka/home/fragments/Home2Fragment.kt @@ -2,6 +2,7 @@ package com.woka.home.fragments import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View @@ -16,8 +17,10 @@ import com.woka.userdata.userDataModels.UserDataResponse import com.woka.networking.ApiResult import com.woka.players.views.LiveStreamPlayerActivity import com.woka.userPreference.UserType +import com.woka.utils.TAG import com.woka.utils.hide import com.woka.utils.show +import com.woka.utils.toast class Home2Fragment : Fragment() { @@ -141,6 +144,15 @@ class Home2Fragment : Fragment() { } companion object { - fun newInstance() = Home2Fragment() + private var instance: Home2Fragment? = null + private val any = Any() + fun getInstance(): Home2Fragment{ + return synchronized(any){ + if (instance == null){ + instance = Home2Fragment() + } + + return@synchronized instance!! } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/woka/home/fragments/MyListFragment.kt b/app/src/main/java/com/woka/home/fragments/MyListFragment.kt index 993b4b1..39c3ad5 100644 --- a/app/src/main/java/com/woka/home/fragments/MyListFragment.kt +++ b/app/src/main/java/com/woka/home/fragments/MyListFragment.kt @@ -8,6 +8,7 @@ import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable import android.os.Bundle import android.text.Html +import android.util.Log import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View @@ -41,6 +42,7 @@ import com.woka.karaoke.models.listing.KaraokeData import com.woka.networking.ApiResult import com.woka.players.models.VideoPlayList import com.woka.players.views.PlayerActivity +import com.woka.utils.TAG import com.woka.utils.hide import com.woka.utils.isNetworkConnected import com.woka.utils.show @@ -732,6 +734,16 @@ class MyListFragment : Fragment() { } companion object { - fun newInstance() = MyListFragment() + private var instance: MyListFragment? = null + private val any = Any() + fun getInstance(): MyListFragment{ + return synchronized(any){ + if (instance == null){ + instance = MyListFragment() + } + + return@synchronized instance!! + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/woka/home/views/HomeActivity.kt b/app/src/main/java/com/woka/home/views/HomeActivity.kt index e028c53..82c0a9c 100644 --- a/app/src/main/java/com/woka/home/views/HomeActivity.kt +++ b/app/src/main/java/com/woka/home/views/HomeActivity.kt @@ -461,10 +461,9 @@ class HomeActivity : WokaBaseActivity(), HOME -> { if (userPrefs?.appTheme == Theme.THEME_TWO){ supportFragmentManager.beginTransaction() - .replace(R.id.fc_home, Home2Fragment.newInstance()) + .replace(R.id.fc_home, Home2Fragment.getInstance()) .runOnCommit { binding.notifications.show() - binding.notiCountView.show() window.lightStatusBar() binding.root.backgroundTintList = ColorStateList.valueOf(getColor(R.color.color_primary)) @@ -475,10 +474,9 @@ class HomeActivity : WokaBaseActivity(), .commit() }else{ supportFragmentManager.beginTransaction() - .replace(R.id.fc_home, Home1Fragment.newInstance()) + .replace(R.id.fc_home, Home1Fragment.getInstance()) .runOnCommit { binding.notifications.show() - binding.notiCountView.show() window.lightStatusBar() binding.root.backgroundTintList = null @@ -504,7 +502,7 @@ class HomeActivity : WokaBaseActivity(), unregisterReceiver(minuteReceiver) supportFragmentManager.beginTransaction() - .replace(R.id.fc_home, MyListFragment.newInstance()) + .replace(R.id.fc_home, MyListFragment.getInstance()) .runOnCommit { binding.root.backgroundTintList = null diff --git a/app/src/main/java/com/woka/webseries/Repository.kt b/app/src/main/java/com/woka/webseries/Repository.kt index 95d3a5c..34b1cf3 100644 --- a/app/src/main/java/com/woka/webseries/Repository.kt +++ b/app/src/main/java/com/woka/webseries/Repository.kt @@ -35,39 +35,6 @@ object Repository { } } - suspend fun loadTeaserData(showId: Int, seasonId: Int): ApiResult { - return handleApiCall{ - apiService.teaserListing( - FormBody.Builder() - .add("watch_show_master_id", "$showId") - .add("season_master_id", "$seasonId") - .build() - ) - } - } - - suspend fun loadEpisodeData(showId: Int, seasonId: Int): ApiResult { - return handleApiCall { - apiService.episodeListing( - FormBody.Builder() - .add("watch_show_master_id", "$showId") - .add("season_master_id", "$seasonId") - .build() - ) - } - } - - suspend fun loadSeasonListing(showId: Int, categoryId: String): ApiResult { - return handleApiCall { - apiService.seasonListing( - FormBody.Builder() - .add("watch_show_id", "$showId") - .add("category_id", categoryId) - .build() - ) - } - } - suspend fun loadContinueWatchData(): ApiResult { return handleApiCall { userActionApiService.continueWatchingShowListing( @@ -91,6 +58,47 @@ object Repository { } } + suspend fun teaserListing(showId: Int, seasonId: Int): ApiResult { + return handleApiCall{ + apiService.teaserListing( + FormBody.Builder() + .add("watch_show_master_id", "$showId") + .add("season_master_id", "$seasonId") + .build() + ) + } + } + + suspend fun episodeListing( + showId: String, + seasonId: Int, + pageNo: Int, + quantity: Int + ): ApiResult { + return handleApiCall { + apiService.episodeListing( + FormBody.Builder() + .add("watch_show_master_id", showId) + .add("season_master_id", "$seasonId") + .add("api_version", "v2") + .add("start", "$pageNo") + .add("limit", "$quantity") + .build() + ) + } + } + + suspend fun seasonListing(showId: String, categoryId: String): ApiResult { + return handleApiCall { + apiService.seasonListing( + FormBody.Builder() + .add("watch_show_id", showId) + .add("category_id", categoryId) + .build() + ) + } + } + fun likeUnLikeShow(postId: String, likeIt: Boolean){ CoroutineScope(Dispatchers.IO).launch { handleApiCall { diff --git a/app/src/main/java/com/woka/webseries/adapters/WebSeriesShowAdapter.kt b/app/src/main/java/com/woka/webseries/adapters/WebSeriesShowAdapter.kt index c02436e..80b5ad3 100644 --- a/app/src/main/java/com/woka/webseries/adapters/WebSeriesShowAdapter.kt +++ b/app/src/main/java/com/woka/webseries/adapters/WebSeriesShowAdapter.kt @@ -18,7 +18,8 @@ import kotlin.math.max class WebSeriesShowAdapter( private val context: Context, - var onShowClicked: (ShowData, Int, String) -> Unit + private var onShowClicked: (ShowData, String) -> Unit, + private var onShowCommonDataChange: ((ShowData, String) -> Unit)? = null ): RecyclerView.Adapter() { inner class ShowViewHolder(val binding: ShowViewHolderBinding): ViewHolder(binding.root) @@ -102,6 +103,10 @@ class WebSeriesShowAdapter( showData.likes_count?.let { likeCount.text = "$it" } + + categoryId?.let { + onShowCommonDataChange?.invoke(showData, it) + } } categoryId?.let { fav.isSelected = showData.isBookMarked(it) } @@ -119,7 +124,13 @@ class WebSeriesShowAdapter( it ) - showData.addAsBookMark(it) + if (showData.isBookMarked(it)){ + // remove from fav + showData.removeAsBookMark(it) + }else{ + // add to fav + showData.addAsBookMark(it) + } fav.isSelected = !fav.isSelected } @@ -127,7 +138,7 @@ class WebSeriesShowAdapter( root.setOnClickListener { categoryId?.let { categoryId -> - onShowClicked(showData, holder.absoluteAdapterPosition, categoryId) + onShowClicked(showData, categoryId) } } } diff --git a/app/src/main/java/com/woka/webseries/models/AgeRangeData.kt b/app/src/main/java/com/woka/webseries/models/AgeRangeData.kt index 6f657c5..84db24f 100644 --- a/app/src/main/java/com/woka/webseries/models/AgeRangeData.kt +++ b/app/src/main/java/com/woka/webseries/models/AgeRangeData.kt @@ -1,6 +1,10 @@ package com.woka.webseries.models +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize data class AgeRangeData( val age_range: String?, val id: Int? -) \ No newline at end of file +): Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/CategoryData.kt b/app/src/main/java/com/woka/webseries/models/CategoryData.kt index f60531f..8f7ba5e 100644 --- a/app/src/main/java/com/woka/webseries/models/CategoryData.kt +++ b/app/src/main/java/com/woka/webseries/models/CategoryData.kt @@ -1,6 +1,10 @@ package com.woka.webseries.models +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize data class CategoryData( val category_name: String?, val id: Int? -) \ No newline at end of file +): Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/ContentMoreDetail.kt b/app/src/main/java/com/woka/webseries/models/ContentMoreDetail.kt index 863d3dd..728ad2b 100644 --- a/app/src/main/java/com/woka/webseries/models/ContentMoreDetail.kt +++ b/app/src/main/java/com/woka/webseries/models/ContentMoreDetail.kt @@ -1,5 +1,9 @@ package com.woka.webseries.models +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize data class ContentMoreDetail( val content_id: Int?, val description: String?, @@ -8,4 +12,4 @@ data class ContentMoreDetail( val post_type: Int?, val tags_keywords: String?, val title: String? -) \ No newline at end of file +): Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/GenderData.kt b/app/src/main/java/com/woka/webseries/models/GenderData.kt index 0749e47..cc7822e 100644 --- a/app/src/main/java/com/woka/webseries/models/GenderData.kt +++ b/app/src/main/java/com/woka/webseries/models/GenderData.kt @@ -1,6 +1,10 @@ package com.woka.webseries.models +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize data class GenderData( val gender_name: String?, val id: Int? -) \ No newline at end of file +): Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/ShowData.kt b/app/src/main/java/com/woka/webseries/models/ShowData.kt index f09e117..c2d7d58 100644 --- a/app/src/main/java/com/woka/webseries/models/ShowData.kt +++ b/app/src/main/java/com/woka/webseries/models/ShowData.kt @@ -1,9 +1,10 @@ package com.woka.webseries.models -import android.util.Log +import android.os.Parcelable import com.woka.home.mylist.models.BookmarkedShowData -import com.woka.utils.TAG +import kotlinx.parcelize.Parcelize +@Parcelize data class ShowData( val age_range_data: List?, val age_range_master_id: String?, @@ -26,7 +27,7 @@ data class ShowData( val total_episodes: Int?, val total_seasons: Int?, val views_count: Int? -){ +): Parcelable{ constructor(bookMarkShowData: BookmarkedShowData): this( null, diff --git a/app/src/main/java/com/woka/webseries/models/episodedata/EpisodeResponseData.kt b/app/src/main/java/com/woka/webseries/models/episodedata/EpisodeResponseData.kt index 72b7535..7d15c3f 100644 --- a/app/src/main/java/com/woka/webseries/models/episodedata/EpisodeResponseData.kt +++ b/app/src/main/java/com/woka/webseries/models/episodedata/EpisodeResponseData.kt @@ -1,6 +1,6 @@ package com.woka.webseries.models.episodedata data class EpisodeResponseData( - val result: List?, + val result: MutableList?, val total_records: Int? ) \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/seasondata/SeasonDataResponse.kt b/app/src/main/java/com/woka/webseries/models/seasondata/SeasonDataResponse.kt index d04c0ec..7576116 100644 --- a/app/src/main/java/com/woka/webseries/models/seasondata/SeasonDataResponse.kt +++ b/app/src/main/java/com/woka/webseries/models/seasondata/SeasonDataResponse.kt @@ -1,6 +1,6 @@ package com.woka.webseries.models.seasondata data class SeasonDataResponse( - val result: List?, + val result: MutableList?, val total_records: Int? ) \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/viewmodel/ViewModel.kt b/app/src/main/java/com/woka/webseries/viewmodel/ViewModel.kt index 713e5e2..deb1d06 100644 --- a/app/src/main/java/com/woka/webseries/viewmodel/ViewModel.kt +++ b/app/src/main/java/com/woka/webseries/viewmodel/ViewModel.kt @@ -1,28 +1,30 @@ package com.woka.webseries.viewmodel -import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.woka.modules.categorymodels.CategoriesResponse import com.woka.networking.ApiResult -import com.woka.utils.TAG +import com.woka.players.models.VideoPlayList import com.woka.webseries.Repository import com.woka.webseries.models.ContinueEpisodeResponse import com.woka.webseries.models.ShowData +import com.woka.webseries.models.episodedata.EpisodeData +import com.woka.webseries.models.seasondata.SeasonData import kotlinx.coroutines.launch class ViewModel : ViewModel() { private val repository = Repository - // categories listing data class PagingData( var nextPageToLoad: Int = 0, var quantityPerPage: Int = 6, var lastPage: Boolean = false ) + // categories listing + private val _showCategoryLiveData = MutableLiveData>() val showCategoryLiveData: LiveData> get() = _showCategoryLiveData @@ -108,4 +110,126 @@ class ViewModel : ViewModel() { } } } + + fun showCommonDataChanged(showData: ShowData, categoryId: String) { + for (cat in webSeriesData.keys){ + if (cat == categoryId) continue + + webSeriesData[cat]?.let {showList -> + for (show in showList){ + if (show.id == showData.id){ + + // updating common data between categorical lists of show + show.likes_count = showData.likes_count + show.is_liked = showData.is_liked + + break + } + } + } + } + } + + // seasonListing data + private val _seasonListingLiveData = MutableLiveData>>() + val seasonListingLiveData: LiveData>> + get() = _seasonListingLiveData + + // seasons data for every show. where {key -> "showId_categoryId"} + var seasonListingMap = HashMap>() + + fun seasonListing(showId: String, categoryId: String){ + if (seasonListingMap.containsKey("${showId}_${categoryId}")){ + _seasonListingLiveData.postValue(ApiResult.Success(seasonListingMap["${showId}_${categoryId}"])) + return + } + + viewModelScope.launch { + when (val response = repository.seasonListing(showId, categoryId)){ + is ApiResult.Error -> {_seasonListingLiveData.postValue(ApiResult.Error(response.errorMessage, response.error))} + is ApiResult.Loading -> {} + is ApiResult.Success -> { + response.data?.result?.filterNotNull()?.toMutableList()?.let { + seasonListingMap["${showId}_${categoryId}"] = it + _seasonListingLiveData.postValue(ApiResult.Success(it)) + } + } + } + } + } + + fun clearSeasonLiveData() { + _seasonListingLiveData.postValue(ApiResult.Loading()) + } + + // episode listing data + private val _episodeListingLiveData = MutableLiveData>>() + val episodeListingLiveData: LiveData>> + get() = _episodeListingLiveData + + var episodePagingData = HashMap() + + // episode data for every season. where {key -> "showId_seasonId"} + private var episodeDataMap = HashMap>() + // episodes playlist, where {key -> "showId_seasonId_categoryId"} + val episodesPlaylistMap = HashMap() + + fun loadEpisodes(showId: String, seasonId: Int){ + val key = "${showId}_$seasonId" + if (episodeDataMap.containsKey(key) && episodeDataMap[key]?.isNotEmpty() == true){ + _episodeListingLiveData.postValue(ApiResult.Success(episodeDataMap[key])) + }else{ + loadMoreEpisodes(showId, seasonId) + } + } + + fun loadMoreEpisodes(showId: String, seasonId: Int) { + viewModelScope.launch { + _episodeListingLiveData.postValue(ApiResult.Loading()) + + val key = "${showId}_$seasonId" + + val pagingData = if (episodePagingData.containsKey(key)){ + episodePagingData[key]!! + }else{ + val pagingData = PagingData() + episodePagingData[key] = pagingData + pagingData + } + + when (val response = repository.episodeListing(showId, seasonId, pagingData.nextPageToLoad, pagingData.quantityPerPage)) { + is ApiResult.Error -> { + _episodeListingLiveData.postValue( + ApiResult.Error( + response.errorMessage, + response.error + ) + ) + } + + is ApiResult.Loading -> { + _episodeListingLiveData.postValue(ApiResult.Loading()) + } + + is ApiResult.Success -> { + response.data?.let { data -> + data.result?.filterNotNull()?.let { newList -> + val currentList = episodeDataMap.getOrDefault(key, ArrayList()) + currentList.addAll(newList) + episodeDataMap[key] = currentList + + pagingData.lastPage = episodeDataMap[key]?.size == data.total_records + + _episodeListingLiveData.postValue(ApiResult.Success(episodeDataMap[key])) + pagingData.nextPageToLoad++ + } + } + } + } + } + } + + fun clearEpisodeListingLiveData(){ + _episodeListingLiveData.postValue(ApiResult.Loading()) + } } \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/views/SeasonActivity.kt b/app/src/main/java/com/woka/webseries/views/SeasonActivity.kt index b507f5f..03d6cfe 100644 --- a/app/src/main/java/com/woka/webseries/views/SeasonActivity.kt +++ b/app/src/main/java/com/woka/webseries/views/SeasonActivity.kt @@ -48,11 +48,10 @@ class SeasonActivity : WokaBaseActivity(), OnTabSelectedListener { } private lateinit var binding: ActivitySeasonBinding + private var showId: Int = -1 private var categoryId: String? = null - private var showData: ShowData? = null - private var showPosition: Int = -1 private lateinit var progressView: ProgressView diff --git a/app/src/main/java/com/woka/webseries/views/SeriesActivity.kt b/app/src/main/java/com/woka/webseries/views/SeriesActivity.kt index 184c900..0c72929 100644 --- a/app/src/main/java/com/woka/webseries/views/SeriesActivity.kt +++ b/app/src/main/java/com/woka/webseries/views/SeriesActivity.kt @@ -29,9 +29,6 @@ class SeriesActivity : WokaBaseActivity() { supportFragmentManager.beginTransaction() .add(R.id.fcv_web_series, WebSeriesFragment.newInstance()) - .runOnCommit { - window.setBackgroundDrawable(AppCompatResources.getDrawable(this, R.drawable.gradient_web_series)) - } .commit() } } \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/views/WebSeriesActivity.kt b/app/src/main/java/com/woka/webseries/views/WebSeriesActivity.kt index d81ace5..9e9eb59 100644 --- a/app/src/main/java/com/woka/webseries/views/WebSeriesActivity.kt +++ b/app/src/main/java/com/woka/webseries/views/WebSeriesActivity.kt @@ -288,11 +288,10 @@ class WebSeriesActivity : WokaBaseActivity(), Observer { binding.errorView.show() @@ -195,14 +203,16 @@ class WebSeriesFragment private constructor(): Fragment() { binding.categorySpinner.setAdapter(catSpinnerAdapter) loadShowData() - viewModel.loadContinueWatching() + if (userPrefs?.userType != UserType.GUEST && viewModel?.continueWatchLiveData?.isInitialized == false){ + viewModel?.loadContinueWatching() + } } } } } } - viewModel.continueWatchLiveData.observe(viewLifecycleOwner){ + viewModel?.continueWatchLiveData?.observe(viewLifecycleOwner){ when (it){ is ApiResult.Error -> { binding.rvContinueWatch.hide() @@ -224,11 +234,11 @@ class WebSeriesFragment private constructor(): Fragment() { } } - viewModel.webSeriesLiveData.observe(viewLifecycleOwner){ + viewModel?.webSeriesLiveData?.observe(viewLifecycleOwner){ when (it){ is ApiResult.Error -> { binding.shimmerShowData.hide() - if (showAdapter.showList.isNotEmpty()){ + if (loadingMore){ // load more binding.loadMoreBtn.text = getString(R.string.retry) binding.loadMoreBtn.show() @@ -242,7 +252,7 @@ class WebSeriesFragment private constructor(): Fragment() { is ApiResult.Loading -> { binding.shimmerShowData.show() binding.loadMoreBtn.hide() - binding.rvWebSeries.setVisibility(showAdapter.showList.isNotEmpty()) + binding.rvWebSeries.setVisibility(loadingMore) } is ApiResult.Success -> { catSpinnerAdapter?.selectedCategoryType?.let { categoryType -> @@ -252,9 +262,9 @@ class WebSeriesFragment private constructor(): Fragment() { binding.shimmerShowData.hide() binding.loadMoreBtn.text = getString(R.string.load_more) - binding.loadMoreBtn.setVisibility(viewModel.categoryPagingData[categoryType]?.lastPage == false) + binding.loadMoreBtn.setVisibility(viewModel?.categoryPagingData?.get(categoryType)?.lastPage == false) - if (showAdapter.showList.isNotEmpty()){ + if (loadingMore){ // loaded more data showAdapter.notifyItemRangeInserted( showAdapter.showList.size, @@ -264,10 +274,6 @@ class WebSeriesFragment private constructor(): Fragment() { // new category data load showAdapter.submitListShowList(showList, categoryType) } - - if (userPrefs?.userType != UserType.GUEST && !viewModel.continueWatchLiveData.isInitialized){ - viewModel.loadContinueWatching() - } } } } @@ -294,8 +300,18 @@ class WebSeriesFragment private constructor(): Fragment() { } } - private fun onShowClicked(showData: ShowData, position: Int, categoryId: String){ + private fun onShowClicked(showData: ShowData, categoryId: String){ + parentFragmentManager.beginTransaction() + .add(R.id.fcv_web_series, WebShowFragment.newInstance(showData, categoryId)) + .hide(this) + .addToBackStack(null) + .commit() + } + // updating common data of shows across all category show list + private fun onShowCommonDataChange(showData: ShowData, categoryId: String){ + // changing in viewmodel data + viewModel?.showCommonDataChanged(showData, categoryId) } private fun onContinueEpisodeClicked(episodeData: ContinueEpisodeData){ diff --git a/app/src/main/java/com/woka/webseries/views/fragments/WebShowFragment.kt b/app/src/main/java/com/woka/webseries/views/fragments/WebShowFragment.kt new file mode 100644 index 0000000..2730f30 --- /dev/null +++ b/app/src/main/java/com/woka/webseries/views/fragments/WebShowFragment.kt @@ -0,0 +1,552 @@ +package com.woka.webseries.views.fragments + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.InsetDrawable +import android.os.Bundle +import android.text.Html +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.appcompat.content.res.AppCompatResources +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import com.google.android.material.tabs.TabLayout +import com.woka.R +import com.woka.WokaApp.Companion.userPrefs +import com.woka.databinding.DialogEpisodeBinding +import com.woka.databinding.FragmentWebShowBinding +import com.woka.networking.ApiResult +import com.woka.utils.hide +import com.woka.utils.isNetworkConnected +import com.woka.utils.lightStatusBar +import com.woka.utils.setVisibility +import com.woka.utils.show +import com.woka.utils.toast +import com.woka.webseries.Repository +import com.woka.webseries.adapters.EpisodeAdapter +import com.woka.webseries.adapters.TeaserAdapter +import com.woka.webseries.models.ShowData +import com.woka.webseries.models.episodedata.EpisodeData +import com.woka.webseries.models.teaserdata.TeaserData +import com.woka.webseries.viewmodel.ViewModel + +private const val EXTRA_SHOW_DATA = "extra_show_id_data" +private const val EXTRA_SHOW_CATEGORY_ID = "extra_show_category_data" + +class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelectedListener { + + companion object { + fun newInstance(showData: ShowData, categoryId: String) = + WebShowFragment().apply { + arguments = Bundle().apply { + putParcelable(EXTRA_SHOW_DATA, showData) + putString(EXTRA_SHOW_CATEGORY_ID, categoryId) + } + } + } + + private lateinit var binding: FragmentWebShowBinding + private var viewModel: ViewModel? = null + + // arguments + private var categoryId: String? = null + private var showData: ShowData? = null + + // variables + + private lateinit var episodeAdapter: EpisodeAdapter + private lateinit var teaserAdapter: TeaserAdapter + + private var selectedSeasonId = -1 + + private lateinit var episodeDialogBinding: DialogEpisodeBinding + private lateinit var episodeDialog: Dialog + + private var loadMoreEpisodes = false + + override fun onAttach(context: Context) { + super.onAttach(context) + episodeAdapter = EpisodeAdapter(context) + teaserAdapter = TeaserAdapter(context) + + episodeDialog = Dialog(context) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + showData = it.getParcelable(EXTRA_SHOW_DATA) + categoryId = it.getString(EXTRA_SHOW_CATEGORY_ID) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentWebShowBinding.inflate(inflater, container, false) + viewModel = ViewModelProvider(this)[ViewModel::class.java] + activity?.let { + it.window.setBackgroundDrawableResource(R.color.web_show_bg) + it.window.lightStatusBar(true) + viewModel = ViewModelProvider(it)[ViewModel::class.java] + } + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initViews() + + initEpisodeDialog() + + clickEvents() + + setObservers() + + binding.seasonProgressView.show() + viewModel?.seasonListing("${showData?.id}", "$categoryId") + } + + override fun onDestroyView() { + super.onDestroyView() + activity?.let { + it.window.setBackgroundDrawable(AppCompatResources.getDrawable(it, R.drawable.gradient_web_series)) + it.window.statusBarColor = 0 + it.window.lightStatusBar(false) + } + + viewModel?.clearSeasonLiveData() + viewModel?.clearEpisodeListingLiveData() + } + + // initializations + private fun initViews() { + if (showData != null && categoryId != null) { + binding.apply { + + likeCount.text = "${showData!!.likes_count}" + + favSeason.isSelected = showData!!.isBookMarked(categoryId!!) + + likeSeason.isSelected = showData!!.is_liked ?: false + + binding.seasonsTab.addOnTabSelectedListener(this@WebShowFragment) + + rvEpisodes.adapter = episodeAdapter + episodeAdapter.onEpisodeClicked = ::onEpisodeClicked + episodeAdapter.onEpisodePlayClicked = ::onEpisodePlayClicked + + rvTeaser.adapter = teaserAdapter + teaserAdapter.onEpisodeClicked = ::onTeaserClicked + teaserAdapter.onEpisodePlayClicked = ::onTeaserPlayClicked + } + } + } + + private fun clickEvents() { + if (showData == null || categoryId == null) return + + binding.apply { + backBtn.setOnClickListener { + activity?.onBackPressedDispatcher?.onBackPressed() + } + + watchCard.setOnClickListener { + playTrailer.performClick() + } + + epLoadMoreBtn.setOnClickListener { + loadMoreEpisodes = true + viewModel?.loadMoreEpisodes("${showData?.id}", selectedSeasonId) + } + + likeSeason.setOnClickListener { + if (activity?.isNetworkConnected() == false) { + toast(getString(R.string.no_internet)) + return@setOnClickListener + } + + Repository.likeUnLikeShow("${showData?.id}", showData?.is_liked == false) + + likeSeason.isSelected = !likeSeason.isSelected + likeCount.text = "${showData?.likes_count}" + } + + favSeason.setOnClickListener { + if (activity?.isNetworkConnected() == false) { + toast(getString(R.string.no_internet)) + return@setOnClickListener + } + + if (showData == null) return@setOnClickListener + + categoryId?.let { + Repository.updateFavShow( + showData!!, + showData?.isBookMarked("$categoryId") == false, + "$categoryId" + ) + + favSeason.isSelected = !favSeason.isSelected + } + } + + playTrailer.setOnClickListener { +// if (seasonsTab.selectedTabPosition >= 0) { +// WebSeriesRepository.seasonDataMap["${showId}_${categoryId}"]?.result?.let { seasonList -> +// if (seasonsTab.selectedTabPosition < seasonList.size) { +// seasonList[seasonsTab.selectedTabPosition]?.let { seasonData -> +// +// seasonData.season_more_details?.let { moreDetailsList -> +// if (moreDetailsList.isNotEmpty()) { +// val videoPlayList = VideoPlayList(ArrayList()) +// +// val playlistItem = PlaylistItem.Builder() +// +// if (categoryId == "18" && moreDetailsList.size > 1) { +// moreDetailsList[1]?.let { seasonMoreData -> +// playlistItem.title(seasonMoreData.title) +// playlistItem.image(seasonMoreData.trailer_hd_url) +// playlistItem.file(seasonMoreData.trailer_hd_url) +// } +// } else { +// moreDetailsList[0]?.let { seasonMoreData -> +// playlistItem.title(seasonMoreData.title) +// playlistItem.image(seasonMoreData.trailer_hd_url) +// playlistItem.file(seasonMoreData.trailer_hd_url) +// } +// } +// +// videoPlayList.playlist.add(playlistItem.build()) +// +// startActivity( +// Intent(this@SeasonActivity, PlayerActivity::class.java) +// .apply { +// putExtra( +// PlayerActivity.EXTRA_PLAY_LIST, +// videoPlayList +// ) +// } +// ) +// } +// } +// } +// } +// } +// } + } + } + } + + private fun initEpisodeDialog() { + episodeDialogBinding = DialogEpisodeBinding.inflate(layoutInflater) + + episodeDialog.setContentView(episodeDialogBinding.root) + + try { + val back = ColorDrawable(Color.TRANSPARENT) + val inset = InsetDrawable(back, 50) + episodeDialog.window!!.setBackgroundDrawable(inset) + } catch (e: Exception) { + // do nothing + } + + try { + val layoutParams = episodeDialog.window!!.attributes + layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT + layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT + episodeDialog.window!!.setAttributes(layoutParams) + } catch (e: Exception) { + // do nothing + } + + episodeDialogBinding.close.setOnClickListener { episodeDialog.dismiss() } + } + + // callbacks + private fun setObservers(){ + viewModel?.seasonListingLiveData?.observe(viewLifecycleOwner){ + binding.seasonsTab.removeAllTabs() + when (it) { + is ApiResult.Error -> { + binding.seasonProgressView.hide() + } + + is ApiResult.Loading -> { + binding.seasonProgressView.show() + } + + is ApiResult.Success -> { + binding.seasonProgressView.hide() + it.data?.let {seasonList -> + for (season in seasonList){ + binding.seasonsTab.addTab( + binding.seasonsTab.newTab().setText("${season.season_number}") + ) + } + } + } + } + } + + viewModel?.episodeListingLiveData?.observe(viewLifecycleOwner){ + when (it){ + is ApiResult.Error -> { + binding.epShimmer.hide() + if (loadMoreEpisodes){ + // load more + binding.epLoadMoreBtn.text = getString(R.string.retry) + binding.epLoadMoreBtn.show() + }else{ + // first time load + binding.rvEpisodes.hide() + binding.epShimmer.hide() + binding.epLoadMoreBtn.hide() + } + } + is ApiResult.Loading -> { + binding.epShimmer.show() + binding.epLoadMoreBtn.hide() + binding.rvEpisodes.setVisibility(loadMoreEpisodes) + } + is ApiResult.Success -> { + it.data?.let {episodeList -> + binding.rvEpisodes.show() + binding.epShimmer.hide() + + binding.epLoadMoreBtn.text = getString(R.string.load_more) + binding.epLoadMoreBtn.setVisibility(viewModel?.episodePagingData?.get("${showData?.id}_$selectedSeasonId")?.lastPage == false) + + if (loadMoreEpisodes){ + // loaded more data + episodeAdapter.notifyItemRangeInserted( + episodeAdapter.currentList.size, + episodeList.size + ) + }else{ + // new category data load + episodeAdapter.submitList(episodeList) + } + } + } + } + } + } + + private fun onEpisodeClicked(position: Int, episodeData: EpisodeData) { + episodeDialogBinding.apply { + episodeData.content_more_details?.let { moreDetailsList -> + + episodeData.thumbnail_path?.let { + image.loadImage(it) + } + + if (moreDetailsList.isNotEmpty()) { + + if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) { + moreDetailsList[1]?.let { data -> + title.text = data.title + description.text = Html.fromHtml( + data.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + val mediaType = + "${getString(R.string.episode)} ${episodeData.episode_number}" + mediaTypeNumber.text = mediaType + duration.text = episodeData.episode_duration + } + } else { + moreDetailsList[0]?.let { data -> + title.text = data.title + description.text = Html.fromHtml( + data.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + val mediaType = + "${getString(R.string.episode)} ${episodeData.episode_number}" + mediaTypeNumber.text = mediaType + duration.text = episodeData.episode_duration + } + } + } else { + title.text = episodeData.episode_title + description.text = Html.fromHtml( + episodeData.episode_description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + val mediaType = "${getString(R.string.episode)} ${episodeData.episode_number}" + mediaTypeNumber.text = mediaType + duration.text = episodeData.episode_duration + } + + watchCard.setOnClickListener { + onEpisodePlayClicked(position) + } + + close.setOnClickListener { + episodeDialog.dismiss() + } + + episodeDialog.show() + } + } + } + + private fun onEpisodePlayClicked(position: Int) { + + } + + private fun onTeaserClicked(position: Int, teaserData: TeaserData) { + episodeDialogBinding.apply { + teaserData.content_more_details?.let { moreDetailsList -> + + teaserData.thumbnail_path?.let { + image.loadImage(it) + } + + if (moreDetailsList.isNotEmpty()) { + + if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) { + moreDetailsList[1]?.let { data -> + title.text = data.title + description.text = Html.fromHtml( + data.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + val mediaType = + "${getString(R.string.teasers)} ${teaserData.teaser_number}" + mediaTypeNumber.text = mediaType + duration.text = teaserData.teaser_duration + } + } else { + moreDetailsList[0]?.let { data -> + title.text = data.title + description.text = Html.fromHtml( + data.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + val mediaType = + "${getString(R.string.teasers)} ${teaserData.teaser_number}" + mediaTypeNumber.text = mediaType + duration.text = teaserData.teaser_duration + } + } + } else { + title.text = teaserData.teaser_title + description.text = Html.fromHtml( + teaserData.teaser_description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + val mediaType = "${getString(R.string.teasers)} ${teaserData.teaser_number}" + mediaTypeNumber.text = mediaType + duration.text = teaserData.teaser_duration + } + + watchCard.setOnClickListener { + onTeaserPlayClicked(position) + } + + close.setOnClickListener { + episodeDialog.dismiss() + } + + episodeDialog.show() + } + } + } + + private fun loadSeasonData() { + binding.apply { + if (seasonsTab.selectedTabPosition >= 0) { + viewModel?.seasonListingMap?.get("${showData?.id}_${categoryId}")?.let { seasonList -> + if (seasonsTab.selectedTabPosition < seasonList.size) { + seasonList[seasonsTab.selectedTabPosition].let { seasonData -> + + playTrailer.show() + detailsProgressiveView.hide() + detailsView.show() + + // loading episode data + seasonData.id?.let { + selectedSeasonId = it + + loadMoreEpisodes = false + viewModel?.loadEpisodes("${showData?.id}", it) +// WebSeriesRepository.loadTeaserData(showId, it) + } + + seasonYear.text = "${seasonData.release_year}" + val noOfEpisodes = + "${seasonData.no_of_episodes} ${seasonData.media_type}" + episodeNumber.text = noOfEpisodes + + seasonMediaType.text = "${seasonData.media_type}" + + seasonData.thumbnail_path?.let { + image.loadImage(it) + } + + seasonData.season_more_details?.let { moreDetailsList -> + if (moreDetailsList.isNotEmpty()) { + if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) { + moreDetailsList[1]?.let { moreDetails -> + seasonTitle.text = moreDetails.title?.uppercase() + seasonDescription.text = Html.fromHtml( + moreDetails.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + } + } else { + moreDetailsList[0]?.let { moreDetails -> + seasonTitle.text = moreDetails.title?.uppercase() + seasonDescription.text = Html.fromHtml( + moreDetails.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + } + } + } + } + } + } + } + } + } + } + + private fun onTeaserPlayClicked(position: Int) { + + } + + // season tab selection + override fun onTabSelected(p0: TabLayout.Tab?) { + loadSeasonData() + } + + override fun onTabUnselected(p0: TabLayout.Tab?) {} + + override fun onTabReselected(p0: TabLayout.Tab?) {} + + +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_web_show.xml b/app/src/main/res/layout/fragment_web_show.xml new file mode 100644 index 0000000..5c67191 --- /dev/null +++ b/app/src/main/res/layout/fragment_web_show.xml @@ -0,0 +1,562 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +