Skip to content

Instantly share code, notes, and snippets.

@eldartsa
Created August 5, 2021 09:46
Show Gist options
  • Save eldartsa/ba3e0f47e279d21242aa09b1c51e863f to your computer and use it in GitHub Desktop.
Save eldartsa/ba3e0f47e279d21242aa09b1c51e863f to your computer and use it in GitHub Desktop.
First Gist
package com.name.activities
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import android.widget.RelativeLayout
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.bundleOf
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.addRepeatingJob
import androidx.navigation.fragment.NavHostFragment
import androidx.viewbinding.ViewBinding
import com.ironsource.mediationsdk.IronSource
import com.name.R
import com.name.customViews.CustomHeaderView
import com.name.dataClasses.HeaderItem
import com.name.dataClasses.Point
import com.name.enums.EAlertDialogType
import com.name.enums.ECountAnimAction
import com.name.helpers.LocaleHelper
import com.name.utils.MyUtils
import com.name.utils.SnappLog
import com.name.utils.constants.General
import com.name.utils.extensions.coinsToHeaderAnimation
import com.name.utils.extensions.getNonNullBundle
import com.name.utils.extensions.navigateSafe
import kotlinx.coroutines.flow.collect
import java.util.*
/**
* Created by Eldar Tsafar on 13/10/2017.
*/
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
private val baseActivitySharedViewModel: BaseActivitySharedViewModel by viewModels()
private val baseActivityViewModel: BaseActivityViewModel by viewModels()
private var _binding: ViewBinding? = null
abstract val bindingInflater: (LayoutInflater) -> VB
protected val navHostFragment
get() = (supportFragmentManager.findFragmentById(getNavHostId()) as NavHostFragment)
protected val currentFragmentId
get() = navHostFragment.navController.currentDestination?.id ?: 0
@Suppress("UNCHECKED_CAST")
protected val binding: VB
get() = _binding as VB
abstract fun getNavHostId(): Int
abstract fun getHeaderView(): CustomHeaderView?
abstract fun getBundle(bundle: Bundle)
abstract fun initView()
abstract fun subscribeToViewModel()
override fun attachBaseContext(newBase: Context) {
SnappLog.log("${javaClass.simpleName}-> attachBaseContext")
super.attachBaseContext(LocaleHelper.onAttach(newBase))
}
@SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
SnappLog.log("${javaClass.simpleName}-> onCreate")
setTheme(R.style.AppTheme)
_binding = bindingInflater.invoke(layoutInflater)
val view = binding.root
setContentView(view)
val screenLayoutSize: Int = resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK
if (screenLayoutSize == Configuration.SCREENLAYOUT_SIZE_SMALL || screenLayoutSize == Configuration.SCREENLAYOUT_SIZE_NORMAL) {
if (!General.IS_DEBUG_MODE) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
getBundle(intent.extras.getNonNullBundle())
initView()
setHeaderViewCallbacks()
subscribeToViewModel()
subscribeBaseActivitySharedViewModel()
}
private fun subscribeBaseActivitySharedViewModel() {
addRepeatingJob(Lifecycle.State.RESUMED) {
baseActivitySharedViewModel.baseActivitySharedViewModelFragment.collect { event ->
when (event) {
is BaseActivitySharedViewModel.BaseActivitySharedViewModelEvent.BlockScreen -> {
blockScreen(event.isBlocked)
}
is BaseActivitySharedViewModel.BaseActivitySharedViewModelEvent.UpdateHeaderContent -> {
updateHeaderContent(event.headerItem)
}
is BaseActivitySharedViewModel.BaseActivitySharedViewModelEvent.UpdateCoinsAmount -> {
updateHeaderCoins(event.coinsAmount)
}
is BaseActivitySharedViewModel.BaseActivitySharedViewModelEvent.ShowCoinsAnimation -> {
showCoinsAnimation(event.amount, event.countAnimAction, event.coinsAmount, event.shouldShowCoinsSpreadAnimation)
}
}
}
}
}
/**
* Header view callbacks
*/
private fun setHeaderViewCallbacks() {
getHeaderView()?.setCallback(object : CustomHeaderView.HeaderCallback {
override fun onCoinsClicked() {
navigateToMarket()
}
override fun onBackClicked() {
onBackPressed()
}
})
}
private fun navigateToMarket() {
navigateSafe(viewId = R.id.nav_host_fragment, resId = R.id.action_global_marketFragment, currentFragmentId = currentFragmentId)
}
/**
* Init header content
*/
private fun updateHeaderContent(headerItem: HeaderItem?) {
val headerView = getHeaderView()
headerView?.let {
headerView.initView(headerItem)
}
}
/**
* Update header coins
*/
private fun updateHeaderCoins(coinsAmount: Int) {
val headerView = getHeaderView()
headerView?.updateCoins(coinsAmount)
}
/**\
* Update header visibility
*/
protected fun showCustomHeaderLayout(shouldShow: Boolean) {
val headerView = getHeaderView()
headerView?.let {
if (shouldShow) {
headerView.visibility = View.VISIBLE
} else {
headerView.visibility = View.GONE
}
}
}
/**
* Block screen for touch
*/
private fun blockScreen(shouldBlock: Boolean) {
if (shouldBlock) {
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
}
}
/**
* Show coins animation in header
*/
private fun showCoinsAnimation(amount: Int, countAnimAction: ECountAnimAction, coinsAmount: Int, shouldShowCoinsSpreadAnimation: Boolean) {
val headerView = getHeaderView()
headerView?.let {
it.updateCoins(coinsAmount)
it.showCoinsAnimation(amount, countAnimAction)
if (shouldShowCoinsSpreadAnimation) {
val desPoint = headerView.getCoinsTvPositionPoint()
animateCoins(desPoint, coinsAmount)
}
}
}
private fun animateCoins(desPoint: Point, coinsAmount: Int) {
val coinSize = resources.getDimensionPixelSize(R.dimen.header_coins_animation_coin_size)
val maximumCoinsAmount = 30
val range = resources.getDimensionPixelOffset(R.dimen.header_coins_animation_range)
val screenCenterX = MyUtils.getScreenWidth() / 2
val screenCenterY = MyUtils.getScreenHeight() / 2
var coinsAmountForAnimation = coinsAmount / 10
if (coinsAmountForAnimation > maximumCoinsAmount) {
coinsAmountForAnimation = maximumCoinsAmount
}
val rnd = Random()
for (i in 0..coinsAmountForAnimation) {
val coinIv = ImageView(this)
val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(coinSize, coinSize)
coinIv.layoutParams = layoutParams
coinIv.setImageResource(R.drawable.ic_coin)
val randomX = (screenCenterX - range / 2) + rnd.nextInt(range)
val randomY = (screenCenterY - range / 2) + rnd.nextInt(range)
coinIv.x = randomX.toFloat()
coinIv.y = randomY.toFloat()
val parentView = binding.root as ViewGroup
parentView.addView(coinIv)
val animDuration = rnd.nextInt(500) + 1000
coinIv.coinsToHeaderAnimation(parentView, desPoint.x, desPoint.y, animDuration.toLong()) {
baseActivityViewModel.onCoinsToHeaderAnimationEnd()
}
}
}
protected fun showAlertDialog(title: String? = null, message: String? = null, positiveButton: String? = null, negativeButton: String? = null,
titleImageRes: Int = 0, isDialogCancelable: Boolean = true, dialogType: EAlertDialogType = EAlertDialogType.NON) {
val args = bundleOf("title" to title, "message" to message, "positiveButton" to positiveButton, "negativeButton" to negativeButton,
"titleImageRes" to titleImageRes, "isDialogCancelable" to isDialogCancelable, "dialogType" to dialogType)
navigateSafe(viewId = getNavHostId(), resId = R.id.action_global_alertDialogFragment, currentFragmentId = currentFragmentId, args = args)
}
override fun onStart() {
super.onStart()
baseActivityViewModel.startTimer()
}
override fun onStop() {
super.onStop()
baseActivityViewModel.stopTimer()
}
override fun onResume() {
super.onResume()
SnappLog.log("${javaClass.simpleName}-> onResume")
IronSource.onResume(this)
}
override fun onPause() {
super.onPause()
SnappLog.log("${javaClass.simpleName}-> onPause")
IronSource.onPause(this)
}
}
package com.name.activities
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.addRepeatingJob
import com.gameanalytics.sdk.GameAnalytics
import com.name.R
import com.name.dataClasses.EventDialogData
import com.name.databinding.ActivityMainBinding
import com.name.enums.EEventDialogType
import com.name.helpers.LocaleHelper
import com.name.helpers.NavigationHelper
import com.name.managers.*
import com.name.managers.adsManager.AdsManager
import com.name.managers.adsManager.BannerHelper
import com.name.utils.FlavorConsts
import com.name.utils.constants.CoinsAmount
import com.name.utils.extensions.*
import com.tenjin.android.TenjinSDK
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import java.util.*
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : BaseActivity<ActivityMainBinding>() {
companion object {
const val SHOULD_AVOID_INTERSTITIAL_RESULT_PARAM = "shouldAvoidInterstitial"
}
override val bindingInflater: (LayoutInflater) -> ActivityMainBinding
get() = ActivityMainBinding::inflate
private val mainActivityViewModel: MainActivityViewModel by viewModels()
private val mainSharedViewModel: MainSharedViewModel by viewModels()
@Inject
lateinit var purchaseManager: PurchaseManager
@Inject
lateinit var adsManager: AdsManager
@Inject
lateinit var actionManager: ActionManager
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
mainActivityViewModel.onNewIntent(intent)
}
override fun getNavHostId() = binding.navHostFragment.id
override fun getHeaderView() = binding.headerView
override fun getBundle(bundle: Bundle) {
}
override fun initView() {
mainActivityViewModel.init()
binding.adViewContainer.layoutParams.height = BannerHelper.getAdmobBannerSize(this).getHeightInPixels(this)
initGameAnalytics()
}
private fun initGameAnalytics() {
// Enable log
GameAnalytics.setEnabledInfoLog(true)
GameAnalytics.setEnabledVerboseLog(true)
// Configure build version
GameAnalytics.configureAutoDetectAppVersion(true)
GameAnalytics.initialize(this, getString(R.string.game_analytics_game_key), getString(R.string.game_analytics_secret_key))
}
private fun firstNavigation(eStartDestination: MainActivityViewModel.EStartDestination) {
val navController = navHostFragment.navController
val inflater = navController.navInflater
val graph = inflater.inflate(R.navigation.nav_main)
when (eStartDestination) {
MainActivityViewModel.EStartDestination.SPLASH -> {
// no need to make a change!!
graph.startDestination = R.id.splashFragment
navController.graph = graph
}
MainActivityViewModel.EStartDestination.CHOOSE_DB_TYPE -> {
graph.startDestination = R.id.chooseDbTypeFragment
navController.graph = graph
}
}
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.splashFragment -> {
showCustomHeaderLayout(shouldShow = false)
showBannerContainer(canShowByPremium = actionManager.shouldShowAds(), canShowByDestination = false)
}
R.id.chooseDbTypeFragment -> {
showCustomHeaderLayout(shouldShow = true)
showBannerContainer(canShowByPremium = actionManager.shouldShowAds(), canShowByDestination = false)
}
else -> {
showCustomHeaderLayout(shouldShow = true)
showBannerContainer(canShowByPremium = actionManager.shouldShowAds(), canShowByDestination = true)
}
}
}
}
override fun subscribeToViewModel() {
addRepeatingJob(Lifecycle.State.CREATED) {
mainActivityViewModel.mainActivityViewModelEvent.collect { event ->
when (event) {
MainActivityViewModel.MainActivityViewModelEvent.ShowWelcomeDialog -> {
navigateSafe(viewId = R.id.nav_host_fragment, resId = R.id.action_global_welcomeDialogFragment, currentFragmentId = currentFragmentId)
}
is MainActivityViewModel.MainActivityViewModelEvent.NavigateToStartDestination -> {
firstNavigation(event.eStartDestination)
}
is MainActivityViewModel.MainActivityViewModelEvent.UpdateLanguage -> {
adsManager.onDestroy()
LocaleHelper.setLocale(this@MainActivity, event.languageCode)
NavigationHelper.restartActivity(this@MainActivity)
}
is MainActivityViewModel.MainActivityViewModelEvent.ShowEventDialog -> {
showEventDialog(event.eventDialogData)
}
MainActivityViewModel.MainActivityViewModelEvent.ShowInterstitialAd -> {
adsManager.showInterstitialAd()
}
is MainActivityViewModel.MainActivityViewModelEvent.NavigateToUrlAndCloseApp -> {
finish()
NavigationHelper.openUrl(this@MainActivity, event.url)
}
MainActivityViewModel.MainActivityViewModelEvent.InitAdsManager -> {
adsManager.init(this@MainActivity, lifecycle)
}
MainActivityViewModel.MainActivityViewModelEvent.InitTenjin -> {
val instance = TenjinSDK.getInstance(this@MainActivity, FlavorConsts.TENJIN_SDK_KEY)
instance.connect()
}
}
}
}
addRepeatingJob(Lifecycle.State.RESUMED) {
mainSharedViewModel.gamePlayChannel.collect { event ->
when (event) {
is MainSharedViewModel.GamePlayEvent.AddLevelFinishDialog -> {
addLevelFinishDialog(event.levelNum)
}
is MainSharedViewModel.GamePlayEvent.AddTopicFinishDialog -> {
addTopicFinishDialog(event.topicName)
}
is MainSharedViewModel.GamePlayEvent.AddGameFinishDialog -> {
addGameFinishDialog()
}
is MainSharedViewModel.GamePlayEvent.AddLevelsUnlockDialog -> {
addLevelsUnlockDialog(event.levels)
}
is MainSharedViewModel.GamePlayEvent.AddTopicUnlockDialog -> {
addTopicUnlockedDialog(event.topicName)
}
is MainSharedViewModel.GamePlayEvent.ShowDialogs -> {
mainActivityViewModel.showEventDialog(event.shouldAvoidInterstitial)
}
MainSharedViewModel.GamePlayEvent.CheckWelcome -> {
mainActivityViewModel.checkWelcomeDialog()
}
is MainSharedViewModel.GamePlayEvent.OnQuestionSolved -> {
// GoogleGameHelper.onQuestionSolved(this@MainActivity, event.topicType, event.solvedQuestionsInGame)
}
}
}
}
addRepeatingJob(Lifecycle.State.RESUMED) {
adsManager.bannerAdsEvent.collect { event ->
when (event) {
is AdsManager.BannerAdsEvent.OnBannerCreated -> {
onBannerCreated(event.adView)
}
is AdsManager.BannerAdsEvent.OnBannerHeightMeasured -> {
setBannerHeight(event.bannerHeight)
}
is AdsManager.BannerAdsEvent.ForceShowBannerContainer -> {
showBannerContainer(event.shouldShow)
}
else -> return@collect
}
}
}
}
private fun setBannerHeight(bannerHeight: Int) {
binding.adViewContainer.layoutParams.height = bannerHeight
binding.adViewContainer.requestLayout()
}
private fun showEventDialog(eventDialogData: EventDialogData) {
getNavigationDialogResult<Boolean>(R.id.nav_host_fragment, currentFragmentId, SHOULD_AVOID_INTERSTITIAL_RESULT_PARAM) { shouldAvoidInterstitial ->
mainActivityViewModel.showEventDialog(shouldAvoidInterstitial)
}
navigateSafe(viewId = R.id.nav_host_fragment,
resId = R.id.action_global_eventDialogFragment,
currentFragmentId = currentFragmentId,
args = bundleOf("eventDialogData" to eventDialogData))
}
private fun onBannerCreated(bannerView: View) {
binding.adViewContainer.apply {
bannerView.parent?.let {
(bannerView.parent as ViewGroup).removeView(bannerView)
}
addView(bannerView)
}
}
private fun showBannerContainer(canShowByPremium: Boolean, canShowByDestination: Boolean = true) {
if (canShowByPremium && canShowByDestination) {
binding.adViewContainer.visibility = View.VISIBLE
} else {
binding.adViewContainer.visibility = View.GONE
}
}
private fun addLevelsUnlockDialog(levels: List<Int>) {
val eventDialogData = EventDialogData(eventDialogType = EEventDialogType.LEVEL_UNLOCK,
title = getString(R.string.level_unlocked_dialog_title),
subTitle = levels.getUnlockLevelsText(this),
backgroundColor = ContextCompat.getColor(this, R.color.dark_gray),
rewardAmount = CoinsAmount.LEVEL_UNLOCKED)
mainActivityViewModel.addEventDialogData(eventDialogData)
}
private fun addLevelFinishDialog(levelNum: Int) {
val eventDialogData = EventDialogData(eventDialogType = EEventDialogType.LEVEL_FINISH,
title = getString(R.string.level_unlocked_dialog_title),
subTitle = getString(R.string.level_finished_dialog_message, levelNum),
backgroundColor = ContextCompat.getColor(this, R.color.dark_gray),
rewardAmount = CoinsAmount.LEVEL_FINISHED)
mainActivityViewModel.addEventDialogData(eventDialogData)
}
private fun addTopicUnlockedDialog(topicName: String) {
val eventDialogData = EventDialogData(eventDialogType = EEventDialogType.TOPIC_UNLOCK,
title = getString(R.string.topic_unlocked_dialog_title),
subTitle = getString(R.string.topic_unlocked_dialog_message, topicName),
backgroundColor = ContextCompat.getColor(this, R.color.dark_gray),
rewardAmount = CoinsAmount.LEVEL_FINISHED)
mainActivityViewModel.addEventDialogData(eventDialogData)
}
private fun addTopicFinishDialog(topicName: String) {
val eventDialogData = EventDialogData(eventDialogType = EEventDialogType.TOPIC_FINISH,
title = getString(R.string.topic_finished_dialog_title),
subTitle = getString(R.string.topic_finished_dialog_message, topicName),
backgroundColor = ContextCompat.getColor(this, R.color.dark_gray),
rewardAmount = CoinsAmount.LEVEL_FINISHED)
mainActivityViewModel.addEventDialogData(eventDialogData)
}
private fun addGameFinishDialog() {
}
@Suppress("DEPRECATION")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (!purchaseManager.bp.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data)
}
}
override fun onResume() {
super.onResume()
mainActivityViewModel.onResume()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment