diff --git a/app/build.gradle b/app/build.gradle
index 6e0b85a97..c12eb555d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,6 @@
plugins {
id 'com.android.application'
- id('kotlin-android')
+ id 'kotlin-android'
}
android {
@@ -8,8 +8,8 @@ android {
defaultConfig {
// versionCode and versionName must be hardcoded to support F-droid
- versionCode 1705011
- versionName '17.5.1'
+ versionCode 1705061
+ versionName '17.5.6'
minSdk 21
targetSdk 34
compileSdk 34
@@ -73,8 +73,7 @@ android {
versionNameSuffix ' S'
apply plugin: 'com.google.gms.google-services'
- apply plugin: 'com.google.firebase.crashlytics'
- apply plugin: 'com.google.firebase.firebase-perf'
+ apply plugin: 'com.bugsnag.android.gradle'
}
googleInstant {
@@ -84,8 +83,6 @@ android {
versionNameSuffix ' I'
apply plugin: 'com.google.gms.google-services'
- apply plugin: 'com.google.firebase.crashlytics'
- apply plugin: 'com.google.firebase.firebase-perf'
}
foss {
diff --git a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt
index e6b87d832..75f1e17e4 100644
--- a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt
+++ b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt
@@ -569,7 +569,7 @@ class GameActivity :
isVisible = canUseHelpNow
text =
if (canRequestHelpWithAds) {
- "+5"
+ "+10"
} else {
gameViewModel.getTips().toL10nString()
}
@@ -577,7 +577,11 @@ class GameActivity :
binding.shortcutIcon.apply {
TooltipCompat.setTooltipText(this, getString(i18n.string.help))
- setImageResource(R.drawable.hint)
+ if (canRequestHelpWithAds) {
+ setImageResource(R.drawable.movie)
+ } else {
+ setImageResource(R.drawable.hint)
+ }
setColorFilter(binding.minesCount.currentTextColor)
if (canUseHelpNow) {
@@ -596,7 +600,6 @@ class GameActivity :
val wasPlaying = gameAudioManager.isPlayingMusic()
adsManager.showRewardedAd(
activity = this@GameActivity,
- skipIfFrequent = false,
onStart = {
if (wasPlaying) {
gameAudioManager.pauseMusic()
@@ -696,26 +699,16 @@ class GameActivity :
}
private fun startNewGameWithAds() {
- if (!preferencesRepository.isPremiumEnabled() && featureFlagManager.isAdsOnNewGameEnabled) {
+ if (!preferencesRepository.isPremiumEnabled()) {
if (featureFlagManager.useInterstitialAd) {
adsManager.showInterstitialAd(
activity = this,
- onDismiss = {
- lifecycleScope.launch {
- gameViewModel.startNewGame()
- }
- },
- )
- } else {
- adsManager.showRewardedAd(
- activity = this,
- skipIfFrequent = true,
- onRewarded = {
+ onError = {
lifecycleScope.launch {
gameViewModel.startNewGame()
}
},
- onFail = {
+ onDismiss = {
lifecycleScope.launch {
gameViewModel.startNewGame()
}
@@ -769,7 +762,7 @@ class GameActivity :
if (!instantAppManager.isEnabled(applicationContext)) {
preferencesRepository.incrementUseCount()
- if (preferencesRepository.getUseCount() > featureFlagManager.minUsageToReview) {
+ if (preferencesRepository.getUseCount() > MIN_USAGE_TO_REVIEW) {
reviewWrapper.startInAppReview(this)
}
}
@@ -889,7 +882,7 @@ class GameActivity :
const val START_GAME = "start_game"
const val RETRY_GAME = "retry_game"
- const val TIP_COOLDOWN_MS = 5 * 1000L
+ const val TIP_COOLDOWN_MS = 2 * 1000L
const val MINE_COUNTER_ANIM_COUNTER_MS = 250L
const val LOADING_INDICATOR_MS = 500L
@@ -897,7 +890,7 @@ class GameActivity :
val CONFETTI_COLORS = listOf(0xfce18a, 0xff726d, 0xf4306d, 0xb48def)
val CONFETTI_POSITION = Position.Relative(0.5, 0.2)
- const val TOAST_OFFSET_Y_DP = 128
+ const val MIN_USAGE_TO_REVIEW = 2
const val ENABLED_SHORTCUT_ALPHA = 1.0f
const val DISABLED_SHORTCUT_ALPHA = 0.3f
diff --git a/app/src/main/java/dev/lucasnlm/antimine/MainApplication.kt b/app/src/main/java/dev/lucasnlm/antimine/MainApplication.kt
index 05e1f47ba..ac92837e9 100644
--- a/app/src/main/java/dev/lucasnlm/antimine/MainApplication.kt
+++ b/app/src/main/java/dev/lucasnlm/antimine/MainApplication.kt
@@ -13,6 +13,7 @@ import dev.lucasnlm.antimine.preferences.PreferencesRepository
import dev.lucasnlm.antimine.support.IapHandler
import dev.lucasnlm.external.AdsManager
import dev.lucasnlm.external.AnalyticsManager
+import dev.lucasnlm.external.CrashReporter
import dev.lucasnlm.external.FeatureFlagManager
import dev.lucasnlm.external.di.ExternalModule
import kotlinx.coroutines.CoroutineScope
@@ -29,18 +30,19 @@ open class MainApplication : MultiDexApplication() {
private val featureFlagManager: FeatureFlagManager by inject()
private val adsManager: AdsManager by inject()
private val iapHandler: IapHandler by inject()
+ private val crashReporter: CrashReporter by inject()
override fun onCreate() {
super.onCreate()
-
DynamicColors.applyToActivitiesIfAvailable(this)
-
stopKoin()
startKoin {
androidContext(applicationContext)
modules(AppModule, CommonModule, CommonIoModule, ExternalModule, LevelModule, ViewModelModule)
}
+ crashReporter.start(this)
+
appScope.launch {
iapHandler.start()
}
@@ -53,9 +55,6 @@ open class MainApplication : MultiDexApplication() {
if (featureFlagManager.isFoss) {
preferencesRepository.setPremiumFeatures(true)
} else {
- appScope.launch {
- featureFlagManager.refresh()
- }
adsManager.start(this)
}
diff --git a/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt b/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt
index 430a16cda..20ada6d5c 100644
--- a/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt
+++ b/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt
@@ -30,7 +30,7 @@ val ViewModelModule =
viewModel { LocalizationViewModel(get(), get()) }
viewModel {
GameViewModel(
- get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
+ get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
)
}
}
diff --git a/app/src/main/java/dev/lucasnlm/antimine/gameover/CommonGameDialogFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/gameover/CommonGameDialogFragment.kt
index a0d699001..e7832b758 100644
--- a/app/src/main/java/dev/lucasnlm/antimine/gameover/CommonGameDialogFragment.kt
+++ b/app/src/main/java/dev/lucasnlm/antimine/gameover/CommonGameDialogFragment.kt
@@ -123,7 +123,7 @@ abstract class CommonGameDialogFragment : AppCompatDialogFragment() {
)
preferencesRepository.setShowMusicBanner(false)
gameAudioManager.playMonetization()
- openComposer(composer.composerLink)
+ openComposer(it.context, composer.composerLink)
}
}
@@ -138,9 +138,10 @@ abstract class CommonGameDialogFragment : AppCompatDialogFragment() {
}
}
- private fun openComposer(composerLink: String) {
- val context = requireContext()
-
+ private fun openComposer(
+ context: Context,
+ composerLink: String,
+ ) {
runCatching {
val intent =
Intent(Intent.ACTION_VIEW, Uri.parse(composerLink)).apply {
@@ -163,7 +164,7 @@ abstract class CommonGameDialogFragment : AppCompatDialogFragment() {
post {
addView(
adsManager.createBannerAd(
- requireContext(),
+ adFrame.context,
onError = {
showHexBanner(this)
},
@@ -212,7 +213,6 @@ abstract class CommonGameDialogFragment : AppCompatDialogFragment() {
if (!activity.isFinishing) {
adsManager.showRewardedAd(
activity,
- skipIfFrequent = false,
onRewarded = {
continueGame()
},
diff --git a/app/src/main/java/dev/lucasnlm/antimine/gameover/GameOverDialogFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/gameover/GameOverDialogFragment.kt
index 3c0e0bf2f..7d9e3b546 100644
--- a/app/src/main/java/dev/lucasnlm/antimine/gameover/GameOverDialogFragment.kt
+++ b/app/src/main/java/dev/lucasnlm/antimine/gameover/GameOverDialogFragment.kt
@@ -97,7 +97,7 @@ class GameOverDialogFragment : CommonGameDialogFragment() {
continueGame.setOnClickListener {
analyticsManager.sentEvent(Analytics.ContinueGame)
- if (featureFlagManager.isAdsOnContinueEnabled && !isPremiumEnabled) {
+ if (!isPremiumEnabled) {
showAdsAndContinue()
} else {
gameViewModel.sendEvent(GameEvent.ContinueGame)
@@ -128,12 +128,9 @@ class GameOverDialogFragment : CommonGameDialogFragment() {
showAdBannerDialog(adFrame)
}
- if (!state.showTutorial &&
- state.showContinueButton &&
- featureFlagManager.isContinueGameEnabled
- ) {
+ if (!state.showTutorial && state.showContinueButton) {
continueGame.isVisible = true
- if (!isPremiumEnabled && featureFlagManager.isAdsOnContinueEnabled) {
+ if (!isPremiumEnabled) {
continueGame.compoundDrawablePadding = 0
continueGame.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.watch_ads_icon,
@@ -165,11 +162,7 @@ class GameOverDialogFragment : CommonGameDialogFragment() {
val intent = Intent(context, TutorialActivity::class.java)
context.startActivity(intent)
}
- } else if (
- !isPremiumEnabled &&
- !isInstantMode &&
- featureFlagManager.isGameOverAdEnabled
- ) {
+ } else if (!isPremiumEnabled && !isInstantMode) {
activity?.let { activity ->
val label = context.getString(i18n.string.remove_ad)
val priceModel = billingManager.getPrice()
diff --git a/app/src/main/java/dev/lucasnlm/antimine/gameover/WinGameDialogFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/gameover/WinGameDialogFragment.kt
index 6d84c8e67..1b44e614e 100644
--- a/app/src/main/java/dev/lucasnlm/antimine/gameover/WinGameDialogFragment.kt
+++ b/app/src/main/java/dev/lucasnlm/antimine/gameover/WinGameDialogFragment.kt
@@ -105,15 +105,14 @@ class WinGameDialogFragment : CommonGameDialogFragment() {
}
newGame.setOnClickListener {
- if (featureFlagManager.isAdsOnContinueEnabled && !isPremiumEnabled) {
+ if (!isPremiumEnabled) {
showAdsAndContinue()
} else {
continueGame()
}
}
- if (!isPremiumEnabled && featureFlagManager.isAdsOnContinueEnabled
- ) {
+ if (!isPremiumEnabled) {
newGame.compoundDrawablePadding = 0
newGame.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.watch_ads_icon,
@@ -143,10 +142,7 @@ class WinGameDialogFragment : CommonGameDialogFragment() {
stats.isVisible = true
}
- if (!isPremiumEnabled &&
- !isInstantMode &&
- featureFlagManager.isGameOverAdEnabled
- ) {
+ if (!isPremiumEnabled && !isInstantMode) {
activity?.let { activity ->
val label = context.getString(i18n.string.remove_ad)
val price = billingManager.getPrice()?.price
diff --git a/app/src/main/java/dev/lucasnlm/antimine/main/MainActivity.kt b/app/src/main/java/dev/lucasnlm/antimine/main/MainActivity.kt
index e6db4cd62..df3d32971 100644
--- a/app/src/main/java/dev/lucasnlm/antimine/main/MainActivity.kt
+++ b/app/src/main/java/dev/lucasnlm/antimine/main/MainActivity.kt
@@ -37,10 +37,12 @@ import dev.lucasnlm.antimine.preferences.PreferencesActivity
import dev.lucasnlm.antimine.preferences.PreferencesRepository
import dev.lucasnlm.antimine.preferences.models.Minefield
import dev.lucasnlm.antimine.stats.StatsActivity
+import dev.lucasnlm.antimine.support.IapHandler
import dev.lucasnlm.antimine.themes.ThemeActivity
import dev.lucasnlm.antimine.ui.ext.ThemedActivity
import dev.lucasnlm.external.*
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject
@@ -63,6 +65,7 @@ class MainActivity : ThemedActivity() {
private val preferenceRepository: PreferencesRepository by inject()
private val soundManager: GameAudioManager by inject()
private val gameLocaleManager: GameLocaleManager by inject()
+ private val iapHandler: IapHandler by inject()
private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
@@ -109,9 +112,22 @@ class MainActivity : ThemedActivity() {
handleBackPressed()
}
+ listenToPurchase()
redirectToGame()
}
+ private fun listenToPurchase() {
+ if (!preferenceRepository.isPremiumEnabled() && iapHandler.isEnabled()) {
+ lifecycleScope.launch {
+ iapHandler.listenPurchase().collect {
+ if (it) {
+ recreate()
+ }
+ }
+ }
+ }
+ }
+
private fun bindMenuButtons() {
binding.continueGame.apply {
if (preferencesRepository.showContinueGame()) {
@@ -431,7 +447,6 @@ class MainActivity : ThemedActivity() {
}
private fun afterGooglePlayGames() {
- playGamesManager.signInToFirebase(this)
inAppUpdateManager.checkUpdate(this)
}
diff --git a/app/src/main/java/dev/lucasnlm/antimine/playgames/viewmodel/PlayGamesEvent.kt b/app/src/main/java/dev/lucasnlm/antimine/playgames/viewmodel/PlayGamesEvent.kt
index 41e0fec4b..9286fcf6c 100644
--- a/app/src/main/java/dev/lucasnlm/antimine/playgames/viewmodel/PlayGamesEvent.kt
+++ b/app/src/main/java/dev/lucasnlm/antimine/playgames/viewmodel/PlayGamesEvent.kt
@@ -2,5 +2,6 @@ package dev.lucasnlm.antimine.playgames.viewmodel
sealed class PlayGamesEvent {
data object OpenAchievements : PlayGamesEvent()
+
data object OpenLeaderboards : PlayGamesEvent()
}
diff --git a/app/src/main/java/dev/lucasnlm/antimine/stats/viewmodel/StatsEvent.kt b/app/src/main/java/dev/lucasnlm/antimine/stats/viewmodel/StatsEvent.kt
index bc7436ccd..effffd5cf 100644
--- a/app/src/main/java/dev/lucasnlm/antimine/stats/viewmodel/StatsEvent.kt
+++ b/app/src/main/java/dev/lucasnlm/antimine/stats/viewmodel/StatsEvent.kt
@@ -2,5 +2,6 @@ package dev.lucasnlm.antimine.stats.viewmodel
sealed class StatsEvent {
data object LoadStats : StatsEvent()
+
data object DeleteStats : StatsEvent()
}
diff --git a/app/src/main/res/drawable/movie.xml b/app/src/main/res/drawable/movie.xml
new file mode 100644
index 000000000..e190aac50
--- /dev/null
+++ b/app/src/main/res/drawable/movie.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index d98990df1..860dac17e 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -26,7 +26,8 @@
android:animateLayoutChanges="true"
android:orientation="vertical"
android:paddingHorizontal="@dimen/main_activity_padding"
- android:paddingVertical="32dp">
+ android:paddingTop="32dp"
+ android:paddingBottom="48dp">
@@ -165,16 +165,15 @@
diff --git a/app/src/main/res/layout/win_dialog.xml b/app/src/main/res/layout/win_dialog.xml
index d61d90a88..d33ec8e3a 100644
--- a/app/src/main/res/layout/win_dialog.xml
+++ b/app/src/main/res/layout/win_dialog.xml
@@ -11,7 +11,7 @@
android:layout_height="wrap_content"
android:background="@drawable/round_background"
android:padding="16dp"
- app:layout_constraintBottom_toTopOf="@+id/adFrame"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
@@ -161,8 +161,7 @@
android:gravity="center"
android:minHeight="50dp"
android:visibility="gone"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintStart_toStartOf="@+id/dialog"
+ app:layout_constraintEnd_toEndOf="@+id/dialog"
app:layout_constraintTop_toBottomOf="@+id/dialog" />
\ No newline at end of file
diff --git a/app/src/test/java/dev/lucasnlm/antimine/di/TestAppModule.kt b/app/src/test/java/dev/lucasnlm/antimine/di/TestAppModule.kt
index 6e2f5eb88..51ce508d3 100644
--- a/app/src/test/java/dev/lucasnlm/antimine/di/TestAppModule.kt
+++ b/app/src/test/java/dev/lucasnlm/antimine/di/TestAppModule.kt
@@ -103,10 +103,6 @@ val AppModule =
}
override fun shouldRequestLogin(): Boolean = false
-
- override fun signInToFirebase(activity: Activity) {
- // Not implemented
- }
}
} bind PlayGamesManager::class
diff --git a/build.gradle b/build.gradle
index 2660e0e8b..eac8650b4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,15 @@
+buildscript {
+ dependencies {
+ classpath 'com.bugsnag:bugsnag-android-gradle-plugin:8.1.0'
+ }
+}
+
plugins {
- id('com.android.application') version '8.1.2' apply false
- id('com.android.library') version '8.1.2' apply false
- id('org.jetbrains.kotlin.android') version '1.9.10' apply false
+ id 'com.android.application' version '8.1.2' apply false
+ id 'com.android.library' version '8.1.2' apply false
+ id 'org.jetbrains.kotlin.android' version '1.9.10' apply false
- id('com.google.gms.google-services') version '4.4.0' apply false
- id('com.google.firebase.crashlytics') version '2.9.9' apply false
- id('com.google.firebase.firebase-perf') version '1.4.2' apply false
+ id 'com.google.gms.google-services' version '4.4.0' apply false
}
allprojects {
diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/view/GameRenderFragment.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/view/GameRenderFragment.kt
index 59ebcc930..38912ccdb 100644
--- a/common/src/main/java/dev/lucasnlm/antimine/common/level/view/GameRenderFragment.kt
+++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/view/GameRenderFragment.kt
@@ -123,7 +123,7 @@ open class GameRenderFragment : AndroidFragmentApplication() {
useCompass = false
useGyroscope = false
useWakelock = false
- useImmersiveMode = true
+ useImmersiveMode = false
disableAudio = true
}
return initializeForView(levelApplicationListener, config)
@@ -168,6 +168,18 @@ open class GameRenderFragment : AndroidFragmentApplication() {
.collect(::refreshState)
}
+ lifecycleScope.launch {
+ gameViewModel
+ .observeState()
+ .distinctUntilChangedBy { it.isActive }
+ .collect { state ->
+ val areActionsEnabled = state.isActive
+ levelApplicationListener.setActionsEnabled(areActionsEnabled)
+ Gdx.graphics.requestRendering()
+ syncControlSwitcher(state.selectedAction)
+ }
+ }
+
lifecycleScope.launch {
gameViewModel.observeState()
.map { it.seed to it.minefield }
@@ -182,7 +194,7 @@ open class GameRenderFragment : AndroidFragmentApplication() {
private fun refreshState(state: GameState) {
levelApplicationListener.bindField(state.field)
- val areActionsEnabled = state.isActive && !state.isGameCompleted
+ val areActionsEnabled = state.isActive
levelApplicationListener.setActionsEnabled(areActionsEnabled)
syncControlSwitcher(state.selectedAction)
diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt
index 26ee9b3cd..2bfb57dcf 100644
--- a/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt
+++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt
@@ -29,7 +29,6 @@ import dev.lucasnlm.antimine.preferences.models.GameControl
import dev.lucasnlm.antimine.preferences.models.Minefield
import dev.lucasnlm.external.Achievement
import dev.lucasnlm.external.AnalyticsManager
-import dev.lucasnlm.external.FeatureFlagManager
import dev.lucasnlm.external.Leaderboard
import dev.lucasnlm.external.PlayGamesManager
import kotlinx.coroutines.Dispatchers
@@ -52,7 +51,6 @@ open class GameViewModel(
private val analyticsManager: AnalyticsManager,
private val playGamesManager: PlayGamesManager,
private val tipRepository: TipRepository,
- private val featureFlagManager: FeatureFlagManager,
private val clockManager: ClockManager,
) : IntentViewModel() {
private lateinit var gameController: GameController
@@ -102,7 +100,7 @@ open class GameViewModel(
sendSideEffect(GameEvent.ShowNewGameDialog)
}
is GameEvent.GiveMoreTip -> {
- tipRepository.increaseTip(5)
+ tipRepository.increaseTip(10)
val newState =
state.copy(
@@ -143,12 +141,17 @@ open class GameViewModel(
emit(
state.copy(
isLoadingMap = false,
- selectedAction = gameController.getSelectedAction(),
+ selectedAction =
+ if (initialized) {
+ gameController.getSelectedAction()
+ } else {
+ preferencesRepository.defaultSwitchButton()
+ },
),
)
}
- if (!state.isGameCompleted && state.hasMines && !state.isLoadingMap) {
+ if (initialized && !state.isGameCompleted && state.hasMines && !state.isLoadingMap) {
if (
!gameController.isGameOver() &&
!gameController.isVictory() &&
@@ -517,7 +520,6 @@ open class GameViewModel(
.longPress(index)
.filterNotNull()
.collect { actionCompleted ->
- onFeedbackAnalytics(actionCompleted.action, index)
onPostAction()
playActionSound(actionCompleted)
refreshField()
@@ -537,7 +539,6 @@ open class GameViewModel(
.doubleClick(index)
.filterNotNull()
.collect { actionCompleted ->
- onFeedbackAnalytics(actionCompleted.action, index)
onPostAction()
playActionSound(actionCompleted)
refreshField()
@@ -553,7 +554,6 @@ open class GameViewModel(
.singleClick(index)
.filterNotNull()
.collect { actionCompleted ->
- onFeedbackAnalytics(actionCompleted.action, index)
onPostAction()
playActionSound(actionCompleted)
refreshField()
@@ -590,31 +590,6 @@ open class GameViewModel(
updateGameState()
}
- private fun onFeedbackAnalytics(
- action: Action,
- index: Int,
- ) {
- if (featureFlagManager.isGameplayAnalyticsEnabled) {
- when (action) {
- Action.OpenTile -> {
- analyticsManager.sentEvent(Analytics.OpenTile(index))
- }
- Action.SwitchMark -> {
- analyticsManager.sentEvent(Analytics.SwitchMark(index))
- }
- Action.OpenNeighbors -> {
- analyticsManager.sentEvent(Analytics.OpenNeighbors(index))
- }
- Action.OpenOrMark -> {
- analyticsManager.sentEvent(Analytics.OpenOrFlagTile(index))
- }
- Action.QuestionMark -> {
- analyticsManager.sentEvent(Analytics.QuestionMark(index))
- }
- }
- }
- }
-
private fun updateGameState() {
when {
gameController.isGameOver() -> {
diff --git a/core/src/main/java/dev/lucasnlm/antimine/core/repository/DimensionRepositoryImpl.kt b/core/src/main/java/dev/lucasnlm/antimine/core/repository/DimensionRepositoryImpl.kt
index 4e68435bd..29cf9763c 100644
--- a/core/src/main/java/dev/lucasnlm/antimine/core/repository/DimensionRepositoryImpl.kt
+++ b/core/src/main/java/dev/lucasnlm/antimine/core/repository/DimensionRepositoryImpl.kt
@@ -28,7 +28,7 @@ class DimensionRepositoryImpl(
}
override fun areaSize(): Float {
- return context.resources.getDimension(R.dimen.field_size)
+ return displaySize().width / 11.0f
}
override fun areaSeparator(): Float {
diff --git a/external/src/main/java/dev/lucasnlm/external/AdsManager.kt b/external/src/main/java/dev/lucasnlm/external/AdsManager.kt
index 51f7650c2..e024786f8 100644
--- a/external/src/main/java/dev/lucasnlm/external/AdsManager.kt
+++ b/external/src/main/java/dev/lucasnlm/external/AdsManager.kt
@@ -11,7 +11,6 @@ interface AdsManager {
fun showRewardedAd(
activity: Activity,
- skipIfFrequent: Boolean,
onStart: (() -> Unit)? = null,
onRewarded: (() -> Unit)?,
onFail: (() -> Unit)?,
diff --git a/external/src/main/java/dev/lucasnlm/external/CrashReporter.kt b/external/src/main/java/dev/lucasnlm/external/CrashReporter.kt
index 7b24f946f..1f4c674d6 100644
--- a/external/src/main/java/dev/lucasnlm/external/CrashReporter.kt
+++ b/external/src/main/java/dev/lucasnlm/external/CrashReporter.kt
@@ -1,5 +1,9 @@
package dev.lucasnlm.external
+import android.app.Application
+
interface CrashReporter {
fun sendError(message: String)
+
+ fun start(application: Application)
}
diff --git a/external/src/main/java/dev/lucasnlm/external/FeatureFlagManager.kt b/external/src/main/java/dev/lucasnlm/external/FeatureFlagManager.kt
index 22ab0a2d1..9977aeae1 100644
--- a/external/src/main/java/dev/lucasnlm/external/FeatureFlagManager.kt
+++ b/external/src/main/java/dev/lucasnlm/external/FeatureFlagManager.kt
@@ -1,18 +1,28 @@
package dev.lucasnlm.external
-abstract class FeatureFlagManager {
- abstract val isGameHistoryEnabled: Boolean
- abstract val isRateUsEnabled: Boolean
- abstract val isGameplayAnalyticsEnabled: Boolean
- abstract val isGameOverAdEnabled: Boolean
- abstract val isAdsOnContinueEnabled: Boolean
- abstract val isAdsOnNewGameEnabled: Boolean
- abstract val useInterstitialAd: Boolean
- abstract val isContinueGameEnabled: Boolean
- abstract val isFoss: Boolean
- abstract val minUsageToReview: Int
- abstract val isBannerAdEnabled: Boolean
- abstract val showCountdownToContinue: Boolean
+interface FeatureFlagManager {
+ /**
+ * Whether the game history feature is enabled.
+ */
+ val isGameHistoryEnabled: Boolean
- abstract suspend fun refresh()
+ /**
+ * Whether the interstitial ad should be used.
+ */
+ val useInterstitialAd: Boolean
+
+ /**
+ * Whether the app is the FOSS version.
+ */
+ val isFoss: Boolean
+
+ /**
+ * Whether the banner ad should be used.
+ */
+ val isBannerAdEnabled: Boolean
+
+ /**
+ * Whether the countdown to continue should be shown.
+ */
+ val showCountdownToContinue: Boolean
}
diff --git a/external/src/main/java/dev/lucasnlm/external/PlayGamesManager.kt b/external/src/main/java/dev/lucasnlm/external/PlayGamesManager.kt
index 6932db961..1be6c6896 100644
--- a/external/src/main/java/dev/lucasnlm/external/PlayGamesManager.kt
+++ b/external/src/main/java/dev/lucasnlm/external/PlayGamesManager.kt
@@ -65,6 +65,4 @@ interface PlayGamesManager {
fun keepRequestingLogin(status: Boolean)
fun shouldRequestLogin(): Boolean
-
- fun signInToFirebase(activity: Activity)
}
diff --git a/foss/build.gradle b/foss/build.gradle
index 1e828e01e..1b4843d31 100644
--- a/foss/build.gradle
+++ b/foss/build.gradle
@@ -19,12 +19,15 @@ android {
}
}
+ buildFeatures {
+ buildConfig true
+ viewBinding true
+ }
+
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
-
-
}
dependencies {
@@ -32,6 +35,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':external')
+ implementation project(':i18n')
// Kotlin
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
@@ -41,4 +45,10 @@ dependencies {
// Koin
implementation 'io.insert-koin:koin-android:3.1.2'
testImplementation 'io.insert-koin:koin-test:3.1.2'
+
+ // Acra
+ implementation 'ch.acra:acra-core:5.11.2'
+ implementation 'ch.acra:acra-mail:5.11.2'
+ implementation 'ch.acra:acra-toast:5.11.2'
+ implementation 'ch.acra:acra-limiter:5.11.2'
}
diff --git a/foss/src/main/java/dev/lucasnlm/external/CrashReporterImpl.kt b/foss/src/main/java/dev/lucasnlm/external/CrashReporterImpl.kt
index 00c7c1b35..3a4d8b916 100644
--- a/foss/src/main/java/dev/lucasnlm/external/CrashReporterImpl.kt
+++ b/foss/src/main/java/dev/lucasnlm/external/CrashReporterImpl.kt
@@ -1,7 +1,38 @@
package dev.lucasnlm.external
+import android.app.Application
+import dev.lucasnlm.antimine.i18n.R
+import org.acra.BuildConfig
+import org.acra.config.limiter
+import org.acra.config.mailSender
+import org.acra.config.toast
+import org.acra.data.StringFormat
+import org.acra.ktx.initAcra
+
class CrashReporterImpl : CrashReporter {
override fun sendError(message: String) {
// FOSS build doesn't log errors.
}
+
+ override fun start(application: Application) {
+ val context = application.applicationContext
+ application.initAcra {
+ buildConfigClass = BuildConfig::class.java
+ reportFormat = StringFormat.JSON
+
+ mailSender {
+ subject = "[antimine] crash report"
+ mailTo = "me@lucasnlm.dev"
+ reportFileName = "report.txt"
+ }
+
+ toast {
+ text = context.getString(R.string.acra_toast_text)
+ }
+
+ limiter {
+ // No changes
+ }
+ }
+ }
}
diff --git a/foss/src/main/java/dev/lucasnlm/external/FeatureFlagManagerImpl.kt b/foss/src/main/java/dev/lucasnlm/external/FeatureFlagManagerImpl.kt
index 075225a02..19956bb03 100644
--- a/foss/src/main/java/dev/lucasnlm/external/FeatureFlagManagerImpl.kt
+++ b/foss/src/main/java/dev/lucasnlm/external/FeatureFlagManagerImpl.kt
@@ -1,20 +1,9 @@
package dev.lucasnlm.external
-class FeatureFlagManagerImpl : FeatureFlagManager() {
+class FeatureFlagManagerImpl : FeatureFlagManager {
override val isGameHistoryEnabled: Boolean = true
- override val isRateUsEnabled: Boolean = false
- override val isGameplayAnalyticsEnabled: Boolean = false
- override val isGameOverAdEnabled: Boolean = false
- override val isAdsOnNewGameEnabled: Boolean = false
- override val isAdsOnContinueEnabled: Boolean = false
- override val isContinueGameEnabled: Boolean = true
override val isFoss: Boolean = true
- override val minUsageToReview: Int = Int.MAX_VALUE
override val useInterstitialAd: Boolean = false
override val isBannerAdEnabled: Boolean = false
override val showCountdownToContinue: Boolean = false
-
- override suspend fun refresh() {
- // No Feature Flags on FOSS
- }
}
diff --git a/foss/src/main/java/dev/lucasnlm/external/NoAdsManager.kt b/foss/src/main/java/dev/lucasnlm/external/NoAdsManager.kt
index b021f2263..460dbbbcf 100644
--- a/foss/src/main/java/dev/lucasnlm/external/NoAdsManager.kt
+++ b/foss/src/main/java/dev/lucasnlm/external/NoAdsManager.kt
@@ -9,7 +9,6 @@ class NoAdsManager : AdsManager {
override fun showRewardedAd(
activity: Activity,
- skipIfFrequent: Boolean,
onStart: (() -> Unit)?,
onRewarded: (() -> Unit)?,
onFail: (() -> Unit)?,
diff --git a/foss/src/main/java/dev/lucasnlm/external/PlayGamesManagerImpl.kt b/foss/src/main/java/dev/lucasnlm/external/PlayGamesManagerImpl.kt
index 0ab837ad6..b9dd72790 100644
--- a/foss/src/main/java/dev/lucasnlm/external/PlayGamesManagerImpl.kt
+++ b/foss/src/main/java/dev/lucasnlm/external/PlayGamesManagerImpl.kt
@@ -68,8 +68,4 @@ class PlayGamesManagerImpl(
override fun shouldRequestLogin(): Boolean {
return false
}
-
- override fun signInToFirebase(activity: Activity) {
- // F-droid build doesn't have Google Play Games
- }
}
diff --git a/gdx/check_import.sh b/gdx/check_import.sh
index f4475aef8..ba94cfbd9 100755
--- a/gdx/check_import.sh
+++ b/gdx/check_import.sh
@@ -14,9 +14,7 @@ fi
# Check if the file contains the string in an unique line
if grep -xq "preBuild.dependsOn copyAndroidNatives" "$1"; then
- echo "achou"
exit 0
else
- echo "n achou"
exit 1
fi
diff --git a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt
index 389fc72f5..7f049ac43 100644
--- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt
+++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt
@@ -123,6 +123,7 @@ class GameApplicationListener(
}
Gdx.input.inputProcessor = InputMultiplexer(GestureDetector(minefieldInputController), minefieldStage)
+ minefieldStage.setZoom(GameContext.zoom)
}
override fun dispose() {
@@ -142,7 +143,6 @@ class GameApplicationListener(
fun onPause() {
GameContext.run {
zoom = 1.0f
- minefieldStage.setZoom(1.0f)
}
}
diff --git a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaActor.kt b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaActor.kt
index 1bab44421..fba57a63a 100644
--- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaActor.kt
+++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaActor.kt
@@ -31,21 +31,21 @@ class AreaActor(
inputListener: InputListener,
var isPressed: Boolean = false,
private var focusScale: Float = 1.0f,
- private var pieces: Map = mapOf(),
+ private var pieces: Set = setOf(),
private val gameRenderingContext: GameRenderingContext,
) : Actor() {
var area: Area? = null
private var areaForm: Int? = null
- private var topId: Int = -1
- private var bottomId: Int = -1
- private var leftId: Int = -1
- private var rightId: Int = -1
- private var topLeftId: Int = -1
- private var topRightId: Int = -1
- private var bottomLeftId: Int = -1
- private var bottomRightId: Int = -1
+ private var topId: Int = NO_LINK
+ private var bottomId: Int = NO_LINK
+ private var leftId: Int = NO_LINK
+ private var rightId: Int = NO_LINK
+ private var topLeftId: Int = NO_LINK
+ private var topRightId: Int = NO_LINK
+ private var bottomLeftId: Int = NO_LINK
+ private var bottomRightId: Int = NO_LINK
init {
width = gameRenderingContext.areaSize
@@ -65,11 +65,9 @@ class AreaActor(
}
if (this.area != area) {
- if (this.area?.id != area.id) {
- x = area.posX * width
- y = area.posY * height
- refreshLinks(area, field)
- }
+ x = area.posX * width
+ y = area.posY * height
+ refreshLinks(area, field)
this.area = area
}
@@ -170,17 +168,15 @@ class AreaActor(
)
} else {
pieces.forEach { piece ->
- if (piece.value) {
- batch.drawRegion(
- texture = GameContext.gameTextures!!.pieces[piece.key]!!,
- x = x - 0.5f,
- y = y - 0.5f,
- width = width + 0.5f,
- height = height + 0.5f,
- color = coverColor,
- blend = false,
- )
- }
+ batch.drawRegion(
+ texture = GameContext.gameTextures!!.pieces[piece]!!,
+ x = x - 0.5f,
+ y = y - 0.5f,
+ width = width + 0.5f,
+ height = height + 0.5f,
+ color = coverColor,
+ blend = false,
+ )
}
}
}
@@ -191,17 +187,15 @@ class AreaActor(
GameContext.atlas?.let { atlas ->
pieces.forEach { piece ->
- if (piece.value) {
- batch.drawRegion(
- texture = atlas.findRegion(piece.key),
- x = x - 0.5f,
- y = y - 0.5f,
- width = width + 1.0f,
- height = height + 1.0f,
- color = coverColor,
- blend = false,
- )
- }
+ batch.drawRegion(
+ texture = atlas.findRegion(piece),
+ x = x - 0.5f,
+ y = y - 0.5f,
+ width = width + 1.0f,
+ height = height + 1.0f,
+ color = coverColor,
+ blend = false,
+ )
}
}
}
@@ -384,14 +378,14 @@ class AreaActor(
bottomLeftId = area.getNeighborIdAtPos(field, -1, -1)
bottomRightId = area.getNeighborIdAtPos(field, 1, -1)
} else {
- topId = -1
- bottomId = -1
- leftId = -1
- rightId = -1
- topLeftId = -1
- topRightId = -1
- bottomLeftId = -1
- bottomRightId = -1
+ topId = NO_LINK
+ bottomId = NO_LINK
+ leftId = NO_LINK
+ rightId = NO_LINK
+ topLeftId = NO_LINK
+ topRightId = NO_LINK
+ bottomLeftId = NO_LINK
+ bottomRightId = NO_LINK
}
}
@@ -419,6 +413,7 @@ class AreaActor(
const val MIN_SCALE = 1.0f
const val MAX_SCALE = 1.15f
const val BASE_ICON_SCALE = 0.8f
+ const val NO_LINK = -1
private fun Area.canLinkTo(area: Area): Boolean {
return isCovered && mark.ligatureMask == area.mark.ligatureMask
diff --git a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaForm.kt b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaForm.kt
index bdc49f0af..feb18c9a5 100644
--- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaForm.kt
+++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaForm.kt
@@ -16,21 +16,21 @@ object AreaForm {
return (this and flag) != 0
}
- private fun Int.top(): Boolean = checkForm(AreaForm.TOP)
+ private fun Int.top(): Boolean = checkForm(TOP)
- private fun Int.bottom(): Boolean = checkForm(AreaForm.BOTTOM)
+ private fun Int.bottom(): Boolean = checkForm(BOTTOM)
- private fun Int.left(): Boolean = checkForm(AreaForm.LEFT)
+ private fun Int.left(): Boolean = checkForm(LEFT)
- private fun Int.right(): Boolean = checkForm(AreaForm.RIGHT)
+ private fun Int.right(): Boolean = checkForm(RIGHT)
- private fun Int.topLeft(): Boolean = checkForm(AreaForm.TOP_LEFT)
+ private fun Int.topLeft(): Boolean = checkForm(TOP_LEFT)
- private fun Int.topRight(): Boolean = checkForm(AreaForm.TOP_RIGHT)
+ private fun Int.topRight(): Boolean = checkForm(TOP_RIGHT)
- private fun Int.bottomLeft(): Boolean = checkForm(AreaForm.BOTTOM_LEFT)
+ private fun Int.bottomLeft(): Boolean = checkForm(BOTTOM_LEFT)
- private fun Int.bottomRight(): Boolean = checkForm(AreaForm.BOTTOM_RIGHT)
+ private fun Int.bottomRight(): Boolean = checkForm(BOTTOM_RIGHT)
fun areaFormOf(
top: Boolean,
@@ -79,7 +79,7 @@ object AreaForm {
return result
}
- fun Int.toAtlasNames(): Map {
+ fun Int.toAtlasNames(): Set {
return mapOf(
AtlasNames.CORE to true,
AtlasNames.TOP to top(),
@@ -98,7 +98,9 @@ object AreaForm {
AtlasNames.FILL_TOP_RIGHT to (top() && right() && topRight()),
AtlasNames.FILL_BOTTOM_LEFT to (bottom() && left() && bottomLeft()),
AtlasNames.FILL_BOTTOM_RIGHT to (bottom() && right() && bottomRight()),
- )
+ ).filter {
+ it.value
+ }.keys
}
const val AREA_NO_FORM = 0b00000000
diff --git a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/stages/MinefieldStage.kt b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/stages/MinefieldStage.kt
index 5de8a6da1..d1d317558 100644
--- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/stages/MinefieldStage.kt
+++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/stages/MinefieldStage.kt
@@ -83,11 +83,9 @@ class MinefieldStage(
this.newBoundAreas = null
}
- if (actors.size != boundAreas.size) {
- if (boundAreas.size < actors.size) {
- actors.removeRange(boundAreas.size, actors.size)
- actors.shrink()
- } else {
+ val forceRebind = actors.size != boundAreas.size
+ if (forceRebind) {
+ if (boundAreas.size > actors.size) {
actors.ensureCapacity(boundAreas.size + 1)
}
@@ -101,10 +99,13 @@ class MinefieldStage(
)
actors.add(areaActor)
}
+ } else if (actors.size > boundAreas.size) {
+ actors.removeRange(boundAreas.size, actors.size - 1)
}
}
val areaSize = gameRenderingContext.areaSize
+ val checkShape = forceRefreshVisibleAreas || forceRebind
Gdx.graphics.isContinuousRendering = true
actors.forEachIndexed { index, actor ->
val areaActor = (actor as AreaActor)
@@ -122,7 +123,7 @@ class MinefieldStage(
reset = resetEvents,
area = area,
field = boundAreas,
- checkShape = forceRefreshVisibleAreas,
+ checkShape = checkShape,
)
}
Gdx.graphics.isContinuousRendering = false
diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml
index 3c39fa780..76fa9bf4f 100644
--- a/i18n/src/main/res/values/strings.xml
+++ b/i18n/src/main/res/values/strings.xml
@@ -146,4 +146,5 @@
Show clock
If you like this game, consider making a donation.
It will help keep this project active!
+ Sorry, an error occurred. Please, send the report to the developers.
diff --git a/preferences/src/main/java/dev/lucasnlm/antimine/preferences/models/GameControl.kt b/preferences/src/main/java/dev/lucasnlm/antimine/preferences/models/GameControl.kt
index a866c9a96..e90438596 100644
--- a/preferences/src/main/java/dev/lucasnlm/antimine/preferences/models/GameControl.kt
+++ b/preferences/src/main/java/dev/lucasnlm/antimine/preferences/models/GameControl.kt
@@ -42,84 +42,84 @@ sealed class GameControl(
val onUncovered: Actions,
) {
data object Standard : GameControl(
- id = ControlStyle.Standard,
- onCovered =
- Actions(
- singleClick = Action.OpenTile,
- longPress = Action.SwitchMark,
- doubleClick = null,
- ),
- onUncovered =
- Actions(
- singleClick = null,
- longPress = Action.OpenNeighbors,
- doubleClick = null,
- ),
- )
+ id = ControlStyle.Standard,
+ onCovered =
+ Actions(
+ singleClick = Action.OpenTile,
+ longPress = Action.SwitchMark,
+ doubleClick = null,
+ ),
+ onUncovered =
+ Actions(
+ singleClick = null,
+ longPress = Action.OpenNeighbors,
+ doubleClick = null,
+ ),
+ )
data object FastFlag : GameControl(
- id = ControlStyle.FastFlag,
- onCovered =
- Actions(
- singleClick = Action.SwitchMark,
- longPress = Action.OpenTile,
- doubleClick = null,
- ),
- onUncovered =
- Actions(
- singleClick = Action.OpenNeighbors,
- longPress = null,
- doubleClick = null,
- ),
- )
+ id = ControlStyle.FastFlag,
+ onCovered =
+ Actions(
+ singleClick = Action.SwitchMark,
+ longPress = Action.OpenTile,
+ doubleClick = null,
+ ),
+ onUncovered =
+ Actions(
+ singleClick = Action.OpenNeighbors,
+ longPress = null,
+ doubleClick = null,
+ ),
+ )
data object DoubleClick : GameControl(
- id = ControlStyle.DoubleClick,
- onCovered =
- Actions(
- singleClick = Action.SwitchMark,
- longPress = null,
- doubleClick = Action.OpenTile,
- ),
- onUncovered =
- Actions(
- singleClick = Action.OpenNeighbors,
- longPress = null,
- doubleClick = null,
- ),
- )
+ id = ControlStyle.DoubleClick,
+ onCovered =
+ Actions(
+ singleClick = Action.SwitchMark,
+ longPress = null,
+ doubleClick = Action.OpenTile,
+ ),
+ onUncovered =
+ Actions(
+ singleClick = Action.OpenNeighbors,
+ longPress = null,
+ doubleClick = null,
+ ),
+ )
data object DoubleClickInverted : GameControl(
- id = ControlStyle.DoubleClickInverted,
- onCovered =
- Actions(
- singleClick = Action.OpenTile,
- longPress = null,
- doubleClick = Action.SwitchMark,
- ),
- onUncovered =
- Actions(
- singleClick = Action.OpenNeighbors,
- longPress = null,
- doubleClick = null,
- ),
- )
+ id = ControlStyle.DoubleClickInverted,
+ onCovered =
+ Actions(
+ singleClick = Action.OpenTile,
+ longPress = null,
+ doubleClick = Action.SwitchMark,
+ ),
+ onUncovered =
+ Actions(
+ singleClick = Action.OpenNeighbors,
+ longPress = null,
+ doubleClick = null,
+ ),
+ )
data object SwitchMarkOpen : GameControl(
- id = ControlStyle.SwitchMarkOpen,
- onCovered =
- Actions(
- singleClick = Action.OpenOrMark,
- longPress = null,
- doubleClick = null,
- ),
- onUncovered =
- Actions(
- singleClick = Action.OpenNeighbors,
- longPress = null,
- doubleClick = null,
- ),
- )
+ id = ControlStyle.SwitchMarkOpen,
+ onCovered =
+ Actions(
+ singleClick = Action.OpenOrMark,
+ longPress = null,
+ doubleClick = null,
+ ),
+ onUncovered =
+ Actions(
+ singleClick = Action.OpenNeighbors,
+ longPress = null,
+ doubleClick = null,
+ ),
+ )
companion object {
fun fromControlType(controlStyle: ControlStyle): GameControl {
diff --git a/proprietary/build.gradle b/proprietary/build.gradle
index 221628fb9..9b0546364 100644
--- a/proprietary/build.gradle
+++ b/proprietary/build.gradle
@@ -40,6 +40,9 @@ dependencies {
implementation 'com.amplitude:android-sdk:2.32.1'
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
+ // Bugsnag
+ implementation 'com.bugsnag:bugsnag-android:5.31.2'
+
// Google
implementation 'com.android.billingclient:billing-ktx:6.0.1'
implementation 'com.google.android.gms:play-services-instantapps:18.0.1'
@@ -51,15 +54,6 @@ dependencies {
// Jetbrains
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3'
- // Firebase
- implementation platform('com.google.firebase:firebase-bom:32.3.1')
- implementation 'com.google.firebase:firebase-analytics-ktx'
- implementation 'com.google.firebase:firebase-crashlytics-ktx'
- implementation 'com.google.firebase:firebase-firestore-ktx'
- implementation 'com.google.firebase:firebase-config-ktx'
- implementation 'com.google.firebase:firebase-auth-ktx'
- implementation 'com.google.firebase:firebase-perf-ktx'
-
// Kotlin
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.9.10'
diff --git a/proprietary/src/main/AndroidManifest.xml b/proprietary/src/main/AndroidManifest.xml
index 390db3035..5995c1d71 100644
--- a/proprietary/src/main/AndroidManifest.xml
+++ b/proprietary/src/main/AndroidManifest.xml
@@ -7,6 +7,9 @@
+
diff --git a/proprietary/src/main/java/dev/lucasnlm/external/AdMobAdsManager.kt b/proprietary/src/main/java/dev/lucasnlm/external/AdMobAdsManager.kt
index f0fcc6983..99e0a4863 100644
--- a/proprietary/src/main/java/dev/lucasnlm/external/AdMobAdsManager.kt
+++ b/proprietary/src/main/java/dev/lucasnlm/external/AdMobAdsManager.kt
@@ -190,47 +190,42 @@ class AdMobAdsManager(
override fun showRewardedAd(
activity: Activity,
- skipIfFrequent: Boolean,
onStart: (() -> Unit)?,
onRewarded: (() -> Unit)?,
onFail: (() -> Unit)?,
) {
- if (skipIfFrequent && (System.currentTimeMillis() - lastShownAd < Ads.MIN_FREQUENCY)) {
- onRewarded?.invoke()
- } else {
- val rewardedAd = this.rewardedAd
- val secondRewardedAd = this.secondRewardedAd
-
- when {
- secondRewardedAd != null -> {
- onStart?.invoke()
- secondRewardedAd.show(activity) {
- if (!activity.isFinishing) {
- lastShownAd = System.currentTimeMillis()
- onRewarded?.invoke()
- scope.launch(Dispatchers.Main) {
- loadSecondRewardAd()
- }
+ val rewardedAd = this.rewardedAd
+ val secondRewardedAd = this.secondRewardedAd
+
+ when {
+ secondRewardedAd != null -> {
+ onStart?.invoke()
+ secondRewardedAd.show(activity) {
+ if (!activity.isFinishing) {
+ lastShownAd = System.currentTimeMillis()
+ onRewarded?.invoke()
+ scope.launch(Dispatchers.Main) {
+ loadSecondRewardAd()
}
}
}
- rewardedAd != null -> {
- onStart?.invoke()
- rewardedAd.show(activity) {
- if (!activity.isFinishing) {
- lastShownAd = System.currentTimeMillis()
- onRewarded?.invoke()
- scope.launch(Dispatchers.Main) {
- loadRewardAd()
- }
+ }
+ rewardedAd != null -> {
+ onStart?.invoke()
+ rewardedAd.show(activity) {
+ if (!activity.isFinishing) {
+ lastShownAd = System.currentTimeMillis()
+ onRewarded?.invoke()
+ scope.launch(Dispatchers.Main) {
+ loadRewardAd()
}
}
}
- else -> {
- val message = failErrorCause?.let { "Fail to load Ad\n$it" } ?: "Fail to load Ad"
- crashReporter.sendError(message)
- onFail?.invoke()
- }
+ }
+ else -> {
+ val message = failErrorCause?.let { "Fail to load Ad\n$it" } ?: "Fail to load Ad"
+ crashReporter.sendError(message)
+ onFail?.invoke()
}
}
}
diff --git a/proprietary/src/main/java/dev/lucasnlm/external/CloudStorageManagerImpl.kt b/proprietary/src/main/java/dev/lucasnlm/external/CloudStorageManagerImpl.kt
index 4f895eeaf..a741fe1f9 100644
--- a/proprietary/src/main/java/dev/lucasnlm/external/CloudStorageManagerImpl.kt
+++ b/proprietary/src/main/java/dev/lucasnlm/external/CloudStorageManagerImpl.kt
@@ -1,62 +1,14 @@
package dev.lucasnlm.external
-import android.text.format.DateUtils
-import android.util.Log
-import com.google.firebase.firestore.FirebaseFirestore
-import com.google.firebase.firestore.ktx.firestore
-import com.google.firebase.ktx.Firebase
-import dev.lucasnlm.antimine.proprietary.BuildConfig
import dev.lucasnlm.external.model.CloudSave
-import dev.lucasnlm.external.model.cloudSaveOf
-import dev.lucasnlm.external.model.toHashMap
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.tasks.await
-import kotlinx.coroutines.withContext
class CloudStorageManagerImpl : CloudStorageManager {
- private val db by lazy { Firebase.firestore }
- private var lastSync: Long = 0L
override fun uploadSave(cloudSave: CloudSave) {
- FirebaseFirestore.setLoggingEnabled(BuildConfig.DEBUG)
- val data = cloudSave.toHashMap()
- db.collection(SAVES)
- .document(cloudSave.playId)
- .set(data)
- .addOnCompleteListener {
- Log.v(TAG, "Cloud storage complete")
- }
- .addOnCanceledListener {
- Log.v(TAG, "Cloud storage canceled")
- }
- .addOnFailureListener {
- Log.e(TAG, "Cloud storage error", it)
- }
- .addOnSuccessListener {
- Log.v(TAG, "Cloud storage success")
- }
+ // Todo
}
override suspend fun getSave(playId: String): CloudSave? {
- return if (System.currentTimeMillis() - lastSync > DateUtils.MINUTE_IN_MILLIS) {
- lastSync = System.currentTimeMillis()
-
- runCatching {
- withContext(Dispatchers.IO) {
- db.collection(SAVES).document(playId).get().await().data?.let {
- cloudSaveOf(playId, it.toMap())
- }
- }
- }.onFailure {
- Log.e(TAG, "Fail to load save on cloud", it)
- }.getOrNull()
- } else {
- null
- }
- }
-
- companion object {
- val TAG = CloudStorageManagerImpl::class.simpleName
- const val SAVES = "saves"
+ return null
}
}
diff --git a/proprietary/src/main/java/dev/lucasnlm/external/CrashReporterImpl.kt b/proprietary/src/main/java/dev/lucasnlm/external/CrashReporterImpl.kt
index 5bcbba901..ae5d83959 100644
--- a/proprietary/src/main/java/dev/lucasnlm/external/CrashReporterImpl.kt
+++ b/proprietary/src/main/java/dev/lucasnlm/external/CrashReporterImpl.kt
@@ -1,9 +1,14 @@
package dev.lucasnlm.external
-import com.google.firebase.crashlytics.FirebaseCrashlytics
+import android.app.Application
+import com.bugsnag.android.Bugsnag
class CrashReporterImpl : CrashReporter {
override fun sendError(message: String) {
- FirebaseCrashlytics.getInstance().recordException(Exception(message))
+ // No-op
+ }
+
+ override fun start(application: Application) {
+ Bugsnag.start(application.applicationContext)
}
}
diff --git a/proprietary/src/main/java/dev/lucasnlm/external/ExternalAnalyticsWrapperImpl.kt b/proprietary/src/main/java/dev/lucasnlm/external/ExternalAnalyticsWrapperImpl.kt
index 11d4ccd81..103cb299f 100644
--- a/proprietary/src/main/java/dev/lucasnlm/external/ExternalAnalyticsWrapperImpl.kt
+++ b/proprietary/src/main/java/dev/lucasnlm/external/ExternalAnalyticsWrapperImpl.kt
@@ -1,20 +1,13 @@
package dev.lucasnlm.external
import android.content.Context
-import android.os.Bundle
import com.amplitude.api.Amplitude
import com.amplitude.api.AmplitudeClient
-import com.google.firebase.analytics.FirebaseAnalytics
import org.json.JSONObject
class ExternalAnalyticsWrapperImpl(
private val context: Context,
) : ExternalAnalyticsWrapper {
- private val firebaseAnalytics: FirebaseAnalytics? by lazy {
- runCatching {
- FirebaseAnalytics.getInstance(context)
- }.getOrNull()
- }
private val amplitudeClient: AmplitudeClient? by lazy {
runCatching {
Amplitude
@@ -28,10 +21,6 @@ class ExternalAnalyticsWrapperImpl(
context: Context,
properties: Map,
) {
- properties.forEach { (key, value) ->
- firebaseAnalytics?.setUserProperty(key, value)
- }
-
amplitudeClient?.setUserProperties(JSONObject(properties))
}
@@ -39,15 +28,6 @@ class ExternalAnalyticsWrapperImpl(
name: String,
content: Map,
) {
- val bundle =
- Bundle().apply {
- putString(FirebaseAnalytics.Param.ITEM_NAME, name)
- content.forEach { (key, value) ->
- putString(key, value)
- }
- }
-
- firebaseAnalytics?.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle)
amplitudeClient?.logEvent(name, JSONObject(content))
}
}
diff --git a/proprietary/src/main/java/dev/lucasnlm/external/FeatureFlagManagerImpl.kt b/proprietary/src/main/java/dev/lucasnlm/external/FeatureFlagManagerImpl.kt
index 55ba7ff47..aad4110ce 100644
--- a/proprietary/src/main/java/dev/lucasnlm/external/FeatureFlagManagerImpl.kt
+++ b/proprietary/src/main/java/dev/lucasnlm/external/FeatureFlagManagerImpl.kt
@@ -1,130 +1,13 @@
package dev.lucasnlm.external
-import android.util.Log
-import com.google.firebase.remoteconfig.FirebaseRemoteConfig
-import dev.lucasnlm.antimine.proprietary.BuildConfig
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.tasks.await
-import kotlinx.coroutines.withContext
-
-class FeatureFlagManagerImpl : FeatureFlagManager() {
- private val defaultMap by lazy {
- mapOf(
- HISTORY_ENABLED to isGameHistoryEnabled,
- RATE_US_ENABLED to isRateUsEnabled,
- GAMEPLAY_EVENTS_ENABLED to isGameplayAnalyticsEnabled,
- GAME_OVER_AD_ENABLED to isGameOverAdEnabled,
- SHOW_ADS_ON_CONTINUE_ENABLED to isAdsOnContinueEnabled,
- SHOW_ADS_ON_NEW_GAME_ENABLED to isAdsOnNewGameEnabled,
- CONTINUE_ENABLED to isContinueGameEnabled,
- MIN_USAGE_TO_REVIEW to minUsageToReview,
- USE_INTERSTITIAL_AD to useInterstitialAd,
- BANNER_AD_ENABLED to isBannerAdEnabled,
- SHOW_COUNTDOWN_TO_CONTINUE to showCountdownToContinue,
- )
- }
-
- private val remoteConfig: FirebaseRemoteConfig? by lazy {
- runCatching {
- FirebaseRemoteConfig.getInstance().apply {
- setDefaultsAsync(defaultMap)
- }
- }.getOrNull()
- }
-
- private fun getBoolean(
- key: String,
- default: Boolean,
- ): Boolean {
- return when {
- BuildConfig.DEBUG -> default
- else -> remoteConfig?.getBoolean(key) ?: default
- }
- }
-
- private fun getInt(
- key: String,
- default: Int,
- ): Int {
- return when {
- BuildConfig.DEBUG -> default
- else -> remoteConfig?.getLong(key)?.toInt() ?: default
- }
- }
-
+class FeatureFlagManagerImpl : FeatureFlagManager {
override val isFoss: Boolean = false
- override val isGameHistoryEnabled: Boolean by lazy {
- getBoolean(HISTORY_ENABLED, false)
- }
-
- override val isRateUsEnabled: Boolean by lazy {
- getBoolean(RATE_US_ENABLED, true)
- }
-
- override val isGameplayAnalyticsEnabled: Boolean by lazy {
- getBoolean(GAMEPLAY_EVENTS_ENABLED, false)
- }
-
- override val isGameOverAdEnabled: Boolean by lazy {
- getBoolean(GAME_OVER_AD_ENABLED, true)
- }
-
- override val isAdsOnContinueEnabled: Boolean by lazy {
- getBoolean(SHOW_ADS_ON_CONTINUE_ENABLED, true)
- }
-
- override val isAdsOnNewGameEnabled: Boolean by lazy {
- getBoolean(SHOW_ADS_ON_NEW_GAME_ENABLED, true)
- }
-
- override val isContinueGameEnabled: Boolean by lazy {
- getBoolean(CONTINUE_ENABLED, true)
- }
-
- override val minUsageToReview: Int by lazy {
- getInt(MIN_USAGE_TO_REVIEW, DEFAULT_MIN_USAGE_TO_REVIEW)
- }
-
- override val useInterstitialAd: Boolean by lazy {
- getBoolean(USE_INTERSTITIAL_AD, true)
- }
-
- override val isBannerAdEnabled: Boolean by lazy {
- getBoolean(BANNER_AD_ENABLED, true)
- }
-
- override val showCountdownToContinue: Boolean by lazy {
- getBoolean(SHOW_COUNTDOWN_TO_CONTINUE, true)
- }
-
- override suspend fun refresh() {
- if (!BuildConfig.DEBUG && remoteConfig != null) {
- withContext(Dispatchers.IO) {
- runCatching {
- remoteConfig?.fetchAndActivate()?.await()
- }.onFailure {
- Log.e(TAG, "Fail to fetch flags", it)
- }
- }
- }
- }
+ override val isGameHistoryEnabled: Boolean = false
- companion object {
- private val TAG = FeatureFlagManagerImpl::class.simpleName
+ override val useInterstitialAd: Boolean = true
- private const val HISTORY_ENABLED = "history_enabled"
- private const val RATE_US_ENABLED = "rate_us_enabled"
- private const val GAMEPLAY_EVENTS_ENABLED = "gameplay_events_enabled"
- private const val GAME_OVER_AD_ENABLED = "game_over_ad_enabled"
- private const val SHOW_ADS_ON_CONTINUE_ENABLED = "ad_on_continue_enabled"
- private const val SHOW_ADS_ON_NEW_GAME_ENABLED = "ad_on_new_game_enabled"
- private const val CONTINUE_ENABLED = "continue_enabled"
- private const val MIN_USAGE_TO_REVIEW = "min_usage_to_review"
- private const val USE_INTERSTITIAL_AD = "use_interstitial_ad"
- private const val BANNER_AD_ENABLED = "banner_ad_enabled"
- private const val SHOW_COUNTDOWN_TO_CONTINUE = "show_countdown_to_continue"
+ override val isBannerAdEnabled: Boolean = true
- private const val DEFAULT_MIN_USAGE_TO_REVIEW = 5
- }
+ override val showCountdownToContinue: Boolean = false
}
diff --git a/proprietary/src/main/java/dev/lucasnlm/external/PlayGamesManagerImpl.kt b/proprietary/src/main/java/dev/lucasnlm/external/PlayGamesManagerImpl.kt
index 113b275a2..c153764ea 100644
--- a/proprietary/src/main/java/dev/lucasnlm/external/PlayGamesManagerImpl.kt
+++ b/proprietary/src/main/java/dev/lucasnlm/external/PlayGamesManagerImpl.kt
@@ -11,10 +11,6 @@ import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.games.Games
-import com.google.firebase.auth.FirebaseAuth
-import com.google.firebase.auth.PlayGamesAuthProvider
-import com.google.firebase.auth.ktx.auth
-import com.google.firebase.ktx.Firebase
import kotlinx.coroutines.tasks.await
class PlayGamesManagerImpl(
@@ -23,9 +19,6 @@ class PlayGamesManagerImpl(
) : PlayGamesManager {
private var account: GoogleSignInAccount? = null
private var requestLogin: Boolean = true
- private val firebaseAuth: FirebaseAuth by lazy {
- Firebase.auth
- }
private fun setupPopUp(
activity: Activity,
@@ -179,28 +172,6 @@ class PlayGamesManagerImpl(
return requestLogin
}
- override fun signInToFirebase(activity: Activity) {
- val googleAccount = account
- val serverAuthCode = googleAccount?.serverAuthCode
-
- if (googleAccount != null && serverAuthCode != null) {
- val credential = PlayGamesAuthProvider.getCredential(serverAuthCode)
-
- firebaseAuth.signInWithCredential(credential)
- .addOnCompleteListener(activity) { task ->
- if (task.isSuccessful) {
- Log.d(TAG, "signInWithCredential:success")
- } else {
- Log.w(TAG, "signInWithCredential:failure", task.exception)
- }
- }
- .addOnFailureListener {
- Log.e(TAG, "Fail to signIn with firebase", it)
- crashReporter.sendError("Fail to signIn with firebase. $it")
- }
- }
- }
-
companion object {
val TAG = PlayGamesManagerImpl::class.simpleName
}