diff --git a/about/build.gradle b/about/build.gradle index 27f60ee01..738d02406 100644 --- a/about/build.gradle +++ b/about/build.gradle @@ -48,10 +48,10 @@ dependencies { // AndroidX implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.activity:activity-ktx:1.8.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' // RecyclerView - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' // Constraint implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/about/src/main/java/dev/lucasnlm/antimine/about/views/AboutInfoFragment.kt b/about/src/main/java/dev/lucasnlm/antimine/about/views/AboutInfoFragment.kt index ad9ca824f..1546704c7 100644 --- a/about/src/main/java/dev/lucasnlm/antimine/about/views/AboutInfoFragment.kt +++ b/about/src/main/java/dev/lucasnlm/antimine/about/views/AboutInfoFragment.kt @@ -24,12 +24,13 @@ import org.koin.androidx.viewmodel.ext.android.sharedViewModel import dev.lucasnlm.antimine.i18n.R as i18n class AboutInfoFragment : Fragment() { - private lateinit var binding: FragmentAboutInfoBinding private val aboutViewModel: AboutViewModel by sharedViewModel() private val audioManager: GameAudioManager by inject() private val analyticsManager: AnalyticsManager by inject() private val unknownVersionName = "?.?.?" + private lateinit var binding: FragmentAboutInfoBinding + private fun PackageManager.getPackageInfoCompat( packageName: String, flags: Int = 0, diff --git a/app/build.gradle b/app/build.gradle index c12eb555d..d612c97f6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { defaultConfig { // versionCode and versionName must be hardcoded to support F-droid - versionCode 1705061 - versionName '17.5.6' + versionCode 1705071 + versionName '17.5.7' minSdk 21 targetSdk 34 compileSdk 34 @@ -22,10 +22,10 @@ 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') + storeFile file('../keystore') + keyAlias System.getenv('BITRISEIO_ANDROID_KEYSTORE_ALIAS') + storePassword System.getenv('BITRISEIO_ANDROID_KEYSTORE_PASSWORD') + keyPassword System.getenv('BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD') } } } @@ -77,7 +77,7 @@ android { } googleInstant { - versionCode 160 + versionCode 161 dimension 'version' applicationId 'dev.lucasnlm.antimine' versionNameSuffix ' I' @@ -122,10 +122,10 @@ dependencies { // AndroidX implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.preference:preference-ktx:1.2.1' - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.activity:activity-ktx:1.8.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' // Lifecycle implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2' @@ -158,7 +158,7 @@ dependencies { testImplementation 'androidx.test:rules:1.5.0' testImplementation 'androidx.test:runner:1.5.2' testImplementation 'androidx.test.espresso:espresso-core:3.5.1' - testImplementation 'androidx.fragment:fragment-testing:1.6.1' + testImplementation 'androidx.fragment:fragment-testing:1.6.2' testImplementation 'org.robolectric:robolectric:4.5.1' testImplementation 'androidx.test.ext:junit:1.1.5' testImplementation 'io.mockk:mockk:1.13.5' @@ -194,6 +194,13 @@ if (System.getenv('IS_GOOGLE_BUILD') == null) { enabled = false } } + + project.tasks.names.findAll { it.contains("Bugsnag") } + .forEach { taskName -> + project.tasks.named(taskName).configure { + enabled = false + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt index 75f1e17e4..6db071ac4 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt @@ -377,8 +377,11 @@ class GameActivity : } is GameEvent.VictoryDialog -> { if (preferencesRepository.showWindowsWhenFinishGame()) { - withContext(Dispatchers.Main) { - showKonfettiView() + val duration = gameViewModel.singleState().duration + if (duration > 5) { + withContext(Dispatchers.Main) { + showKonfettiView() + } } lifecycleScope.launch { diff --git a/app/src/main/java/dev/lucasnlm/antimine/custom/CustomLevelDialogFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/custom/CustomLevelDialogFragment.kt index 081143bab..e85ed725e 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/custom/CustomLevelDialogFragment.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/custom/CustomLevelDialogFragment.kt @@ -5,8 +5,12 @@ import android.content.DialogInterface import android.os.Bundle import android.view.LayoutInflater import android.view.View +import android.view.animation.AnimationUtils import androidx.appcompat.app.AppCompatDialogFragment +import androidx.core.widget.doAfterTextChanged import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.textfield.TextInputEditText +import dev.lucasnlm.antimine.R import dev.lucasnlm.antimine.core.models.Difficulty import dev.lucasnlm.antimine.custom.viewmodel.CreateGameViewModel import dev.lucasnlm.antimine.custom.viewmodel.CustomEvent @@ -47,12 +51,68 @@ class CustomLevelDialogFragment : AppCompatDialogFragment() { mapHeight.setText(state.height.toString()) mapMines.setText(state.mines.toString()) seed.setText("") + + mapWidth.checkLimit(MIN_WIDTH, MAX_WIDTH) + mapHeight.checkLimit(MIN_HEIGHT, MAX_HEIGHT) + mapMines.checkProportionOnChange() } } return binding.root } + private fun TextInputEditText.checkProportion() { + val width = binding.mapWidth.text.toString().toIntOrNull() + val height = binding.mapHeight.text.toString().toIntOrNull() + val current = text.toString().toIntOrNull() + error = + if (current != null && width != null && height != null) { + val minMines = (width * height * 0.5 - MIN_SAFE_AREA).toInt().coerceAtLeast(1) + if (current <= minMines) { + null + } else { + val maxProportion = width * height * 0.75 + if (current >= maxProportion) { + getString(i18n.string.proportion_too_high) + } else { + null + } + } + } else { + null + } + } + + private fun TextInputEditText.checkProportionOnChange() { + doAfterTextChanged { + checkProportion() + } + } + + private fun TextInputEditText.checkLimit( + min: Int, + max: Int, + ) { + doAfterTextChanged { + if (it?.isNotBlank() == true) { + val current = text.toString().toIntOrNull() + if (current != null) { + if (current > max) { + error = getString(i18n.string.value_limit_max, max) + } else if (current < min) { + error = getString(i18n.string.value_limit_min, min) + } + } else { + error = null + } + } else { + error = null + } + + binding.mapMines.checkProportion() + } + } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return MaterialAlertDialogBuilder(requireContext()).apply { setTitle(i18n.string.new_game) @@ -67,6 +127,56 @@ class CustomLevelDialogFragment : AppCompatDialogFragment() { }.create() } + private fun checkLimitFeedbacks(): Boolean { + val wantedWidth = binding.mapWidth.text.toString().toIntOrNull() + var allValid = true + if (wantedWidth == null) { + binding.mapWidth.startAnimation(AnimationUtils.loadAnimation(context, R.anim.fast_shake)) + allValid = false + } else if (wantedWidth >= MAX_WIDTH) { + binding.mapWidth.setText(MAX_WIDTH.toString()) + binding.mapWidth.startAnimation(AnimationUtils.loadAnimation(context, R.anim.fast_shake)) + allValid = false + } else if (wantedWidth <= MIN_WIDTH) { + binding.mapWidth.setText(MIN_WIDTH.toString()) + binding.mapWidth.startAnimation(AnimationUtils.loadAnimation(context, R.anim.fast_shake)) + allValid = false + } + + val wantedHeight = binding.mapHeight.text.toString().toIntOrNull() + if (wantedHeight == null) { + binding.mapHeight.startAnimation(AnimationUtils.loadAnimation(context, R.anim.fast_shake)) + allValid = false + } else if (wantedHeight >= MAX_HEIGHT) { + binding.mapHeight.setText(MAX_HEIGHT.toString()) + binding.mapHeight.startAnimation(AnimationUtils.loadAnimation(context, R.anim.fast_shake)) + allValid = false + } else if (wantedHeight <= MIN_HEIGHT) { + binding.mapHeight.setText(MIN_HEIGHT.toString()) + binding.mapHeight.startAnimation(AnimationUtils.loadAnimation(context, R.anim.fast_shake)) + allValid = false + } + + if (allValid && wantedWidth != null && wantedHeight != null) { + val wantedMines = binding.mapMines.text.toString().toIntOrNull() + val maxMines = wantedWidth * wantedHeight - MIN_SAFE_AREA + if (wantedMines == null) { + binding.mapMines.startAnimation(AnimationUtils.loadAnimation(context, R.anim.fast_shake)) + allValid = false + } else if (wantedMines >= maxMines) { + binding.mapMines.setText((wantedWidth * wantedHeight - MIN_SAFE_AREA).toString()) + binding.mapMines.startAnimation(AnimationUtils.loadAnimation(context, R.anim.fast_shake)) + allValid = false + } else if (wantedMines <= MIN_MINES) { + binding.mapMines.setText(MIN_MINES.toString()) + binding.mapMines.startAnimation(AnimationUtils.loadAnimation(context, R.anim.fast_shake)) + allValid = false + } + } + + return allValid + } + override fun onDismiss(dialog: DialogInterface) { if (activity is DialogInterface.OnDismissListener) { (activity as DialogInterface.OnDismissListener).onDismiss(dialog) @@ -79,8 +189,8 @@ class CustomLevelDialogFragment : AppCompatDialogFragment() { const val MIN_HEIGHT = 5 const val MIN_MINES = 3 const val MIN_SAFE_AREA = 9 - const val MAX_WIDTH = 50 - const val MAX_HEIGHT = 50 + const val MAX_WIDTH = 100 + const val MAX_HEIGHT = 100 private fun filterInput( target: String, diff --git a/app/src/main/java/dev/lucasnlm/antimine/di/AppModule.kt b/app/src/main/java/dev/lucasnlm/antimine/di/AppModule.kt index 74d1f6059..cf358280c 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/di/AppModule.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/di/AppModule.kt @@ -8,6 +8,8 @@ import dev.lucasnlm.antimine.core.analytics.ProdAnalyticsManager import dev.lucasnlm.antimine.core.cloud.CloudSaveManager import dev.lucasnlm.antimine.core.haptic.HapticFeedbackManager import dev.lucasnlm.antimine.core.haptic.HapticFeedbackManagerImpl +import dev.lucasnlm.antimine.core.repository.DimensionRepository +import dev.lucasnlm.antimine.core.repository.DimensionRepositoryImpl import dev.lucasnlm.antimine.l10n.GameLocaleManager import dev.lucasnlm.antimine.l10n.GameLocaleManagerImpl import dev.lucasnlm.antimine.support.AppVersionManagerImpl @@ -25,6 +27,8 @@ val AppModule = module { factory { CoroutineScope(Dispatchers.Main + SupervisorJob()) } + single { DimensionRepositoryImpl(get()) } bind DimensionRepository::class + single { IapHandler(get(), get(), get()) } single { 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 df3d32971..7aa9acca1 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/main/MainActivity.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/main/MainActivity.kt @@ -22,6 +22,7 @@ import dev.lucasnlm.antimine.common.io.models.SaveStatus import dev.lucasnlm.antimine.common.level.repository.MinefieldRepository import dev.lucasnlm.antimine.common.level.repository.SavesRepository import dev.lucasnlm.antimine.control.ControlActivity +import dev.lucasnlm.antimine.core.ActivityExt.compatOverridePendingTransition import dev.lucasnlm.antimine.core.audio.GameAudioManager import dev.lucasnlm.antimine.core.models.Analytics import dev.lucasnlm.antimine.core.models.Difficulty diff --git a/app/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesActivity.kt b/app/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesActivity.kt index c4411d72d..bf605717d 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesActivity.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesActivity.kt @@ -35,10 +35,12 @@ class PreferencesActivity : private val settingsBackupManager: SettingsBackupManager by lazy { SettingsBackupManager(applicationContext) } + private val binding: ActivityPreferencesBinding by lazy { + ActivityPreferencesBinding.inflate(layoutInflater) + } private lateinit var exportResultLauncher: ActivityResultLauncher private lateinit var importResultLauncher: ActivityResultLauncher - private lateinit var binding: ActivityPreferencesBinding private val preferenceManager by lazy { PreferenceManager.getDefaultSharedPreferences(applicationContext) @@ -61,7 +63,6 @@ class PreferencesActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityPreferencesBinding.inflate(layoutInflater) setContentView(binding.root) exportResultLauncher = @@ -205,6 +206,11 @@ class PreferencesActivity : onChangeValue = { preferencesRepository.setDimNumbers(it) }, ) + binding.immersiveMode.bindItem( + initialValue = preferencesRepository.useImmersiveMode(), + onChangeValue = { preferencesRepository.setImmersiveMode(it) }, + ) + if (playGamesManager.hasGooglePlayGames()) { binding.playGames.bindItem( initialValue = preferencesRepository.keepRequestPlayGames(), diff --git a/app/src/main/res/layout/activity_preferences.xml b/app/src/main/res/layout/activity_preferences.xml index 4b27af5ff..7c6be351b 100644 --- a/app/src/main/res/layout/activity_preferences.xml +++ b/app/src/main/res/layout/activity_preferences.xml @@ -103,6 +103,13 @@ android:layout_height="wrap_content" android:paddingVertical="16dp" android:text="@string/google_play_games" /> + + diff --git a/app/src/main/res/layout/game_over_dialog.xml b/app/src/main/res/layout/game_over_dialog.xml index bb2d2326f..25b001bed 100644 --- a/app/src/main/res/layout/game_over_dialog.xml +++ b/app/src/main/res/layout/game_over_dialog.xml @@ -65,6 +65,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="?selectableItemBackgroundBorderless" + android:tooltipText="@string/close" android:contentDescription="@string/cancel" android:importantForAccessibility="no" android:padding="8dp" @@ -78,9 +79,11 @@ style="@style/Widget.Material3.Button.IconButton" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:background="?selectableItemBackgroundBorderless" android:contentDescription="@string/settings" android:importantForAccessibility="no" android:padding="8dp" + android:tooltipText="@string/settings" app:icon="@drawable/settings" app:iconTint="?colorOnBackground" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/win_dialog.xml b/app/src/main/res/layout/win_dialog.xml index d33ec8e3a..c115bd002 100644 --- a/app/src/main/res/layout/win_dialog.xml +++ b/app/src/main/res/layout/win_dialog.xml @@ -81,6 +81,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="?selectableItemBackgroundBorderless" + android:tooltipText="@string/close" android:contentDescription="@string/cancel" android:importantForAccessibility="no" android:padding="8dp" @@ -95,6 +96,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="?selectableItemBackgroundBorderless" + android:tooltipText="@string/settings" android:contentDescription="@string/settings" android:importantForAccessibility="no" android:padding="8dp" diff --git a/app/src/test/java/dev/lucasnlm/antimine/di/TestCommonModule.kt b/app/src/test/java/dev/lucasnlm/antimine/di/TestCommonModule.kt index 42852d5cf..c694c40ed 100644 --- a/app/src/test/java/dev/lucasnlm/antimine/di/TestCommonModule.kt +++ b/app/src/test/java/dev/lucasnlm/antimine/di/TestCommonModule.kt @@ -9,6 +9,7 @@ import dev.lucasnlm.antimine.ui.model.AppSkin import dev.lucasnlm.antimine.ui.model.AppTheme import dev.lucasnlm.antimine.ui.repository.Skins import dev.lucasnlm.antimine.ui.repository.ThemeRepository +import dev.lucasnlm.antimine.ui.repository.Themes.darkTheme import dev.lucasnlm.antimine.ui.repository.Themes.lightTheme import io.mockk.mockk import org.koin.dsl.bind @@ -34,6 +35,8 @@ val TestCommonModule = override fun getAllThemes(): List = listOf(lightTheme()) + override fun getAllDarkThemes(): List = listOf(darkTheme()) + override fun getAllSkins(): List = Skins.getAllSkins() override fun setTheme(themeId: Long) {} diff --git a/app/src/test/java/dev/lucasnlm/antimine/mocks/MockPreferencesRepository.kt b/app/src/test/java/dev/lucasnlm/antimine/mocks/MockPreferencesRepository.kt index cd2e72b3c..e72e9539b 100644 --- a/app/src/test/java/dev/lucasnlm/antimine/mocks/MockPreferencesRepository.kt +++ b/app/src/test/java/dev/lucasnlm/antimine/mocks/MockPreferencesRepository.kt @@ -156,12 +156,6 @@ class MockPreferencesRepository : PreferencesRepository { // Not implemented } - override fun useAnimations(): Boolean = false - - override fun setAnimations(enabled: Boolean) { - // Not implemented - } - override fun useQuestionMark(): Boolean = false override fun setQuestionMark(value: Boolean) { @@ -303,4 +297,12 @@ class MockPreferencesRepository : PreferencesRepository { override fun setDefaultSwitchButton(action: Action) { // Not implemented } + + override fun useImmersiveMode(): Boolean { + return false + } + + override fun setImmersiveMode(enabled: Boolean) { + // Not implemented + } } diff --git a/common/build.gradle b/common/build.gradle index da2ee1007..64136be49 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -45,9 +45,9 @@ dependencies { // AndroidX implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.preference:preference-ktx:1.2.1' - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation 'androidx.activity:activity-ktx:1.8.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' // Google implementation 'com.google.android.material:material:1.10.0' 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 38912ccdb..6327bceb7 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 @@ -5,9 +5,14 @@ import android.os.Bundle import android.text.format.DateUtils import android.view.Gravity import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View +import android.view.ViewConfiguration import android.view.ViewGroup import android.widget.FrameLayout +import androidx.core.view.InputDeviceCompat +import androidx.core.view.MotionEventCompat +import androidx.core.view.ViewConfigurationCompat import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope @@ -123,10 +128,26 @@ open class GameRenderFragment : AndroidFragmentApplication() { useCompass = false useGyroscope = false useWakelock = false - useImmersiveMode = false + useImmersiveMode = preferencesRepository.useImmersiveMode() disableAudio = true } - return initializeForView(levelApplicationListener, config) + return initializeForView(levelApplicationListener, config).apply { + setOnGenericMotionListener { _, event -> + if (event.action == MotionEvent.ACTION_SCROLL && + event.isFromSource(InputDeviceCompat.SOURCE_ROTARY_ENCODER) + ) { + val delta = + -event.getAxisValue(MotionEventCompat.AXIS_SCROLL) * + ViewConfigurationCompat.getScaledVerticalScrollFactor( + ViewConfiguration.get(context), context, + ) + levelApplicationListener.onScroll(delta) + true + } else { + false + } + } + } } override fun onPause() { 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 2bfb57dcf..1b675f40e 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 @@ -228,7 +228,7 @@ open class GameViewModel( newState = newState.copy(field = gameController.field()) val sideEffect = GameEvent.GameOverDialog( - delayToShow = explosionDelay(), + delayToShow = EXPLOSION_DELAY_MS, totalMines = gameController.mines().count(), rightMines = gameController.mines().count { it.mark.isNotNone() }, timestamp = state.duration, @@ -659,8 +659,6 @@ open class GameViewModel( } } - private fun explosionDelay() = if (preferencesRepository.useAnimations()) EXPLOSION_DELAY else 0L - fun hasUnknownMines(): Boolean { return !gameController.hasIsolatedAllMines() && gameController.remainingMines() > 1 } @@ -961,7 +959,7 @@ open class GameViewModel( } companion object { - const val EXPLOSION_DELAY = 400L + const val EXPLOSION_DELAY_MS = 1000L const val THIRTY_SECONDS_ACHIEVEMENT = 30L const val MIN_REWARD_GAME_SECONDS = 10L const val REWARD_RATIO_WITH_MISTAKES = 0.025 diff --git a/common/src/main/java/dev/lucasnlm/antimine/core/di/CommonModule.kt b/common/src/main/java/dev/lucasnlm/antimine/core/di/CommonModule.kt index b55f5df35..2ef234d05 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/core/di/CommonModule.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/core/di/CommonModule.kt @@ -3,8 +3,6 @@ package dev.lucasnlm.antimine.core.di import android.view.ViewConfiguration import dev.lucasnlm.antimine.core.audio.GameAudioManager import dev.lucasnlm.antimine.core.audio.GameAudioManagerImpl -import dev.lucasnlm.antimine.core.repository.DimensionRepository -import dev.lucasnlm.antimine.core.repository.DimensionRepositoryImpl import dev.lucasnlm.antimine.preferences.PreferencesManager import dev.lucasnlm.antimine.preferences.PreferencesManagerImpl import dev.lucasnlm.antimine.preferences.PreferencesRepository @@ -18,8 +16,6 @@ val CommonModule = module { single { PreferencesManagerImpl(get()) } bind PreferencesManager::class - single { DimensionRepositoryImpl(get()) } bind DimensionRepository::class - single { PreferencesRepositoryImpl( get(), diff --git a/control/build.gradle b/control/build.gradle index 13b2960c2..a2acaf021 100644 --- a/control/build.gradle +++ b/control/build.gradle @@ -46,10 +46,10 @@ dependencies { // AndroidX implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.activity:activity-ktx:1.8.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' // RecyclerView - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' // Constraint implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/control/src/main/res/layout/activity_control.xml b/control/src/main/res/layout/activity_control.xml index 58f9a3e87..1b5f8fd3d 100644 --- a/control/src/main/res/layout/activity_control.xml +++ b/control/src/main/res/layout/activity_control.xml @@ -128,6 +128,9 @@ android:valueFrom="0" android:valueTo="15" app:tickVisible="false" + app:trackHeight="24dp" + app:thumbElevation="0dp" + app:thumbColor="?colorOnPrimary" app:trackColorInactive="?colorOnSurfaceVariant" /> @@ -155,6 +158,9 @@ android:valueFrom="0" android:valueTo="200" app:tickVisible="false" + app:trackHeight="24dp" + app:thumbElevation="0dp" + app:thumbColor="?colorOnPrimary" app:trackColorInactive="?colorOnSurfaceVariant" /> @@ -181,6 +187,9 @@ android:valueFrom="0" android:valueTo="2000" app:tickVisible="false" + app:trackHeight="24dp" + app:thumbElevation="0dp" + app:thumbColor="?colorOnPrimary" app:trackColorInactive="?colorOnSurfaceVariant" /> @@ -206,7 +215,10 @@ android:value="250" android:valueFrom="100" android:valueTo="700" + app:trackHeight="24dp" app:tickVisible="false" + app:thumbElevation="0dp" + app:thumbColor="?colorOnPrimary" app:trackColorInactive="?colorOnSurfaceVariant" /> diff --git a/core/src/main/java/dev/lucasnlm/antimine/core/ActivityExt.kt b/core/src/main/java/dev/lucasnlm/antimine/core/ActivityExt.kt new file mode 100644 index 000000000..93d52041a --- /dev/null +++ b/core/src/main/java/dev/lucasnlm/antimine/core/ActivityExt.kt @@ -0,0 +1,17 @@ +package dev.lucasnlm.antimine.core + +import android.app.Activity +import android.os.Build +import androidx.activity.ComponentActivity + +object ActivityExt { + @Suppress("DEPRECATION") + fun Activity.compatOverridePendingTransition() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + overridePendingTransition(0, 0) + } else { + overrideActivityTransition(ComponentActivity.OVERRIDE_TRANSITION_OPEN, 0, 0) + overrideActivityTransition(ComponentActivity.OVERRIDE_TRANSITION_CLOSE, 0, 0) + } + } +} diff --git a/core/src/main/java/dev/lucasnlm/antimine/core/repository/WearDimensionRepositoryImpl.kt b/core/src/main/java/dev/lucasnlm/antimine/core/repository/WearDimensionRepositoryImpl.kt new file mode 100644 index 000000000..528f97c5e --- /dev/null +++ b/core/src/main/java/dev/lucasnlm/antimine/core/repository/WearDimensionRepositoryImpl.kt @@ -0,0 +1,48 @@ +package dev.lucasnlm.antimine.core.repository + +import android.content.Context +import android.content.res.Resources +import dev.lucasnlm.antimine.core.R +import dev.lucasnlm.antimine.core.models.MinefieldSize + +class WearDimensionRepositoryImpl( + private val context: Context, +) : DimensionRepository { + + override fun areaSize(): Float { + return displaySize().width / 5.0f + } + + override fun areaSeparator(): Float { + return context.resources.getDimension(R.dimen.field_padding) + } + + override fun areaSizeWithPadding(): Float { + return areaSize() + 2 * areaSeparator() + } + + override fun displaySize(): MinefieldSize = + with(Resources.getSystem().displayMetrics) { + return MinefieldSize(this.widthPixels, this.heightPixels) + } + + override fun actionBarSizeWithStatus(): Int { + return 0 + } + + override fun actionBarSize(): Int { + return 0 + } + + override fun navigationBarHeight(): Int { + return 0 + } + + override fun verticalNavigationBarHeight(): Int { + return 0 + } + + override fun horizontalNavigationBarHeight(): Int { + return 0 + } +} diff --git a/donation/build.gradle b/donation/build.gradle index d44e3feac..7fcf42811 100644 --- a/donation/build.gradle +++ b/donation/build.gradle @@ -44,13 +44,13 @@ dependencies { // AndroidX implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.activity:activity-ktx:1.8.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' // Google implementation 'com.google.android.material:material:1.10.0' // RecyclerView - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' // Constraint implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/gdx/build.gradle b/gdx/build.gradle index 74b7c5e17..ad0572126 100644 --- a/gdx/build.gradle +++ b/gdx/build.gradle @@ -48,7 +48,7 @@ dependencies { // AndroidX implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.activity:activity-ktx:1.8.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' // Koin implementation 'io.insert-koin:koin-android:3.1.2' 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 7f049ac43..1af3e23a6 100644 --- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt +++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt @@ -189,6 +189,10 @@ class GameApplicationListener( minefieldStage.setZoom(GameContext.zoom) } + fun onScroll(delta: Float) { + minefieldStage.scaleZoom(delta) + } + fun refreshSettings() { actionSettings = with(preferencesRepository) { diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 76fa9bf4f..1aba078f4 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -105,6 +105,7 @@ Language Licenses Google Play Games + Immersive Mode Loading… Creating valid game… Connect @@ -142,6 +143,9 @@ The current minefield may not be guess-free! Fixed Size Progressive + Min is %d + Max is %d + Proportion is too high! Highlight unsolved numbers Show clock If you like this game, consider making a donation. diff --git a/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferenceKeys.kt b/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferenceKeys.kt index c4cf824de..715bcfea8 100644 --- a/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferenceKeys.kt +++ b/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferenceKeys.kt @@ -4,7 +4,6 @@ object PreferenceKeys { const val PREFERENCE_VIBRATION = "preference_vibration" const val PREFERENCE_VIBRATION_LEVEL = "preference_vibration_level" const val PREFERENCE_ASSISTANT = "preference_assistant" - const val PREFERENCE_ANIMATION = "preference_animation" const val PREFERENCE_QUESTION_MARK = "preference_use_question_mark" const val PREFERENCE_USE_HINT = "preference_use_help" const val PREFERENCE_LAST_HELP_USED = "preference_last_help_used" @@ -49,4 +48,5 @@ object PreferenceKeys { const val PREFERENCE_REQUEST_PLAY_GAMES = "preference_play_games" const val PREFERENCE_LAST_VERSION = "preference_last_version" const val PREFERENCE_DEFAULT_SWITCH_BUTTON = "preference_default_switch_button" + const val PREFERENCE_USE_IMMERSIVE_MODE = "preference_use_immersive_mode" } diff --git a/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesRepository.kt b/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesRepository.kt index c960db0fa..f4988f813 100644 --- a/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesRepository.kt +++ b/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesRepository.kt @@ -129,10 +129,6 @@ interface PreferencesRepository { fun resetHapticFeedbackLevel() - fun useAnimations(): Boolean - - fun setAnimations(enabled: Boolean) - fun useQuestionMark(): Boolean fun setQuestionMark(value: Boolean) @@ -200,4 +196,8 @@ interface PreferencesRepository { fun defaultSwitchButton(): Action fun setDefaultSwitchButton(action: Action) + + fun useImmersiveMode(): Boolean + + fun setImmersiveMode(enabled: Boolean) } diff --git a/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesRepositoryImpl.kt b/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesRepositoryImpl.kt index 256bc6805..e1136f6cb 100644 --- a/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesRepositoryImpl.kt +++ b/preferences/src/main/java/dev/lucasnlm/antimine/preferences/PreferencesRepositoryImpl.kt @@ -24,7 +24,6 @@ class PreferencesRepositoryImpl( private val listOfSettingsCustoms = listOf( PreferenceKeys.PREFERENCE_ASSISTANT, - PreferenceKeys.PREFERENCE_ANIMATION, PreferenceKeys.PREFERENCE_QUESTION_MARK, PreferenceKeys.PREFERENCE_USE_HINT, PreferenceKeys.PREFERENCE_SOUND_EFFECTS, @@ -109,14 +108,6 @@ class PreferencesRepositoryImpl( preferencesManager.removeKey(PreferenceKeys.PREFERENCE_VIBRATION_LEVEL) } - override fun useAnimations(): Boolean { - return preferencesManager.getBoolean(PreferenceKeys.PREFERENCE_ANIMATION, true) - } - - override fun setAnimations(enabled: Boolean) { - preferencesManager.putBoolean(PreferenceKeys.PREFERENCE_ANIMATION, enabled) - } - override fun useQuestionMark(): Boolean = preferencesManager.getBoolean(PreferenceKeys.PREFERENCE_QUESTION_MARK, false) @@ -494,4 +485,12 @@ class PreferencesRepositoryImpl( override fun setDefaultSwitchButton(action: Action) { preferencesManager.putInt(PreferenceKeys.PREFERENCE_DEFAULT_SWITCH_BUTTON, action.ordinal) } + + override fun useImmersiveMode(): Boolean { + return preferencesManager.getBoolean(PreferenceKeys.PREFERENCE_USE_IMMERSIVE_MODE, false) + } + + override fun setImmersiveMode(enabled: Boolean) { + preferencesManager.putBoolean(PreferenceKeys.PREFERENCE_USE_IMMERSIVE_MODE, enabled) + } } diff --git a/themes/build.gradle b/themes/build.gradle index 024570d28..b42b3b4a4 100644 --- a/themes/build.gradle +++ b/themes/build.gradle @@ -44,13 +44,13 @@ dependencies { // AndroidX implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.activity:activity-ktx:1.8.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' // Google implementation 'com.google.android.material:material:1.10.0' // RecyclerView - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' // Constraint implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/themes/src/main/java/dev/lucasnlm/antimine/themes/ThemeActivity.kt b/themes/src/main/java/dev/lucasnlm/antimine/themes/ThemeActivity.kt index 8158633ad..fbd5f0b6d 100644 --- a/themes/src/main/java/dev/lucasnlm/antimine/themes/ThemeActivity.kt +++ b/themes/src/main/java/dev/lucasnlm/antimine/themes/ThemeActivity.kt @@ -27,10 +27,7 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import dev.lucasnlm.antimine.i18n.R as i18n class ThemeActivity : ThemedActivity() { - private lateinit var binding: ActivityThemeBinding - private val themeViewModel by viewModel() - private val dimensionRepository: DimensionRepository by inject() private val cloudSaveManager by inject() private val preferencesRepository: PreferencesRepository by inject() @@ -39,9 +36,12 @@ class ThemeActivity : ThemedActivity() { private val analyticsManager: AnalyticsManager by inject() private val gameAudioManager: GameAudioManager by inject() + private val binding: ActivityThemeBinding by lazy { + ActivityThemeBinding.inflate(layoutInflater) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityThemeBinding.inflate(layoutInflater) setContentView(binding.root) analyticsManager.sentEvent(Analytics.OpenThemes) diff --git a/tutorial/build.gradle b/tutorial/build.gradle index 989daab10..1bbefac6d 100644 --- a/tutorial/build.gradle +++ b/tutorial/build.gradle @@ -46,13 +46,13 @@ dependencies { // AndroidX implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.activity:activity-ktx:1.8.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' // Google implementation 'com.google.android.material:material:1.10.0' // RecyclerView - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' // Constraint implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/ui/build.gradle b/ui/build.gradle index 71cb2221b..e413bd7b2 100644 --- a/ui/build.gradle +++ b/ui/build.gradle @@ -46,10 +46,10 @@ dependencies { // AndroidX implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.activity:activity-ktx:1.8.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' // RecyclerView - implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.3.2' // Constraint implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/ui/src/main/java/dev/lucasnlm/antimine/ui/ext/ThemedActivity.kt b/ui/src/main/java/dev/lucasnlm/antimine/ui/ext/ThemedActivity.kt index 2e28eb99e..5fe031f13 100644 --- a/ui/src/main/java/dev/lucasnlm/antimine/ui/ext/ThemedActivity.kt +++ b/ui/src/main/java/dev/lucasnlm/antimine/ui/ext/ThemedActivity.kt @@ -1,11 +1,11 @@ package dev.lucasnlm.antimine.ui.ext -import android.os.Build import android.os.Bundle import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import com.google.android.material.appbar.MaterialToolbar +import dev.lucasnlm.antimine.core.ActivityExt.compatOverridePendingTransition import dev.lucasnlm.antimine.ui.model.AppSkin import dev.lucasnlm.antimine.ui.model.AppTheme import dev.lucasnlm.antimine.ui.model.TopBarAction @@ -62,16 +62,6 @@ abstract class ThemedActivity : AppCompatActivity() { return super.onCreateOptionsMenu(menu) } - @Suppress("DEPRECATION") - protected fun compatOverridePendingTransition() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - overridePendingTransition(0, 0) - } else { - overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, 0, 0) - overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, 0, 0) - } - } - override fun onResume() { super.onResume() diff --git a/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/ThemeRepository.kt b/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/ThemeRepository.kt index d228f8a84..385b33af2 100644 --- a/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/ThemeRepository.kt +++ b/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/ThemeRepository.kt @@ -12,6 +12,8 @@ interface ThemeRepository { fun getAllThemes(): List + fun getAllDarkThemes(): List + fun getAllSkins(): List fun setTheme(themeId: Long) diff --git a/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/ThemeRepositoryImpl.kt b/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/ThemeRepositoryImpl.kt index 7c7e0f8a0..b9d6de5f1 100644 --- a/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/ThemeRepositoryImpl.kt +++ b/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/ThemeRepositoryImpl.kt @@ -40,7 +40,13 @@ class ThemeRepositoryImpl( return getCustomTheme() ?: getDefaultTheme() } - override fun getAllThemes(): List = listOf(buildSystemTheme()) + Themes.getAllCustom() + override fun getAllThemes(): List { + return listOf(buildSystemTheme()) + Themes.getAllCustom() + } + + override fun getAllDarkThemes(): List { + return getAllThemes().filter { it.isDarkTheme } + } override fun getAllSkins(): List { return Skins.getAllSkins() diff --git a/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/Themes.kt b/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/Themes.kt index cb4b11943..88a457abe 100644 --- a/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/Themes.kt +++ b/ui/src/main/java/dev/lucasnlm/antimine/ui/repository/Themes.kt @@ -34,7 +34,7 @@ object Themes { isDarkTheme = false, ) - private fun darkTheme() = + fun darkTheme() = AppTheme( id = 3L, theme = R.style.CustomDarkTheme, diff --git a/ui/src/main/res/values/themes.xml b/ui/src/main/res/values/themes.xml index 0c75aaaf8..75d651c54 100644 --- a/ui/src/main/res/values/themes.xml +++ b/ui/src/main/res/values/themes.xml @@ -79,7 +79,7 @@ icon_preferred - - - - - - - - - - - - - - - - +