Update Feature Flags to use Firebase
This commit is contained in:
parent
460e92bee2
commit
543adc0d68
13 changed files with 126 additions and 39 deletions
|
@ -488,8 +488,8 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
val isNotInstant = !instantAppManager.isEnabled(applicationContext)
|
||||
findItem(R.id.share_now).isVisible = isNotInstant
|
||||
findItem(R.id.remove_ads).isVisible = !preferencesRepository.isPremiumEnabled() && isNotInstant
|
||||
findItem(R.id.previous_games).isVisible = featureFlagManager.isGameHistoryEnabled()
|
||||
findItem(R.id.rate).isVisible = featureFlagManager.isRateUsEnabled()
|
||||
findItem(R.id.previous_games).isVisible = featureFlagManager.isGameHistoryEnabled
|
||||
findItem(R.id.rate).isVisible = featureFlagManager.isRateUsEnabled
|
||||
|
||||
if (!playGamesManager.hasGooglePlayGames()) {
|
||||
removeGroup(R.id.play_games_group)
|
||||
|
@ -928,7 +928,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
}
|
||||
|
||||
private fun refreshAds() {
|
||||
if (featureFlagManager.isInAppAdsEnabled()) {
|
||||
if (featureFlagManager.isInAppAdsEnabled) {
|
||||
val isTutorialComplete = preferencesRepository.isTutorialCompleted()
|
||||
if (isTutorialComplete && !preferencesRepository.isPremiumEnabled() && billingManager.isEnabled()) {
|
||||
if (!instantAppManager.isEnabled(this)) {
|
||||
|
|
|
@ -27,9 +27,13 @@ val ViewModelModule = module {
|
|||
viewModel { ThemeViewModel(get(), get(), get(), get()) }
|
||||
viewModel { SplashViewModel(get(), get(), get(), get()) }
|
||||
viewModel {
|
||||
GameViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
|
||||
GameViewModel(
|
||||
get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()
|
||||
)
|
||||
}
|
||||
viewModel {
|
||||
TutorialViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
|
||||
TutorialViewModel(
|
||||
get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.core.app.ActivityCompat
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import dev.lucasnlm.antimine.GameActivity
|
||||
import dev.lucasnlm.antimine.splash.viewmodel.SplashViewModel
|
||||
import dev.lucasnlm.external.IFeatureFlagManager
|
||||
import dev.lucasnlm.external.IPlayGamesManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -16,6 +17,7 @@ import org.koin.androidx.viewmodel.ext.android.viewModel
|
|||
|
||||
class SplashActivity : AppCompatActivity() {
|
||||
private val playGamesManager: IPlayGamesManager by inject()
|
||||
private val featureFlagManager: IFeatureFlagManager by inject()
|
||||
|
||||
private val splashViewMode: SplashViewModel by viewModel()
|
||||
|
||||
|
@ -24,6 +26,10 @@ class SplashActivity : AppCompatActivity() {
|
|||
|
||||
splashViewMode.startIap()
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
featureFlagManager.refresh()
|
||||
}
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
if (playGamesManager.hasGooglePlayGames()) {
|
||||
var logged: Boolean
|
||||
|
|
|
@ -18,6 +18,7 @@ import dev.lucasnlm.antimine.preferences.IPreferencesRepository
|
|||
import dev.lucasnlm.antimine.core.sound.ISoundManager
|
||||
import dev.lucasnlm.antimine.ui.repository.IThemeRepository
|
||||
import dev.lucasnlm.antimine.tutorial.view.TutorialField
|
||||
import dev.lucasnlm.external.IFeatureFlagManager
|
||||
import dev.lucasnlm.external.IPlayGamesManager
|
||||
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -31,6 +32,7 @@ class TutorialViewModel(
|
|||
minefieldRepository: IMinefieldRepository,
|
||||
analyticsManager: IAnalyticsManager,
|
||||
playGamesManager: IPlayGamesManager,
|
||||
featureFlagManager: IFeatureFlagManager,
|
||||
tipRepository: ITipRepository,
|
||||
private val clock: Clock,
|
||||
private val context: Context,
|
||||
|
@ -48,6 +50,7 @@ class TutorialViewModel(
|
|||
analyticsManager,
|
||||
playGamesManager,
|
||||
tipRepository,
|
||||
featureFlagManager,
|
||||
clock,
|
||||
) {
|
||||
val tutorialState = MutableStateFlow(
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:minHeight="66dp"
|
||||
android:minHeight="60dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:background="@color/accent"
|
||||
|
|
|
@ -27,6 +27,7 @@ import dev.lucasnlm.antimine.preferences.models.GameControl
|
|||
import dev.lucasnlm.antimine.ui.model.AppTheme
|
||||
import dev.lucasnlm.antimine.ui.repository.IThemeRepository
|
||||
import dev.lucasnlm.external.Achievement
|
||||
import dev.lucasnlm.external.IFeatureFlagManager
|
||||
import dev.lucasnlm.external.IPlayGamesManager
|
||||
import dev.lucasnlm.external.Leaderboard
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -48,6 +49,7 @@ open class GameViewModel(
|
|||
private val analyticsManager: IAnalyticsManager,
|
||||
private val playGamesManager: IPlayGamesManager,
|
||||
private val tipRepository: ITipRepository,
|
||||
private val featureFlagManager: IFeatureFlagManager,
|
||||
private val clock: Clock,
|
||||
) : ViewModel() {
|
||||
val eventObserver = MutableLiveData<Event>()
|
||||
|
@ -308,21 +310,23 @@ open class GameViewModel(
|
|||
}
|
||||
|
||||
private fun onFeedbackAnalytics(action: ActionResponse, index: Int) {
|
||||
when (action) {
|
||||
ActionResponse.OpenTile -> {
|
||||
analyticsManager.sentEvent(Analytics.OpenTile(index))
|
||||
}
|
||||
ActionResponse.SwitchMark -> {
|
||||
analyticsManager.sentEvent(Analytics.SwitchMark(index))
|
||||
}
|
||||
ActionResponse.HighlightNeighbors -> {
|
||||
analyticsManager.sentEvent(Analytics.HighlightNeighbors(index))
|
||||
}
|
||||
ActionResponse.OpenNeighbors -> {
|
||||
analyticsManager.sentEvent(Analytics.OpenNeighbors(index))
|
||||
}
|
||||
ActionResponse.OpenOrMark -> {
|
||||
analyticsManager.sentEvent(Analytics.OpenOrFlagTile(index))
|
||||
if (featureFlagManager.isGameplayAnalyticsEnabled) {
|
||||
when (action) {
|
||||
ActionResponse.OpenTile -> {
|
||||
analyticsManager.sentEvent(Analytics.OpenTile(index))
|
||||
}
|
||||
ActionResponse.SwitchMark -> {
|
||||
analyticsManager.sentEvent(Analytics.SwitchMark(index))
|
||||
}
|
||||
ActionResponse.HighlightNeighbors -> {
|
||||
analyticsManager.sentEvent(Analytics.HighlightNeighbors(index))
|
||||
}
|
||||
ActionResponse.OpenNeighbors -> {
|
||||
analyticsManager.sentEvent(Analytics.OpenNeighbors(index))
|
||||
}
|
||||
ActionResponse.OpenOrMark -> {
|
||||
analyticsManager.sentEvent(Analytics.OpenOrFlagTile(index))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,10 +113,6 @@ sealed class Analytics(
|
|||
|
||||
object UnlockIapDialog : Analytics("IAP Dialog Unlock")
|
||||
|
||||
object UnlockRewardedDialog : Analytics("Rewarded Dialog Unlock")
|
||||
|
||||
class ShowRatingRequest(usages: Int) : Analytics("Shown Rating Request", mapOf("Usages" to usages.toString()))
|
||||
|
||||
object TapRatingRequest : Analytics("Rating Request")
|
||||
|
||||
object UseTip : Analytics("Use Tip")
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
interface IFeatureFlagManager {
|
||||
fun isGameHistoryEnabled(): Boolean
|
||||
fun isRateUsEnabled(): Boolean
|
||||
fun isInAppAdsEnabled(): Boolean
|
||||
abstract class IFeatureFlagManager {
|
||||
abstract val isGameHistoryEnabled: Boolean
|
||||
abstract val isRateUsEnabled: Boolean
|
||||
abstract val isInAppAdsEnabled: Boolean
|
||||
abstract val isGameplayAnalyticsEnabled: Boolean
|
||||
|
||||
abstract suspend fun refresh()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
class FeatureFlagManager : IFeatureFlagManager {
|
||||
override fun isGameHistoryEnabled(): Boolean = true
|
||||
override fun isRateUsEnabled(): Boolean = false
|
||||
override fun isInAppAdsEnabled(): Boolean = false
|
||||
class FeatureFlagManager : IFeatureFlagManager() {
|
||||
override val isGameHistoryEnabled: Boolean = true
|
||||
override val isRateUsEnabled: Boolean = false
|
||||
override val isInAppAdsEnabled: Boolean = false
|
||||
override val isGameplayAnalyticsEnabled: Boolean = false
|
||||
|
||||
override suspend fun refresh() {
|
||||
// No Feature Flags on FOSS
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,8 @@ dependencies {
|
|||
// Firebase
|
||||
implementation 'com.google.firebase:firebase-analytics-ktx:18.0.0'
|
||||
implementation 'com.google.firebase:firebase-crashlytics:17.3.0'
|
||||
implementation 'com.google.firebase:firebase-firestore-ktx:22.0.0'
|
||||
implementation 'com.google.firebase:firebase-firestore-ktx:22.0.1'
|
||||
implementation 'com.google.firebase:firebase-config-ktx:20.0.2'
|
||||
|
||||
// Kotlin
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
|
||||
|
|
|
@ -1,7 +1,48 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
class FeatureFlagManager : IFeatureFlagManager {
|
||||
override fun isGameHistoryEnabled(): Boolean = false
|
||||
override fun isRateUsEnabled(): Boolean = true
|
||||
override fun isInAppAdsEnabled(): Boolean = false
|
||||
import com.google.android.gms.tasks.Tasks
|
||||
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class FeatureFlagManager : IFeatureFlagManager() {
|
||||
private val remoteConfig: FirebaseRemoteConfig by lazy {
|
||||
FirebaseRemoteConfig.getInstance().apply {
|
||||
setDefaultsAsync(mapOf(
|
||||
HISTORY_ENABLED to false,
|
||||
RATE_US_ENABLED to true,
|
||||
IN_APP_ADS_ENABLED to false,
|
||||
GAMEPLAY_EVENTS_ENABLED to false,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
override val isGameHistoryEnabled: Boolean by lazy {
|
||||
remoteConfig.getBoolean(HISTORY_ENABLED)
|
||||
}
|
||||
|
||||
override val isRateUsEnabled: Boolean by lazy {
|
||||
remoteConfig.getBoolean(RATE_US_ENABLED)
|
||||
}
|
||||
|
||||
override val isInAppAdsEnabled: Boolean by lazy {
|
||||
remoteConfig.getBoolean(IN_APP_ADS_ENABLED)
|
||||
}
|
||||
|
||||
override val isGameplayAnalyticsEnabled: Boolean by lazy {
|
||||
remoteConfig.getBoolean(GAMEPLAY_EVENTS_ENABLED)
|
||||
}
|
||||
|
||||
override suspend fun refresh() {
|
||||
withContext(Dispatchers.IO) {
|
||||
Tasks.await(remoteConfig.fetchAndActivate())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val HISTORY_ENABLED = "history_enabled"
|
||||
private const val RATE_US_ENABLED = "rate_us_enabled"
|
||||
private const val IN_APP_ADS_ENABLED = "in_app_ads_enabled"
|
||||
private const val GAMEPLAY_EVENTS_ENABLED = "gameplay_events_enabled"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,30 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
if (System.getenv('IS_RELEASE_BUILD')) {
|
||||
storeFile file(System.getenv('KEYSTORE'))
|
||||
keyAlias System.getenv('KEY_ALIAS')
|
||||
storePassword System.getenv('KEY_STORE_PASSWORD')
|
||||
keyPassword System.getenv('KEY_PASSWORD')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
targetCompatibility 1.8
|
||||
sourceCompatibility 1.8
|
||||
|
|
|
@ -6,6 +6,6 @@ import org.koin.dsl.module
|
|||
|
||||
val ViewModelModule = module {
|
||||
viewModel {
|
||||
GameViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
|
||||
GameViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue