Seasons tab ui
Integrated Episode Api listing and caching PlayerActivity created for playing any listed passed Created and cached playlist for episodes for every season of show Updated jwplayer to latest v4.17.0 Integrated teasers api and cached data Created and cached playlist for teasers for every season of show
This commit is contained in:
@@ -47,8 +47,8 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
ext.jwPlayerVersion = '4.6.0'
|
||||
ext.exoplayerVersion = '2.19.1'
|
||||
ext.jwPlayerVersion = '4.17.0'
|
||||
ext.exoplayerVersion = '1.1.1'
|
||||
|
||||
dependencies {
|
||||
implementation "com.facebook.shimmer:shimmer:0.5.0"
|
||||
@@ -83,6 +83,11 @@ dependencies {
|
||||
implementation "com.jwplayer:jwplayer-core:$jwPlayerVersion"
|
||||
implementation "com.jwplayer:jwplayer-common:$jwPlayerVersion"
|
||||
|
||||
// exoplayer2
|
||||
implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1'
|
||||
implementation 'com.google.android.exoplayer:exoplayer-ui:2.19.1'
|
||||
implementation 'com.google.android.exoplayer:exoplayer-hls:2.19.1'
|
||||
|
||||
implementation libs.androidx.core.ktx
|
||||
implementation libs.androidx.appcompat
|
||||
implementation libs.material
|
||||
|
||||
@@ -15,10 +15,18 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Woka"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".players.PlayerActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:theme="@style/FullScreenTheme"/>
|
||||
<activity
|
||||
android:name=".webseries.views.SeasonActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".webseries.views.WebSeriesActivity"
|
||||
android:exported="false"
|
||||
|
||||
@@ -98,6 +98,7 @@ class LiveStreamPlayerActivity : AppCompatActivity(), FullscreenHandler {
|
||||
}
|
||||
|
||||
override fun onAllowRotationChanged(allowRotation: Boolean) {}
|
||||
override fun onAllowFullscreenPortraitChanged(allowFullscreenPortrait: Boolean) {}
|
||||
|
||||
override fun updateLayoutParams(layoutParams: ViewGroup.LayoutParams?) {}
|
||||
|
||||
|
||||
87
app/src/main/java/com/woka/players/PlayerActivity.kt
Normal file
87
app/src/main/java/com/woka/players/PlayerActivity.kt
Normal file
@@ -0,0 +1,87 @@
|
||||
package com.woka.players
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.jwplayer.pub.api.JWPlayer
|
||||
import com.jwplayer.pub.api.configuration.PlayerConfig
|
||||
import com.jwplayer.pub.api.fullscreen.FullscreenHandler
|
||||
import com.jwplayer.pub.api.media.playlists.PlaylistItem
|
||||
import com.woka.databinding.ActivityPlayerBinding
|
||||
|
||||
class PlayerActivity : AppCompatActivity(), FullscreenHandler {
|
||||
|
||||
companion object{
|
||||
const val EXTRA_PLAY_LIST = "episode_key"
|
||||
const val EXTRA_EPISODE_INDEX = "episode_index"
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityPlayerBinding
|
||||
private lateinit var player: JWPlayer
|
||||
|
||||
private var playList: List<PlaylistItem>? = null
|
||||
private var playIndex: Int = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivityPlayerBinding.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
|
||||
}
|
||||
|
||||
val windowInsetsController =
|
||||
WindowCompat.getInsetsController(window, window.decorView)
|
||||
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
playList = intent.getParcelableArrayListExtra(EXTRA_PLAY_LIST)
|
||||
playIndex = intent.getIntExtra(EXTRA_EPISODE_INDEX, 0)
|
||||
|
||||
setUpPlayer()
|
||||
|
||||
}
|
||||
|
||||
private fun setUpPlayer() {
|
||||
if (playList == null) return
|
||||
player = binding.playerView.getPlayer(this)
|
||||
|
||||
player.setFullscreenHandler(this)
|
||||
|
||||
player.setFullscreen(true, false)
|
||||
|
||||
// to keep up the screen om when video is being played
|
||||
KeepScreenOnHandler(player, window)
|
||||
|
||||
val config = PlayerConfig.Builder()
|
||||
.playlist(playList)
|
||||
.build()
|
||||
|
||||
player.setup(config)
|
||||
player.playlistItem(playIndex)
|
||||
}
|
||||
|
||||
override fun onFullscreenRequested() {}
|
||||
|
||||
override fun onFullscreenExitRequested() {
|
||||
player.stop()
|
||||
val windowInsetsController =
|
||||
WindowCompat.getInsetsController(window, window.decorView)
|
||||
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onAllowRotationChanged(allowRotation: Boolean) {}
|
||||
override fun onAllowFullscreenPortraitChanged(allowFullscreenPortrait: Boolean) {}
|
||||
|
||||
override fun updateLayoutParams(layoutParams: ViewGroup.LayoutParams?) {}
|
||||
|
||||
override fun setUseFullscreenLayoutFlags(flags: Boolean) {}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.woka.R
|
||||
import kotlin.reflect.KFunction
|
||||
|
||||
class AdiImageView: FrameLayout {
|
||||
|
||||
@@ -29,6 +30,9 @@ class AdiImageView: FrameLayout {
|
||||
private var progressView: ProgressBar? = null
|
||||
private var cardView: CardView? = null
|
||||
|
||||
// variables
|
||||
var onLoadSuccessListener: (() -> Unit)? = null
|
||||
|
||||
constructor(context: Context?) : super(context!!)
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(
|
||||
context!!, attrs
|
||||
@@ -93,6 +97,7 @@ class AdiImageView: FrameLayout {
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
progressView?.hide()
|
||||
onLoadSuccessListener?.invoke()
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.woka.webseries
|
||||
|
||||
import com.woka.networking.ApiResponse
|
||||
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 okhttp3.FormBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
@@ -15,4 +17,10 @@ interface WebSeriesApiService {
|
||||
|
||||
@POST("season_listing")
|
||||
suspend fun seasonListing(@Body formBody: FormBody): Response<ApiResponse<SeasonDataResponse>>
|
||||
|
||||
@POST("episode_listing")
|
||||
suspend fun episodeListing(@Body formBody: FormBody): Response<ApiResponse<EpisodeResponseData>>
|
||||
|
||||
@POST("teaser_listing")
|
||||
suspend fun teaserListing(@Body formBody: FormBody): Response<ApiResponse<TeaserResponseData>>
|
||||
}
|
||||
@@ -2,6 +2,7 @@ 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.MyFavApiService
|
||||
import com.woka.home.mylist.MyListRepository
|
||||
import com.woka.home.mylist.models.BookmarkedShowData
|
||||
@@ -12,8 +13,11 @@ import com.woka.networking.RetrofitHelper
|
||||
import com.woka.webseries.models.ContinueEpisodeResponse
|
||||
import com.woka.webseries.models.ShowData
|
||||
import com.woka.webseries.models.WebSeriesResponse
|
||||
import com.woka.webseries.models.episodedata.EpisodeData
|
||||
import com.woka.webseries.models.episodedata.EpisodeResponseData
|
||||
import com.woka.webseries.models.seasondata.SeasonData
|
||||
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
|
||||
@@ -54,6 +58,10 @@ object WebSeriesRepository {
|
||||
|
||||
var continueWatchData: ContinueEpisodeResponse? = null
|
||||
|
||||
init {
|
||||
loadContinueWatchData()
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
@@ -65,15 +73,174 @@ object WebSeriesRepository {
|
||||
val seasonDataLiveData: LiveData<ApiResult<SeasonDataResponse>>
|
||||
get() = _seasonDataLiveData
|
||||
|
||||
private var seasonDataMap = HashMap<Int, SeasonDataResponse>()
|
||||
// seasons data for every show. where {key -> "showId_categoryId"}
|
||||
var seasonDataMap = HashMap<String, SeasonDataResponse>()
|
||||
|
||||
init {
|
||||
loadContinueWatchData()
|
||||
// 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"}
|
||||
var episodesPlaylistMap = HashMap<String, ArrayList<PlaylistItem>>()
|
||||
|
||||
// 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, ArrayList<PlaylistItem>>()
|
||||
|
||||
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 hindiPlayList = ArrayList<PlaylistItem>()
|
||||
val englishPlayList = ArrayList<PlaylistItem>()
|
||||
|
||||
for (teaser in it){
|
||||
teaser.content_more_details?.let {moreDetailsList ->
|
||||
if (moreDetailsList.isNotEmpty()){
|
||||
if (moreDetailsList.size > 1){
|
||||
moreDetailsList[1]?.let {moreDetail ->
|
||||
hindiPlayList.add(
|
||||
PlaylistItem.Builder()
|
||||
.file(moreDetail.content_hd_url)
|
||||
.title(moreDetail.title)
|
||||
.image(teaser.thumbnail_path)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
moreDetailsList[0]?.let {moreDetail ->
|
||||
englishPlayList.add(
|
||||
PlaylistItem.Builder()
|
||||
.file(moreDetail.content_hd_url)
|
||||
.title(moreDetail.title)
|
||||
.image(teaser.thumbnail_path)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
teasersPlaylistMap["${showId}_${seasonId}_18"] = hindiPlayList
|
||||
teasersPlaylistMap["${showId}_${seasonId}_1"] = englishPlayList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 hindiPlayList = ArrayList<PlaylistItem>()
|
||||
val englishPlayList = ArrayList<PlaylistItem>()
|
||||
|
||||
for (episode in it){
|
||||
episode.content_more_details?.let {moreDetailsList ->
|
||||
if (moreDetailsList.isNotEmpty()){
|
||||
if (moreDetailsList.size > 1){
|
||||
moreDetailsList[1]?.let {moreDetail ->
|
||||
hindiPlayList.add(
|
||||
PlaylistItem.Builder()
|
||||
.file(moreDetail.content_hd_url)
|
||||
.title(moreDetail.title)
|
||||
.image(episode.thumbnail_path)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
moreDetailsList[0]?.let {moreDetail ->
|
||||
englishPlayList.add(
|
||||
PlaylistItem.Builder()
|
||||
.file(moreDetail.content_hd_url)
|
||||
.title(moreDetail.title)
|
||||
.image(episode.thumbnail_path)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
episodesPlaylistMap["${showId}_${seasonId}_18"] = hindiPlayList
|
||||
episodesPlaylistMap["${showId}_${seasonId}_1"] = englishPlayList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearEpisodeData(){
|
||||
_episodeDataLiveData.postValue(ApiResult.Loading())
|
||||
}
|
||||
|
||||
fun loadSeasonListing(showId: Int, categoryId: String){
|
||||
if (seasonDataMap.containsKey(showId)){
|
||||
_seasonDataLiveData.postValue(ApiResult.Success(data = seasonDataMap[showId]))
|
||||
if (seasonDataMap.containsKey("${showId}_${categoryId}")){
|
||||
_seasonDataLiveData.postValue(ApiResult.Success(data = seasonDataMap["${showId}_${categoryId}"]))
|
||||
return
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
@@ -92,7 +259,7 @@ object WebSeriesRepository {
|
||||
is ApiResult.Loading -> {}
|
||||
is ApiResult.Success -> {
|
||||
response.data?.let {seasonList ->
|
||||
seasonDataMap[showId] = seasonList
|
||||
seasonDataMap["${showId}_${categoryId}"] = seasonList
|
||||
}
|
||||
|
||||
_seasonDataLiveData.postValue(ApiResult.Success(response.data))
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
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
|
||||
import com.woka.WokaApp.Companion.userPrefs
|
||||
import com.woka.databinding.EpisodeViewHolderBinding
|
||||
import com.woka.webseries.models.episodedata.EpisodeData
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class EpisodeAdapter private constructor(val context: Context,
|
||||
config: AsyncDifferConfig<EpisodeData>
|
||||
): ListAdapter<EpisodeData, EpisodeAdapter.EpisodeViewHolder>(config) {
|
||||
|
||||
inner class EpisodeViewHolder(val binding: EpisodeViewHolderBinding): RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
companion object{
|
||||
|
||||
private val DIFF_UTIL = object : DiffUtil.ItemCallback<EpisodeData>(){
|
||||
override fun areItemsTheSame(oldItem: EpisodeData, newItem: EpisodeData): Boolean = oldItem.id == newItem.id
|
||||
override fun areContentsTheSame(oldItem: EpisodeData, newItem: EpisodeData): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
private val DIFF_CONFIG = AsyncDifferConfig.Builder(DIFF_UTIL)
|
||||
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
|
||||
.build()
|
||||
}
|
||||
|
||||
constructor(context: Context): this(context, DIFF_CONFIG)
|
||||
|
||||
var onEpisodeClicked: ((position: Int) -> Unit)? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EpisodeViewHolder {
|
||||
return EpisodeViewHolder(
|
||||
EpisodeViewHolderBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: EpisodeViewHolder, position: Int) {
|
||||
val episode = getItem(holder.absoluteAdapterPosition)
|
||||
|
||||
holder.binding.apply {
|
||||
episode.thumbnail_path?.let {
|
||||
epImage.loadImage(it)
|
||||
}
|
||||
epDuration.text = "${episode.episode_duration}"
|
||||
episode.content_more_details?.let {moreDetailsList ->
|
||||
if (moreDetailsList.isNotEmpty()){
|
||||
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1){
|
||||
moreDetailsList[1]?.let {moreDetail ->
|
||||
epTitle.text = moreDetail.title
|
||||
}
|
||||
}else{
|
||||
moreDetailsList[0]?.let {moreDetail ->
|
||||
epTitle.text = moreDetail.title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
card.setOnClickListener {
|
||||
episode.id?.let { episodeId ->
|
||||
onEpisodeClicked?.invoke(holder.absoluteAdapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
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
|
||||
import com.woka.WokaApp.Companion.userPrefs
|
||||
import com.woka.databinding.EpisodeViewHolderBinding
|
||||
import com.woka.webseries.models.teaserdata.TeaserData
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class TeaserAdapter private constructor(val context: Context,
|
||||
config: AsyncDifferConfig<TeaserData>
|
||||
): ListAdapter<TeaserData, TeaserAdapter.EpisodeViewHolder>(config) {
|
||||
|
||||
inner class EpisodeViewHolder(val binding: EpisodeViewHolderBinding): RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
companion object{
|
||||
|
||||
private val DIFF_UTIL = object : DiffUtil.ItemCallback<TeaserData>(){
|
||||
override fun areItemsTheSame(oldItem: TeaserData, newItem: TeaserData): Boolean = oldItem.id == newItem.id
|
||||
override fun areContentsTheSame(oldItem: TeaserData, newItem: TeaserData): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
private val DIFF_CONFIG = AsyncDifferConfig.Builder(DIFF_UTIL)
|
||||
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
|
||||
.build()
|
||||
}
|
||||
|
||||
constructor(context: Context): this(context, DIFF_CONFIG)
|
||||
|
||||
var onEpisodeClicked: ((position: Int) -> Unit)? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EpisodeViewHolder {
|
||||
return EpisodeViewHolder(
|
||||
EpisodeViewHolderBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: EpisodeViewHolder, position: Int) {
|
||||
val episode = getItem(holder.absoluteAdapterPosition)
|
||||
|
||||
holder.binding.apply {
|
||||
episode.thumbnail_path?.let {
|
||||
epImage.loadImage(it)
|
||||
}
|
||||
epDuration.text = "${episode.teaser_duration}"
|
||||
episode.content_more_details?.let {moreDetailsList ->
|
||||
if (moreDetailsList.isNotEmpty()){
|
||||
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1){
|
||||
moreDetailsList[1]?.let {moreDetail ->
|
||||
epTitle.text = moreDetail.title
|
||||
}
|
||||
}else{
|
||||
moreDetailsList[0]?.let {moreDetail ->
|
||||
epTitle.text = moreDetail.title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
card.setOnClickListener {
|
||||
onEpisodeClicked?.invoke(holder.absoluteAdapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.woka.webseries.models.episodedata
|
||||
|
||||
data class ContentMoreDetail(
|
||||
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?
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.woka.webseries.models.episodedata
|
||||
|
||||
data class EpisodeData(
|
||||
val content_more_details: List<ContentMoreDetail?>?,
|
||||
val episode_description: String?,
|
||||
val episode_duration: String?,
|
||||
val episode_number: Int?,
|
||||
val episode_title: String?,
|
||||
val episode_url: String?,
|
||||
val id: Int?,
|
||||
val language_master_id: Int?,
|
||||
val release_date: String?,
|
||||
val season_data: SeasonData?,
|
||||
val season_master_id: Int?,
|
||||
val tags_keyword: String?,
|
||||
val thumbnail_img_url: String?,
|
||||
val thumbnail_path: String?,
|
||||
val user_video_view: List<Any?>?,
|
||||
val watch_shows_master_id: Int?
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.woka.webseries.models.episodedata
|
||||
|
||||
data class EpisodeResponseData(
|
||||
val result: List<EpisodeData?>?,
|
||||
val total_records: Int?
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.woka.webseries.models.episodedata
|
||||
|
||||
data class SeasonData(
|
||||
val id: Int?,
|
||||
val season_number: String?,
|
||||
val watch_shows_master_id: Int?
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.woka.webseries.models.teaserdata
|
||||
|
||||
data class ContentMoreDetail(
|
||||
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?
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.woka.webseries.models.teaserdata
|
||||
|
||||
data class SeasonData(
|
||||
val id: Int?,
|
||||
val season_number: String?,
|
||||
val watch_shows_master_id: Int?
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.woka.webseries.models.teaserdata
|
||||
|
||||
data class TeaserData(
|
||||
val content_more_details: List<ContentMoreDetail?>?,
|
||||
val id: Int?,
|
||||
val language_master_id: Int?,
|
||||
val release_date: String?,
|
||||
val season_data: SeasonData?,
|
||||
val season_master_id: Int?,
|
||||
val serial_number: Int?,
|
||||
val tags_keyword: String?,
|
||||
val teaser_description: String?,
|
||||
val teaser_duration: String?,
|
||||
val teaser_number: Int?,
|
||||
val teaser_title: String?,
|
||||
val teaser_url: String?,
|
||||
val thumbnail_img_url: String?,
|
||||
val thumbnail_path: String?,
|
||||
val user_video_view: List<Any?>?,
|
||||
val watch_shows_master_id: Int?
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.woka.webseries.models.teaserdata
|
||||
|
||||
data class TeaserResponseData(
|
||||
val result: List<TeaserData?>?,
|
||||
val total_records: Int?
|
||||
)
|
||||
@@ -3,20 +3,30 @@ package com.woka.webseries.views
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.bumptech.glide.Glide
|
||||
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.networking.ApiResult
|
||||
import com.woka.players.PlayerActivity
|
||||
import com.woka.players.PlayerActivity.Companion.EXTRA_EPISODE_INDEX
|
||||
import com.woka.players.PlayerActivity.Companion.EXTRA_PLAY_LIST
|
||||
import com.woka.utils.ProgressView
|
||||
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 kotlin.math.max
|
||||
|
||||
@@ -36,6 +46,13 @@ class SeasonActivity : AppCompatActivity(), OnTabSelectedListener {
|
||||
|
||||
private var showPosition: Int = -1
|
||||
|
||||
private lateinit var progressView: ProgressView
|
||||
|
||||
private lateinit var episodeAdapter: EpisodeAdapter
|
||||
private lateinit var teaserAdapter: TeaserAdapter
|
||||
|
||||
private var selectedSeasonId = -1
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
@@ -61,6 +78,12 @@ class SeasonActivity : AppCompatActivity(), OnTabSelectedListener {
|
||||
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){
|
||||
@@ -77,17 +100,16 @@ class SeasonActivity : AppCompatActivity(), OnTabSelectedListener {
|
||||
setObservers()
|
||||
|
||||
WebSeriesRepository.loadSeasonListing(showId, categoryId!!)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
WebSeriesRepository.clearEpisodeData()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
if (showData != null && categoryId != null) {
|
||||
binding.apply {
|
||||
showData!!.thumbnail_path?.let {
|
||||
Glide.with(applicationContext)
|
||||
.load(it)
|
||||
.into(image)
|
||||
}
|
||||
|
||||
likeCount.text = "${showData!!.likes_count}"
|
||||
|
||||
@@ -95,6 +117,15 @@ class SeasonActivity : AppCompatActivity(), OnTabSelectedListener {
|
||||
|
||||
likeSeason.isSelected = showData!!.is_liked?:false
|
||||
|
||||
binding.seasonsTab.addOnTabSelectedListener(this@SeasonActivity)
|
||||
|
||||
image.onLoadSuccessListener = {playTrailer.show()}
|
||||
|
||||
rvEpisodes.adapter = episodeAdapter
|
||||
episodeAdapter.onEpisodeClicked = ::onEpisodeClicked
|
||||
|
||||
rvTeaser.adapter = teaserAdapter
|
||||
teaserAdapter.onEpisodeClicked = ::onTeaserClicked
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,10 +192,16 @@ class SeasonActivity : AppCompatActivity(), OnTabSelectedListener {
|
||||
|
||||
private fun setObservers(){
|
||||
WebSeriesRepository.seasonDataLiveData.observe(this){
|
||||
binding.seasonsTab.removeAllTabs()
|
||||
when(it){
|
||||
is ApiResult.Error -> {}
|
||||
is ApiResult.Loading -> {}
|
||||
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
|
||||
@@ -174,9 +211,131 @@ class SeasonActivity : AppCompatActivity(), OnTabSelectedListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTabSelected(p0: TabLayout.Tab?) {}
|
||||
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){
|
||||
startActivity(Intent(this, PlayerActivity::class.java).apply {
|
||||
putParcelableArrayListExtra(EXTRA_PLAY_LIST, WebSeriesRepository.episodesPlaylistMap["${showId}_${selectedSeasonId}_$categoryId"])
|
||||
putExtra(EXTRA_EPISODE_INDEX, position)
|
||||
})
|
||||
}
|
||||
|
||||
private fun onTeaserClicked(position: Int){
|
||||
startActivity(Intent(this, PlayerActivity::class.java).apply {
|
||||
putParcelableArrayListExtra(EXTRA_PLAY_LIST, WebSeriesRepository.teasersPlaylistMap["${showId}_${selectedSeasonId}_$categoryId"])
|
||||
putExtra(EXTRA_EPISODE_INDEX, position)
|
||||
})
|
||||
}
|
||||
|
||||
override fun onTabSelected(p0: TabLayout.Tab?) {
|
||||
binding.playTrailer.hide()
|
||||
loadSeasonData()
|
||||
}
|
||||
|
||||
override fun onTabUnselected(p0: TabLayout.Tab?) {}
|
||||
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:aapt="http://schemas.android.com/aapt">
|
||||
<item android:state_focused="true" android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<stroke android:width="3dp" android:color="#5e1fc4"/>
|
||||
<corners android:radius="25dp"/>
|
||||
<solid android:color="@color/white"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<stroke android:width="3dp" android:color="@color/color_primary"/>
|
||||
<stroke android:width="10dp" android:color="@color/color_primary"/>
|
||||
<corners android:radius="25dp"/>
|
||||
<solid android:color="#5e1fc4"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_selected="false">
|
||||
<shape android:shape="rectangle">
|
||||
<stroke android:width="10dp" android:color="@color/color_primary"/>
|
||||
<corners android:radius="25dp"/>
|
||||
<solid android:color="@color/white"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_focused="true">
|
||||
<shape android:shape="rectangle">
|
||||
<stroke android:width="3dp" android:color="#5e1fc4"/>
|
||||
<corners android:radius="25dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
|
||||
</selector>
|
||||
7
app/src/main/res/layout/activity_player.xml
Normal file
7
app/src/main/res/layout/activity_player.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.jwplayer.pub.view.JWPlayerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/player_view"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".players.PlayerActivity"/>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
@@ -19,7 +19,7 @@
|
||||
android:background="@drawable/img_season_bg"
|
||||
android:paddingHorizontal="15dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingBottom="35dp"
|
||||
android:paddingBottom="40dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
@@ -57,7 +57,7 @@
|
||||
android:layout_margin="10dp"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
<com.woka.utils.AdiImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -65,9 +65,12 @@
|
||||
android:src="@android:color/darker_gray"
|
||||
android:contentDescription="@string/masila"
|
||||
android:scaleType="fitXY"
|
||||
app:imageCornerRadius="5dp"
|
||||
/>
|
||||
|
||||
<com.woka.utils.PressableCard
|
||||
android:id="@+id/play_trailer"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/round_25"
|
||||
@@ -143,23 +146,6 @@
|
||||
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/seaon_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
tools:text="1 HR 25 MIN"
|
||||
android:fontFamily="@font/exo_2"
|
||||
android:textColor="@color/color_primary"
|
||||
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginStart="25dp"
|
||||
|
||||
app:layout_constraintStart_toEndOf="@id/episode_number"
|
||||
app:layout_constraintTop_toBottomOf="@id/season_title"
|
||||
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -222,9 +208,7 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/season_description"
|
||||
|
||||
app:isHapticEnabled="true"
|
||||
|
||||
android:layout_marginTop="20dp">
|
||||
app:isHapticEnabled="true">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
@@ -387,8 +371,8 @@
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/seasons_tab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="15dp"
|
||||
app:tabBackground="@drawable/season_tab_bg"
|
||||
app:tabGravity="start"
|
||||
@@ -397,20 +381,94 @@
|
||||
app:tabPaddingEnd="25dp"
|
||||
app:tabPaddingStart="25dp"
|
||||
app:tabRippleColor="@android:color/transparent"
|
||||
app:tabSelectedTextColor="@color/color_primary"
|
||||
app:tabTextColor="@color/white" />
|
||||
app:tabSelectedTextColor="@color/white"
|
||||
app:tabTextColor="@color/color_primary" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
android:id="@+id/ep_shimmer"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:shimmer_auto_start="true"
|
||||
app:shimmer_highlight_alpha="0.5"
|
||||
app:shimmer_base_alpha="0.6"
|
||||
android:layout_marginTop="10dp"
|
||||
>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="none"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/episode_view_holder"/>
|
||||
<include layout="@layout/episode_view_holder"/>
|
||||
<include layout="@layout/episode_view_holder"/>
|
||||
<include layout="@layout/episode_view_holder"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/season_media_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
|
||||
android:indeterminateTint="@color/white"
|
||||
android:layout_gravity="center_horizontal"
|
||||
tools:text="Episodes"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/_14ssp"
|
||||
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_episodes"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/episode_view_holder"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="15dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/teaser_txt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
|
||||
android:text="@string/teasers"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/_14ssp"
|
||||
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="10dp"
|
||||
/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_teaser"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/episode_view_holder"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="15dp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
@@ -272,10 +272,9 @@
|
||||
<Spinner
|
||||
android:id="@+id/category_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/_37sdp"
|
||||
android:layout_height="@dimen/_40sdp"
|
||||
|
||||
android:popupBackground="@drawable/round_bg_5_white"
|
||||
|
||||
android:textAlignment="textStart"
|
||||
/>
|
||||
|
||||
|
||||
88
app/src/main/res/layout/episode_view_holder.xml
Normal file
88
app/src/main/res/layout/episode_view_holder.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tool="http://schemas.android.com/tools"
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
app:cardBackgroundColor="@color/white"
|
||||
app:cardCornerRadius="15dp"
|
||||
app:cardElevation="5dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="10">
|
||||
|
||||
<com.woka.utils.AdiImageView
|
||||
android:id="@+id/ep_image"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/_45sdp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_weight="3"
|
||||
app:imageCornerRadius="5dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_weight="6"
|
||||
android:layout_gravity="top"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ep_title"
|
||||
tool:text="Episode name here"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:maxLines="2"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
android:textColor="@color/color_primary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ep_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:textSize="@dimen/_10ssp"
|
||||
tool:text="00:00:12"
|
||||
android:fontFamily="@font/exo_2"
|
||||
android:textColor="@color/color_primary" />
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="7dp"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:visibility="gone"
|
||||
tool:progress="100"
|
||||
app:indicatorColor="#ce0000"
|
||||
app:trackColor="#b2b2b2"
|
||||
app:trackCornerRadius="5dp"
|
||||
app:trackThickness="3dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/play_btn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/_24ssp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_weight="1"
|
||||
android:contentDescription="@string/play"
|
||||
android:src="@drawable/ic_play_filled"
|
||||
app:tint="@color/color_primary" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
@@ -4,8 +4,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
|
||||
android:paddingVertical="15dp"
|
||||
android:paddingHorizontal="15dp"
|
||||
|
||||
xmlns:ools="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
@@ -19,6 +17,8 @@
|
||||
android:textColor="@color/black"
|
||||
android:textSize="@dimen/_12ssp"
|
||||
|
||||
android:layout_margin="15dp"
|
||||
|
||||
android:textAlignment="textStart"
|
||||
|
||||
ools:layout_constraintStart_toStartOf="parent"
|
||||
@@ -36,6 +36,8 @@
|
||||
android:contentDescription="@string/image"
|
||||
android:src="@drawable/ic_tick"
|
||||
|
||||
android:layout_marginEnd="15dp"
|
||||
|
||||
ools:layout_constraintEnd_toEndOf="parent"
|
||||
ools:layout_constraintTop_toTopOf="parent"
|
||||
ools:layout_constraintBottom_toBottomOf="parent"
|
||||
|
||||
@@ -181,4 +181,6 @@
|
||||
<string name="add">ADD</string>
|
||||
<string name="rate">RATE</string>
|
||||
<string name="share">SHARE</string>
|
||||
<string name="couldnt_play_video">Couldn\'t play video</string>
|
||||
<string name="teasers">Teasers</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user