Update IAP

This commit is contained in:
Lucas Lima 2020-09-04 16:50:33 -03:00
parent 50879b67bb
commit 90d25d08f3
55 changed files with 412 additions and 171 deletions

View file

@ -39,10 +39,12 @@ import dev.lucasnlm.antimine.share.ShareManager
import dev.lucasnlm.antimine.stats.StatsActivity
import dev.lucasnlm.antimine.support.SupportAppDialogFragment
import dev.lucasnlm.antimine.theme.ThemeActivity
import dev.lucasnlm.external.IBillingManager
import dev.lucasnlm.external.IInstantAppManager
import dev.lucasnlm.external.IPlayGamesManager
import dev.lucasnlm.external.ReviewWrapper
import kotlinx.android.synthetic.main.activity_game.*
import kotlinx.android.synthetic.main.activity_game.ad_placeholder
import kotlinx.android.synthetic.main.activity_game.minesCount
import kotlinx.android.synthetic.main.activity_game.timer
import kotlinx.android.synthetic.main.activity_tv_game.*
@ -54,6 +56,8 @@ import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.OnDismissListener {
private val billingManager: IBillingManager by inject()
private val preferencesRepository: IPreferencesRepository by inject()
private val analyticsManager: IAnalyticsManager by inject()
@ -91,6 +95,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
bindToolbar()
bindDrawer()
bindNavigationMenu()
showAds()
findViewById<FrameLayout>(R.id.levelContainer).doOnLayout {
loadGameFragment()
@ -346,6 +351,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
R.id.previous_games -> openSaveHistory()
R.id.stats -> openStats()
R.id.play_games -> googlePlay()
R.id.remove_ads -> showSupportAppDialog()
else -> handled = false
}
@ -356,10 +362,16 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
handled
}
navigationView.menu.findItem(R.id.share_now).isVisible = !instantAppManager.isEnabled(applicationContext)
navigationView.menu.apply {
findItem(R.id.share_now).isVisible = !instantAppManager.isEnabled(applicationContext)
findItem(R.id.remove_ads).isVisible = !preferencesRepository.isPremiumEnabled()
if (!playGamesManager.hasGooglePlayGames()) {
navigationView.menu.removeGroup(R.id.play_games_group)
if (!playGamesManager.hasGooglePlayGames()) {
removeGroup(R.id.play_games_group)
}
// New Features
findItem(R.id.themes).setActionView(R.layout.new_icon)
}
}
@ -370,7 +382,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
} else {
val current = preferencesRepository.getUseCount()
val shouldRequestRating = preferencesRepository.isRequestRatingEnabled()
val shouldRequestSupport = preferencesRepository.isPremiumEnabled()
val shouldRequestSupport = !preferencesRepository.isPremiumEnabled() && billingManager.isEnabled()
if (current >= MIN_USAGES_TO_IAP && !shouldRequestSupport) {
analyticsManager.sentEvent(Analytics.UnlockIapDialog)
@ -535,6 +547,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
Event.StartNewGame -> {
status = Status.PreGame
refreshShortcutIcon()
showAds()
}
Event.Resume, Event.Running -> {
status = Status.Running
@ -636,6 +649,22 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
refreshUserPreferences()
resumeGame()
}
if (preferencesRepository.isPremiumEnabled()) {
disableAds()
}
}
private fun showAds() {
if (!preferencesRepository.isPremiumEnabled()) {
ad_placeholder.visibility = View.VISIBLE
ad_placeholder.loadAd()
}
}
private fun disableAds() {
navigationView.menu.findItem(R.id.remove_ads).isVisible = false
ad_placeholder.visibility = View.GONE
}
private fun silentGooglePlayLogin() {

View file

@ -17,7 +17,7 @@ val ViewModelModule = module {
viewModel { AboutViewModel(get()) }
viewModel { ControlViewModel(get()) }
viewModel { CreateGameViewModel(get()) }
viewModel { HistoryViewModel(get(), get()) }
viewModel { HistoryViewModel(get(), get(), get()) }
viewModel { EndGameDialogViewModel(get()) }
viewModel { PlayGamesViewModel(get(), get()) }
viewModel { StatsViewModel(get(), get()) }

View file

@ -3,5 +3,6 @@ package dev.lucasnlm.antimine.history.viewmodel
import dev.lucasnlm.antimine.common.level.database.models.Save
data class HistoryState(
val saveList: List<Save>
val saveList: List<Save>,
val showAds: Boolean,
)

View file

@ -5,16 +5,19 @@ import android.content.Intent
import android.net.Uri
import dev.lucasnlm.antimine.DeepLink
import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.core.viewmodel.IntentViewModel
import kotlinx.coroutines.flow.flow
class HistoryViewModel(
private val context: Context,
private val savesRepository: ISavesRepository,
private val preferencesRepository: IPreferencesRepository,
) : IntentViewModel<HistoryEvent, HistoryState>() {
override fun initialState() = HistoryState(
saveList = listOf()
saveList = listOf(),
showAds = !preferencesRepository.isPremiumEnabled(),
)
override fun onEvent(event: HistoryEvent) {

View file

@ -10,6 +10,7 @@ import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.history.viewmodel.HistoryEvent
import dev.lucasnlm.antimine.history.viewmodel.HistoryViewModel
import kotlinx.android.synthetic.main.fragment_history.*
import kotlinx.android.synthetic.main.fragment_history.view.*
import kotlinx.coroutines.flow.collect
import org.koin.androidx.viewmodel.ext.android.viewModel
@ -28,6 +29,11 @@ class HistoryFragment : Fragment(R.layout.fragment_history) {
layoutManager = LinearLayoutManager(view.context)
adapter = HistoryAdapter(it.saveList, historyViewModel)
}
if (it.showAds) {
view.ad_placeholder.visibility = View.VISIBLE
view.ad_placeholder.loadAd()
}
}
}
}

View file

@ -3,6 +3,7 @@ package dev.lucasnlm.antimine.stats
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import dev.lucasnlm.antimine.R
@ -21,13 +22,17 @@ class StatsActivity : ThematicActivity(R.layout.activity_stats) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
refreshStats(StatsViewModel.emptyStats)
lifecycleScope.launchWhenResumed {
statsViewModel.sendEvent(StatsEvent.LoadStats)
statsViewModel.observeState().collect {
refreshStats(it)
if (it.showAds) {
ad_placeholder.visibility = View.VISIBLE
ad_placeholder.loadAd()
}
}
}
}

View file

@ -7,4 +7,5 @@ data class StatsModel(
val mines: Int,
val victory: Int,
val openArea: Int,
val showAds: Boolean,
)

View file

@ -17,7 +17,15 @@ class StatsViewModel(
return if (statsCount > 0) {
val result = stats.fold(
StatsModel(statsCount, 0L, 0L, 0, 0, 0)
StatsModel(
totalGames = statsCount,
duration = 0,
averageDuration = 0,
mines = 0,
victory = 0,
openArea = 0,
showAds = !preferenceRepository.isPremiumEnabled(),
)
) { acc, value ->
StatsModel(
acc.totalGames,
@ -25,12 +33,21 @@ class StatsViewModel(
0,
acc.mines + value.mines,
acc.victory + value.victory,
acc.openArea + value.openArea
acc.openArea + value.openArea,
showAds = !preferenceRepository.isPremiumEnabled(),
)
}
result.copy(averageDuration = result.duration / result.totalGames)
} else {
emptyStats
StatsModel(
totalGames = 0,
duration = 0,
averageDuration = 0,
mines = 0,
victory = 0,
openArea = 0,
showAds = !preferenceRepository.isPremiumEnabled()
)
}
}
@ -40,7 +57,15 @@ class StatsViewModel(
}
}
override fun initialState(): StatsModel = emptyStats
override fun initialState() = StatsModel(
totalGames = 0,
duration = 0,
averageDuration = 0,
mines = 0,
victory = 0,
openArea = 0,
showAds = !preferenceRepository.isPremiumEnabled()
)
override suspend fun mapEventToState(event: StatsEvent) = flow {
when (event) {
@ -49,12 +74,17 @@ class StatsViewModel(
}
is StatsEvent.DeleteStats -> {
deleteAll()
emit(emptyStats)
emit(
state.copy(
totalGames = 0,
duration = 0,
averageDuration = 0,
mines = 0,
victory = 0,
openArea = 0,
)
)
}
}
}
companion object {
val emptyStats = StatsModel(0, 0, 0, 0, 0, 0)
}
}

