diff --git a/.idea/misc.xml b/.idea/misc.xml index b65eb9a..465d9b6 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/build.gradle b/app/build.gradle index 4de626d..a7c3aae 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,6 +51,8 @@ ext.jwPlayerVersion = '4.6.0' ext.exoplayerVersion = '2.19.1' dependencies { + implementation "com.facebook.shimmer:shimmer:0.5.0" + implementation "com.airbnb.android:lottie:6.4.0" implementation "androidx.core:core-splashscreen:1.0.1" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cc26b38..9c3775b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,10 +15,14 @@ android:supportsRtl="true" android:theme="@style/Theme.Woka" tools:targetApi="31"> + + android:screenOrientation="portrait" /> >() val myFavListLiveData: LiveData> get() { - // also updating local changes - myFavData?.let { _myFavListLiveData.postValue(ApiResult.Success(it)) } + if (shallLoadNewData){ + // load new data + loadMyFavList() + shallLoadNewData = false + }else{ + // also updating local changes + myFavData?.let { + _myFavListLiveData.postValue(ApiResult.Success(it)) + } + } return _myFavListLiveData } private var myFavData: MyListResponse? = null + /* + flag to load a new data whenever necessary + for eg. when user has signed in with new account and the apps hold the older repository + */ + private var shallLoadNewData: Boolean = false + init { loadMyFavList() } + fun clearData(){ + shallLoadNewData = true + myFavData = null + } + private fun loadMyFavList(){ CoroutineScope(Dispatchers.IO).launch { _myFavListLiveData.postValue(ApiResult.Loading()) diff --git a/app/src/main/java/com/woka/modules/ModuleApiService.kt b/app/src/main/java/com/woka/modules/ModuleApiService.kt index dc4f12f..48af77d 100644 --- a/app/src/main/java/com/woka/modules/ModuleApiService.kt +++ b/app/src/main/java/com/woka/modules/ModuleApiService.kt @@ -5,6 +5,7 @@ import com.woka.modules.categorymodels.CategoriesResponse import com.woka.modules.faqs.models.FaqResponse import com.woka.modules.wokasongs.models.WokaSongsResponse import com.woka.networking.ApiResponse +import com.woka.webseries.models.ContinueEpisodeResponse import okhttp3.FormBody import retrofit2.Response import retrofit2.http.Body @@ -30,4 +31,7 @@ interface ModuleApiService { @POST("category_listing") suspend fun categoryListing(@Body body: FormBody): Response> + + @POST("continue_watching") + suspend fun continueWatchingShowListing(@Body body: FormBody): Response> } \ No newline at end of file diff --git a/app/src/main/java/com/woka/userPreference/UserPreference.kt b/app/src/main/java/com/woka/userPreference/UserPreference.kt index a302e0f..1df9896 100644 --- a/app/src/main/java/com/woka/userPreference/UserPreference.kt +++ b/app/src/main/java/com/woka/userPreference/UserPreference.kt @@ -9,12 +9,14 @@ import android.provider.Settings.Secure import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.woka.home.Theme +import com.woka.home.mylist.MyListRepository import com.woka.networking.ApiResult import com.woka.onboard.OnboardActivity import com.woka.userdata.UserRepository import com.woka.userdata.userDataModels.UserData import com.woka.userdata.userDataModels.UserDataResponse import com.woka.utils.UserType +import com.woka.webseries.WebSeriesRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -107,5 +109,12 @@ class UserPreference(val context: Context) { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) }) } + + clearData() + } + + private fun clearData(){ + WebSeriesRepository.clearData() + MyListRepository.clearData() } } \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/WebSeriesRepository.kt b/app/src/main/java/com/woka/webseries/WebSeriesRepository.kt index 1f2e042..c19337b 100644 --- a/app/src/main/java/com/woka/webseries/WebSeriesRepository.kt +++ b/app/src/main/java/com/woka/webseries/WebSeriesRepository.kt @@ -5,9 +5,13 @@ import androidx.lifecycle.MutableLiveData import com.woka.home.mylist.MyFavApiService import com.woka.home.mylist.MyListRepository import com.woka.home.mylist.models.BookmarkedShowData +import com.woka.home.mylist.models.MyListResponse import com.woka.home.mylist.models.PostType +import com.woka.modules.ModuleApiService import com.woka.networking.ApiResult import com.woka.networking.RetrofitHelper +import com.woka.userdata.UserRepository +import com.woka.webseries.models.ContinueEpisodeResponse import com.woka.webseries.models.ShowData import com.woka.webseries.models.WebSeriesResponse import kotlinx.coroutines.CoroutineScope @@ -17,9 +21,14 @@ import okhttp3.FormBody import kotlin.math.max object WebSeriesRepository { + // api services private val apiService = RetrofitHelper.getRetrofit().create(WebSeriesApiService::class.java) private val myFavApiService = RetrofitHelper.getRetrofit().create(MyFavApiService::class.java) + private val moduleApiService = RetrofitHelper.getRetrofit().create(ModuleApiService::class.java) + // live data + + // show data (loose caching) private val _englishWebSeriesLiveData = MutableLiveData>() val englishWebSeriesLiveData: LiveData> get() = _englishWebSeriesLiveData @@ -32,6 +41,69 @@ object WebSeriesRepository { var hindiWebSeriesData: WebSeriesResponse? = null + // continue watching (tight caching) + private val _continueWatchLiveData = MutableLiveData>() + val continueWatchLiveData: LiveData> + get() { + if (shallLoadNewContinueWatchData){ + // load new data + loadContinueWatchData() + shallLoadNewContinueWatchData = false + }else{ + // also updating local changes + continueWatchData?.let { + _continueWatchLiveData.postValue(ApiResult.Success(it)) + } + } + return _continueWatchLiveData + } + + var continueWatchData: ContinueEpisodeResponse? = null + + /* + flag to load a new data whenever necessary + for eg. when user has signed in with new account and the apps hold the older repository + */ + private var shallLoadNewContinueWatchData: Boolean = false + + init { + loadContinueWatchData() + } + + private fun loadContinueWatchData(){ + CoroutineScope(Dispatchers.IO).launch { + _continueWatchLiveData.postValue(ApiResult.Loading()) + val response = RetrofitHelper.handleApiCall { + moduleApiService.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) + } + } + + fun clearData(){ + _englishWebSeriesLiveData.postValue(ApiResult.Loading()) + _hindiWebSeriesLiveData.postValue(ApiResult.Loading()) + + hindiWebSeriesData = null + englishWebSeriesData = null + + shallLoadNewContinueWatchData = true + continueWatchData = null + } + fun loadEnglishWebSeries(){ CoroutineScope(Dispatchers.IO).launch { _englishWebSeriesLiveData.postValue(ApiResult.Loading()) diff --git a/app/src/main/java/com/woka/webseries/adapters/ContinueEpisodeAdapter.kt b/app/src/main/java/com/woka/webseries/adapters/ContinueEpisodeAdapter.kt new file mode 100644 index 0000000..3f5b3f4 --- /dev/null +++ b/app/src/main/java/com/woka/webseries/adapters/ContinueEpisodeAdapter.kt @@ -0,0 +1,62 @@ +package com.woka.webseries.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.AsyncDifferConfig +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.bumptech.glide.Glide +import com.woka.databinding.ContinueShowViewHolderBinding +import com.woka.webseries.models.ContinueEpisodeData +import java.util.concurrent.Executors + +class ContinueEpisodeAdapter(val context: Context, + config: AsyncDifferConfig): ListAdapter(config) { + + inner class ContinueEpisodeViewHolder(val binding: ContinueShowViewHolderBinding): ViewHolder(binding.root) + + companion object{ + + private val DIFF_UTIL = object : DiffUtil.ItemCallback(){ + override fun areItemsTheSame(oldItem: ContinueEpisodeData, newItem: ContinueEpisodeData): Boolean = oldItem.id == newItem.id + override fun areContentsTheSame(oldItem: ContinueEpisodeData, newItem: ContinueEpisodeData): Boolean { + return true + } + } + + private val DIFF_CONFIG = AsyncDifferConfig.Builder(DIFF_UTIL) + .setBackgroundThreadExecutor(Executors.newSingleThreadExecutor()) + .build() + } + + constructor(context: Context): this(context, DIFF_CONFIG) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContinueEpisodeViewHolder { + return ContinueEpisodeViewHolder( + ContinueShowViewHolderBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ContinueEpisodeViewHolder, position: Int) { + val episode = getItem(holder.absoluteAdapterPosition) + + holder.binding.apply { + episode.thumbnail_path?.let { + Glide.with(context.applicationContext) + .load(it) + .error(android.R.color.darker_gray) + .into(image) + } + + title.text = episode.episode_title + val detail = "S${episode.season_number}-E${episode.episode_number}" + details.text = detail + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/adapters/SpinnerAdapter.kt b/app/src/main/java/com/woka/webseries/adapters/SpinnerAdapter.kt new file mode 100644 index 0000000..871e078 --- /dev/null +++ b/app/src/main/java/com/woka/webseries/adapters/SpinnerAdapter.kt @@ -0,0 +1,48 @@ +package com.woka.webseries.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.ImageView +import android.widget.TextView +import com.woka.R +import com.woka.utils.hide +import com.woka.utils.show + +class SpinnerAdapter(context: Context, list: List): ArrayAdapter(context, 0, list){ + + private var currentSelection: Int = 0 + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + return createViewFromResource(convertView, parent, position) + } + + override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { + val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.spinner_view_holder, parent, false) + val textView = view.findViewById(R.id.spinner_text_view) + val tickView = view.findViewById(R.id.spinner_tick) + textView.text = getItem(position) + + if (currentSelection == position){ + tickView.show() + }else{ + tickView.hide() + } + + return view + } + + private fun createViewFromResource(convertView: View?, parent: ViewGroup, position: Int): View { + val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.spinner_view_holder, parent, false) + val textView = view.findViewById(R.id.spinner_text_view) + textView.text = getItem(position) + return view + } + + fun selectPosition(position: Int){ + this.currentSelection = position + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/ContentMoreDetailX.kt b/app/src/main/java/com/woka/webseries/models/ContentMoreDetailX.kt new file mode 100644 index 0000000..8fa906a --- /dev/null +++ b/app/src/main/java/com/woka/webseries/models/ContentMoreDetailX.kt @@ -0,0 +1,13 @@ +package com.woka.webseries.models + +data class ContentMoreDetailX( + val content_hd_url: String?, + val content_id: Int?, + val content_sd_url: String?, + val description: String?, + val id: Int?, + val language_master_id: Int?, + val post_type: Int?, + val tags_keywords: String?, + val title: String? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/ContinueEpisodeData.kt b/app/src/main/java/com/woka/webseries/models/ContinueEpisodeData.kt new file mode 100644 index 0000000..d374df5 --- /dev/null +++ b/app/src/main/java/com/woka/webseries/models/ContinueEpisodeData.kt @@ -0,0 +1,26 @@ +package com.woka.webseries.models + +data class ContinueEpisodeData( + val bookmark_count: Int?, + val content_more_details: List?, + val episode_description: String?, + val episode_duration: String?, + val episode_number: Int?, + val episode_title: String?, + val episode_url: String?, + val id: Int?, + val is_liked: Boolean?, + val language_master_id: Int?, + val likes_count: Int?, + val mark_as_favourite: Boolean?, + val release_date: String?, + val season_master_id: Int?, + val season_number: String?, + val show_data_count: ShowDataCount?, + val tags_keyword: String?, + val thumbnail_img_url: String?, + val thumbnail_path: String?, + val user_video_view: List?, + val views_count: Int?, + val watch_shows_master_id: Int? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/ContinueEpisodeResponse.kt b/app/src/main/java/com/woka/webseries/models/ContinueEpisodeResponse.kt new file mode 100644 index 0000000..158cac1 --- /dev/null +++ b/app/src/main/java/com/woka/webseries/models/ContinueEpisodeResponse.kt @@ -0,0 +1,6 @@ +package com.woka.webseries.models + +data class ContinueEpisodeResponse( + val result: List?, + val total_records: Int? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/ShowData.kt b/app/src/main/java/com/woka/webseries/models/ShowData.kt index 6023ce8..c9e0cbd 100644 --- a/app/src/main/java/com/woka/webseries/models/ShowData.kt +++ b/app/src/main/java/com/woka/webseries/models/ShowData.kt @@ -13,7 +13,7 @@ data class ShowData( val gender_master_id: String?, val id: Int?, var is_liked: Boolean?, - val liked_category_ids: Int?, + val liked_category_ids: String?, var likes_count: Int?, var mark_as_favourite: Boolean?, val season_data: List?, diff --git a/app/src/main/java/com/woka/webseries/models/ShowDataCount.kt b/app/src/main/java/com/woka/webseries/models/ShowDataCount.kt new file mode 100644 index 0000000..3e2f973 --- /dev/null +++ b/app/src/main/java/com/woka/webseries/models/ShowDataCount.kt @@ -0,0 +1,7 @@ +package com.woka.webseries.models + +data class ShowDataCount( + val id: Int?, + val total_episodes: Int?, + val total_seasons: Int? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/models/UserVideoView.kt b/app/src/main/java/com/woka/webseries/models/UserVideoView.kt new file mode 100644 index 0000000..357c512 --- /dev/null +++ b/app/src/main/java/com/woka/webseries/models/UserVideoView.kt @@ -0,0 +1,11 @@ +package com.woka.webseries.models + +data class UserVideoView( + val category_id: Int?, + val id: Int?, + val last_watched_left: Any?, + val post_id: Int?, + val post_type: Int?, + val total_watched_duration: String?, + val user_id: Int? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/viewmodel/WebSeriesViewModel.kt b/app/src/main/java/com/woka/webseries/viewmodel/WebSeriesViewModel.kt index e879d4b..7b5d9a4 100644 --- a/app/src/main/java/com/woka/webseries/viewmodel/WebSeriesViewModel.kt +++ b/app/src/main/java/com/woka/webseries/viewmodel/WebSeriesViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import com.woka.networking.ApiResult import com.woka.webseries.WebSeriesRepository +import com.woka.webseries.models.ContinueEpisodeResponse import com.woka.webseries.models.WebSeriesResponse class WebSeriesViewModel: ViewModel() { @@ -13,4 +14,7 @@ class WebSeriesViewModel: ViewModel() { val hindiWebSeriesData: LiveData> get() = WebSeriesRepository.hindiWebSeriesLiveData + + val continueWatchLiveData: LiveData> + get() = WebSeriesRepository.continueWatchLiveData } \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/views/SeasonActivity.kt b/app/src/main/java/com/woka/webseries/views/SeasonActivity.kt new file mode 100644 index 0000000..65d8298 --- /dev/null +++ b/app/src/main/java/com/woka/webseries/views/SeasonActivity.kt @@ -0,0 +1,26 @@ +package com.woka.webseries.views + +import android.os.Bundle +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import com.woka.R +import com.woka.databinding.ActivitySeasonBinding + +class SeasonActivity : AppCompatActivity() { + + private lateinit var binding: ActivitySeasonBinding + + 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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/views/WebSeriesActivity.kt b/app/src/main/java/com/woka/webseries/views/WebSeriesActivity.kt index 5b0d20b..7619a26 100644 --- a/app/src/main/java/com/woka/webseries/views/WebSeriesActivity.kt +++ b/app/src/main/java/com/woka/webseries/views/WebSeriesActivity.kt @@ -1,7 +1,6 @@ package com.woka.webseries.views import android.os.Bundle -import android.util.Log import android.view.View import android.widget.AdapterView import android.widget.AdapterView.OnItemSelectedListener @@ -17,17 +16,17 @@ import com.woka.R import com.woka.databinding.ActivityWebSeriesBinding import com.woka.modules.ModuleRepository import com.woka.networking.ApiResult -import com.woka.utils.TAG import com.woka.utils.WokaBaseActivity import com.woka.utils.hide import com.woka.utils.show -import com.woka.utils.toast 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.WebSeriesResponse import com.woka.webseries.viewmodel.WebSeriesViewModel -class WebSeriesActivity : WokaBaseActivity(), Observer> { +class WebSeriesActivity : WokaBaseActivity(), Observer>{ private lateinit var binding: ActivityWebSeriesBinding @@ -36,6 +35,10 @@ class WebSeriesActivity : WokaBaseActivity(), Observer { + binding.rvContinueWatch.hide() + binding.continueWatchTxt.hide() + } + is ApiResult.Loading -> {} + is ApiResult.Success -> { + it.data?.result?.let {continueWatchList -> + continueWatchAdapter.submitList(continueWatchList) + } + } + } + } + + binding.categorySpinner.onItemSelectedListener = object : OnItemSelectedListener{ + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + catSpinnerAdapter?.selectPosition(position) + loadShowData() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } ModuleRepository.showCategoryLiveData.observe(this){ when(it){ - is ApiResult.Error -> {} + is ApiResult.Error -> { + binding.errorView.show() + binding.shimmer.hide() + } is ApiResult.Loading -> {} is ApiResult.Success -> { @@ -115,45 +159,42 @@ class WebSeriesActivity : WokaBaseActivity(), Observer = ArrayAdapter( - this, - android.R.layout.simple_spinner_item, - categories - ) + catSpinnerAdapter = SpinnerAdapter(this, categories) - adapter.setDropDownViewResource( - android.R.layout.simple_spinner_dropdown_item - ) + loadShowData() - binding.categorySpinner.setAdapter(adapter) + binding.categorySpinner.setAdapter(catSpinnerAdapter) } } } - - binding.categorySpinner.onItemSelectedListener = object : OnItemSelectedListener{ - override fun onItemSelected( - parent: AdapterView<*>?, - view: View?, - position: Int, - id: Long - ) { - if (binding.categorySpinner.selectedItemId == 0L){ - WebSeriesRepository.loadHindiWebSeries() - }else{ - WebSeriesRepository.loadEnglishWebSeries() - } - } - - override fun onNothingSelected(parent: AdapterView<*>?) {} - } } override fun onChanged(value: ApiResult) { when (value){ - is ApiResult.Error -> {} + is ApiResult.Error -> { + binding.shimmer.hide() + binding.errorView.show() + } is ApiResult.Loading -> {} is ApiResult.Success -> { value.data?.show_data?.let { + binding.trailerView.show() + + adjustMasilaImage() + + binding.shimmer.hide() + + binding.spinnerCard.show() + binding.selectLangTxt.show() + + WebSeriesRepository.continueWatchData?.let { + binding.continueWatchTxt.show() + binding.rvContinueWatch.show() + }?:{ + binding.continueWatchTxt.hide() + binding.rvContinueWatch.hide() + } + if (binding.categorySpinner.selectedItemPosition <= 0){ // hindi binding.rvHindiWebSeries.show() @@ -171,4 +212,23 @@ class WebSeriesActivity : WokaBaseActivity(), Observer + + + + diff --git a/app/src/main/res/layout/activity_season.xml b/app/src/main/res/layout/activity_season.xml new file mode 100644 index 0000000..b27ef24 --- /dev/null +++ b/app/src/main/res/layout/activity_season.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_web_series.xml b/app/src/main/res/layout/activity_web_series.xml index ea348cf..e7f2bd7 100644 --- a/app/src/main/res/layout/activity_web_series.xml +++ b/app/src/main/res/layout/activity_web_series.xml @@ -12,11 +12,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +