Update Feature Flags to use Firebase

This commit is contained in:
Lucas Lima 2021-01-03 12:06:02 -03:00 committed by Lucas Nunes
parent 460e92bee2
commit 543adc0d68
13 changed files with 126 additions and 39 deletions

View file

@ -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)) {

View file

@ -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()
)
}
}

View file

@ -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

View file

@ -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(

View file

@ -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"

View file

@ -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,6 +310,7 @@ open class GameViewModel(
}
private fun onFeedbackAnalytics(action: ActionResponse, index: Int) {
if (featureFlagManager.isGameplayAnalyticsEnabled) {
when (action) {
ActionResponse.OpenTile -> {
analyticsManager.sentEvent(Analytics.OpenTile(index))
@ -326,6 +329,7 @@ open class GameViewModel(
}
}
}
}
private fun refreshMineCount() = mineCount.postValue(gameController.remainingMines())

View file

@ -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")

View file

@ -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()
}

View file

@ -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
}
}

View file

@ -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'

View file

@ -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"
}
}

View file

@ -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

View file

@ -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())
}
}