Teaser listing api in ViewModel.

Creating playlist for episodes and teasers in viewmodel

opening fragment from the MyListFragment: Receiving callback and updating data accordingly.

Corrected home1 fragment's background timings

Updating UI language of MyListFragment when user changes the language with the callback.

NoSignInDialog : UI and functionality

Implemented in MyListFragment
This commit is contained in:
2024-07-18 21:07:14 +05:30
parent 2e2c9cb853
commit 8a3ca80f09
26 changed files with 1055 additions and 2339 deletions

View File

@@ -18,7 +18,7 @@
android:theme="@style/Theme.Woka"
tools:targetApi="31">
<activity
android:name=".webseries.views.SeriesActivity"
android:name=".webseries.views.WebSeriesActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
@@ -55,14 +55,6 @@
android:screenOrientation="sensorLandscape"
android:supportsPictureInPicture="true"
android:theme="@style/FullScreenTheme" />
<activity
android:name=".webseries.views.SeasonActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".webseries.views.WebSeriesActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".home.views.FMActivity"
android:exported="false"

View File

@@ -10,7 +10,6 @@ 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
@@ -31,12 +30,9 @@ 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
@@ -64,6 +60,8 @@ class Home1Fragment : Fragment(), Listener {
viewModel = ViewModelProvider(it)[HomeViewModel::class.java]
}
currentBackground = null
initViews()
initPlayerView()
@@ -183,7 +181,7 @@ class Home1Fragment : Fragment(), Listener {
webSeries.setOnClickListener {
activity?.let {
startActivity(Intent(it, SeriesActivity::class.java))
startActivity(Intent(it, WebSeriesActivity::class.java))
}
}

View File

@@ -8,8 +8,6 @@ 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
import android.view.ViewGroup
@@ -18,11 +16,13 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.SimpleItemAnimator
import com.jwplayer.pub.api.media.playlists.PlaylistItem
import com.woka.R
import com.woka.WokaApp
import com.woka.WokaApp.Companion.userPrefs
import com.woka.audiobooks.AudioBookRepository
import com.woka.audiobooks.models.audiodata.AudioBookData
import com.woka.databinding.DialogModuleShowerBinding
@@ -32,22 +32,28 @@ import com.woka.home.mylist.adapters.FavAudioAdapter
import com.woka.home.mylist.adapters.FavGamesAdapter
import com.woka.home.mylist.adapters.FavKaraokeAdapter
import com.woka.home.mylist.adapters.WebSeriesAdapter
import com.woka.home.mylist.models.PostType
import com.woka.home.mylist.models.BookmarkedShowData
import com.woka.home.mylist.models.FavAudioBookData
import com.woka.home.mylist.models.FavGameData
import com.woka.home.mylist.models.FavKaraokeData
import com.woka.home.mylist.models.PostType
import com.woka.home.viewmodels.HomeViewModel
import com.woka.karaoke.KaraokeRepository
import com.woka.karaoke.models.listing.KaraokeData
import com.woka.networking.ApiResult
import com.woka.onboard.views.OnboardActivity
import com.woka.players.models.VideoPlayList
import com.woka.players.views.PlayerActivity
import com.woka.utils.TAG
import com.woka.userPreference.UserType
import com.woka.utils.hide
import com.woka.utils.isNetworkConnected
import com.woka.utils.show
import com.woka.utils.toast
import com.woka.webseries.views.SeasonActivity
import com.woka.webseries.models.ShowData
import com.woka.webseries.views.WebSeriesActivity
import com.woka.webseries.views.WebSeriesActivity.Companion.EXTRA_SHOW_CATEGORY
import com.woka.webseries.views.WebSeriesActivity.Companion.EXTRA_SHOW_DATA
import com.woka.webseries.views.WebSeriesActivity.Companion.EXTRA_SHOW_ID
import com.woka.wokagames.GamesRepository
import com.woka.wokagames.models.listing.GameData
import com.woka.wokagames.playerr.GamePlayerActivity
@@ -60,6 +66,7 @@ import java.util.Locale
class MyListFragment : Fragment() {
private lateinit var binding: FragmentMyListBinding
private lateinit var viewModel: HomeViewModel
private lateinit var webSeriesEAdapter: WebSeriesAdapter
private lateinit var webSeriesHAdapter: WebSeriesAdapter
@@ -77,6 +84,7 @@ class MyListFragment : Fragment() {
savedInstanceState: Bundle?
): View {
binding = FragmentMyListBinding.inflate(inflater, container, false)
viewModel = ViewModelProvider(requireActivity())[HomeViewModel::class.java]
// adapters
webSeriesEAdapter = WebSeriesAdapter(requireContext(),"1", ::onListGotEmpty)
@@ -100,6 +108,12 @@ class MyListFragment : Fragment() {
registerIntentLaunchers()
if (userPrefs?.userType != UserType.GUEST) {
MyListRepository.loadMyFavList()
}else{
binding.noSignInView.show()
}
return binding.root
}
@@ -127,7 +141,19 @@ class MyListFragment : Fragment() {
private fun clickEvents(){
binding.apply {
backBtn.setOnClickListener {
activity?.onBackPressedDispatcher?.onBackPressed()
@Suppress("DEPRECATION")
activity?.onBackPressed()
}
noSignIn.closeBtn.setOnClickListener {
@Suppress("DEPRECATION")
activity?.onBackPressed()
}
noSignIn.signIm.setOnClickListener {
context?.startActivity(Intent(context, OnboardActivity::class.java).apply {
putExtra(OnboardActivity.ONBOARD_ACTIVITY_INTENT, OnboardActivity.LOG_IN_INTENT)
})
}
}
}
@@ -135,8 +161,10 @@ class MyListFragment : Fragment() {
private fun registerIntentLaunchers(){
webShowIntentLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
if (it.resultCode == AppCompatActivity.RESULT_OK){
it.data?.getIntExtra(SeasonActivity.EXTRA_SHOW_ID, -1)?.let { showId ->
onWebShowItemChanged(showId)
it.data?.getIntExtra(EXTRA_SHOW_ID, -1)?.let { showId ->
if (showId != -1){
onWebShowItemChanged(showId)
}
}
}
}
@@ -144,6 +172,37 @@ class MyListFragment : Fragment() {
private fun setObservers(){
binding.apply {
viewModel.localeChangeLiveData.observe(viewLifecycleOwner){
webSeriesEngTxt.text = getString(R.string.web_series_english)
webSeriesHinTxt.text = getString(R.string.web_series_hindi)
audioBooksTxt.text = getString(R.string.audio_books)
karaokeTxt.text = getString(R.string.karaoke)
gamesTxt.text = getString(R.string.games)
noData.text = getString(R.string.no_favorites_added)
if (webSeriesEAdapter.currentList.isNotEmpty()){
webSeriesEAdapter.notifyItemRangeChanged(0, webSeriesEAdapter.currentList.size)
}
if (webSeriesHAdapter.currentList.isNotEmpty()){
webSeriesHAdapter.notifyItemRangeChanged(0, webSeriesHAdapter.currentList.size)
}
if (audioBooksAdapter.currentList.isNotEmpty()){
audioBooksAdapter.notifyItemRangeChanged(0, audioBooksAdapter.currentList.size)
}
if (karaokeAdapter.currentList.isNotEmpty()){
karaokeAdapter.notifyItemRangeChanged(0, karaokeAdapter.currentList.size)
}
if (gamesAdapter.currentList.isNotEmpty()){
gamesAdapter.notifyItemRangeChanged(0, gamesAdapter.currentList.size)
}
}
MyListRepository.myFavListLiveData.observe(viewLifecycleOwner){
lifecycleScope.launch {
when (it){
@@ -151,7 +210,10 @@ class MyListFragment : Fragment() {
progressBar.hide()
noData.show()
}
is ApiResult.Loading -> {}
is ApiResult.Loading -> {
progressBar.show()
noData.hide()
}
is ApiResult.Success -> {
it.data?.result?.let { result ->
@@ -298,11 +360,10 @@ class MyListFragment : Fragment() {
}
// web show
private fun onWebShowClicked(showData: BookmarkedShowData, position:Int, categoryId: String){
webShowIntentLauncher?.launch(Intent(context, SeasonActivity::class.java).apply {
putExtra(SeasonActivity.EXTRA_SHOW_ID, showData.id)
putExtra(SeasonActivity.EXTRA_SHOW_CATEGORY_DATA, categoryId)
putExtra(SeasonActivity.EXTRA_SHOW_POSITION, position)
private fun onWebShowClicked(showData: BookmarkedShowData, categoryId: String){
webShowIntentLauncher?.launch(Intent(context, WebSeriesActivity::class.java).apply {
putExtra(EXTRA_SHOW_DATA, ShowData(showData))
putExtra(EXTRA_SHOW_CATEGORY, categoryId)
})
}
@@ -316,7 +377,6 @@ class MyListFragment : Fragment() {
if (hindiPosition > -1 && hindiPosition < webSeriesHAdapter.currentList.size){
webSeriesHAdapter.notifyItemChanged(hindiPosition)
}
}
// audio books
@@ -347,7 +407,7 @@ class MyListFragment : Fragment() {
if (moreDetailsList.isNotEmpty()) {
if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
moreDetailsList[1]?.let { data ->
title.text = data.title
description.text = Html.fromHtml(
@@ -447,7 +507,7 @@ class MyListFragment : Fragment() {
audioBookData.content_more_details?.let {moreDetails ->
if (moreDetails.isNotEmpty()){
if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetails.size > 1){
if (userPrefs?.appLanguage == "hi" && moreDetails.size > 1){
url = moreDetails[1]?.url
title = moreDetails[1]?.title?:""
}else{
@@ -512,7 +572,7 @@ class MyListFragment : Fragment() {
if (moreDetailsList.isNotEmpty()) {
if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
moreDetailsList[1]?.let { data ->
title.text = data.title
description.text = Html.fromHtml(
@@ -639,7 +699,7 @@ class MyListFragment : Fragment() {
if (moreDetailsList.isNotEmpty()) {
if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
moreDetailsList[1]?.let { data ->
title.text = data.title
description.text = Html.fromHtml(

View File

@@ -12,11 +12,18 @@ enum class TimePeriod{
fun getCurrentTimePeriod(): TimePeriod {
val cal = Calendar.getInstance()
val hrs = cal.get(Calendar.HOUR_OF_DAY)
val min = cal.get(Calendar.MINUTE)
return when (hrs) {
in 6..10 -> MORNING
in 11..15 -> AFTERNOON
in 16..19 -> EVENING
in 11..16 -> {
if (hrs == 16 && min >= 30) EVENING
else AFTERNOON
}
in 17..18 -> {
if (hrs == 18 && min >= 30) NIGHT
else EVENING
}
else -> NIGHT
}
}

View File

@@ -14,17 +14,7 @@ object MyListRepository {
private val _myFavListLiveData = MutableLiveData<ApiResult<MyListResponse>>()
val myFavListLiveData: LiveData<ApiResult<MyListResponse>>
get() {
if (shallLoadNewData){
// load new data
loadMyFavList()
shallLoadNewData = false
}else{
// also updating local changes
_myFavListLiveData.postValue(ApiResult.Success(myFavData))
}
return _myFavListLiveData
}
get() = _myFavListLiveData
var myFavData: MyListResponse = MyListResponse(Result(
mutableListOf(),
@@ -51,23 +41,26 @@ object MyListRepository {
}
}
private fun loadMyFavList(){
CoroutineScope(Dispatchers.IO).launch {
_myFavListLiveData.postValue(ApiResult.Loading())
val response = UserRepository.loadMyFavList()
fun loadMyFavList(){
if (shallLoadNewData) {
// shall load new data
CoroutineScope(Dispatchers.IO).launch {
_myFavListLiveData.postValue(ApiResult.Loading())
val response = UserRepository.loadMyFavList()
when (response){
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
response.data?.let {
myFavData = it
shallLoadNewData = false
when (response){
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
response.data?.let {
myFavData = it
shallLoadNewData = false
}
}
}
}
_myFavListLiveData.postValue(response)
_myFavListLiveData.postValue(response)
}
}
}
}

View File

@@ -38,7 +38,7 @@ class WebSeriesAdapter(private val context: Context,
.build()
}
var onShowClickListener: ((BookmarkedShowData, Int, String) -> Unit)? = null
var onShowClickListener: ((BookmarkedShowData, String) -> Unit)? = null
var onLikeChanged: ((Int) -> Unit)? = null
constructor(context: Context,
@@ -109,7 +109,7 @@ class WebSeriesAdapter(private val context: Context,
showData.likes_count?.let {
likeCount.text = "$it"
}
showData.id?.let {
showData?.id?.let {
onLikeChanged?.invoke(it)
}
}
@@ -151,7 +151,7 @@ class WebSeriesAdapter(private val context: Context,
}
card.setOnClickListener {
onShowClickListener?.invoke(showData, holder.absoluteAdapterPosition, categoryId)
onShowClickListener?.invoke(showData, categoryId)
}
}

View File

@@ -8,10 +8,8 @@ import android.content.SharedPreferences
import android.provider.Settings.Secure
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.woka.audiobooks.AudioBookRepository
import com.woka.home.models.Theme
import com.woka.home.mylist.MyListRepository
import com.woka.karaoke.KaraokeRepository
import com.woka.networking.ApiResult
import com.woka.onboard.views.OnboardActivity
import com.woka.userdata.UserRepository
@@ -115,7 +113,6 @@ class UserPreference(val context: Context) {
}
private fun clearData(){
WebSeriesRepository.clearData()
MyListRepository.clearData()
}
}

View File

@@ -0,0 +1,73 @@
package com.woka.utils
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.InsetDrawable
import android.view.LayoutInflater
import android.view.WindowManager
import com.woka.databinding.LayoutNoSignInDialogBinding
import com.woka.onboard.views.OnboardActivity
class NoSignInDialog(context: Context) {
private val binding: LayoutNoSignInDialogBinding
private val dialog: Dialog
init {
binding = LayoutNoSignInDialogBinding.inflate(
LayoutInflater.from(context)
)
dialog = Dialog(context)
dialog.setContentView(binding.getRoot())
dialog.setCancelable(false)
binding.signIm.setOnClickListener {
dismiss()
context.startActivity(Intent(context, OnboardActivity::class.java).apply {
putExtra(OnboardActivity.ONBOARD_ACTIVITY_INTENT, OnboardActivity.LOG_IN_INTENT)
})
}
try {
val back = ColorDrawable(Color.TRANSPARENT)
val inset = InsetDrawable(back, 50)
dialog.window!!.setBackgroundDrawable(inset)
} catch (e: Exception) {
// do nothing
}
try {
val layoutParams = dialog.window!!.attributes
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
dialog.window!!.setAttributes(layoutParams)
} catch (e: Exception) {
// do nothing
}
}
fun show(titleTxt: String, messageTxt: String): NoSignInDialog{
binding.apply {
title.text = titleTxt
message.text = messageTxt
}.also {
show()
}
return this
}
fun closeListener(click: () -> Unit){
binding.closeBtn.setOnClickListener {
click()
}
}
fun show() = dialog.show()
fun dismiss() = dialog.dismiss()
}

View File

@@ -1,197 +0,0 @@
package com.woka.webseries
import com.woka.home.mylist.MyListRepository
import com.woka.home.mylist.models.BookmarkedShowData
import com.woka.home.mylist.models.PostType
import com.woka.modules.categorymodels.CategoriesResponse
import com.woka.networking.ApiResult
import com.woka.networking.RetrofitHelper
import com.woka.networking.RetrofitHelper.handleApiCall
import com.woka.userdata.UserActionApiService
import com.woka.webseries.models.ContinueEpisodeResponse
import com.woka.webseries.models.ShowData
import com.woka.webseries.models.WebSeriesResponse
import com.woka.webseries.models.episodedata.EpisodeResponseData
import com.woka.webseries.models.seasondata.SeasonDataResponse
import com.woka.webseries.models.teaserdata.TeaserResponseData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.FormBody
import kotlin.math.max
object Repository {
// api services
private val apiService = RetrofitHelper.getRetrofit().create(WebSeriesApiService::class.java)
private val userActionApiService = RetrofitHelper.getRetrofit().create(UserActionApiService::class.java)
suspend fun loadCategories(): ApiResult<CategoriesResponse> {
return handleApiCall {
apiService.categoryListing(
FormBody.Builder()
.add("module_id", "7")
.build()
)
}
}
suspend fun loadContinueWatchData(): ApiResult<ContinueEpisodeResponse> {
return handleApiCall {
userActionApiService.continueWatchingShowListing(
FormBody.Builder()
.add("post_type", "3")
.build()
)
}
}
suspend fun loadWebSeries(categoryId: String, pageNo: Int, quantity: Int): ApiResult<WebSeriesResponse> {
return handleApiCall {
apiService.getWebSeries(
FormBody.Builder()
.add("category_id", categoryId)
.add("api_version", "v2")
.add("start", "$pageNo")
.add("limit", "$quantity")
.build()
)
}
}
suspend fun teaserListing(showId: Int, seasonId: Int): ApiResult<TeaserResponseData> {
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<EpisodeResponseData> {
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<SeasonDataResponse> {
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 {
if (likeIt){
userActionApiService.likePost(
FormBody.Builder()
.add("post_id", postId)
.add("post_type", PostType.WEB_SERIES.value)
.build()
)
}else{
userActionApiService.unLikePost(
FormBody.Builder()
.add("post_id", postId)
.add("post_type", PostType.WEB_SERIES.value)
.build()
)
}
}
}
changeLikeLocally(postId, likeIt)
}
private fun changeLikeLocally(id: String, isLiked: Boolean){
// changing in fav list locally
MyListRepository.myFavData.result?.show_data?.let {
for (show in it){
if ("${show.id}" == id){
show.is_liked = isLiked
show.likes_count?.let { count ->
show.likes_count = if (isLiked) count + 1
else max(0, count - 1)
}
break
}
}
}
}
fun updateFavShow(showData: ShowData, addToBookmark: Boolean, categoryId: String){
CoroutineScope(Dispatchers.IO).launch {
handleApiCall {
if (addToBookmark){
userActionApiService.addToFav(
FormBody.Builder()
.add("post_id", "${showData.id}")
.add("post_type", PostType.WEB_SERIES.value)
.add("category_id", categoryId)
.build()
)
}else{
userActionApiService.removeFromFav(
FormBody.Builder()
.add("id", "${showData.id}")
.add("post_type", PostType.WEB_SERIES.value)
.add("category_id", categoryId)
.build()
)
}
}
}
MyListRepository.myFavData.result?.show_data?.let {
var found = false
var showFound: BookmarkedShowData? = null
for (show in it){
if (showData.id == show.id){
if (addToBookmark){
show.addAsBookMark(categoryId)
}else{
show.removeAsBookMark(categoryId)
}
showFound = show
found = true
break
}
}
if (!found && addToBookmark){
MyListRepository.myFavData.result?.show_data?.add(BookmarkedShowData(showData, categoryId))
}
if (found){
showFound?.let {bookMarkShowData ->
if (!bookMarkShowData.isBookMarked()){
it.remove(bookMarkShowData)
}
}
}
}
}
}

View File

@@ -1,16 +1,12 @@
package com.woka.webseries
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.jwplayer.pub.api.media.playlists.PlaylistItem
import com.woka.home.mylist.MyListRepository
import com.woka.home.mylist.models.BookmarkedShowData
import com.woka.home.mylist.models.PostType
import com.woka.modules.ModuleApiService
import com.woka.modules.categorymodels.CategoriesResponse
import com.woka.networking.ApiResult
import com.woka.networking.RetrofitHelper
import com.woka.players.models.VideoPlayList
import com.woka.players.models.VideoViewRequestData
import com.woka.networking.RetrofitHelper.handleApiCall
import com.woka.userdata.UserActionApiService
import com.woka.webseries.models.ContinueEpisodeResponse
import com.woka.webseries.models.ShowData
@@ -29,309 +25,83 @@ object WebSeriesRepository {
private val apiService = RetrofitHelper.getRetrofit().create(WebSeriesApiService::class.java)
private val userActionApiService = RetrofitHelper.getRetrofit().create(UserActionApiService::class.java)
// live data
// show data (loose caching)
private val _webSeriesLiveData = MutableLiveData<ApiResult<HashMap<String, WebSeriesResponse>>>()
val webSeriesLiveData: LiveData<ApiResult<HashMap<String, WebSeriesResponse>>>
get() = _webSeriesLiveData
var webSeriesData = HashMap<String, WebSeriesResponse>()
// continue watching (tight caching)
private val _continueWatchLiveData = MutableLiveData<ApiResult<ContinueEpisodeResponse>>()
val continueWatchLiveData: LiveData<ApiResult<ContinueEpisodeResponse>>
get() = _continueWatchLiveData
var continueWatchData: ContinueEpisodeResponse? = null
// seasonListing data
private val _seasonDataLiveData = MutableLiveData<ApiResult<SeasonDataResponse>>()
val seasonDataLiveData: LiveData<ApiResult<SeasonDataResponse>>
get() = _seasonDataLiveData
// seasons data for every show. where {key -> "showId_categoryId"}
var seasonDataMap = HashMap<String, SeasonDataResponse>()
// episode listing data
private val _episodeDataLiveData = MutableLiveData<ApiResult<EpisodeResponseData>>()
val episodeDataLiveData: LiveData<ApiResult<EpisodeResponseData>>
get() = _episodeDataLiveData
// episode data for every season. where {key -> "showId_seasonId"}
private var episodeDataMap = HashMap<String, EpisodeResponseData>()
// episodes playlist, where {key -> "showId_seasonId_categoryId"}
val episodesPlaylistMap = HashMap<String, VideoPlayList>()
// teaser listing data
private val _teaserDataLiveData = MutableLiveData<ApiResult<TeaserResponseData>>()
val teaserDataLiveData: LiveData<ApiResult<TeaserResponseData>>
get() = _teaserDataLiveData
// teaser data for every season. where {key -> "showId_seasonId"}
private var teaserDataMap = HashMap<String, TeaserResponseData>()
// teasers playlist, where {key -> "showId_seasonId_categoryId"}
var teasersPlaylistMap = HashMap<String, VideoPlayList>()
fun loadTeaserData(showId: Int, seasonId: Int){
if (teaserDataMap.containsKey("${showId}_${seasonId}")){
_teaserDataLiveData.postValue(ApiResult.Success(data = teaserDataMap["${showId}_${seasonId}"]))
return
}
CoroutineScope(Dispatchers.IO).launch {
_teaserDataLiveData.postValue(ApiResult.Loading())
val response = RetrofitHelper.handleApiCall {
apiService.teaserListing(
FormBody.Builder()
.add("watch_show_master_id", "$showId")
.add("season_master_id", "$seasonId")
.build()
)
}
when (response){
is ApiResult.Error -> {
_teaserDataLiveData.postValue(response)
}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
response.data?.let {teaserData ->
teaserDataMap["${showId}_${seasonId}"] = teaserData
}
_teaserDataLiveData.postValue(response)
// creating a playable playlist
teaserDataMap["${showId}_${seasonId}"]?.result?.filterNotNull()?.let {
val videoPlayListEng = VideoPlayList(ArrayList(), ArrayList())
val videoPlayListHin = VideoPlayList(ArrayList(), ArrayList())
for (teaser in it){
teaser.content_more_details?.let {moreDetailsList ->
if (moreDetailsList.isNotEmpty()){
if (moreDetailsList.size > 1){
moreDetailsList[1]?.let {moreDetail ->
videoPlayListHin.playlist.add(
PlaylistItem.Builder()
.file(moreDetail.content_hd_url)
.title(moreDetail.title)
.image(teaser.thumbnail_path)
.build()
)
}
}
moreDetailsList[0]?.let {moreDetail ->
videoPlayListEng.playlist.add(
PlaylistItem.Builder()
.file(moreDetail.content_hd_url)
.title(moreDetail.title)
.image(teaser.thumbnail_path)
.build()
)
}
}
}
}
teasersPlaylistMap["${showId}_${seasonId}_18"] = videoPlayListHin
teasersPlaylistMap["${showId}_${seasonId}_1"] = videoPlayListEng
}
}
}
suspend fun loadCategories(): ApiResult<CategoriesResponse> {
return handleApiCall {
apiService.categoryListing(
FormBody.Builder()
.add("module_id", "7")
.build()
)
}
}
fun loadEpisodeData(showId: Int, seasonId: Int){
if (episodeDataMap.containsKey("${showId}_${seasonId}")){
_episodeDataLiveData.postValue(ApiResult.Success(data = episodeDataMap["${showId}_${seasonId}"]))
return
}
CoroutineScope(Dispatchers.IO).launch {
_episodeDataLiveData.postValue(ApiResult.Loading())
val response = RetrofitHelper.handleApiCall {
apiService.episodeListing(
FormBody.Builder()
.add("watch_show_master_id", "$showId")
.add("season_master_id", "$seasonId")
.build()
)
}
when (response){
is ApiResult.Error -> {
_episodeDataLiveData.postValue(response)
}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
response.data?.let {episodeData ->
episodeDataMap["${showId}_${seasonId}"] = episodeData
}
_episodeDataLiveData.postValue(response)
// creating a playable playlist
episodeDataMap["${showId}_${seasonId}"]?.result?.filterNotNull()?.let {
val videoPlayListItemHin = VideoPlayList(ArrayList(), ArrayList())
val videoPlayListItemEng = VideoPlayList(ArrayList(), ArrayList())
for (episode in it){
episode.content_more_details?.let {moreDetailsList ->
if (moreDetailsList.isNotEmpty()){
if (moreDetailsList.size > 1){
moreDetailsList[1]?.let {moreDetail ->
videoPlayListItemHin.playlist.add(
PlaylistItem.Builder()
.file(moreDetail.content_hd_url)
.title(moreDetail.title)
.image(episode.thumbnail_path)
.build()
)
videoPlayListItemHin.videoViewRequestDataList?.add(
VideoViewRequestData(
post_id = "${episode.id}",
post_type = "3",
total_watched_duration = null,
category_id = "18"
)
)
}
}
moreDetailsList[0]?.let {moreDetail ->
videoPlayListItemEng.playlist.add(
PlaylistItem.Builder()
.file(moreDetail.content_hd_url)
.title(moreDetail.title)
.image(episode.thumbnail_path)
.build()
)
videoPlayListItemEng.videoViewRequestDataList?.add(
VideoViewRequestData(
post_id = "${episode.id}",
post_type = "3",
total_watched_duration = null,
category_id = "1"
)
)
}
}
}
}
episodesPlaylistMap["${showId}_${seasonId}_18"] = videoPlayListItemHin
episodesPlaylistMap["${showId}_${seasonId}_1"] = videoPlayListItemEng
}
}
}
suspend fun loadContinueWatchData(): ApiResult<ContinueEpisodeResponse> {
return handleApiCall {
userActionApiService.continueWatchingShowListing(
FormBody.Builder()
.add("post_type", "3")
.build()
)
}
}
fun clearEpisodeData(){
_episodeDataLiveData.postValue(ApiResult.Loading())
}
fun loadSeasonListing(showId: Int, categoryId: String){
if (seasonDataMap.containsKey("${showId}_${categoryId}")){
_seasonDataLiveData.postValue(ApiResult.Success(data = seasonDataMap["${showId}_${categoryId}"]))
return
}
CoroutineScope(Dispatchers.IO).launch {
_seasonDataLiveData.postValue(ApiResult.Loading())
val response = RetrofitHelper.handleApiCall {
apiService.seasonListing(
FormBody.Builder()
.add("watch_show_id", "$showId")
.add("category_id", categoryId)
.build()
)
}
when (response){
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
response.data?.let {seasonList ->
seasonDataMap["${showId}_${categoryId}"] = seasonList
}
_seasonDataLiveData.postValue(ApiResult.Success(response.data))
}
}
suspend fun loadWebSeries(categoryId: String, pageNo: Int, quantity: Int): ApiResult<WebSeriesResponse> {
return handleApiCall {
apiService.getWebSeries(
FormBody.Builder()
.add("category_id", categoryId)
.add("api_version", "v2")
.add("start", "$pageNo")
.add("limit", "$quantity")
.build()
)
}
}
fun loadContinueWatchData(){
if (continueWatchData != null){
_continueWatchLiveData.postValue(ApiResult.Success(continueWatchData))
return
}
CoroutineScope(Dispatchers.IO).launch {
_continueWatchLiveData.postValue(ApiResult.Loading())
val response = RetrofitHelper.handleApiCall {
userActionApiService.continueWatchingShowListing(
FormBody.Builder()
.add("post_type", "3")
.build()
)
}
when (response){
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
continueWatchData = response.data
}
}
_continueWatchLiveData.postValue(response)
suspend fun teaserListing(showId: String, seasonId: Int): ApiResult<TeaserResponseData> {
return handleApiCall{
apiService.teaserListing(
FormBody.Builder()
.add("watch_show_master_id", showId)
.add("season_master_id", "$seasonId")
.build()
)
}
}
fun loadWebSeries(categoryId: String){
if (webSeriesData.containsKey(categoryId)){
webSeriesData[categoryId]?.let {
_webSeriesLiveData.postValue(ApiResult.Success(webSeriesData))
return
}
suspend fun episodeListing(
showId: String,
seasonId: Int,
pageNo: Int,
quantity: Int
): ApiResult<EpisodeResponseData> {
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()
)
}
}
CoroutineScope(Dispatchers.IO).launch {
_webSeriesLiveData.postValue(ApiResult.Loading())
val response = RetrofitHelper.handleApiCall {
apiService.getWebSeries(
FormBody.Builder()
.add("category_id", categoryId)
.build()
)
}
when (response){
is ApiResult.Error -> {
_webSeriesLiveData.postValue(ApiResult.Error(response.errorMessage, response.error))
}
is ApiResult.Loading -> {
_webSeriesLiveData.postValue(ApiResult.Loading())
}
is ApiResult.Success -> {
response.data?.let {
webSeriesData[categoryId] = it
_webSeriesLiveData.postValue(ApiResult.Success(data = webSeriesData))
}
}
}
suspend fun seasonListing(showId: String, categoryId: String): ApiResult<SeasonDataResponse> {
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 {
RetrofitHelper.handleApiCall {
handleApiCall {
if (likeIt){
userActionApiService.likePost(
FormBody.Builder()
@@ -354,29 +124,6 @@ object WebSeriesRepository {
}
private fun changeLikeLocally(id: String, isLiked: Boolean){
// changing in web series locally
for (showDataResponse in webSeriesData.values){
showDataResponse.show_data?.let {shows ->
for (show in shows){
var found = false
show?.let {data ->
if ("${data.id}" == id){
data.is_liked = isLiked
data.likes_count?.let { count ->
data.likes_count = if (isLiked) count + 1
else max(0, count - 1)
}
found = true
}
}
if (found) break
}
}
}
// changing in fav list locally
MyListRepository.myFavData.result?.show_data?.let {
for (show in it){
@@ -394,7 +141,7 @@ object WebSeriesRepository {
fun updateFavShow(showData: ShowData, addToBookmark: Boolean, categoryId: String){
CoroutineScope(Dispatchers.IO).launch {
RetrofitHelper.handleApiCall {
handleApiCall {
if (addToBookmark){
userActionApiService.addToFav(
FormBody.Builder()
@@ -446,33 +193,5 @@ object WebSeriesRepository {
}
}
}
webSeriesData[categoryId]?.show_data?.let {
for (show in it){
var found = false
show?.let {data ->
if (showData.id == data.id){
found = true
if (addToBookmark){
show.addAsBookMark(categoryId)
}else{
show.removeAsBookMark(categoryId)
}
}
}
if (found) break
}
}
}
fun clearData(){
_webSeriesLiveData.postValue(ApiResult.Loading())
_continueWatchLiveData.postValue(ApiResult.Loading())
webSeriesData.clear()
continueWatchData = null
}
}

View File

@@ -12,7 +12,7 @@ import com.woka.databinding.ShowViewHolderBinding
import com.woka.utils.isNetworkConnected
import com.woka.utils.show
import com.woka.utils.toast
import com.woka.webseries.Repository
import com.woka.webseries.WebSeriesRepository
import com.woka.webseries.models.ShowData
import kotlin.math.max
@@ -85,7 +85,7 @@ class WebSeriesShowAdapter(
return@setOnClickListener
}
Repository.likeUnLikeShow("${showData.id}", !like.isSelected)
WebSeriesRepository.likeUnLikeShow("${showData.id}", !like.isSelected)
showData.likes_count?.let {
showData.likes_count = if (like.isSelected){
@@ -118,7 +118,7 @@ class WebSeriesShowAdapter(
}
categoryId?.let {
Repository.updateFavShow(
WebSeriesRepository.updateFavShow(
showData,
!fav.isSelected,
it

View File

@@ -1,6 +1,6 @@
package com.woka.webseries.models.teaserdata
data class TeaserResponseData(
val result: List<TeaserData?>?,
val result: MutableList<TeaserData?>?,
val total_records: Int?
)

View File

@@ -1,235 +0,0 @@
package com.woka.webseries.viewmodel
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.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
data class PagingData(
var nextPageToLoad: Int = 0, var quantityPerPage: Int = 6,
var lastPage: Boolean = false
)
// categories listing
private val _showCategoryLiveData = MutableLiveData<ApiResult<CategoriesResponse>>()
val showCategoryLiveData: LiveData<ApiResult<CategoriesResponse>>
get() = _showCategoryLiveData
var categoryPagingData = HashMap<String, PagingData>()
fun loadCategories() {
viewModelScope.launch {
_showCategoryLiveData.postValue(ApiResult.Loading())
_showCategoryLiveData.postValue(repository.loadCategories())
}
}
// continue watching
private val _continueWatchLiveData = MutableLiveData<ApiResult<ContinueEpisodeResponse>>()
val continueWatchLiveData: LiveData<ApiResult<ContinueEpisodeResponse>>
get() = _continueWatchLiveData
fun loadContinueWatching() {
viewModelScope.launch {
_continueWatchLiveData.postValue(ApiResult.Loading())
_continueWatchLiveData.postValue(repository.loadContinueWatchData())
}
}
// show data
private val _webSeriesLiveData =
MutableLiveData<ApiResult<MutableList<ShowData>>>()
val webSeriesLiveData: LiveData<ApiResult<MutableList<ShowData>>>
get() = _webSeriesLiveData
// map of series data where key is category
private var webSeriesData = HashMap<String, MutableList<ShowData>>()
fun loadWebSeries(categoryId: String){
if (webSeriesData.containsKey(categoryId) && webSeriesData[categoryId]?.isNotEmpty() == true){
_webSeriesLiveData.postValue(ApiResult.Success(webSeriesData[categoryId]))
}else{
loadMoreWebSeries(categoryId)
}
}
fun loadMoreWebSeries(categoryId: String) {
viewModelScope.launch {
_webSeriesLiveData.postValue(ApiResult.Loading())
val pagingData = if (categoryPagingData.containsKey(categoryId)){
categoryPagingData[categoryId]!!
}else{
val pagingData = PagingData()
categoryPagingData[categoryId] = pagingData
pagingData
}
when (val response = repository.loadWebSeries(categoryId, pagingData.nextPageToLoad, pagingData.quantityPerPage)) {
is ApiResult.Error -> {
_webSeriesLiveData.postValue(
ApiResult.Error(
response.errorMessage,
response.error
)
)
}
is ApiResult.Loading -> {
_webSeriesLiveData.postValue(ApiResult.Loading())
}
is ApiResult.Success -> {
response.data?.let { data ->
data.show_data?.filterNotNull()?.let { newList ->
val currentList = webSeriesData.getOrDefault(categoryId, ArrayList())
currentList.addAll(newList)
webSeriesData[categoryId] = currentList
pagingData.lastPage = webSeriesData[categoryId]?.size == data.total_records
_webSeriesLiveData.postValue(ApiResult.Success(webSeriesData[categoryId]))
pagingData.nextPageToLoad++
}
}
}
}
}
}
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<ApiResult<MutableList<SeasonData>>>()
val seasonListingLiveData: LiveData<ApiResult<MutableList<SeasonData>>>
get() = _seasonListingLiveData
// seasons data for every show. where {key -> "showId_categoryId"}
var seasonListingMap = HashMap<String, MutableList<SeasonData>>()
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<ApiResult<MutableList<EpisodeData>>>()
val episodeListingLiveData: LiveData<ApiResult<MutableList<EpisodeData>>>
get() = _episodeListingLiveData
var episodePagingData = HashMap<String, PagingData>()
// episode data for every season. where {key -> "showId_seasonId"}
private var episodeDataMap = HashMap<String, MutableList<EpisodeData>>()
// episodes playlist, where {key -> "showId_seasonId_categoryId"}
val episodesPlaylistMap = HashMap<String, VideoPlayList>()
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())
}
}

View File

@@ -1,17 +1,389 @@
package com.woka.webseries.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.jwplayer.pub.api.media.playlists.PlaylistItem
import com.woka.modules.categorymodels.CategoriesResponse
import com.woka.networking.ApiResult
import com.woka.players.models.VideoPlayList
import com.woka.webseries.WebSeriesRepository
import com.woka.webseries.models.ContinueEpisodeResponse
import com.woka.webseries.models.WebSeriesResponse
import com.woka.webseries.models.ShowData
import com.woka.webseries.models.episodedata.EpisodeData
import com.woka.webseries.models.seasondata.SeasonData
import com.woka.webseries.models.teaserdata.TeaserData
import kotlinx.coroutines.launch
class WebSeriesViewModel: ViewModel() {
class WebSeriesViewModel : ViewModel() {
val webSeriesLiveData: LiveData<ApiResult<HashMap<String, WebSeriesResponse>>>
get() = WebSeriesRepository.webSeriesLiveData
private val repository = WebSeriesRepository
data class PagingData(
var nextPageToLoad: Int = 0, var quantityPerPage: Int = 6,
var lastPage: Boolean = false
)
// categories listing
private val _showCategoryLiveData = MutableLiveData<ApiResult<CategoriesResponse>>()
val showCategoryLiveData: LiveData<ApiResult<CategoriesResponse>>
get() = _showCategoryLiveData
var categoryPagingData = HashMap<String, PagingData>()
fun loadCategories() {
viewModelScope.launch {
_showCategoryLiveData.postValue(ApiResult.Loading())
_showCategoryLiveData.postValue(repository.loadCategories())
}
}
// continue watching
private val _continueWatchLiveData = MutableLiveData<ApiResult<ContinueEpisodeResponse>>()
val continueWatchLiveData: LiveData<ApiResult<ContinueEpisodeResponse>>
get() = WebSeriesRepository.continueWatchLiveData
get() = _continueWatchLiveData
fun loadContinueWatching() {
viewModelScope.launch {
_continueWatchLiveData.postValue(ApiResult.Loading())
_continueWatchLiveData.postValue(repository.loadContinueWatchData())
}
}
// show data
private val _webSeriesLiveData =
MutableLiveData<ApiResult<MutableList<ShowData>>>()
val webSeriesLiveData: LiveData<ApiResult<MutableList<ShowData>>>
get() = _webSeriesLiveData
// map of series data where key is category
private var webSeriesData = HashMap<String, MutableList<ShowData>>()
fun loadWebSeries(categoryId: String) {
if (webSeriesData.containsKey(categoryId) && webSeriesData[categoryId]?.isNotEmpty() == true) {
_webSeriesLiveData.postValue(ApiResult.Success(webSeriesData[categoryId]))
} else {
loadMoreWebSeries(categoryId)
}
}
fun loadMoreWebSeries(categoryId: String) {
viewModelScope.launch {
_webSeriesLiveData.postValue(ApiResult.Loading())
val pagingData = if (categoryPagingData.containsKey(categoryId)) {
categoryPagingData[categoryId]!!
} else {
val pagingData = PagingData()
categoryPagingData[categoryId] = pagingData
pagingData
}
when (val response = repository.loadWebSeries(
categoryId,
pagingData.nextPageToLoad,
pagingData.quantityPerPage
)) {
is ApiResult.Error -> {
_webSeriesLiveData.postValue(
ApiResult.Error(
response.errorMessage,
response.error
)
)
}
is ApiResult.Loading -> {
_webSeriesLiveData.postValue(ApiResult.Loading())
}
is ApiResult.Success -> {
response.data?.let { data ->
data.show_data?.filterNotNull()?.let { newList ->
val currentList = webSeriesData.getOrDefault(categoryId, ArrayList())
currentList.addAll(newList)
webSeriesData[categoryId] = currentList
pagingData.lastPage =
webSeriesData[categoryId]?.size == data.total_records
pagingData.nextPageToLoad++
_webSeriesLiveData.postValue(ApiResult.Success(webSeriesData[categoryId]))
}
}
}
}
}
}
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<ApiResult<MutableList<SeasonData>>>()
val seasonListingLiveData: LiveData<ApiResult<MutableList<SeasonData>>>
get() = _seasonListingLiveData
// seasons data for every show. where {key -> "showId_categoryId"}
var seasonListingMap = HashMap<String, MutableList<SeasonData>>()
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<ApiResult<MutableList<EpisodeData>>>()
val episodeListingLiveData: LiveData<ApiResult<MutableList<EpisodeData>>>
get() = _episodeListingLiveData
var episodePagingData = HashMap<String, PagingData>()
// episode data for every season. where {key -> "showId_seasonId"}
private var episodeDataMap = HashMap<String, MutableList<EpisodeData>>()
// episodes playlist, where {key -> "showId_seasonId_categoryId"}
val episodesPlaylistMap = HashMap<String, VideoPlayList>()
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
pagingData.nextPageToLoad++
_episodeListingLiveData.postValue(ApiResult.Success(episodeDataMap[key]))
val currentPlayListEng =
episodesPlaylistMap["${key}_1"] ?: VideoPlayList(
ArrayList(),
ArrayList()
)
val currentPlayListHin =
episodesPlaylistMap["${key}_18"] ?: VideoPlayList(
ArrayList(),
ArrayList()
)
for (episode in newList) {
episode.content_more_details?.let { moreDetailsList ->
if (moreDetailsList.isNotEmpty()) {
if (moreDetailsList.size > 1) {
moreDetailsList[1]?.let { moreDetail ->
currentPlayListHin.playlist.add(
PlaylistItem.Builder()
.file(moreDetail.content_hd_url)
.title(moreDetail.title)
.image(episode.thumbnail_path)
.build()
)
}
}
moreDetailsList[0]?.let { moreDetail ->
currentPlayListEng.playlist.add(
PlaylistItem.Builder()
.file(moreDetail.content_hd_url)
.title(moreDetail.title)
.image(episode.thumbnail_path)
.build()
)
}
}
}
}
episodesPlaylistMap["${key}_18"] = currentPlayListHin
episodesPlaylistMap["${key}_1"] = currentPlayListEng
}
}
}
}
}
}
fun clearEpisodeListingLiveData() {
_episodeListingLiveData.postValue(ApiResult.Loading())
}
// teaser listing
private val _teaserListingLiveData = MutableLiveData<ApiResult<MutableList<TeaserData>>>()
val teaserListingLiveData: LiveData<ApiResult<MutableList<TeaserData>>>
get() = _teaserListingLiveData
// teaser data for every season. where {key -> "showId_seasonId"}
private var teaserDataMap = HashMap<String, MutableList<TeaserData>>()
// teaser playlist, where {key -> "showId_seasonId_categoryId"}
val teaserPlaylistMap = HashMap<String, VideoPlayList>()
fun loadTeasers(showId: String, seasonId: Int) {
val key = "${showId}_$seasonId"
if (teaserDataMap.containsKey(key) && teaserDataMap[key]?.isNotEmpty() == true) {
_teaserListingLiveData.postValue(ApiResult.Success(teaserDataMap[key]))
return
}
viewModelScope.launch {
_teaserListingLiveData.postValue(ApiResult.Loading())
when (val response = repository.teaserListing(showId, seasonId)) {
is ApiResult.Error -> {
_teaserListingLiveData.postValue(
ApiResult.Error(
response.errorMessage,
response.error
)
)
}
is ApiResult.Loading -> {
_teaserListingLiveData.postValue(ApiResult.Loading())
}
is ApiResult.Success -> {
response.data?.let { data ->
data.result?.filterNotNull()?.toMutableList()?.let { newList ->
teaserDataMap[key] = newList
_teaserListingLiveData.postValue(ApiResult.Success(teaserDataMap[key]))
val currentPlayListEng = teaserPlaylistMap["${key}_1"] ?: VideoPlayList(
ArrayList(),
ArrayList()
)
val currentPlayListHin =
teaserPlaylistMap["${key}_18"] ?: VideoPlayList(
ArrayList(),
ArrayList()
)
for (teaser in newList) {
teaser.content_more_details?.let { moreDetailsList ->
if (moreDetailsList.isNotEmpty()) {
if (moreDetailsList.size > 1) {
moreDetailsList[1]?.let { moreDetail ->
currentPlayListHin.playlist.add(
PlaylistItem.Builder()
.file(moreDetail.content_hd_url)
.title(moreDetail.title)
.image(teaser.thumbnail_path)
.build()
)
}
}
moreDetailsList[0]?.let { moreDetail ->
currentPlayListEng.playlist.add(
PlaylistItem.Builder()
.file(moreDetail.content_hd_url)
.title(moreDetail.title)
.image(teaser.thumbnail_path)
.build()
)
}
}
}
}
teaserPlaylistMap["${key}_18"] = currentPlayListHin
teaserPlaylistMap["${key}_1"] = currentPlayListEng
}
}
}
}
}
}
fun clearTeaserListingLiveData() {
_teaserListingLiveData.postValue(ApiResult.Loading())
}
}

View File

@@ -1,594 +0,0 @@
package com.woka.webseries.views
import android.app.Dialog
import android.content.Intent
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.WindowManager
import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.jwplayer.pub.api.media.playlists.PlaylistItem
import com.woka.R
import com.woka.WokaApp.Companion.userPrefs
import com.woka.databinding.ActivitySeasonBinding
import com.woka.databinding.DialogEpisodeBinding
import com.woka.home.mylist.MyListRepository
import com.woka.networking.ApiResult
import com.woka.players.views.PlayerActivity
import com.woka.players.views.PlayerActivity.Companion.EXTRA_PLAY_INDEX
import com.woka.players.views.PlayerActivity.Companion.EXTRA_PLAY_LIST
import com.woka.players.models.VideoPlayList
import com.woka.utils.ProgressView
import com.woka.utils.WokaBaseActivity
import com.woka.utils.hide
import com.woka.utils.isNetworkConnected
import com.woka.utils.lightStatusBar
import com.woka.utils.show
import com.woka.utils.toast
import com.woka.webseries.WebSeriesRepository
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 kotlin.math.max
class SeasonActivity : WokaBaseActivity(), OnTabSelectedListener {
companion object {
const val EXTRA_SHOW_ID = "extra_show_id_data"
const val EXTRA_SHOW_CATEGORY_DATA = "extra_show_category_data"
const val EXTRA_SHOW_POSITION = "extra_show_position"
}
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
private lateinit var episodeAdapter: EpisodeAdapter
private lateinit var teaserAdapter: TeaserAdapter
private var selectedSeasonId = -1
private lateinit var episodeDialogBinding: DialogEpisodeBinding
private lateinit var episodeDialog: Dialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivitySeasonBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
with(window) {
statusBarColor = Color.parseColor("#D3EFF8")
lightStatusBar(true)
}
showId = intent.getIntExtra(EXTRA_SHOW_ID, -1)
categoryId = intent.getStringExtra(EXTRA_SHOW_CATEGORY_DATA)
showPosition = intent.getIntExtra(EXTRA_SHOW_POSITION, -1)
if (showId == -1 || categoryId == null) {
finish()
return
}
episodeAdapter = EpisodeAdapter(this)
teaserAdapter = TeaserAdapter(this)
progressView = ProgressView(this)
progressView.show(getString(R.string.please_wait))
WebSeriesRepository.webSeriesData[categoryId]?.show_data?.let {
for (show in it) {
if (showId == show?.id) {
showData = show
break
}
}
}
if (showData == null) {
MyListRepository.myFavData.result?.show_data?.let {
for (show in it) {
if (showId == show.id) {
showData = ShowData(show)
break
}
}
}
}
initViews()
initEpisodeDialog()
clickEvents()
setObservers()
WebSeriesRepository.loadSeasonListing(showId, categoryId!!)
}
override fun onDestroy() {
super.onDestroy()
WebSeriesRepository.clearEpisodeData()
}
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@SeasonActivity)
image.onLoadSuccessListener = { playTrailer.show() }
rvEpisodes.adapter = episodeAdapter
episodeAdapter.onEpisodeClicked = ::onEpisodeClicked
episodeAdapter.onEpisodePlayClicked = ::onEpisodePlayClicked
rvTeaser.adapter = teaserAdapter
teaserAdapter.onEpisodeClicked = ::onTeaserClicked
teaserAdapter.onEpisodePlayClicked = ::onTeaserPlayClicked
}
}
}
private fun initEpisodeDialog() {
episodeDialogBinding = DialogEpisodeBinding.inflate(layoutInflater)
episodeDialog = Dialog(this)
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() }
}
private fun clickEvents() {
binding.apply {
backBtn.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
watchCard.setOnClickListener {
playTrailer.performClick()
}
likeSeason.setOnClickListener {
if (!isNetworkConnected()) {
toast(getString(R.string.no_internet))
return@setOnClickListener
}
if (likeSeason.isSelected) {
WebSeriesRepository.likeUnLikeShow("${showData?.id}", false)
} else {
WebSeriesRepository.likeUnLikeShow("${showData?.id}", true)
}
likeSeason.isSelected = !likeSeason.isSelected
likeCount.text = "${showData?.likes_count}"
setResult(RESULT_OK, Intent().apply {
putExtra(EXTRA_SHOW_ID, showId)
})
}
favSeason.setOnClickListener {
if (!isNetworkConnected()) {
toast(getString(R.string.no_internet))
return@setOnClickListener
}
if (showData == null) return@setOnClickListener
categoryId?.let {
WebSeriesRepository.updateFavShow(
showData!!,
!favSeason.isSelected,
it
)
favSeason.isSelected = !favSeason.isSelected
setResult(RESULT_OK, Intent().apply {
putExtra(EXTRA_SHOW_ID, showId)
})
}
}
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(
EXTRA_PLAY_LIST,
videoPlayList
)
}
)
}
}
}
}
}
}
}
}
}
private fun setObservers() {
WebSeriesRepository.seasonDataLiveData.observe(this) {
binding.seasonsTab.removeAllTabs()
when (it) {
is ApiResult.Error -> {
progressView.hide()
}
is ApiResult.Loading -> {
progressView.show()
}
is ApiResult.Success -> {
progressView.hide()
it.data?.result?.let { seasonList ->
for (season in seasonList) {
if (season == null) continue
binding.seasonsTab.addTab(
binding.seasonsTab.newTab().setText("${season.season_number}")
)
}
}
}
}
}
WebSeriesRepository.episodeDataLiveData.observe(this) {
when (it) {
is ApiResult.Error -> {
binding.epShimmer.hide()
binding.rvEpisodes.hide()
binding.seasonMediaType.hide()
}
is ApiResult.Loading -> {
binding.epShimmer.show()
binding.rvEpisodes.hide()
binding.seasonMediaType.hide()
}
is ApiResult.Success -> {
it.data?.result?.filterNotNull()?.let { episodeList ->
if (episodeList.isNotEmpty()) {
episodeAdapter.submitList(episodeList) {
binding.epShimmer.hide()
binding.rvEpisodes.show()
binding.seasonMediaType.show()
}
} else {
binding.epShimmer.hide()
binding.seasonMediaType.hide()
binding.rvEpisodes.hide()
}
}
}
}
}
WebSeriesRepository.teaserDataLiveData.observe(this) {
when (it) {
is ApiResult.Error -> {
binding.rvTeaser.hide()
binding.teaserTxt.hide()
}
is ApiResult.Loading -> {
binding.rvTeaser.hide()
binding.teaserTxt.hide()
}
is ApiResult.Success -> {
it.data?.result?.filterNotNull()?.let { episodeList ->
if (episodeList.isNotEmpty()) {
teaserAdapter.submitList(episodeList) {
binding.rvTeaser.show()
binding.teaserTxt.show()
}
} else {
binding.teaserTxt.hide()
binding.rvTeaser.hide()
}
}
}
}
}
}
private fun loadSeasonData() {
binding.apply {
if (seasonsTab.selectedTabPosition >= 0) {
WebSeriesRepository.seasonDataMap["${showId}_${categoryId}"]?.result?.let { seasonList ->
if (seasonsTab.selectedTabPosition < seasonList.size) {
seasonList[seasonsTab.selectedTabPosition]?.let { seasonData ->
// loading episode data
seasonData.id?.let {
selectedSeasonId = it
WebSeriesRepository.loadEpisodeData(showId, 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(
"<br>",
" "
), Html.FROM_HTML_MODE_LEGACY
)
}
} else {
moreDetailsList[0]?.let { moreDetails ->
seasonTitle.text = moreDetails.title?.uppercase()
seasonDescription.text = Html.fromHtml(
moreDetails.description?.replace(
"<br>",
" "
), Html.FROM_HTML_MODE_LEGACY
)
}
}
}
}
}
}
}
}
}
}
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(
"<br>",
" "
), 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(
"<br>",
" "
), 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(
"<br>",
" "
), 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) {
startActivity(Intent(this, PlayerActivity::class.java).apply {
putExtra(
EXTRA_PLAY_LIST,
WebSeriesRepository.episodesPlaylistMap["${showId}_${selectedSeasonId}_$categoryId"]
)
putExtra(EXTRA_PLAY_INDEX, position)
})
}
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(
"<br>",
" "
), 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(
"<br>",
" "
), 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(
"<br>",
" "
), 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 onTeaserPlayClicked(position: Int) {
startActivity(Intent(this, PlayerActivity::class.java).apply {
putExtra(
EXTRA_PLAY_LIST,
WebSeriesRepository.teasersPlaylistMap["${showId}_${selectedSeasonId}_$categoryId"]
)
putExtra(EXTRA_PLAY_INDEX, position)
})
}
override fun onTabSelected(p0: TabLayout.Tab?) {
binding.playTrailer.hide()
loadSeasonData()
}
override fun onTabUnselected(p0: TabLayout.Tab?) {}
override fun onTabReselected(p0: TabLayout.Tab?) {}
}

View File

@@ -1,34 +0,0 @@
package com.woka.webseries.views
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.woka.R
import com.woka.databinding.ActivitySeriesBinding
import com.woka.utils.WokaBaseActivity
import com.woka.webseries.views.fragments.WebSeriesFragment
class SeriesActivity : WokaBaseActivity() {
private lateinit var binding: ActivitySeriesBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivitySeriesBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
window.navigationBarColor = getColor(R.color.color_primary_dark)
supportFragmentManager.beginTransaction()
.add(R.id.fcv_web_series, WebSeriesFragment.newInstance())
.commit()
}
}

View File

@@ -1,404 +1,51 @@
package com.woka.webseries.views
import android.app.Dialog
import android.content.Intent
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.View
import android.view.WindowManager
import android.widget.AdapterView
import android.widget.AdapterView.OnItemSelectedListener
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.jwplayer.pub.api.media.playlists.PlaylistItem
import com.woka.R
import com.woka.WokaApp.Companion.userPrefs
import com.woka.databinding.ActivityWebSeriesBinding
import com.woka.databinding.DialogContinueEpisodeBinding
import com.woka.modules.ModuleRepository
import com.woka.networking.ApiResult
import com.woka.players.views.PlayerActivity
import com.woka.players.views.PlayerActivity.Companion.EXTRA_PLAY_INDEX
import com.woka.players.views.PlayerActivity.Companion.EXTRA_PLAY_LIST
import com.woka.players.models.VideoPlayList
import com.woka.players.models.VideoViewRequestData
import com.woka.utils.WokaBaseActivity
import com.woka.utils.hide
import com.woka.utils.show
import com.woka.webseries.WebSeriesRepository
import com.woka.webseries.adapters.ContinueEpisodeAdapter
import com.woka.webseries.adapters.SpinnerAdapter
import com.woka.webseries.adapters.WebSeriesShowAdapter
import com.woka.webseries.models.ContinueEpisodeData
import com.woka.webseries.models.ShowData
import com.woka.webseries.models.WebSeriesResponse
import com.woka.webseries.viewmodel.WebSeriesViewModel
import com.woka.webseries.views.fragments.WebSeriesFragment
import com.woka.webseries.views.fragments.WebShowFragment
class WebSeriesActivity : WokaBaseActivity(), Observer<ApiResult<HashMap<String, WebSeriesResponse>>>{
class WebSeriesActivity : WokaBaseActivity() {
companion object {
const val EXTRA_SHOW_DATA = "extra_show_data"
const val EXTRA_SHOW_CATEGORY = "extra_show_category"
const val EXTRA_SHOW_ID = "extra_show_id"
}
private lateinit var binding: ActivityWebSeriesBinding
private lateinit var viewmodel: WebSeriesViewModel
private lateinit var showAdapter: WebSeriesShowAdapter
private lateinit var continueWatchAdapter: ContinueEpisodeAdapter
private var catSpinnerAdapter: SpinnerAdapter? = null
private var showIntentLauncher: ActivityResultLauncher<Intent>? = null
private lateinit var episodeDialogBinding: DialogContinueEpisodeBinding
private lateinit var episodeDialog: Dialog
@Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityWebSeriesBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
viewmodel = ViewModelProvider(this)[WebSeriesViewModel::class.java]
window.navigationBarColor = getColor(R.color.color_primary_dark)
initViews()
val showData = intent.getParcelableExtra<ShowData>(EXTRA_SHOW_DATA)
val categoryId = intent.getStringExtra(EXTRA_SHOW_CATEGORY)
initEpisodeDialog()
clickEvents()
setObservers()
resultLaunchers()
if(!ModuleRepository.showCategoryLiveData.isInitialized){
ModuleRepository.showCategories()
}
}
private fun clickEvents() {
binding.apply {
toolbar.backBtn.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
retryBtn.setOnClickListener {
binding.shimmer.show()
binding.errorView.hide()
if (ModuleRepository.showCategoryLiveData.isInitialized && ModuleRepository.showCategoryLiveData.value is ApiResult.Success){
// categories already available
loadShowData()
}else{
// categories not available
ModuleRepository.showCategories()
supportFragmentManager.beginTransaction()
.add(
R.id.fcv_web_series, if (showData != null && categoryId != null) {
WebShowFragment.newInstance(showData, categoryId)
} else {
WebSeriesFragment.newInstance()
}
}
}
}
private fun initViews(){
// adapters
showAdapter = WebSeriesShowAdapter(this, ::onShowClicked)
continueWatchAdapter = ContinueEpisodeAdapter(this)
binding.apply {
adjustMasilaImage()
toolbar.title.text = getString(R.string.web_series)
rvWebSeries.adapter = showAdapter
rvContinueWatch.adapter = continueWatchAdapter
continueWatchAdapter.onEpisodeClicked = ::onContinueEpisodeClicked
((rvWebSeries.itemAnimator) as SimpleItemAnimator).supportsChangeAnimations = false
((rvContinueWatch.itemAnimator) as SimpleItemAnimator).supportsChangeAnimations = false
}
}
private fun initEpisodeDialog(){
episodeDialogBinding = DialogContinueEpisodeBinding.inflate(layoutInflater)
episodeDialog = Dialog(this)
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
layoutParams.horizontalMargin = 0F
episodeDialog.window!!.setAttributes(layoutParams)
} catch (e: Exception) {
// do nothing
}
episodeDialogBinding.close.setOnClickListener { episodeDialog.dismiss() }
}
private fun resultLaunchers(){
showIntentLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
if (it.resultCode == RESULT_OK){
it.data?.getIntExtra(SeasonActivity.EXTRA_SHOW_ID, -1)?.let {showId ->
onWebShowItemChanged(showId)
}
}
}
}
private fun setObservers(){
viewmodel.webSeriesLiveData.observe(this, this)
viewmodel.continueWatchLiveData.observe(this){
when (it){
is ApiResult.Error -> {
binding.rvContinueWatch.hide()
binding.continueWatchTxt.hide()
}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
it.data?.result?.let {continueWatchList ->
if (continueWatchList.isNotEmpty()){
binding.rvContinueWatch.show()
binding.continueWatchTxt.show()
continueWatchAdapter.submitList(continueWatchList.asReversed())
}else{
binding.rvContinueWatch.hide()
binding.continueWatchTxt.hide()
}
}
}
}
}
ModuleRepository.showCategoryLiveData.observe(this){
when(it){
is ApiResult.Error -> {
binding.errorView.show()
binding.shimmer.hide()
}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
it.data?.result?.let {catList ->
binding.shimmer.hide()
binding.trailerView.show()
adjustMasilaImage()
binding.spinnerCard.show()
binding.selectLangTxt.show()
WebSeriesRepository.continueWatchData?.let {
binding.continueWatchTxt.show()
binding.rvContinueWatch.show()
}?:{
binding.continueWatchTxt.hide()
binding.rvContinueWatch.hide()
}
catSpinnerAdapter = SpinnerAdapter(this, catList.filterNotNull())
binding.categorySpinner.setAdapter(catSpinnerAdapter)
loadShowData()
WebSeriesRepository.loadContinueWatchData()
}
}
}
}
binding.categorySpinner.onItemSelectedListener = object : OnItemSelectedListener{
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
if (position != catSpinnerAdapter?.currentSelection){
catSpinnerAdapter?.selectPosition(position)
loadShowData()
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
override fun onChanged(value: ApiResult<HashMap<String, WebSeriesResponse>>) {
when (value){
is ApiResult.Error -> {
binding.shimmerShowData.hide()
binding.rvWebSeries.hide()
}
is ApiResult.Loading -> {
binding.shimmerShowData.show()
binding.rvWebSeries.hide()
}
is ApiResult.Success -> {
catSpinnerAdapter?.selectedCategoryType?.let { categoryType ->
value.data?.get(categoryType)?.show_data?.let {showList ->
binding.shimmerShowData.hide()
showAdapter.submitListShowList(showList.filterNotNull(), categoryType)
binding.rvWebSeries.show()
}
}
}
}
}
private fun loadShowData(){
catSpinnerAdapter?.selectedCategoryType?.let {
WebSeriesRepository.loadWebSeries(it)
}
}
private fun adjustMasilaImage() {
// making space for masila image
binding.apply {
topPinnedView.post {
val imgParams = masilaImage.layoutParams as CollapsingToolbarLayout.LayoutParams
imgParams.setMargins(0, 0, 0, topPinnedView.height)
masilaImage.layoutParams = imgParams
}
}
}
private fun onShowClicked(showData: ShowData, categoryId: String){
showIntentLauncher?.launch(Intent(this, SeasonActivity::class.java).apply {
putExtra(SeasonActivity.EXTRA_SHOW_ID, showData.id)
putExtra(SeasonActivity.EXTRA_SHOW_CATEGORY_DATA, categoryId)
})
}
private fun onWebShowItemChanged(showId: Int){
val englishPosition = showAdapter.showList.indexOfFirst { it.id == showId }
if (englishPosition > -1 && englishPosition < showAdapter.showList.size){
showAdapter.notifyItemChanged(englishPosition)
}
}
private fun onContinueEpisodeClicked(episodeData: ContinueEpisodeData){
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(
"<br>",
" "
), Html.FROM_HTML_MODE_LEGACY
)
}
}else{
moreDetailsList[0].let {data ->
title.text = data.title
description.text = Html.fromHtml(
data.description?.replace(
"<br>",
" "
), Html.FROM_HTML_MODE_LEGACY
)
}
}
}else{
title.text = episodeData.episode_title
description.text = Html.fromHtml(
episodeData.episode_description?.replace(
"<br>",
" "
), Html.FROM_HTML_MODE_LEGACY
)
}
watchCard.setOnClickListener {
episodeData.content_more_details.let {moreDetailsList ->
val videoPlayList = VideoPlayList(ArrayList(), ArrayList())
if (moreDetailsList.isNotEmpty()){
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1){
videoPlayList.playlist.add(
PlaylistItem.Builder()
.file(moreDetailsList[1].content_hd_url)
.title(moreDetailsList[1].title)
.image(moreDetailsList[1].content_hd_url)
.build()
)
videoPlayList.videoViewRequestDataList?.add(
VideoViewRequestData(
"${episodeData.id}",
"3",
null,
category_id = "18"
)
)
}else{
videoPlayList.playlist.add(
PlaylistItem.Builder()
.file(moreDetailsList[0].content_hd_url)
.title(moreDetailsList[0].title)
.image(moreDetailsList[0].content_hd_url)
.build()
)
videoPlayList.videoViewRequestDataList?.add(
VideoViewRequestData(
"${episodeData.id}",
"3",
null,
"1"
)
)
}
}
startActivity(Intent(this@WebSeriesActivity, PlayerActivity::class.java)
.apply {
putExtra(EXTRA_PLAY_LIST, videoPlayList)
putExtra(EXTRA_PLAY_INDEX, 0)
})
}
}
close.setOnClickListener {
episodeDialog.dismiss()
}
episodeDialog.show()
}
}
)
.commit()
}
}

View File

@@ -0,0 +1,8 @@
package com.woka.webseries.views.fragments
import com.woka.webseries.models.ShowData
import java.io.Serializable
interface ShowChangeInterface: Serializable {
fun onShowDataChanged(showData: ShowData, categoryId: String)
}

View File

@@ -38,12 +38,12 @@ import com.woka.webseries.adapters.SpinnerAdapter
import com.woka.webseries.adapters.WebSeriesShowAdapter
import com.woka.webseries.models.ContinueEpisodeData
import com.woka.webseries.models.ShowData
import com.woka.webseries.viewmodel.ViewModel
import com.woka.webseries.viewmodel.WebSeriesViewModel
class WebSeriesFragment private constructor(): Fragment() {
private lateinit var binding: FragmentWebSeriesBinding
private var viewModel: ViewModel? = null
private var viewModel: WebSeriesViewModel? = null
private lateinit var showAdapter: WebSeriesShowAdapter
@@ -69,7 +69,7 @@ class WebSeriesFragment private constructor(): Fragment() {
it.window.setBackgroundDrawable(AppCompatResources.getDrawable(it, R.drawable.gradient_web_series))
it.window.statusBarColor = 0
it.window.lightStatusBar(false)
viewModel = ViewModelProvider(it)[ViewModel::class.java]
viewModel = ViewModelProvider(it)[WebSeriesViewModel::class.java]
}
return binding.root
}
@@ -302,7 +302,7 @@ class WebSeriesFragment private constructor(): Fragment() {
private fun onShowClicked(showData: ShowData, categoryId: String){
parentFragmentManager.beginTransaction()
.add(R.id.fcv_web_series, WebShowFragment.newInstance(showData, categoryId))
.add(R.id.fcv_web_series, WebShowFragment.newInstance(showData, categoryId, ::onShowDataChanged))
.hide(this)
.addToBackStack(null)
.commit()
@@ -314,6 +314,20 @@ class WebSeriesFragment private constructor(): Fragment() {
viewModel?.showCommonDataChanged(showData, categoryId)
}
private fun onShowDataChanged(showData: ShowData, categoryId: String) {
viewModel?.showCommonDataChanged(showData, categoryId)
val position = showAdapter.showList.indexOfFirst { it.id == showData.id }
if (position >= 0 && position < showAdapter.showList.size){
val currData = showAdapter.showList[position]
currData.is_liked = showData.is_liked
currData.likes_count = showData.likes_count
currData.favourite_category_ids = showData.favourite_category_ids
showAdapter.notifyItemChanged(position)
}
}
private fun onContinueEpisodeClicked(episodeData: ContinueEpisodeData){
episodeDialogBinding.apply {
episodeData.content_more_details?.let {moreDetailsList ->

View File

@@ -1,7 +1,9 @@
package com.woka.webseries.views.fragments
import android.app.Activity.RESULT_OK
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.InsetDrawable
@@ -15,46 +17,47 @@ import androidx.appcompat.content.res.AppCompatResources
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.tabs.TabLayout
import com.jwplayer.pub.api.media.playlists.PlaylistItem
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.players.models.VideoPlayList
import com.woka.players.views.PlayerActivity
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.WebSeriesRepository
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
import com.woka.webseries.viewmodel.WebSeriesViewModel
import com.woka.webseries.views.WebSeriesActivity.Companion.EXTRA_SHOW_ID
import kotlin.math.max
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 {
class WebShowFragment private constructor(
private var showData: ShowData,
private val categoryId: String,
private val onShowDataChange: ((ShowData, String) -> Unit)? = null
) : 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)
}
}
fun newInstance(
showData: ShowData,
categoryId: String,
onShowDataChange: ((ShowData, String) -> Unit)? = null
) =
WebShowFragment(showData, categoryId, onShowDataChange)
}
private lateinit var binding: FragmentWebShowBinding
private var viewModel: ViewModel? = null
// arguments
private var categoryId: String? = null
private var showData: ShowData? = null
private var viewModel: WebSeriesViewModel? = null
// variables
@@ -67,6 +70,7 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
private lateinit var episodeDialog: Dialog
private var loadMoreEpisodes = false
private var showDataChanged: Boolean = false
override fun onAttach(context: Context) {
super.onAttach(context)
@@ -76,24 +80,16 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
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]
viewModel = ViewModelProvider(this)[WebSeriesViewModel::class.java]
activity?.let {
it.window.setBackgroundDrawableResource(R.color.web_show_bg)
it.window.lightStatusBar(true)
viewModel = ViewModelProvider(it)[ViewModel::class.java]
viewModel = ViewModelProvider(it)[WebSeriesViewModel::class.java]
}
return binding.root
}
@@ -109,48 +105,54 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
setObservers()
binding.seasonProgressView.show()
viewModel?.seasonListing("${showData?.id}", "$categoryId")
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.setBackgroundDrawable(
AppCompatResources.getDrawable(
it,
R.drawable.gradient_web_series
)
)
it.window.statusBarColor = 0
it.window.lightStatusBar(false)
}
viewModel?.clearSeasonLiveData()
viewModel?.clearEpisodeListingLiveData()
viewModel?.clearTeaserListingLiveData()
if (showDataChanged) {
onShowDataChange?.invoke(showData, categoryId)
}
}
// initializations
private fun initViews() {
if (showData != null && categoryId != null) {
binding.apply {
binding.apply {
likeCount.text = "${showData!!.likes_count}"
likeCount.text = "${showData.likes_count}"
favSeason.isSelected = showData!!.isBookMarked(categoryId!!)
favSeason.isSelected = showData.isBookMarked(categoryId)
likeSeason.isSelected = showData!!.is_liked ?: false
likeSeason.isSelected = showData.is_liked ?: false
binding.seasonsTab.addOnTabSelectedListener(this@WebShowFragment)
binding.seasonsTab.addOnTabSelectedListener(this@WebShowFragment)
rvEpisodes.adapter = episodeAdapter
episodeAdapter.onEpisodeClicked = ::onEpisodeClicked
episodeAdapter.onEpisodePlayClicked = ::onEpisodePlayClicked
rvEpisodes.adapter = episodeAdapter
episodeAdapter.onEpisodeClicked = ::onEpisodeClicked
episodeAdapter.onEpisodePlayClicked = ::onEpisodePlayClicked
rvTeaser.adapter = teaserAdapter
teaserAdapter.onEpisodeClicked = ::onTeaserClicked
teaserAdapter.onEpisodePlayClicked = ::onTeaserPlayClicked
}
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()
@@ -162,7 +164,7 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
epLoadMoreBtn.setOnClickListener {
loadMoreEpisodes = true
viewModel?.loadMoreEpisodes("${showData?.id}", selectedSeasonId)
viewModel?.loadMoreEpisodes("${showData.id}", selectedSeasonId)
}
likeSeason.setOnClickListener {
@@ -171,10 +173,27 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
return@setOnClickListener
}
Repository.likeUnLikeShow("${showData?.id}", showData?.is_liked == false)
WebSeriesRepository.likeUnLikeShow("${showData.id}", showData.is_liked == false)
showData.likes_count?.let {
showData.likes_count = if (likeSeason.isSelected){
// unlike
max(0, it - 1)
}else{
// like
it + 1
}
}
showData.is_liked = !likeSeason.isSelected
likeSeason.isSelected = !likeSeason.isSelected
likeCount.text = "${showData?.likes_count}"
likeCount.text = "${showData.likes_count}"
showDataChanged = true
activity?.setResult(RESULT_OK, Intent().apply {
putExtra(EXTRA_SHOW_ID, showData.id?:-1)
})
}
favSeason.setOnClickListener {
@@ -183,62 +202,74 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
return@setOnClickListener
}
if (showData == null) return@setOnClickListener
WebSeriesRepository.updateFavShow(
showData,
!showData.isBookMarked(categoryId),
categoryId
)
categoryId?.let {
Repository.updateFavShow(
showData!!,
showData?.isBookMarked("$categoryId") == false,
"$categoryId"
)
favSeason.isSelected = !favSeason.isSelected
if (showData.isBookMarked(categoryId)){
// remove from fav
showData.removeAsBookMark(categoryId)
}else{
// add to fav
showData.addAsBookMark(categoryId)
}
favSeason.isSelected = !favSeason.isSelected
showDataChanged = true
activity?.setResult(RESULT_OK, Intent().apply {
putExtra(EXTRA_SHOW_ID, showData.id?:-1)
})
}
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
// )
// }
// )
// }
// }
// }
// }
// }
// }
if (seasonsTab.selectedTabPosition >= 0) {
viewModel?.seasonListingMap?.get("${showData.id}_${categoryId}")
?.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())
activity?.let {
startActivity(
Intent(it, PlayerActivity::class.java)
.apply {
putExtra(
PlayerActivity.EXTRA_PLAY_LIST,
videoPlayList
)
}
)
}
}
}
}
}
}
}
}
}
}
@@ -269,8 +300,8 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
}
// callbacks
private fun setObservers(){
viewModel?.seasonListingLiveData?.observe(viewLifecycleOwner){
private fun setObservers() {
viewModel?.seasonListingLiveData?.observe(viewLifecycleOwner) {
binding.seasonsTab.removeAllTabs()
when (it) {
is ApiResult.Error -> {
@@ -283,8 +314,8 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
is ApiResult.Success -> {
binding.seasonProgressView.hide()
it.data?.let {seasonList ->
for (season in seasonList){
it.data?.let { seasonList ->
for (season in seasonList) {
binding.seasonsTab.addTab(
binding.seasonsTab.newTab().setText("${season.season_number}")
)
@@ -294,41 +325,46 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
}
}
viewModel?.episodeListingLiveData?.observe(viewLifecycleOwner){
when (it){
viewModel?.episodeListingLiveData?.observe(viewLifecycleOwner) {
when (it) {
is ApiResult.Error -> {
binding.epShimmer.hide()
if (loadMoreEpisodes){
if (loadMoreEpisodes) {
// load more
binding.epLoadMoreBtn.text = getString(R.string.retry)
binding.epLoadMoreBtn.show()
}else{
} else {
// first time load
binding.rvEpisodes.hide()
binding.seasonMediaType.hide()
binding.epShimmer.hide()
binding.epLoadMoreBtn.hide()
}
}
is ApiResult.Loading -> {
binding.epShimmer.show()
binding.epLoadMoreBtn.hide()
binding.rvEpisodes.setVisibility(loadMoreEpisodes)
binding.seasonMediaType.setVisibility(loadMoreEpisodes)
}
is ApiResult.Success -> {
it.data?.let {episodeList ->
it.data?.let { episodeList ->
binding.rvEpisodes.show()
binding.seasonMediaType.show()
binding.epShimmer.hide()
binding.epLoadMoreBtn.text = getString(R.string.load_more)
binding.epLoadMoreBtn.setVisibility(viewModel?.episodePagingData?.get("${showData?.id}_$selectedSeasonId")?.lastPage == false)
binding.epLoadMoreBtn.setVisibility(viewModel?.episodePagingData?.get("${showData.id}_$selectedSeasonId")?.lastPage == false)
if (loadMoreEpisodes){
if (loadMoreEpisodes) {
// loaded more data
episodeAdapter.notifyItemRangeInserted(
episodeAdapter.currentList.size,
episodeList.size
)
}else{
} else {
// new category data load
episodeAdapter.submitList(episodeList)
}
@@ -336,6 +372,35 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
}
}
}
viewModel?.teaserListingLiveData?.observe(viewLifecycleOwner) {
when (it) {
is ApiResult.Error -> {
binding.rvTeaser.hide()
binding.teaserTxt.hide()
}
is ApiResult.Loading -> {
binding.rvTeaser.hide()
binding.teaserTxt.hide()
}
is ApiResult.Success -> {
it.data?.let { teaserList ->
if (teaserList.isNotEmpty()) {
teaserAdapter.submitList(teaserList) {
binding.rvTeaser.show()
binding.teaserTxt.show()
}
} else {
binding.teaserTxt.hide()
binding.rvTeaser.hide()
}
}
}
}
}
}
private fun onEpisodeClicked(position: Int, episodeData: EpisodeData) {
@@ -404,7 +469,15 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
}
private fun onEpisodePlayClicked(position: Int) {
activity?.let {
startActivity(Intent(it, PlayerActivity::class.java).apply {
putExtra(
PlayerActivity.EXTRA_PLAY_LIST,
viewModel?.episodesPlaylistMap?.get("${showData.id}_${selectedSeasonId}_$categoryId")
)
putExtra(PlayerActivity.EXTRA_PLAY_INDEX, position)
})
}
}
private fun onTeaserClicked(position: Int, teaserData: TeaserData) {
@@ -475,68 +548,77 @@ class WebShowFragment private constructor(): Fragment(), TabLayout.OnTabSelected
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 ->
viewModel?.seasonListingMap?.get("${showData.id}_${categoryId}")
?.let { seasonList ->
if (seasonsTab.selectedTabPosition < seasonList.size) {
seasonList[seasonsTab.selectedTabPosition].let { seasonData ->
playTrailer.show()
detailsProgressiveView.hide()
detailsView.show()
playTrailer.show()
detailsProgressiveView.hide()
detailsView.show()
// loading episode data
seasonData.id?.let {
selectedSeasonId = it
// loading episode data
seasonData.id?.let {
selectedSeasonId = it
loadMoreEpisodes = false
viewModel?.loadEpisodes("${showData?.id}", it)
// WebSeriesRepository.loadTeaserData(showId, it)
}
loadMoreEpisodes = false
viewModel?.loadEpisodes("${showData.id}", it)
viewModel?.loadTeasers("${showData.id}", it)
}
seasonYear.text = "${seasonData.release_year}"
val noOfEpisodes =
"${seasonData.no_of_episodes} ${seasonData.media_type}"
episodeNumber.text = noOfEpisodes
seasonYear.text = "${seasonData.release_year}"
val noOfEpisodes =
"${seasonData.no_of_episodes} ${seasonData.media_type}"
episodeNumber.text = noOfEpisodes
seasonMediaType.text = "${seasonData.media_type}"
seasonMediaType.text = "${seasonData.media_type}"
seasonData.thumbnail_path?.let {
image.loadImage(it)
}
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(
"<br>",
" "
), Html.FROM_HTML_MODE_LEGACY
)
}
} else {
moreDetailsList[0]?.let { moreDetails ->
seasonTitle.text = moreDetails.title?.uppercase()
seasonDescription.text = Html.fromHtml(
moreDetails.description?.replace(
"<br>",
" "
), Html.FROM_HTML_MODE_LEGACY
)
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(
"<br>",
" "
), Html.FROM_HTML_MODE_LEGACY
)
}
} else {
moreDetailsList[0]?.let { moreDetails ->
seasonTitle.text = moreDetails.title?.uppercase()
seasonDescription.text = Html.fromHtml(
moreDetails.description?.replace(
"<br>",
" "
), Html.FROM_HTML_MODE_LEGACY
)
}
}
}
}
}
}
}
}
}
}
}
private fun onTeaserPlayClicked(position: Int) {
activity?.let {
startActivity(Intent(it, PlayerActivity::class.java).apply {
putExtra(
PlayerActivity.EXTRA_PLAY_LIST,
viewModel?.teaserPlaylistMap?.get("${showData.id}_${selectedSeasonId}_$categoryId")
)
putExtra(PlayerActivity.EXTRA_PLAY_INDEX, position)
})
}
}
// season tab selection

View File

@@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="800dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,12C3,7.029 7.029,3 12,3C16.971,3 21,7.029 21,12C21,16.971 16.971,21 12,21C7.029,21 3,16.971 3,12Z"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"/>
<path
android:pathData="M9,9L15,15M15,9L9,15"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fcv_web_series"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".webseries.views.SeriesActivity"/>

View File

@@ -1,325 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:id="@+id/fcv_web_series"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/gradient_web_series"
android:orientation="vertical"
tools:context=".webseries.views.WebSeriesActivity">
<include android:id="@+id/toolbar"
layout="@layout/layout_toolbar"/>
<RelativeLayout
android:id="@+id/shimmer"
android:visibility="visible"
android:background="@color/color_primary_dark"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.facebook.shimmer.ShimmerFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:shimmer_auto_start="true"
app:shimmer_duration="1500"
app:shimmer_highlight_alpha="0.5"
>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="@dimen/_160sdp"
android:background="@color/white_50"
/>
<View
android:layout_width="@dimen/_100sdp"
android:layout_height="@dimen/_37sdp"
android:background="@color/white_50"
android:layout_marginHorizontal="15dp"
android:layout_marginVertical="10dp"
/>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/_140sdp"
android:background="@color/white_50"
android:layout_marginHorizontal="15dp"
/>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/_140sdp"
android:background="@color/white_50"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="15dp"
/>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/_140sdp"
android:background="@color/white_50"
android:layout_marginVertical="15dp"
android:layout_marginHorizontal="15dp"
/>
</LinearLayout>
</ScrollView>
</com.facebook.shimmer.ShimmerFrameLayout>
</RelativeLayout>
<LinearLayout
android:id="@+id/error_view"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_primary_dark"
android:orientation="vertical"
android:gravity="center"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/something_went_wrong"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
/>
<Button
android:id="@+id/retry_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/retry"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
android:paddingHorizontal="25dp"
android:layout_marginTop="15dp"
android:background="@drawable/grad_btn_bg_4"
/>
</LinearLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/trailer_view"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|snap|exitUntilCollapsed">
<ImageView
android:id="@+id/masila_image"
android:layout_width="match_parent"
android:layout_height="@dimen/_160sdp"
android:contentDescription="@string/image"
android:src="@drawable/img_masila_full"
android:scaleType="fitXY"
app:layout_collapseMode="parallax"
android:layout_gravity="top"
/>
<androidx.appcompat.widget.Toolbar
android:id="@+id/top_pinned_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_primary_dark"
app:layout_collapseMode="pin"
app:contentInsetStart="0dp"
app:contentInsetLeft="0dp"
app:contentInsetStartWithNavigation="0dp"
android:layout_gravity="bottom">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp"
android:gravity="center"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/masila"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_16ssp"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/trailer"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_12ssp"
android:paddingHorizontal="25dp"
android:layout_marginTop="15dp"
android:background="@drawable/trailer_btn_bg"
/>
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_primary_dark"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/continue_watch_txt"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/continue_watching"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
android:layout_marginHorizontal="15dp"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_continue_watch"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/continue_show_view_holder"
android:orientation="horizontal"
/>
<TextView
android:id="@+id/select_lang_txt"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/select_video_language"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
android:layout_marginHorizontal="15dp"
/>
<androidx.cardview.widget.CardView
android:id="@+id/spinner_card"
android:visibility="gone"
android:layout_width="@dimen/_160sdp"
android:layout_height="@dimen/_40sdp"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="10dp"
app:cardCornerRadius="5dp"
>
<Spinner
android:id="@+id/category_spinner"
android:layout_width="match_parent"
android:layout_height="@dimen/_40sdp"
android:popupBackground="@drawable/round_bg_5_white"
android:textAlignment="textStart"
/>
</androidx.cardview.widget.CardView>
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmer_show_data"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:shimmer_base_alpha="0.8"
app:shimmer_auto_start="true"
app:shimmer_highlight_alpha="0.5"
app:shimmer_duration="1500"
android:layout_marginVertical="10dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/show_view_holder"/>
<include layout="@layout/show_view_holder"/>
<include layout="@layout/show_view_holder"/>
<include layout="@layout/show_view_holder"/>
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_web_series"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/show_view_holder"
android:layout_marginTop="15dp"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>
tools:context=".webseries.views.WebSeriesActivity"/>

View File

@@ -98,6 +98,7 @@
android:orientation="vertical">
<TextView
android:id="@+id/web_series_eng_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -130,6 +131,7 @@
android:orientation="vertical">
<TextView
android:id="@+id/web_series_hin_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -164,6 +166,7 @@
android:orientation="vertical">
<TextView
android:id="@+id/audio_books_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -197,6 +200,7 @@
android:orientation="vertical">
<TextView
android:id="@+id/karaoke_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -230,6 +234,7 @@
android:orientation="vertical">
<TextView
android:id="@+id/games_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -278,6 +283,7 @@
<ProgressBar
android:id="@+id/progress_bar"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -291,6 +297,23 @@
/>
<RelativeLayout
android:id="@+id/no_sign_in_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:background="@color/black_50"
>
<include android:id="@+id/no_sign_in"
layout="@layout/layout_no_sign_in_dialog"
android:layout_centerInParent="true"
android:layout_marginHorizontal="15dp"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/round_25"
android:backgroundTint="@color/white"
android:orientation="vertical"
android:paddingVertical="0dp">
<ImageView
android:id="@+id/close_btn"
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@string/image"
android:src="@drawable/ic_close_circle"
android:layout_marginEnd="15dp"
android:layout_marginBottom="-40dp"
android:layout_marginTop="15dp"
android:layout_gravity="end"
app:tint="@color/color_primary" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_gravity="center_horizontal"
android:layout_marginHorizontal="15dp"
android:ellipsize="end"
android:fontFamily="@font/exo_2_bold"
android:text="@string/continue_to_woka"
android:maxLines="2"
android:textSize="@dimen/_14ssp"
android:textColor="@color/color_primary"
android:layout_marginTop="20dp"
/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="15dp"
android:alpha="0.5"
android:layout_marginTop="10dp"
android:layout_marginBottom="5dp"
android:background="@android:color/darker_gray" />
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:layout_marginVertical="10dp"
android:fontFamily="@font/exo_2_medium"
android:textSize="@dimen/_11ssp"
android:textColor="@color/color_primary"
android:text="@string/to_continue_please_sign_in_for_more_adventures_and_features"
android:layout_gravity="center_horizontal"
/>
<Button
android:id="@+id/sign_im"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:visibility="visible"
android:text="@string/free_sign_in"
android:textColor="@color/white"
android:fontFamily="@font/exo_2_medium"
android:textSize="@dimen/_12ssp"
android:textAllCaps="false"
android:background="@drawable/round_bg_25"
android:backgroundTint="#5E1FC4"
android:layout_gravity="center_horizontal"
android:paddingHorizontal="25dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="20dp"
android:layout_marginHorizontal="15dp" />
</LinearLayout>

View File

@@ -220,4 +220,7 @@
<string name="download_complete">Download complete</string>
<string name="file_saved_to_your_music_folder">File saved to your music folder.</string>
<string name="load_more">LOAD MORE</string>
<string name="continue_to_woka">Continue to WOKA</string>
<string name="to_continue_please_sign_in_for_more_adventures_and_features">To continue, please sign in for more adventures and features...</string>
<string name="free_sign_in">Free Sign In</string>
</resources>