diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e09df64..a34d286 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,6 +17,10 @@ android:supportsRtl="true" android:theme="@style/Theme.Woka" tools:targetApi="31"> + diff --git a/app/src/main/java/com/woka/WokaApp.kt b/app/src/main/java/com/woka/WokaApp.kt index 94d55ba..59f0658 100644 --- a/app/src/main/java/com/woka/WokaApp.kt +++ b/app/src/main/java/com/woka/WokaApp.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.Application import com.jwplayer.pub.api.license.LicenseUtil import com.onesignal.OneSignal +import com.woka.advertisements.AdsRepository import com.woka.database.AppDatabase import com.woka.streamingurls.StreamingUrlRepository import com.woka.userPreference.UserPreference @@ -30,6 +31,10 @@ class WokaApp: Application() { StreamingUrlRepository.loadLiveStreamingUrls() + CoroutineScope(Dispatchers.IO).launch { + AdsRepository.getADs() + } + initOneSignal() } diff --git a/app/src/main/java/com/woka/advertisements/AdsApiService.kt b/app/src/main/java/com/woka/advertisements/AdsApiService.kt new file mode 100644 index 0000000..051fc4b --- /dev/null +++ b/app/src/main/java/com/woka/advertisements/AdsApiService.kt @@ -0,0 +1,12 @@ +package com.woka.advertisements + +import com.woka.advertisements.models.AdsResponse +import com.woka.networking.ApiResponse +import retrofit2.Response +import retrofit2.http.GET + +interface AdsApiService { + + @GET("get_ad_data") + suspend fun getAdsData() : Response> +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/advertisements/AdsRepository.kt b/app/src/main/java/com/woka/advertisements/AdsRepository.kt new file mode 100644 index 0000000..f049d46 --- /dev/null +++ b/app/src/main/java/com/woka/advertisements/AdsRepository.kt @@ -0,0 +1,31 @@ +package com.woka.advertisements + +import com.woka.advertisements.models.AdData +import com.woka.networking.ApiResult +import com.woka.networking.RetrofitHelper +import com.woka.networking.RetrofitHelper.handleApiCall + +object AdsRepository { + + private val apiService = RetrofitHelper.getRetrofit().create(AdsApiService::class.java) + + @Volatile + private var adsData: List? = null + + suspend fun getADs(): List?{ + if (adsData != null){ + return adsData + } + + when (val response = handleApiCall{ apiService.getAdsData() }){ + is ApiResult.Error -> {} + is ApiResult.Loading -> {} + is ApiResult.Success -> { + adsData = response.data?.result?.filterNotNull() + } + } + + return adsData + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/advertisements/models/AdData.kt b/app/src/main/java/com/woka/advertisements/models/AdData.kt new file mode 100644 index 0000000..b77da63 --- /dev/null +++ b/app/src/main/java/com/woka/advertisements/models/AdData.kt @@ -0,0 +1,11 @@ +package com.woka.advertisements.models + +data class AdData( + val ad_company: String?, + val ad_link: String?, + val banner_image: String?, + val button_image: String?, + val for_page: String?, + val id: Int?, + val title: String? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/advertisements/models/AdsResponse.kt b/app/src/main/java/com/woka/advertisements/models/AdsResponse.kt new file mode 100644 index 0000000..196b623 --- /dev/null +++ b/app/src/main/java/com/woka/advertisements/models/AdsResponse.kt @@ -0,0 +1,6 @@ +package com.woka.advertisements.models + +data class AdsResponse( + val result: List?, + val total_records: Int? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/advertisements/models/ForPage.kt b/app/src/main/java/com/woka/advertisements/models/ForPage.kt new file mode 100644 index 0000000..76f5b9c --- /dev/null +++ b/app/src/main/java/com/woka/advertisements/models/ForPage.kt @@ -0,0 +1,11 @@ +package com.woka.advertisements.models + +enum class ForPage(val value: String) { + THEME_1("theme-1"), + THEME_2("theme-2"), + WEB_SERIES("web-series"), + KARAOKE("karaoke"), + AUDIO_BOOKS("audio-books"), + GAMES("games"), + SHOP_SUPER_CATEGORY("shop-super-category"); +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/audiobooks/views/AudioBooksActivity.kt b/app/src/main/java/com/woka/audiobooks/views/AudioBooksActivity.kt index c685dbe..0c30c62 100644 --- a/app/src/main/java/com/woka/audiobooks/views/AudioBooksActivity.kt +++ b/app/src/main/java/com/woka/audiobooks/views/AudioBooksActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable +import android.net.Uri import android.os.Bundle import android.text.Html import android.view.WindowManager @@ -14,17 +15,21 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope 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.advertisements.AdsRepository +import com.woka.advertisements.models.ForPage import com.woka.audiobooks.AudioBookRepository import com.woka.audiobooks.adapters.AudioBooksAdapter import com.woka.audiobooks.adapters.ContinueAudioAdapter import com.woka.audiobooks.models.audiodata.AudioBookData import com.woka.audiobooks.models.continuedata.ContinueAudioData import com.woka.audiobooks.viewmodels.AudioBookViewModel +import com.woka.database.helpers.AdClicksHelper import com.woka.database.helpers.ClicksHelper import com.woka.database.models.ContentType import com.woka.databinding.ActivityAudioBooksBinding @@ -42,6 +47,7 @@ import com.woka.utils.setVisibility import com.woka.utils.shareWokaApp import com.woka.utils.show import com.woka.utils.toast +import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale @@ -62,6 +68,8 @@ class AudioBooksActivity : WokaBaseActivity() { private lateinit var playerLauncher: ActivityResultLauncher + private var adLoaded = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -101,6 +109,8 @@ class AudioBooksActivity : WokaBaseActivity() { setObservers() + loadAds() + if (!viewModel.audioBookLiveData.isInitialized) { viewModel.loadAudioSongs() } @@ -182,6 +192,47 @@ class AudioBooksActivity : WokaBaseActivity() { } } + private fun loadAds(){ + lifecycleScope.launch { + AdsRepository.getADs()?.let { ads -> + val theme1ads = ads.filter { it.for_page == ForPage.AUDIO_BOOKS.value} + + if (theme1ads.isNotEmpty()){ + val adDetails = theme1ads.first() + + AdClicksHelper.addImpression(adDetails.id) + + adLoaded = true + + binding.trailerBtn.hide() + binding.trailerName.hide() + + binding.adButtonView.show() + + binding.trailerView.show() + adjustTrailerImage() + + adDetails.banner_image?.let {url -> + binding.trailerImage.loadImage(url) + } + + adDetails.button_image?.let {url -> + binding.adButtonImg.loadImage(url) + } + + adDetails.ad_link?.let {url -> + binding.adBtn.setOnClickListener { + AdClicksHelper.addClick(adDetails.id) + startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(url)) + ) + } + } + } + } + } + } + private fun setObservers() { viewModel.audioBookLiveData.observe(this) { when (it) { @@ -275,6 +326,8 @@ class AudioBooksActivity : WokaBaseActivity() { } private fun loadTrailerData(audioBookData: AudioBookData) { + if (adLoaded) return + binding.apply { trailerView.show() adjustTrailerImage() diff --git a/app/src/main/java/com/woka/database/AppDatabase.kt b/app/src/main/java/com/woka/database/AppDatabase.kt index 1c53ff8..32ffba1 100644 --- a/app/src/main/java/com/woka/database/AppDatabase.kt +++ b/app/src/main/java/com/woka/database/AppDatabase.kt @@ -4,14 +4,16 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase +import com.woka.database.dao.AdClickDao import com.woka.database.dao.ClicksDao import com.woka.database.dao.VideoViewDao +import com.woka.database.models.adclicks.AdClickEvent import com.woka.database.models.clicks.ClickEvent import com.woka.database.models.videoview.VideoViewEvent private const val DATABASE_NAME = "woka_local_db" -@Database(entities = [ClickEvent::class, VideoViewEvent::class], version = 1) +@Database(entities = [ClickEvent::class, VideoViewEvent::class, AdClickEvent::class], version = 1) abstract class AppDatabase : RoomDatabase(){ companion object{ @@ -35,4 +37,6 @@ abstract class AppDatabase : RoomDatabase(){ abstract fun clicksDao(): ClicksDao abstract fun videoViewDao(): VideoViewDao + + abstract fun adClicksDao(): AdClickDao } \ No newline at end of file diff --git a/app/src/main/java/com/woka/database/dao/AdClickDao.kt b/app/src/main/java/com/woka/database/dao/AdClickDao.kt new file mode 100644 index 0000000..5400585 --- /dev/null +++ b/app/src/main/java/com/woka/database/dao/AdClickDao.kt @@ -0,0 +1,43 @@ +package com.woka.database.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import com.woka.database.models.adclicks.AdClickEvent +import com.woka.database.models.clicks.ClickEvent + +@Dao +interface AdClickDao { + @Insert(onConflict = OnConflictStrategy.IGNORE) + suspend fun insert(vararg adClickEvent: AdClickEvent) + + @Query("UPDATE ad_click_events " + + "SET no_of_click = no_of_click + :clicksCount, " + + "no_of_impression = no_of_impression + :impressionCount " + + "WHERE ad_id = :adId") + suspend fun updateAndGetRowCount(adId: Int, clicksCount: Int, impressionCount: Int): Int + + @Transaction + suspend fun upsertClickEvent(adId: Int, clicksCount: Int, impressionCount: Int){ + val count = updateAndGetRowCount(adId, clicksCount, impressionCount) + if (count == 0){ + insert( + AdClickEvent( + adId, + clicksCount, + impressionCount + ) + ) + } + } + + @Query("select * from ad_click_events limit :batchSize") + suspend fun getAdClickEventBatch(batchSize: Int): List + + @Delete + suspend fun deleteAdClickEvents(clickEvent: List): Int + +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/database/helpers/AdClicksHelper.kt b/app/src/main/java/com/woka/database/helpers/AdClicksHelper.kt new file mode 100644 index 0000000..4b33b4a --- /dev/null +++ b/app/src/main/java/com/woka/database/helpers/AdClicksHelper.kt @@ -0,0 +1,37 @@ +package com.woka.database.helpers + +import com.woka.WokaApp.Companion.appDatabase +import com.woka.database.dao.AdClickDao +import com.woka.database.dao.ClicksDao +import com.woka.database.models.ContentType +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +object AdClicksHelper { + private val adClicksDao: AdClickDao? = appDatabase?.adClicksDao() + + fun addImpression(adId: Int?, impressionCount: Int = 1){ + CoroutineScope(Dispatchers.IO).launch { + if (adId != null){ + adClicksDao?.upsertClickEvent( + adId, + 0, + impressionCount + ) + } + } + } + + fun addClick(adId: Int?, clickCount: Int = 1){ + CoroutineScope(Dispatchers.IO).launch { + if (adId != null){ + adClicksDao?.upsertClickEvent( + adId, + clickCount, + 0 + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/database/helpers/RemoteSync.kt b/app/src/main/java/com/woka/database/helpers/RemoteSync.kt index 3c79852..3623042 100644 --- a/app/src/main/java/com/woka/database/helpers/RemoteSync.kt +++ b/app/src/main/java/com/woka/database/helpers/RemoteSync.kt @@ -12,13 +12,16 @@ private const val SYNC_BATCH_COUNT = 15 object RemoteSync { - private const val TAG = "aditya_RemoteSync" + private const val CLICKS_TAG = "aditya_RemoteSync_clicks" + private const val AD_CLICKS_TAG = "aditya_RemoteSync_ad_clicks" private val clicksDao = appDatabase?.clicksDao() + private val adClickDao = appDatabase?.adClicksDao() + private val apiService = RetrofitHelper.getRetrofit().create(RemoteApiService::class.java) suspend fun syncClickEvents(){ - Log.d(TAG, "syncClickEvents: CLICK EVENTS SYNC CALLED") + Log.d(CLICKS_TAG, "syncClickEvents: CLICK EVENTS SYNC CALLED") var roundCount = 0 @@ -26,36 +29,77 @@ object RemoteSync { val clicksBatch = clicksDao?.getClickEventBatch(SYNC_BATCH_COUNT) if (clicksDao == null || clicksBatch.isNullOrEmpty()){ - Log.d(TAG, "syncClickEvents: RECEIVED BATCH IS EMPTY") + Log.d(CLICKS_TAG, "syncClickEvents: RECEIVED BATCH IS EMPTY") break } - Log.d(TAG, "syncClickEvents: BATCH COUNT FROM DATABASE ${clicksBatch.size}") + Log.d(CLICKS_TAG, "syncClickEvents: BATCH COUNT FROM DATABASE ${clicksBatch.size}") when (val response = handleApiCall{ apiService.sendClickEvents(clicksBatch.map { RemoteClickEvent(it) })}){ is ApiResult.Error -> { - Log.e(TAG, "syncClickEvents: BATCH SYNC FAILED due to ${response.errorMessage}", response.error) + Log.e(CLICKS_TAG, "syncClickEvents: BATCH SYNC FAILED due to ${response.errorMessage}", response.error) break } is ApiResult.Loading -> {} is ApiResult.Success -> { - Log.d(TAG, "syncClickEvents: BATCH SYNC IS SUCCESSFUL") + Log.d(CLICKS_TAG, "syncClickEvents: BATCH SYNC IS SUCCESSFUL") - Log.d(TAG, "syncClickEvents: DELETING SYNCED DATA FROM LOCAL DATABASE") + Log.d(CLICKS_TAG, "syncClickEvents: DELETING SYNCED DATA FROM LOCAL DATABASE") val deleteCount = clicksDao.deleteClickEvents(clicksBatch) - Log.d(TAG, "syncClickEvents: $deleteCount ENTRIES ARE DELETED FROM LOCAL DATABASE") + Log.d(CLICKS_TAG, "syncClickEvents: $deleteCount ENTRIES ARE DELETED FROM LOCAL DATABASE") if (deleteCount <= 0){ - Log.e(TAG, "syncClickEvents: NO ENTRIES WERE DELETED FROM LOCAL DATABASE") + Log.e(CLICKS_TAG, "syncClickEvents: NO ENTRIES WERE DELETED FROM LOCAL DATABASE") } roundCount++ } } - Log.d(TAG, "syncClickEvents: \n") + Log.d(CLICKS_TAG, "syncClickEvents: \n") } - Log.d(TAG, "syncClickEvents: NUMBER OF ROUND : $roundCount") + Log.d(CLICKS_TAG, "syncClickEvents: NUMBER OF ROUND : $roundCount") + } + + suspend fun syncAdClickEvents() { + Log.d(AD_CLICKS_TAG, "syncClickEvents: AD CLICK EVENTS SYNC CALLED") + + var roundCount = 0 + + while (true){ + val clicksBatch = adClickDao?.getAdClickEventBatch(SYNC_BATCH_COUNT) + + if (adClickDao == null || clicksBatch.isNullOrEmpty()){ + Log.d(AD_CLICKS_TAG, "syncClickEvents: RECEIVED BATCH IS EMPTY") + break + } + + Log.d(AD_CLICKS_TAG, "syncClickEvents: BATCH COUNT FROM DATABASE ${clicksBatch.size}") + when (val response = handleApiCall{ apiService.updateAdsCount(clicksBatch)}){ + is ApiResult.Error -> { + Log.e(AD_CLICKS_TAG, "syncClickEvents: BATCH SYNC FAILED due to ${response.errorMessage}", response.error) + break + } + is ApiResult.Loading -> {} + is ApiResult.Success -> { + Log.d(AD_CLICKS_TAG, "syncClickEvents: BATCH SYNC IS SUCCESSFUL") + + Log.d(AD_CLICKS_TAG, "syncClickEvents: DELETING SYNCED DATA FROM LOCAL DATABASE") + val deleteCount = adClickDao.deleteAdClickEvents(clicksBatch) + Log.d(AD_CLICKS_TAG, "syncClickEvents: $deleteCount ENTRIES ARE DELETED FROM LOCAL DATABASE") + + if (deleteCount <= 0){ + Log.e(AD_CLICKS_TAG, "syncClickEvents: NO ENTRIES WERE DELETED FROM LOCAL DATABASE") + } + + roundCount++ + } + } + + Log.d(AD_CLICKS_TAG, "syncClickEvents: \n") + } + + Log.d(AD_CLICKS_TAG, "syncClickEvents: NUMBER OF ROUND : $roundCount") } } \ No newline at end of file diff --git a/app/src/main/java/com/woka/database/models/adclicks/AdClickEvent.kt b/app/src/main/java/com/woka/database/models/adclicks/AdClickEvent.kt new file mode 100644 index 0000000..ac192c2 --- /dev/null +++ b/app/src/main/java/com/woka/database/models/adclicks/AdClickEvent.kt @@ -0,0 +1,15 @@ +package com.woka.database.models.adclicks + +import androidx.room.Entity +import androidx.room.PrimaryKey + + +private const val DB_TABLE_NAME = "ad_click_events" + +@Entity(tableName = DB_TABLE_NAME) +data class AdClickEvent( + @PrimaryKey + val ad_id: Int, + val no_of_click: Int?, + val no_of_impression: Int? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/database/remote/RemoteApiService.kt b/app/src/main/java/com/woka/database/remote/RemoteApiService.kt index 9ba0a71..0f8a7ae 100644 --- a/app/src/main/java/com/woka/database/remote/RemoteApiService.kt +++ b/app/src/main/java/com/woka/database/remote/RemoteApiService.kt @@ -1,5 +1,6 @@ package com.woka.database.remote +import com.woka.database.models.adclicks.AdClickEvent import com.woka.database.remote.models.RemoteClickEvent import com.woka.networking.ApiResponse import retrofit2.Response @@ -10,4 +11,7 @@ interface RemoteApiService { @POST("v2/user_clicks") suspend fun sendClickEvents(@Body clickEventList: List): Response> + + @POST("update_ad_count") + suspend fun updateAdsCount(@Body adClickEvents: List): Response> } \ No newline at end of file diff --git a/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt b/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt index c741ed9..f69d330 100644 --- a/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt +++ b/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt @@ -17,10 +17,14 @@ import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.bumptech.glide.Glide import com.woka.R import com.woka.WokaApp.Companion.userPrefs +import com.woka.advertisements.AdsRepository +import com.woka.advertisements.models.ForPage import com.woka.audiobooks.views.AudioBooksActivity +import com.woka.database.helpers.AdClicksHelper import com.woka.database.helpers.ClicksHelper import com.woka.database.models.ContentType import com.woka.databinding.FragmentHome1Binding @@ -40,9 +44,9 @@ import com.woka.utils.changeLocale 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.WebSeriesActivity import com.woka.wokagames.views.GamesActivity +import kotlinx.coroutines.launch class Home1Fragment : Fragment() { @@ -62,6 +66,7 @@ class Home1Fragment : Fragment() { private lateinit var progressView: ProgressView private val clickHelper = ClicksHelper + private val adClickHelper = AdClicksHelper override fun onAttach(context: Context) { super.onAttach(context) @@ -88,6 +93,8 @@ class Home1Fragment : Fragment() { clickEvents() + loadAds() + return binding.root } @@ -219,12 +226,6 @@ class Home1Fragment : Fragment() { liveTv.performClick() } - adView.setOnClickListener { - startActivity( - Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com/")) - ) - } - more.setOnClickListener { activity?.let { startActivity( @@ -522,6 +523,36 @@ class Home1Fragment : Fragment() { } } + private fun loadAds(){ + lifecycleScope.launch { + AdsRepository.getADs()?.let {ads -> + val homeAds = ads.filter { it.for_page == ForPage.THEME_1.value } + + if (homeAds.isNotEmpty()){ + val adDetails = homeAds.first() + + adClickHelper.addImpression(adDetails.id) + + adDetails.banner_image?.let {url -> + binding.imgAd.loadImage(url) + binding.adView.show() + } + + adDetails.ad_link?.let {url -> + binding.adView.setOnClickListener { + + adClickHelper.addClick(adDetails.id) + + startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(url)) + ) + } + } + } + } + } + } + companion object { fun getInstance() = Home1Fragment() } diff --git a/app/src/main/java/com/woka/home/viewmodels/HomeViewModel.kt b/app/src/main/java/com/woka/home/viewmodels/HomeViewModel.kt index 14ca837..58b19ec 100644 --- a/app/src/main/java/com/woka/home/viewmodels/HomeViewModel.kt +++ b/app/src/main/java/com/woka/home/viewmodels/HomeViewModel.kt @@ -110,6 +110,7 @@ class HomeViewModel : ViewModel() { viewModelScope.launch { _logoutLiveData.postValue(ApiResult.Loading()) RemoteSync.syncClickEvents() + RemoteSync.syncAdClickEvents() _logoutLiveData.postValue(UserRepository.logout()) } } diff --git a/app/src/main/java/com/woka/home/views/HomeActivity.kt b/app/src/main/java/com/woka/home/views/HomeActivity.kt index 0574878..3a1ad44 100644 --- a/app/src/main/java/com/woka/home/views/HomeActivity.kt +++ b/app/src/main/java/com/woka/home/views/HomeActivity.kt @@ -15,6 +15,7 @@ import android.net.Network import android.net.NetworkRequest import android.os.Build import android.os.Bundle +import android.util.Log import android.view.HapticFeedbackConstants import android.view.View.GONE import android.view.View.VISIBLE @@ -165,7 +166,7 @@ class HomeActivity : WokaBaseActivity(), decisionDialog.setPositiveButton(getString(R.string.yes)){ lifecycleScope.launch { RemoteSync.syncClickEvents() - + RemoteSync.syncAdClickEvents() @Suppress("DEPRECATION") super.onBackPressed() } diff --git a/app/src/main/java/com/woka/karaoke/views/KaraokeActivity.kt b/app/src/main/java/com/woka/karaoke/views/KaraokeActivity.kt index cc6d41a..5b44ddc 100644 --- a/app/src/main/java/com/woka/karaoke/views/KaraokeActivity.kt +++ b/app/src/main/java/com/woka/karaoke/views/KaraokeActivity.kt @@ -6,6 +6,7 @@ import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable +import android.net.Uri import android.os.Bundle import android.text.Html import android.view.WindowManager @@ -15,10 +16,15 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.SimpleItemAnimator +import com.bumptech.glide.Glide import com.google.android.material.appbar.CollapsingToolbarLayout import com.woka.R import com.woka.WokaApp.Companion.userPrefs +import com.woka.advertisements.AdsRepository +import com.woka.advertisements.models.ForPage +import com.woka.database.helpers.AdClicksHelper import com.woka.database.helpers.ClicksHelper import com.woka.database.models.ContentType import com.woka.databinding.ActivityKaraokeBinding @@ -41,6 +47,7 @@ import com.woka.utils.setVisibility import com.woka.utils.shareWokaApp import com.woka.utils.show import com.woka.utils.toast +import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale @@ -62,6 +69,8 @@ class KaraokeActivity : WokaBaseActivity() { private lateinit var playerLauncher: ActivityResultLauncher + private var adLoaded = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityKaraokeBinding.inflate(layoutInflater) @@ -103,6 +112,8 @@ class KaraokeActivity : WokaBaseActivity() { setObservers() + loadAds() + if (!viewModel.karaokeLiveData.isInitialized) { viewModel.loadKaraokeSongs() } @@ -192,6 +203,8 @@ class KaraokeActivity : WokaBaseActivity() { } private fun loadTrailerData(karaokeData: KaraokeData) { + if (adLoaded) return + binding.apply { trailerView.show() adjustTrailerImage() @@ -542,4 +555,45 @@ class KaraokeActivity : WokaBaseActivity() { } } } + + private fun loadAds(){ + lifecycleScope.launch { + AdsRepository.getADs()?.let { ads -> + val theme1ads = ads.filter { it.for_page == ForPage.KARAOKE.value} + + if (theme1ads.isNotEmpty()){ + val adDetails = theme1ads.first() + + AdClicksHelper.addImpression(adDetails.id) + + adLoaded = true + + binding.trailerBtn.hide() + binding.trailerName.hide() + + binding.adButtonView.show() + + binding.trailerView.show() + adjustTrailerImage() + + adDetails.banner_image?.let {url -> + binding.trailerImage.loadImage(url) + } + + adDetails.button_image?.let {url -> + binding.adButtonImg.loadImage(url) + } + + adDetails.ad_link?.let {url -> + binding.adBtn.setOnClickListener { + AdClicksHelper.addClick(adDetails.id) + startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(url)) + ) + } + } + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/woka/shop/views/fragments/shop/ShopFragment1.kt b/app/src/main/java/com/woka/shop/views/fragments/shop/ShopFragment1.kt index 52ad2e2..0d08a9a 100644 --- a/app/src/main/java/com/woka/shop/views/fragments/shop/ShopFragment1.kt +++ b/app/src/main/java/com/woka/shop/views/fragments/shop/ShopFragment1.kt @@ -1,13 +1,19 @@ package com.woka.shop.views.fragments.shop +import android.content.Intent +import android.net.Uri import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import com.woka.R +import com.woka.advertisements.AdsRepository +import com.woka.advertisements.models.ForPage +import com.woka.database.helpers.AdClicksHelper import com.woka.database.helpers.ClicksHelper import com.woka.database.models.ContentType import com.woka.databinding.FragmentShop1Binding @@ -17,6 +23,7 @@ import com.woka.shop.models.BaseCategory import com.woka.shop.viewmodels.ShopViewModel import com.woka.utils.hide import com.woka.utils.show +import kotlinx.coroutines.launch class ShopFragment1 : Fragment() { @@ -24,6 +31,9 @@ class ShopFragment1 : Fragment() { private lateinit var viewModel: ShopViewModel private lateinit var adapter: CategoryAdapter + private var adId: Int? = null + private var adLink: String? = null + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -67,9 +77,25 @@ class ShopFragment1 : Fragment() { ClicksHelper.upsertClickEvent(ContentType.OTHERS) } - adapter.onCategoryClickListener = {id, _ -> - findNavController().navigate(ShopFragment1Directions.actionShopFragment1ToShopFragment2(id)) - ClicksHelper.upsertClickEvent(ContentType.SHOP_PRODUCT, id) + adapter.onCategoryClickListener = { id, _ -> + if (id == -1){ + // ad clicked + AdClicksHelper.addClick(adId) + startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse(adLink) + ) + ) + }else{ + // normal click + findNavController().navigate( + ShopFragment1Directions.actionShopFragment1ToShopFragment2( + id + ) + ) + ClicksHelper.upsertClickEvent(ContentType.SHOP_PRODUCT, id) + } } } } @@ -92,16 +118,19 @@ class ShopFragment1 : Fragment() { is ApiResult.Success -> { it.data?.let { categoryList -> - adapter.submitList(categoryList.map { category -> + val baseCategoryList = categoryList.map { category -> BaseCategory( category.id, category.super_category_thumbnail, category.super_category_name ) - } - ) { + }.toMutableList() + + adapter.submitList(baseCategoryList) { rvSuperCategory.show() + loadAds(baseCategoryList) + shimmer.hide() errorView.hide() } @@ -112,8 +141,23 @@ class ShopFragment1 : Fragment() { } } - companion object { - @JvmStatic - fun newInstance() = ShopFragment1() + private fun loadAds(categoryList: MutableList) { + lifecycleScope.launch { + AdsRepository.getADs()?.let { ads -> + val superShopAds = ads.filter { it.for_page == ForPage.SHOP_SUPER_CATEGORY.value } + + if (superShopAds.isNotEmpty()) { + val adDetails = superShopAds.first() + + AdClicksHelper.addImpression(adDetails.id) + adId = adDetails.id + + categoryList.add(0, BaseCategory(-1, adDetails.banner_image, adDetails.title)) + adLink = adDetails.ad_link + + adapter.notifyItemInserted(0) + } + } + } } } \ 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 bc441af..7752024 100644 --- a/app/src/main/java/com/woka/userPreference/UserPreference.kt +++ b/app/src/main/java/com/woka/userPreference/UserPreference.kt @@ -8,13 +8,17 @@ import android.content.SharedPreferences import android.provider.Settings.Secure import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import com.woka.R import com.woka.home.models.Theme import com.woka.home.mylist.MyListRepository import com.woka.networking.ApiResult import com.woka.onboard.views.OnboardActivity +import com.woka.shop.ShopRepository import com.woka.userdata.UserRepository import com.woka.userdata.userDataModels.UserData import com.woka.userdata.userDataModels.UserDataResponse +import com.woka.utils.DecisionDialog +import com.woka.utils.SessionExpiredActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -108,8 +112,8 @@ class UserPreference(val context: Context) { _userLiveData.postValue(null) if (openLoginScreen){ - context.startActivity(Intent(context, OnboardActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + context.startActivity(Intent(context, SessionExpiredActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) } @@ -118,5 +122,6 @@ class UserPreference(val context: Context) { private fun clearData(){ MyListRepository.clearData() + ShopRepository.clearCart() } } \ No newline at end of file diff --git a/app/src/main/java/com/woka/utils/AdiImageView.kt b/app/src/main/java/com/woka/utils/AdiImageView.kt index 50afa15..40cfad6 100644 --- a/app/src/main/java/com/woka/utils/AdiImageView.kt +++ b/app/src/main/java/com/woka/utils/AdiImageView.kt @@ -29,9 +29,6 @@ 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 @@ -69,7 +66,7 @@ class AdiImageView: FrameLayout { } - fun loadImage(url: String?) { + fun loadImage(url: String?, successListener: (() -> Unit)? = null) { imageView?.let { progressView?.show() @@ -96,7 +93,7 @@ class AdiImageView: FrameLayout { isFirstResource: Boolean ): Boolean { progressView?.hide() - onLoadSuccessListener?.invoke() + successListener?.invoke() return false } }) diff --git a/app/src/main/java/com/woka/utils/DecisionDialog.kt b/app/src/main/java/com/woka/utils/DecisionDialog.kt index a73d3cc..f2cd238 100644 --- a/app/src/main/java/com/woka/utils/DecisionDialog.kt +++ b/app/src/main/java/com/woka/utils/DecisionDialog.kt @@ -83,6 +83,10 @@ class DecisionDialog(context: Context) { } } + fun setCancelable(cancelable: Boolean){ + dialog.setCancelable(cancelable) + } + fun show(titleTxt: String, messageTxt: String): DecisionDialog{ binding.apply { title.text = titleTxt diff --git a/app/src/main/java/com/woka/utils/SessionExpiredActivity.kt b/app/src/main/java/com/woka/utils/SessionExpiredActivity.kt new file mode 100644 index 0000000..0e6c2f2 --- /dev/null +++ b/app/src/main/java/com/woka/utils/SessionExpiredActivity.kt @@ -0,0 +1,36 @@ +package com.woka.utils + +import android.content.Intent +import android.os.Bundle +import androidx.activity.addCallback +import androidx.activity.enableEdgeToEdge +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import com.woka.R +import com.woka.onboard.views.OnboardActivity + +class SessionExpiredActivity : WokaBaseActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContentView(R.layout.activity_session_expired) + 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 + } + + val decisionDialog = DecisionDialog(this) + decisionDialog.setCancelable(false) + + decisionDialog.setPositiveButton(getString(R.string.login)){ + startActivity(Intent(this, OnboardActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + }) + } + + decisionDialog.show(getString(R.string.session_expired), getString(R.string.please_login_again)) + + onBackPressedDispatcher.addCallback{} + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/webseries/views/fragments/WebSeriesFragment.kt b/app/src/main/java/com/woka/webseries/views/fragments/WebSeriesFragment.kt index 167f615..ecdc20a 100644 --- a/app/src/main/java/com/woka/webseries/views/fragments/WebSeriesFragment.kt +++ b/app/src/main/java/com/woka/webseries/views/fragments/WebSeriesFragment.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable +import android.net.Uri import android.os.Bundle import android.text.Html import android.view.LayoutInflater @@ -17,12 +18,17 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.content.res.AppCompatResources import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.SimpleItemAnimator +import com.bumptech.glide.Glide 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.advertisements.AdsRepository +import com.woka.advertisements.models.ForPage +import com.woka.database.helpers.AdClicksHelper import com.woka.database.helpers.ClicksHelper import com.woka.database.models.ContentType import com.woka.databinding.DialogContinueEpisodeBinding @@ -45,6 +51,7 @@ import com.woka.webseries.adapters.WebSeriesShowAdapter import com.woka.webseries.models.ContinueEpisodeData import com.woka.webseries.models.ShowData import com.woka.webseries.viewmodel.WebSeriesViewModel +import kotlinx.coroutines.launch class WebSeriesFragment : Fragment() { @@ -103,6 +110,8 @@ class WebSeriesFragment : Fragment() { setObservers() + loadAds() + if (!viewModel.showCategoryLiveData.isInitialized) { viewModel.loadCategories() } @@ -379,6 +388,44 @@ class WebSeriesFragment : Fragment() { } } + private fun loadAds(){ + lifecycleScope.launch { + AdsRepository.getADs()?.let { ads -> + val theme1ads = ads.filter { it.for_page == ForPage.WEB_SERIES.value} + + if (theme1ads.isNotEmpty()){ + val adDetails = theme1ads.first() + + AdClicksHelper.addImpression(adDetails.id) + + binding.trailerBtn.hide() + binding.trailerTxt.hide() + + binding.adButtonView.show() + + adDetails.banner_image?.let {url -> + Glide.with(binding.masilaImage) + .load(url) + .into(binding.masilaImage) + } + + adDetails.button_image?.let {url -> + binding.adButtonImg.loadImage(url) + } + + adDetails.ad_link?.let {url -> + binding.adBtn.setOnClickListener { + AdClicksHelper.addClick(adDetails.id) + startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(url)) + ) + } + } + } + } + } + } + private fun onShowClicked(showData: ShowData, categoryId: String) { findNavController().navigate( WebSeriesFragmentDirections.actionWebSeriesFragment2ToWebShowFragment( diff --git a/app/src/main/java/com/woka/wokagames/views/GamesActivity.kt b/app/src/main/java/com/woka/wokagames/views/GamesActivity.kt index ee32a4c..a3a3460 100644 --- a/app/src/main/java/com/woka/wokagames/views/GamesActivity.kt +++ b/app/src/main/java/com/woka/wokagames/views/GamesActivity.kt @@ -6,6 +6,7 @@ import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable +import android.net.Uri import android.os.Bundle import android.text.Html import android.view.WindowManager @@ -13,10 +14,14 @@ import androidx.activity.enableEdgeToEdge import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.SimpleItemAnimator import com.google.android.material.appbar.CollapsingToolbarLayout import com.woka.R import com.woka.WokaApp.Companion.userPrefs +import com.woka.advertisements.AdsRepository +import com.woka.advertisements.models.ForPage +import com.woka.database.helpers.AdClicksHelper import com.woka.database.helpers.ClicksHelper import com.woka.database.models.ContentType import com.woka.databinding.ActivityGamesBinding @@ -38,6 +43,7 @@ import com.woka.wokagames.playerr.GamePlayerActivity import com.woka.wokagames.playerr.GamePlayerActivity.Companion.EXTRA_GAME_PLAYER_DATA import com.woka.wokagames.playerr.GamePlayerData import com.woka.wokagames.viewmodels.GamesViewModel +import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale @@ -55,6 +61,8 @@ class GamesActivity : WokaBaseActivity() { private lateinit var noSignInDialog: NoSignInDialog + private var adLoaded = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityGamesBinding.inflate(layoutInflater) @@ -90,6 +98,8 @@ class GamesActivity : WokaBaseActivity() { setObservers() + loadAds() + if (!viewModel.gamesLiveData.isInitialized){ viewModel.loadGames() } @@ -170,6 +180,8 @@ class GamesActivity : WokaBaseActivity() { } private fun loadTrailerData(gameData: GameData) { + if (adLoaded) return + binding.apply { trailerView.show() adjustTrailerImage() @@ -432,4 +444,45 @@ class GamesActivity : WokaBaseActivity() { } } } + + private fun loadAds(){ + lifecycleScope.launch { + AdsRepository.getADs()?.let { ads -> + val theme1ads = ads.filter { it.for_page == ForPage.GAMES.value} + + if (theme1ads.isNotEmpty()){ + val adDetails = theme1ads.first() + + AdClicksHelper.addImpression(adDetails.id) + + adLoaded = true + + binding.trailerBtn.hide() + binding.trailerName.hide() + + binding.adButtonView.show() + + binding.trailerView.show() + adjustTrailerImage() + + adDetails.banner_image?.let {url -> + binding.trailerImage.loadImage(url) + } + + adDetails.button_image?.let {url -> + binding.adButtonImg.loadImage(url) + } + + adDetails.ad_link?.let {url -> + binding.adBtn.setOnClickListener { + AdClicksHelper.addClick(adDetails.id) + startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(url)) + ) + } + } + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_audio_books.xml b/app/src/main/res/layout/activity_audio_books.xml index 9d9e10c..c931869 100644 --- a/app/src/main/res/layout/activity_audio_books.xml +++ b/app/src/main/res/layout/activity_audio_books.xml @@ -202,6 +202,31 @@ /> + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_games.xml b/app/src/main/res/layout/activity_games.xml index efc81b1..f44914f 100644 --- a/app/src/main/res/layout/activity_games.xml +++ b/app/src/main/res/layout/activity_games.xml @@ -202,6 +202,31 @@ /> + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_karaoke.xml b/app/src/main/res/layout/activity_karaoke.xml index adfeb30..15c3769 100644 --- a/app/src/main/res/layout/activity_karaoke.xml +++ b/app/src/main/res/layout/activity_karaoke.xml @@ -9,72 +9,66 @@ android:orientation="vertical" tools:context=".webseries.views.WebSeriesActivity"> - + + android:layout_height="match_parent" + android:background="@color/color_primary_dark" + android:visibility="gone"> + app:shimmer_highlight_alpha="0.5"> + android:layout_height="wrap_content"> + android:orientation="vertical" + android:scrollbars="none"> + android:background="@color/white_50" /> + android:background="@color/white_50" /> + android:background="@color/white_50" /> + android:background="@color/white_50" /> + android:layout_marginVertical="15dp" + android:background="@color/white_50" /> @@ -86,20 +80,19 @@ + android:orientation="vertical" + android:visibility="gone"> @@ -127,15 +120,13 @@ + android:background="@color/color_primary_dark"> + android:visibility="gone"> + android:contentDescription="@string/image" + android:scaleType="fitXY" + android:src="@drawable/img_masila_full" + app:layout_collapseMode="parallax" /> + app:layout_collapseMode="pin"> + android:orientation="vertical" + android:padding="15dp"> @@ -190,18 +179,43 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/sing" + android:layout_marginTop="15dp" + android:background="@drawable/play_btn_bg" android:fontFamily="@font/exo_2_bold" + android:paddingHorizontal="25dp" + + android:text="@string/sing" android:textColor="@color/white" + android:textSize="@dimen/_12ssp" - android:paddingHorizontal="25dp" - android:layout_marginTop="15dp" - - android:background="@drawable/play_btn_bg" - /> + + + + + + + + + + @@ -223,91 +237,89 @@ + android:orientation="horizontal" + android:visibility="gone" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/continue_show_view_holder" /> + tools:listitem="@layout/show_view_holder" />