View file

@ -1,7 +1,9 @@
package dev.lucasnlm.antimine.support
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.lifecycle.lifecycleScope
@ -44,12 +46,19 @@ class SupportAppDialogFragment : AppCompatDialogFragment() {
if (showUnlockMessage) {
setNeutralButton(R.string.try_it) { _, _ ->
analyticsManager.sentEvent(Analytics.UnlockRewardedDialog)
adsManager.requestRewarded(requireActivity(), Ads.RewardsAds) {
if (targetThemeId > 0) {
themeRepository.setTheme(targetThemeId)
requireActivity().recreate()
adsManager.requestRewarded(
requireActivity(),
Ads.RewardsAds,
onRewarded = {
if (targetThemeId > 0) {
themeRepository.setTheme(targetThemeId)
requireActivity().recreate()
}
},
onFail = {
Toast.makeText(context, R.string.sign_in_failed, Toast.LENGTH_SHORT).show()
}
}
)
}
} else {
setNeutralButton(R.string.rating_button_no) { _, _ ->
@ -66,6 +75,13 @@ class SupportAppDialogFragment : AppCompatDialogFragment() {
}.create()
}
override fun onDismiss(dialog: DialogInterface) {
if (activity is DialogInterface.OnDismissListener) {
(activity as DialogInterface.OnDismissListener).onDismiss(dialog)
}
super.onDismiss(dialog)
}
companion object {
val TAG = SupportAppDialogFragment::class.simpleName

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,4L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,6c0,-1.11 -0.89,-2 -2,-2zM8.5,14.21c0,0.43 -0.36,0.79 -0.79,0.79 -0.25,0 -0.49,-0.12 -0.64,-0.33L4.75,11.5v2.88c0,0.35 -0.28,0.62 -0.62,0.62s-0.63,-0.28 -0.63,-0.62L3.5,9.79c0,-0.43 0.36,-0.79 0.79,-0.79h0.05c0.26,0 0.5,0.12 0.65,0.33l2.26,3.17L7.25,9.62c0,-0.34 0.28,-0.62 0.63,-0.62s0.62,0.28 0.62,0.62v4.59zM13.5,9.64c0,0.35 -0.28,0.62 -0.62,0.62L11,10.26v1.12h1.88c0.35,0 0.62,0.28 0.62,0.62v0.01c0,0.35 -0.28,0.62 -0.62,0.62L11,12.63v1.11h1.88c0.35,0 0.62,0.28 0.62,0.62 0,0.35 -0.28,0.62 -0.62,0.62h-2.53c-0.47,0 -0.85,-0.38 -0.85,-0.85v-4.3c0,-0.45 0.38,-0.83 0.85,-0.83h2.53c0.35,0 0.62,0.28 0.62,0.62v0.02zM20.5,14c0,0.55 -0.45,1 -1,1h-4c-0.55,0 -1,-0.45 -1,-1L14.5,9.62c0,-0.34 0.28,-0.62 0.62,-0.62s0.62,0.28 0.62,0.62v3.89h1.13v-2.9c0,-0.35 0.28,-0.62 0.62,-0.62s0.62,0.28 0.62,0.62v2.89h1.12L19.23,9.62c0,-0.35 0.28,-0.62 0.62,-0.62s0.62,0.28 0.62,0.62L20.47,14z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13.41,18.09v0.58c0,0.73 -0.6,1.33 -1.33,1.33h-0.01c-0.73,0 -1.33,-0.6 -1.33,-1.33v-0.6c-1.33,-0.28 -2.51,-1.01 -3.01,-2.24 -0.23,-0.55 0.2,-1.16 0.8,-1.16h0.24c0.37,0 0.67,0.25 0.81,0.6 0.29,0.75 1.05,1.27 2.51,1.27 1.96,0 2.4,-0.98 2.4,-1.59 0,-0.83 -0.44,-1.61 -2.67,-2.14 -2.48,-0.6 -4.18,-1.62 -4.18,-3.67 0,-1.72 1.39,-2.84 3.11,-3.21v-0.6c0,-0.73 0.6,-1.33 1.33,-1.33h0.01c0.73,0 1.33,0.6 1.33,1.33v0.62c1.38,0.34 2.25,1.2 2.63,2.26 0.2,0.55 -0.22,1.13 -0.81,1.13h-0.26c-0.37,0 -0.67,-0.26 -0.77,-0.62 -0.23,-0.76 -0.86,-1.25 -2.12,-1.25 -1.5,0 -2.4,0.68 -2.4,1.64 0,0.84 0.65,1.39 2.67,1.91s4.18,1.39 4.18,3.91c-0.02,1.83 -1.39,2.83 -3.13,3.16z"/>
</vector>

View file

@ -92,10 +92,20 @@
android:id="@+id/levelContainer"
android:layout_width="0dp"
android:layout_height="0dp"
ads:layout_constraintBottom_toBottomOf="parent"
ads:layout_constraintBottom_toTopOf="@+id/ad_placeholder"
ads:layout_constraintLeft_toLeftOf="parent"
ads:layout_constraintRight_toRightOf="parent"
ads:layout_constraintTop_toBottomOf="@id/menu" />
<dev.lucasnlm.external.view.AdPlaceHolderView
android:id="@+id/ad_placeholder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:visibility="visible"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView

View file

@ -1,123 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="1"
android:fitsSystemWindows="true"
android:divider="?android:listDivider"
android:showDividers="middle"
tools:context=".stats.StatsActivity">
xmlns:app="http://schemas.android.com/apk/res-auto">
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/games" />
<dev.lucasnlm.external.view.AdPlaceHolderView
android:id="@+id/ad_placeholder"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible"/>
<TextView
android:id="@+id/totalGames"
android:gravity="end"
android:padding="16dp"
android:text="0"
tools:ignore="HardcodedText" />
</TableRow>
<TableLayout
android:id="@+id/stats"
android:layout_width="match_parent"
android:layout_height="0dp"
android:stretchColumns="1"
android:fitsSystemWindows="true"
android:divider="?android:listDivider"
android:showDividers="middle"
app:layout_constraintTop_toBottomOf="@id/ad_placeholder"
app:layout_constraintBottom_toBottomOf="parent"
tools:context=".stats.StatsActivity">
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/mines" />
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/games" />
<TextView
android:id="@+id/minesCount"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TextView
android:id="@+id/totalGames"
android:gravity="end"
android:padding="16dp"
android:text="0"
tools:ignore="HardcodedText" />
</TableRow>
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/total_time" />
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/mines" />
<TextView
android:id="@+id/totalTime"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TextView
android:id="@+id/minesCount"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/average_time" />
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/total_time" />
<TextView
android:id="@+id/averageTime"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TextView
android:id="@+id/totalTime"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/open_areas" />
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/average_time" />
<TextView
android:id="@+id/openAreas"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TextView
android:id="@+id/averageTime"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/performance" />
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/open_areas" />
<TextView
android:id="@+id/performance"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TextView
android:id="@+id/openAreas"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/victories" />
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/performance" />
<TextView
android:id="@+id/victory"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TextView
android:id="@+id/performance"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/defeats" />
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/victories" />
<TextView
android:id="@+id/defeat"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
</TableLayout>
<TextView
android:id="@+id/victory"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
<TableRow>
<TextView
android:padding="16dp"
android:textStyle="bold"
android:text="@string/defeats" />
<TextView
android:id="@+id/defeat"
android:gravity="end"
android:padding="16dp"
android:text="-"
tools:ignore="HardcodedText" />
</TableRow>
</TableLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -8,15 +8,25 @@
android:fitsSystemWindows="false"
tools:context="dev.lucasnlm.antimine.history.views.HistoryFragment">
<dev.lucasnlm.external.view.AdPlaceHolderView
android:id="@+id/ad_placeholder"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/saveHistory"
android:layout_width="match_parent"
android:layout_height="0dp"
android:clipToPadding="false"
android:overScrollMode="never"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/ad_placeholder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ic_new"
android:importantForAccessibility="no"
android:layout_gravity="center_vertical"
app:tint="@color/accent"/>
</FrameLayout>

View file

@ -69,6 +69,13 @@
android:checkable="false"
android:icon="@drawable/settings"
android:title="@string/settings" />
<item
android:id="@+id/remove_ads"
android:checkable="false"
android:visible="false"
android:icon="@drawable/remove_ads"
android:title="@string/remove_ads" />
</group>
<group android:id="@+id/play_games_group">

View file

@ -104,12 +104,10 @@ class AreaAdapter(
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> {
view.isPressed = true
viewModel.startLongPressFeedback()
true
}
MotionEvent.ACTION_UP -> {
view.isPressed = false
viewModel.cancelLongPressFeedback()
val dt = motionEvent.eventTime - motionEvent.downTime
if (dt > preferencesRepository.customLongPressTimeout()) {

View file

@ -29,11 +29,9 @@ import dev.lucasnlm.external.Achievement
import dev.lucasnlm.external.IPlayGamesManager
import dev.lucasnlm.external.Leaderboard
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -57,7 +55,6 @@ class GameViewModel(
private lateinit var gameController: GameController
private var initialized = false
private var currentDifficulty: Difficulty = Difficulty.Standard
private var longPressFeedback: Job? = null
val field = MutableLiveData<List<Area>>()
val elapsedTimeSeconds = MutableLiveData<Long>()
@ -240,24 +237,6 @@ class GameViewModel(
}
}
fun startLongPressFeedback() {
if (preferencesRepository.useHapticFeedback()) {
longPressFeedback = viewModelScope.launch {
delay(preferencesRepository.customLongPressTimeout())
if (isActive) {
hapticFeedbackManager.longPressFeedback()
longPressFeedback = null
}
}
}
}
fun cancelLongPressFeedback() {
longPressFeedback?.cancel()
longPressFeedback = null
}
suspend fun onLongClick(index: Int) {
gameController
.longPress(index)

View file

@ -60,7 +60,7 @@
<string name="arrow"></string>
<string name="single_click">Single Click</string>
<string name="double_click">Double Click</string>
<string name="long_press">Long Press</string>
<string name="long_press">Lang-druk</string>
<string name="open_tile">Open Tile</string>
<string name="flag_tile">Flag Tile</string>
<string name="retry">Weer probeer</string>
@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="remove_ad">Verwyder Advertensies</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">لقد فجرت لغم!</string>
<string name="flag_placed">وضع العلم!</string>
<string name="flag_removed">إزالة العلم!</string>
<string name="remove_ad">إزالة الإعلانات</string>
</resources>

View file

@ -90,7 +90,7 @@
<string name="close_menu">Затвори менюто</string>
<string name="delete_all">Изтрий всички</string>
<string name="themes">Теми</string>
<string name="try_it">Опитайте</string>
<string name="try_it">Ще опитам</string>
<string name="delete_all_message">Изтрий всички завинаги.</string>
<string name="all_mines_disabled">Всички мини бяха деактивирани.</string>
<string name="desc_convered_area">Покрита площ</string>
@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Ти взриви мина!</string>
<string name="flag_placed">Флагът е сложен!</string>
<string name="flag_removed">Глагът е премахнат!</string>
<string name="remove_ad">Премахване на реклами</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Has explotat una mina!</string>
<string name="flag_placed">Bandera col·locada!</string>
<string name="flag_removed">Has tret la bandera!</string>
<string name="remove_ad">Elimina els anuncis</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Odpálili jste minu!</string>
<string name="flag_placed">Vlajka umístěna!</string>
<string name="flag_removed">Vlajka odstraněna!</string>
<string name="remove_ad">Odstranit reklamy</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="remove_ad">Remove Ads</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Du hast eine Mine zur Explosion gebracht!</string>
<string name="flag_placed">Markierung platziert!</string>
<string name="flag_removed">Markierung entfernt!</string>
<string name="remove_ad">Werbung entfernen</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Ανατίναξες μια νάρκη!</string>
<string name="flag_placed">Η Σημαία τοποθετήθηκε!</string>
<string name="flag_removed">Η σημαία αφαιρέθηκε!</string>
<string name="remove_ad">Κατάργηση διαφημίσεων</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="remove_ad">Remove Ads</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">¡Has explotado una mina!</string>
<string name="flag_placed">¡Bandera colocada!</string>
<string name="flag_removed">¡Bandera eliminada!</string>
<string name="remove_ad">Eliminar anuncios</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Räjäytit miinan!</string>
<string name="flag_placed">Lippu asetettu!</string>
<string name="flag_removed">Lippu poistettu!</string>
<string name="remove_ad">Poista Mainokset</string>
</resources>

View file

@ -32,7 +32,7 @@
<string name="size">Taille</string>
<string name="system">Système</string>
<string name="rating">Retour d\'expérience</string>
<string name="support_title">Supporte nous !</string>
<string name="support_title">Soutenez-nous !</string>
<string name="support_description">Avec votre aide, nous serons en mesure de implémenter de nouvelles fonctionnalités et de maintenir notre projet actif.</string>
<string name="support_action">Assistance</string>
<string name="rating_message">Si vous aimez ce jeu, n\'hésitez pas à jour donner un retour. Ça nous serait très utile.</string>
@ -71,7 +71,7 @@
<string name="resume">Nouv. partie</string>
<string name="yes">Oui</string>
<string name="unlock">Déverrouiller</string>
<string name="achievements">Réalisations</string>
<string name="achievements">Succès</string>
<string name="rating_button_no">Non</string>
<string name="general">Général</string>
<string name="source_code">Code source</string>
@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Vous avez touché une mine !</string>
<string name="flag_placed">Drapeau placé !</string>
<string name="flag_removed">Drapeau retiré !</string>
<string name="remove_ad">Supprimer les publicités</string>
</resources>

View file

@ -90,7 +90,7 @@
<string name="close_menu">मेनू बंद करें</string>
<string name="delete_all">सभी मिटाएँ</string>
<string name="themes">थीम्स</string>
<string name="try_it">Try It</string>
<string name="try_it">इसे आज़माएँ</string>
<string name="delete_all_message">पुष्टि करें</string>
<string name="all_mines_disabled">All mines were disabled.</string>
<string name="desc_convered_area">Covered area</string>
@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="remove_ad">विज्ञापन निकालें</string>
</resources>

View file

@ -32,8 +32,8 @@
<string name="size">Méret</string>
<string name="system">Rendszer</string>
<string name="rating">Visszajelzés</string>
<string name="support_title">Támogasson bennünket!</string>
<string name="support_description">With your help, we\'ll be able to implement new features and keep our project active.</string>
<string name="support_title">Támogass minket!</string>
<string name="support_description">Segítségével képesek leszünk új funkciók bevezetésére és projektünk aktív működésére.</string>
<string name="support_action">Támogasson bennünket</string>
<string name="rating_message">Ha tetszik ez a játék, kérlek jelezz vissza nekünk. Sokat segít.</string>
<string name="used_software_text">Ez a játék a következő harmadik féltől származó programokat használja:</string>
@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Felrobbantottál egy aknát!</string>
<string name="flag_placed">Zászló elhelyezve!</string>
<string name="flag_removed">Zászló eltávolítva!</string>
<string name="remove_ad">Reklámok eltávolítása</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Kamu meledakkan ranjau!</string>
<string name="flag_placed">Bendera diletakkan!</string>
<string name="flag_removed">Bendera dihapus!</string>
<string name="remove_ad">Hapus Iklan</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Hai esploso una mina!</string>
<string name="flag_placed">Bandiera piazzata!</string>
<string name="flag_removed">Bandiera rimossa!</string>
<string name="remove_ad">Rimuovi gli Annunci</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="remove_ad">Remove Ads</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">地雷を爆発させました!</string>
<string name="flag_placed">フラグを立てました!</string>
<string name="flag_removed">フラグを消しました!</string>
<string name="remove_ad">広告を削除</string>
</resources>

View file

@ -4,22 +4,22 @@
<string name="app_description">You have to clear a rectangular board containing hidden mines without detonating any of them.</string>
<string name="games">Games</string>
<string name="previous_games">Previous Games</string>
<string name="minefield">Difficulty</string>
<string name="standard">Standard</string>
<string name="beginner">Beginner</string>
<string name="intermediate">Intermediate</string>
<string name="expert">Expert</string>
<string name="minefield">난이도</string>
<string name="standard">기본</string>
<string name="beginner">초급</string>
<string name="intermediate">중급</string>
<string name="expert">고급</string>
<string name="open">Open</string>
<string name="settings">Settings</string>
<string name="settings">설정</string>
<string name="animations">Animations</string>
<string name="vibration">Haptic Feedback</string>
<string name="about">About</string>
<string name="events">Statistics</string>
<string name="events">통계</string>
<string name="custom">Custom</string>
<string name="start">Start</string>
<string name="start">시작</string>
<string name="width">Width</string>
<string name="height">Height</string>
<string name="mines">Mines</string>
<string name="mines">지뢰 수</string>
<string name="retry_sure">If you start a new game, your current progress will be lost.</string>
<string name="show_licenses">Show Licenses</string>
<string name="new_game_request">Do you want to start a new game?</string>
@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="remove_ad">Remove Ads</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Je hebt een mijn ontploft!</string>
<string name="flag_placed">Vlag geplaatst!</string>
<string name="flag_removed">Vlag verwijderd!</string>
<string name="remove_ad">Verwijder advertenties</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Du detonerte en mine!</string>
<string name="flag_placed">Flagg plassert!</string>
<string name="flag_removed">Flagg fjernet!</string>
<string name="remove_ad">Fjern Reklame</string>
</resources>

View file

@ -90,7 +90,7 @@
<string name="close_menu">Zamknij menu</string>
<string name="delete_all">Usuń wszystkie</string>
<string name="themes">Motywy</string>
<string name="try_it">Spróbuj</string>
<string name="try_it">Wypróbuj</string>
<string name="delete_all_message">Usuń wszystkie zdarzenia na stałe.</string>
<string name="all_mines_disabled">Wszystkie miny zostały rozbrojone.</string>
<string name="desc_convered_area">Nieodkryte pole</string>
@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Zdetonowano minę!</string>
<string name="flag_placed">Umieszczono flagę!</string>
<string name="flag_removed">Usunięto flagę!</string>
<string name="remove_ad">Usunąć reklamy</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Você explodiu uma mina!</string>
<string name="flag_placed">Bandeira colocada!</string>
<string name="flag_removed">Bandeira removida!</string>
<string name="remove_ad">Remover Propagandas</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Você explodiu uma mina!</string>
<string name="flag_placed">Bandeira colocada!</string>
<string name="flag_removed">Bandeira removida!</string>
<string name="remove_ad">Remover Anúncios</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Ai explodat o mină!</string>
<string name="flag_placed">Steagul a fost plasat!</string>
<string name="flag_removed">Steagul a fost eliminat!</string>
<string name="remove_ad">Remove Ads</string>
</resources>

View file

@ -90,7 +90,7 @@
<string name="close_menu">Закрыть меню</string>
<string name="delete_all">Удалить все</string>
<string name="themes">Темы</string>
<string name="try_it">Попробуйте It</string>
<string name="try_it">Попробуйте</string>
<string name="delete_all_message">Удалить все события навсегда.</string>
<string name="all_mines_disabled">Все мины обезврежены.</string>
<string name="desc_convered_area">Покрытая область</string>
@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Вы подорвали мину!</string>
<string name="flag_placed">Флаг установлен!</string>
<string name="flag_removed">Флаг снят!</string>
<string name="remove_ad">Удалить рекламу</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="remove_ad">Remove Ads</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="remove_ad">Remove Ads</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Bir mayını patlattın!</string>
<string name="flag_placed">Bayrak yerleştirildi!</string>
<string name="flag_removed">Bayrak kaldırıldı!</string>
<string name="remove_ad">Reklamları kaldır</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Ви натрапили на міну!</string>
<string name="flag_placed">Прапор розміщено!</string>
<string name="flag_removed">Прапор вилучено!</string>
<string name="remove_ad">Прибрати рекламу</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">Bạn làm nổ mìn rồi!</string>
<string name="flag_placed">Đã cắm cờ!</string>
<string name="flag_removed">Đã tháo cờ hiệu!</string>
<string name="remove_ad">Loại bỏ quảng cáo</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">你引爆了一个地雷!</string>
<string name="flag_placed">标记已放置!</string>
<string name="flag_removed">标记已移除!</string>
<string name="remove_ad">去除广告</string>
</resources>

View file

@ -102,4 +102,5 @@
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="remove_ads">Remove Ads</string>
</resources>

View file

@ -6,7 +6,12 @@ import android.content.Context
interface IAdsManager {
fun start(context: Context)
fun isReady(): Boolean
fun requestRewarded(activity: Activity, adUnitId: String, onRewarded: (() -> Unit)? = null)
fun requestRewarded(
activity: Activity,
adUnitId: String,
onRewarded: (() -> Unit)? = null,
onFail: (() -> Unit)? = null,
)
}
object Ads {

View file

@ -0,0 +1,11 @@
package dev.lucasnlm.external.view
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
class AdPlaceHolderView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
init {
visibility = GONE
}
}

View file

@ -42,7 +42,12 @@ class AdsManager : IAdsManager {
return unlockTheme != null
}
override fun requestRewarded(activity: Activity, adUnitId: String, onRewarded: (() -> Unit)?) {
override fun requestRewarded(
activity: Activity,
adUnitId: String,
onRewarded: (() -> Unit)?,
onFail: (() -> Unit)?
) {
if (isReady()) {
val context = activity.applicationContext
@ -61,7 +66,7 @@ class AdsManager : IAdsManager {
}
override fun onRewardedAdFailedToShow(adError: AdError) {
// Ad failed
onFail?.invoke()
}
}

View file

@ -0,0 +1,37 @@
package dev.lucasnlm.external.view
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
class AdPlaceHolderView : FrameLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
private val adView: AdView = AdView(context).apply {
adSize = AdSize.BANNER
adUnitId = "ca-app-pub-3940256099942544/6300978111"
}
init {
addView(adView)
setOnClickListener {
adView.performClick()
this.visibility = View.GONE
}
}
fun loadAd() {
adView.loadAd(AdRequest.Builder().build())
if (!adView.isLoading) {
this.visibility = View.GONE
}
}
}