Add support to Google stuff and small optimizations
This commit is contained in:
parent
90041d9bc5
commit
ac8868d56a
64 changed files with 1262 additions and 146 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -12,3 +12,5 @@ app/local.properties
|
|||
app/release/
|
||||
app/standalone/
|
||||
app/google/
|
||||
app/google-services.json
|
||||
proprietary/google-services.json
|
|
@ -9,8 +9,8 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
// versionCode and versionName must be hardcoded to support F-droid
|
||||
versionCode 800041
|
||||
versionName '8.0.4'
|
||||
versionCode 800051
|
||||
versionName '8.0.5'
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
multiDexEnabled true
|
||||
|
@ -93,6 +93,7 @@ dependencies {
|
|||
// Dependencies must be hardcoded to support F-droid
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(':external')
|
||||
implementation project(':common')
|
||||
|
||||
googleImplementation project(':proprietary')
|
||||
|
|
|
@ -12,6 +12,4 @@ object DeepLink {
|
|||
const val EXPERT_PATH = "expert"
|
||||
const val STANDARD_PATH = "standard"
|
||||
const val CUSTOM_PATH = "custom"
|
||||
|
||||
const val CUSTOM_NEW_GAME = "antimine://new-game/custom"
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.os.Handler
|
|||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.FrameLayout
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
|
@ -16,6 +17,7 @@ import androidx.appcompat.widget.TooltipCompat
|
|||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.HandlerCompat.postDelayed
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
|
@ -39,10 +41,12 @@ import dev.lucasnlm.antimine.history.HistoryActivity
|
|||
import dev.lucasnlm.antimine.instant.InstantAppManager
|
||||
import dev.lucasnlm.antimine.level.view.EndGameDialogFragment
|
||||
import dev.lucasnlm.antimine.level.view.LevelFragment
|
||||
import dev.lucasnlm.antimine.playgames.PlayGamesDialogFragment
|
||||
import dev.lucasnlm.antimine.preferences.PreferencesActivity
|
||||
import dev.lucasnlm.antimine.share.viewmodel.ShareViewModel
|
||||
import dev.lucasnlm.antimine.stats.StatsActivity
|
||||
import dev.lucasnlm.antimine.theme.ThemeActivity
|
||||
import dev.lucasnlm.external.IPlayGamesManager
|
||||
import kotlinx.android.synthetic.main.activity_game.*
|
||||
import kotlinx.android.synthetic.main.activity_game.minesCount
|
||||
import kotlinx.android.synthetic.main.activity_game.timer
|
||||
|
@ -66,6 +70,9 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
@Inject
|
||||
lateinit var savesRepository: ISavesRepository
|
||||
|
||||
@Inject
|
||||
lateinit var playGamesManager: IPlayGamesManager
|
||||
|
||||
val viewModel: GameViewModel by viewModels()
|
||||
private val shareViewModel: ShareViewModel by viewModels()
|
||||
|
||||
|
@ -90,7 +97,10 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
bindToolbar()
|
||||
bindDrawer()
|
||||
bindNavigationMenu()
|
||||
loadGameFragment()
|
||||
|
||||
findViewById<FrameLayout>(R.id.levelContainer).doOnLayout {
|
||||
loadGameFragment()
|
||||
}
|
||||
|
||||
if (instantAppManager.isEnabled()) {
|
||||
bindInstantApp()
|
||||
|
@ -201,6 +211,8 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
|
||||
analyticsManager.sentEvent(Analytics.Resume)
|
||||
}
|
||||
|
||||
silentGooglePlayLogin()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,6 +339,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
R.id.previous_games -> openSaveHistory()
|
||||
R.id.stats -> openStats()
|
||||
R.id.install_new -> installFromInstantApp()
|
||||
R.id.play_games -> googlePlay()
|
||||
else -> handled = false
|
||||
}
|
||||
|
||||
|
@ -338,6 +351,10 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
}
|
||||
|
||||
navigationView.menu.findItem(R.id.share_now).isVisible = instantAppManager.isNotEnabled()
|
||||
|
||||
if (!playGamesManager.hasGooglePlayGames()) {
|
||||
navigationView.menu.removeGroup(R.id.play_games_group)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUseCount() {
|
||||
|
@ -369,22 +386,22 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
}
|
||||
|
||||
private fun loadGameFragment() {
|
||||
val fragmentManager = supportFragmentManager
|
||||
supportFragmentManager.apply {
|
||||
popBackStack()
|
||||
|
||||
fragmentManager.popBackStack()
|
||||
findFragmentById(R.id.levelContainer)?.let { it ->
|
||||
beginTransaction().apply {
|
||||
remove(it)
|
||||
commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
fragmentManager.findFragmentById(R.id.levelContainer)?.let { it ->
|
||||
fragmentManager.beginTransaction().apply {
|
||||
remove(it)
|
||||
beginTransaction().apply {
|
||||
replace(R.id.levelContainer, LevelFragment())
|
||||
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
fragmentManager.beginTransaction().apply {
|
||||
replace(R.id.levelContainer, LevelFragment())
|
||||
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showRequestRating() {
|
||||
|
@ -649,6 +666,32 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
}
|
||||
}
|
||||
|
||||
private fun silentGooglePlayLogin() {
|
||||
if (playGamesManager.hasGooglePlayGames()) {
|
||||
playGamesManager.silentLogin(this)
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
}
|
||||
|
||||
private fun googlePlay() {
|
||||
if (playGamesManager.isLogged()) {
|
||||
PlayGamesDialogFragment().show(supportFragmentManager, PlayGamesDialogFragment.TAG)
|
||||
} else {
|
||||
playGamesManager.getLoginIntent()?.let {
|
||||
startActivityForResult(it, GOOGLE_PLAY_REQUEST_CODE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
if (requestCode == GOOGLE_PLAY_REQUEST_CODE) {
|
||||
playGamesManager.handleLoginResult(data)
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PREFERENCE_FIRST_USE = "preference_first_use"
|
||||
const val PREFERENCE_USE_COUNT = "preference_use_count"
|
||||
|
@ -656,6 +699,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
|
|||
|
||||
const val IA_REFERRER = "InstallApiActivity"
|
||||
const val IA_REQUEST_CODE = 5
|
||||
const val GOOGLE_PLAY_REQUEST_CODE = 6
|
||||
|
||||
const val MIN_USAGES_TO_RATING = 4
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@ class ControlDialogFragment : AppCompatDialogFragment() {
|
|||
private val adapter by lazy { ControlListAdapter(controlViewModel) }
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val currentControl = controlViewModel.singleState().selectedId
|
||||
val state = controlViewModel.singleState()
|
||||
return AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle(R.string.control)
|
||||
setSingleChoiceItems(adapter, currentControl, null)
|
||||
setSingleChoiceItems(adapter, state.selectedIndex, null)
|
||||
setPositiveButton(R.string.ok, null)
|
||||
}.create()
|
||||
}
|
||||
|
@ -41,8 +41,6 @@ class ControlDialogFragment : AppCompatDialogFragment() {
|
|||
) : BaseAdapter() {
|
||||
private val controlList = controlViewModel.singleState().gameControls
|
||||
|
||||
fun getSelectedId() = controlViewModel.singleState().selectedId
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
|
||||
val view = if (convertView == null) {
|
||||
ControlItemView(parent!!.context)
|
||||
|
@ -50,12 +48,12 @@ class ControlDialogFragment : AppCompatDialogFragment() {
|
|||
(convertView as ControlItemView)
|
||||
}
|
||||
|
||||
val selectedId = getSelectedId()
|
||||
val selected = controlViewModel.singleState().selected
|
||||
|
||||
return view.apply {
|
||||
val controlModel = controlList[position]
|
||||
bind(controlModel)
|
||||
setRadio(selectedId == controlModel.controlStyle.ordinal)
|
||||
setRadio(selected == controlModel.controlStyle)
|
||||
setOnClickListener {
|
||||
controlViewModel.sendEvent(ControlEvent.SelectControlStyle(controlModel.controlStyle))
|
||||
notifyDataSetChanged()
|
||||
|
|
|
@ -6,7 +6,6 @@ import dev.lucasnlm.antimine.core.control.ControlStyle
|
|||
data class ControlDetails(
|
||||
val id: Long,
|
||||
val controlStyle: ControlStyle,
|
||||
@StringRes val titleId: Int,
|
||||
@StringRes val firstActionId: Int,
|
||||
@StringRes val firstActionResponseId: Int,
|
||||
@StringRes val secondActionId: Int,
|
||||
|
|
|
@ -17,7 +17,6 @@ class ControlItemView : FrameLayout {
|
|||
|
||||
private val radio: AppCompatRadioButton
|
||||
private val root: View
|
||||
private val title: TextView
|
||||
private val firstAction: TextView
|
||||
private val firstActionResponse: TextView
|
||||
private val secondAction: TextView
|
||||
|
@ -30,7 +29,6 @@ class ControlItemView : FrameLayout {
|
|||
|
||||
radio = findViewById(R.id.radio)
|
||||
root = findViewById(R.id.root)
|
||||
title = findViewById(R.id.title)
|
||||
firstAction = findViewById(R.id.firstAction)
|
||||
firstActionResponse = findViewById(R.id.firstActionResponse)
|
||||
secondAction = findViewById(R.id.secondAction)
|
||||
|
@ -38,7 +36,6 @@ class ControlItemView : FrameLayout {
|
|||
}
|
||||
|
||||
fun bind(controlDetails: ControlDetails) {
|
||||
title.text = context.getString(controlDetails.titleId)
|
||||
firstAction.text = context.getString(controlDetails.firstActionId)
|
||||
firstActionResponse.text = context.getString(controlDetails.firstActionResponseId)
|
||||
secondAction.text = context.getString(controlDetails.secondActionId)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package dev.lucasnlm.antimine.control.viewmodel
|
||||
|
||||
import dev.lucasnlm.antimine.control.models.ControlDetails
|
||||
import dev.lucasnlm.antimine.core.control.ControlStyle
|
||||
|
||||
data class ControlState(
|
||||
val selectedId: Int,
|
||||
val selectedIndex: Int,
|
||||
val selected: ControlStyle,
|
||||
val gameControls: List<ControlDetails>
|
||||
)
|
||||
|
|
|
@ -16,7 +16,6 @@ class ControlViewModel @ViewModelInject constructor(
|
|||
ControlDetails(
|
||||
id = 0L,
|
||||
controlStyle = ControlStyle.Standard,
|
||||
titleId = R.string.standard,
|
||||
firstActionId = R.string.single_click,
|
||||
firstActionResponseId = R.string.open_tile,
|
||||
secondActionId = R.string.long_press,
|
||||
|
@ -25,7 +24,6 @@ class ControlViewModel @ViewModelInject constructor(
|
|||
ControlDetails(
|
||||
id = 1L,
|
||||
controlStyle = ControlStyle.FastFlag,
|
||||
titleId = R.string.flag_first,
|
||||
firstActionId = R.string.single_click,
|
||||
firstActionResponseId = R.string.flag_tile,
|
||||
secondActionId = R.string.long_press,
|
||||
|
@ -34,29 +32,43 @@ class ControlViewModel @ViewModelInject constructor(
|
|||
ControlDetails(
|
||||
id = 2L,
|
||||
controlStyle = ControlStyle.DoubleClick,
|
||||
titleId = R.string.double_click,
|
||||
firstActionId = R.string.single_click,
|
||||
firstActionResponseId = R.string.flag_tile,
|
||||
secondActionId = R.string.double_click,
|
||||
secondActionResponseId = R.string.open_tile
|
||||
),
|
||||
ControlDetails(
|
||||
id = 3L,
|
||||
controlStyle = ControlStyle.DoubleClickInverted,
|
||||
firstActionId = R.string.single_click,
|
||||
firstActionResponseId = R.string.open_tile,
|
||||
secondActionId = R.string.double_click,
|
||||
secondActionResponseId = R.string.flag_tile
|
||||
)
|
||||
)
|
||||
|
||||
override fun initialState(): ControlState =
|
||||
ControlState(
|
||||
selectedId = gameControlOptions.firstOrNull {
|
||||
it.controlStyle == preferencesRepository.controlStyle()
|
||||
}?.id?.toInt() ?: 0,
|
||||
override fun initialState(): ControlState {
|
||||
val controlDetails = gameControlOptions.firstOrNull {
|
||||
it.controlStyle == preferencesRepository.controlStyle()
|
||||
}
|
||||
return ControlState(
|
||||
selectedIndex = controlDetails?.id?.toInt() ?: 0,
|
||||
selected = controlDetails?.controlStyle ?: ControlStyle.Standard,
|
||||
gameControls = gameControlOptions
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
override suspend fun mapEventToState(event: ControlEvent) = flow {
|
||||
if (event is ControlEvent.SelectControlStyle) {
|
||||
val controlStyle = event.controlStyle
|
||||
preferencesRepository.useControlStyle(controlStyle)
|
||||
|
||||
val selected = state.gameControls.first { it.controlStyle == event.controlStyle }
|
||||
|
||||
val newState = state.copy(
|
||||
selectedId = state.gameControls.first { it.controlStyle == event.controlStyle }.id.toInt()
|
||||
selectedIndex = selected.id.toInt(),
|
||||
selected = selected.controlStyle
|
||||
)
|
||||
|
||||
emit(newState)
|
||||
|
|
|
@ -6,13 +6,48 @@ import dagger.Provides
|
|||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ApplicationComponent
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dev.lucasnlm.antimine.common.BuildConfig
|
||||
import dev.lucasnlm.antimine.core.analytics.DebugAnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.ProdAnalyticsManager
|
||||
import dev.lucasnlm.antimine.instant.InstantAppManager
|
||||
import dev.lucasnlm.external.BillingManager
|
||||
import dev.lucasnlm.external.ExternalAnalyticsWrapper
|
||||
import dev.lucasnlm.external.IBillingManager
|
||||
import dev.lucasnlm.external.IPlayGamesManager
|
||||
import dev.lucasnlm.external.PlayGamesManager
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ApplicationComponent::class)
|
||||
class AppModule {
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideInstantAppManager(
|
||||
@ApplicationContext context: Context
|
||||
): InstantAppManager = InstantAppManager(context)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideBillingManager(
|
||||
@ApplicationContext context: Context
|
||||
): IBillingManager = BillingManager(context)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun providePlayGamesManager(
|
||||
@ApplicationContext context: Context
|
||||
): IPlayGamesManager = PlayGamesManager(context)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAnalyticsManager(
|
||||
@ApplicationContext context: Context
|
||||
): IAnalyticsManager {
|
||||
return if (BuildConfig.DEBUG) {
|
||||
DebugAnalyticsManager()
|
||||
} else {
|
||||
ProdAnalyticsManager(ExternalAnalyticsWrapper(context))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,15 @@ package dev.lucasnlm.antimine.instant
|
|||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import dev.lucasnlm.external.InstantAppWrapper
|
||||
import dev.lucasnlm.external.InstantAppManager
|
||||
|
||||
class InstantAppManager(
|
||||
private val context: Context
|
||||
) {
|
||||
fun isEnabled(): Boolean = InstantAppWrapper().isEnabled(context)
|
||||
fun isEnabled(): Boolean = InstantAppManager().isInstantAppSupported(context)
|
||||
|
||||
fun isNotEnabled(): Boolean = isEnabled().not()
|
||||
|
||||
fun showInstallPrompt(activity: Activity, intent: Intent?, requestCode: Int, referrer: String?) =
|
||||
InstantAppWrapper().showInstallPrompt(activity, intent, requestCode, referrer)
|
||||
InstantAppManager().showInstallPrompt(activity, intent, requestCode, referrer)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package dev.lucasnlm.antimine.level.view
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
@ -11,27 +11,47 @@ import dev.lucasnlm.antimine.DeepLink
|
|||
import dev.lucasnlm.antimine.common.R
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import dev.lucasnlm.antimine.common.level.models.Event
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.common.level.view.CommonLevelFragment
|
||||
import dev.lucasnlm.antimine.common.level.view.SpaceItemDecoration
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@AndroidEntryPoint
|
||||
open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) {
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
GlobalScope.launch {
|
||||
lifecycleScope.launch {
|
||||
viewModel.saveGame()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
recyclerGrid = view.findViewById(R.id.recyclerGrid)
|
||||
recyclerGrid.doOnLayout {
|
||||
lifecycleScope.launch {
|
||||
val loadGameUid = checkLoadGameDeepLink()
|
||||
val newGameDeepLink = checkNewGameDeepLink()
|
||||
val retryDeepLink = checkRetryGameDeepLink()
|
||||
|
||||
val levelSetup = when {
|
||||
loadGameUid != null -> viewModel.loadGame(loadGameUid)
|
||||
newGameDeepLink != null -> viewModel.startNewGame(newGameDeepLink)
|
||||
retryDeepLink != null -> viewModel.retryGame(retryDeepLink)
|
||||
else -> viewModel.loadLastGame()
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
recyclerGrid.apply {
|
||||
addItemDecoration(SpaceItemDecoration(R.dimen.field_padding))
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
setupRecyclerViewSize(levelSetup)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.run {
|
||||
field.observe(
|
||||
|
@ -44,13 +64,7 @@ open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) {
|
|||
levelSetup.observe(
|
||||
viewLifecycleOwner,
|
||||
Observer {
|
||||
recyclerGrid.apply {
|
||||
val horizontalPadding = calcHorizontalPadding(it.width)
|
||||
val verticalPadding = calcVerticalPadding(it.height)
|
||||
layoutManager = makeNewLayoutManager(it.width)
|
||||
setHasFixedSize(true)
|
||||
setPadding(horizontalPadding, verticalPadding, 0, 0)
|
||||
}
|
||||
setupRecyclerViewSize(it)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -81,38 +95,19 @@ open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
private fun setupRecyclerViewSize(levelSetup: Minefield) {
|
||||
recyclerGrid.apply {
|
||||
val horizontalPadding = calcHorizontalPadding(levelSetup.width)
|
||||
val verticalPadding = calcVerticalPadding(levelSetup.height)
|
||||
setPadding(horizontalPadding, verticalPadding, 0, 0)
|
||||
layoutManager = makeNewLayoutManager(levelSetup.width)
|
||||
adapter = areaAdapter
|
||||
alpha = 0.0f
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val loadGameUid = checkLoadGameDeepLink()
|
||||
val newGameDeepLink = checkNewGameDeepLink()
|
||||
val retryDeepLink = checkRetryGameDeepLink()
|
||||
|
||||
val levelSetup = when {
|
||||
loadGameUid != null -> viewModel.loadGame(loadGameUid)
|
||||
newGameDeepLink != null -> viewModel.startNewGame(newGameDeepLink)
|
||||
retryDeepLink != null -> viewModel.retryGame(retryDeepLink)
|
||||
else -> viewModel.loadLastGame()
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
recyclerGrid.apply {
|
||||
val horizontalPadding = calcHorizontalPadding(levelSetup.width)
|
||||
val verticalPadding = calcVerticalPadding(levelSetup.height)
|
||||
addItemDecoration(SpaceItemDecoration(R.dimen.field_padding))
|
||||
setPadding(horizontalPadding, verticalPadding, 0, 0)
|
||||
layoutManager = makeNewLayoutManager(levelSetup.width)
|
||||
setHasFixedSize(true)
|
||||
adapter = areaAdapter
|
||||
alpha = 0.0f
|
||||
|
||||
animate().apply {
|
||||
alpha(1.0f)
|
||||
duration = DateUtils.SECOND_IN_MILLIS
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
animate().apply {
|
||||
alpha(1.0f)
|
||||
duration = DateUtils.SECOND_IN_MILLIS
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ class EndGameDialogViewModel @ViewModelInject constructor(
|
|||
R.drawable.emoji_grinning_squinting_face,
|
||||
R.drawable.emoji_smiling_face_with_sunglasses,
|
||||
R.drawable.emoji_squinting_face_with_tongue,
|
||||
R.drawable.emoji_hugging_face,
|
||||
R.drawable.emoji_partying_face,
|
||||
R.drawable.emoji_clapping_hands,
|
||||
R.drawable.emoji_triangular_flag
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
package dev.lucasnlm.antimine.playgames
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.lucasnlm.antimine.R
|
||||
import dev.lucasnlm.antimine.playgames.viewmodel.PlayGamesEvent
|
||||
import dev.lucasnlm.antimine.playgames.viewmodel.PlayGamesViewModel
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.filter
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PlayGamesDialogFragment : DialogFragment() {
|
||||
private val playGamesViewModel by viewModels<PlayGamesViewModel>()
|
||||
private val adapter by lazy { PlayGamesAdapter(playGamesViewModel) }
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle(R.string.google_play_games)
|
||||
setAdapter(adapter, null)
|
||||
setPositiveButton(R.string.ok, null)
|
||||
}.create()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleScope.launchWhenCreated {
|
||||
playGamesViewModel.observeEvent().collect {
|
||||
when (it) {
|
||||
is PlayGamesEvent.OpenAchievements -> {
|
||||
activity?.let { activity ->
|
||||
playGamesViewModel.openAchievements(activity)
|
||||
}
|
||||
}
|
||||
is PlayGamesEvent.OpenLeaderboards -> {
|
||||
activity?.let { activity ->
|
||||
playGamesViewModel.openLeaderboards(activity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PlayGamesAdapter(
|
||||
private val playGamesViewModel: PlayGamesViewModel
|
||||
) : BaseAdapter() {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
|
||||
val view = if (convertView == null) {
|
||||
PlayGamesButton(parent!!.context)
|
||||
} else {
|
||||
(convertView as PlayGamesButton)
|
||||
}
|
||||
|
||||
val item = playGamesViewModel.playGamesItems[position]
|
||||
|
||||
return view.apply {
|
||||
text.text = view.context.getString(item.stringRes)
|
||||
icon.setImageResource(item.iconRes)
|
||||
|
||||
setOnClickListener {
|
||||
playGamesViewModel.sendEvent(item.triggerEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasStableIds(): Boolean = true
|
||||
|
||||
override fun getItem(position: Int): Any = playGamesViewModel.playGamesItems[position]
|
||||
|
||||
override fun getItemId(position: Int): Long = playGamesViewModel.playGamesItems[position].id.toLong()
|
||||
|
||||
override fun getCount(): Int = playGamesViewModel.playGamesItems.count()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = PlayGamesDialogFragment::class.simpleName
|
||||
}
|
||||
}
|
||||
|
||||
class PlayGamesButton : 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)
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.view_play_games_button, this)
|
||||
|
||||
icon = findViewById(R.id.icon)
|
||||
text = findViewById(R.id.text)
|
||||
}
|
||||
|
||||
val icon: AppCompatImageView
|
||||
val text: TextView
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package dev.lucasnlm.antimine.playgames.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import dev.lucasnlm.antimine.playgames.viewmodel.PlayGamesEvent
|
||||
|
||||
data class PlayGamesItem(
|
||||
val id: Int,
|
||||
@DrawableRes val iconRes: Int,
|
||||
@StringRes val stringRes: Int,
|
||||
val triggerEvent: PlayGamesEvent
|
||||
)
|
|
@ -0,0 +1,6 @@
|
|||
package dev.lucasnlm.antimine.playgames.viewmodel
|
||||
|
||||
sealed class PlayGamesEvent {
|
||||
object OpenAchievements : PlayGamesEvent()
|
||||
object OpenLeaderboards : PlayGamesEvent()
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package dev.lucasnlm.antimine.playgames.viewmodel
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dev.lucasnlm.antimine.R
|
||||
import dev.lucasnlm.antimine.core.viewmodel.StatelessViewModel
|
||||
import dev.lucasnlm.antimine.playgames.model.PlayGamesItem
|
||||
import dev.lucasnlm.external.IPlayGamesManager
|
||||
|
||||
class PlayGamesViewModel @ViewModelInject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val playGamesManager: IPlayGamesManager
|
||||
) : StatelessViewModel<PlayGamesEvent>() {
|
||||
|
||||
val playGamesItems = listOf(
|
||||
PlayGamesItem(0, R.drawable.games_achievements, R.string.achievements, PlayGamesEvent.OpenAchievements),
|
||||
PlayGamesItem(1, R.drawable.games_leaderboards, R.string.leaderboards, PlayGamesEvent.OpenLeaderboards)
|
||||
)
|
||||
|
||||
fun openAchievements(activity: Activity) {
|
||||
playGamesManager.openAchievements(activity)
|
||||
}
|
||||
|
||||
fun openLeaderboards(activity: Activity) {
|
||||
playGamesManager.openLeaderboards(activity)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package dev.lucasnlm.antimine.support
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.lucasnlm.antimine.R
|
||||
import dev.lucasnlm.external.IBillingManager
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SupportAppDialogFragment : AppCompatDialogFragment() {
|
||||
@Inject
|
||||
lateinit var billingManager: IBillingManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
billingManager.start()
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return AlertDialog.Builder(requireContext()).apply {
|
||||
setView(R.layout.dialog_payments)
|
||||
setNeutralButton(R.string.rating_button_no, null)
|
||||
setPositiveButton(R.string.unlock) { _, _ ->
|
||||
lifecycleScope.launch {
|
||||
billingManager.charge(requireActivity())
|
||||
}
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = SupportAppDialogFragment::class.simpleName
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package dev.lucasnlm.antimine.theme
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -9,6 +10,8 @@ import dev.lucasnlm.antimine.R
|
|||
import dev.lucasnlm.antimine.ThematicActivity
|
||||
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
|
||||
import dev.lucasnlm.antimine.common.level.view.SpaceItemDecoration
|
||||
import dev.lucasnlm.antimine.custom.CustomLevelDialogFragment
|
||||
import dev.lucasnlm.antimine.support.SupportAppDialogFragment
|
||||
import dev.lucasnlm.antimine.theme.view.ThemeAdapter
|
||||
import dev.lucasnlm.antimine.theme.viewmodel.ThemeViewModel
|
||||
import kotlinx.android.synthetic.main.activity_theme.*
|
||||
|
@ -41,5 +44,15 @@ class ThemeActivity : ThematicActivity(R.layout.activity_theme) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
showUnlockDialog()
|
||||
}
|
||||
|
||||
private fun showUnlockDialog() {
|
||||
if (supportFragmentManager.findFragmentByTag(SupportAppDialogFragment.TAG) == null) {
|
||||
SupportAppDialogFragment().apply {
|
||||
show(supportFragmentManager, SupportAppDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
359
app/src/main/res/drawable/emoji_hugging_face.xml
Normal file
359
app/src/main/res/drawable/emoji_hugging_face.xml
Normal file
|
@ -0,0 +1,359 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="M63.6,118.8c-27.9,0 -58,-17.5 -58,-55.9S35.7,7 63.6,7c15.5,0 29.8,5.1 40.4,14.4c11.5,10.2 17.6,24.6 17.6,41.5s-6.1,31.2 -17.6,41.4c-10.6,9.3 -25,14.5 -40.4,14.5z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="56.96"
|
||||
android:centerX="63.6"
|
||||
android:centerY="62.9"
|
||||
android:type="radial">
|
||||
<item android:offset="0.5" android:color="#FFFDE030"/>
|
||||
<item android:offset="0.919" android:color="#FFF7C02B"/>
|
||||
<item android:offset="1" android:color="#FFF4A223"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M63.6,118.8c-27.9,0 -58,-17.5 -58,-55.9S35.7,7 63.6,7c15.5,0 29.8,5.1 40.4,14.4c11.5,10.2 17.6,24.6 17.6,41.5s-6.1,31.2 -17.6,41.4c-10.6,9.3 -25,14.5 -40.4,14.5z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="118.8"
|
||||
android:startX="63.6"
|
||||
android:endY="7"
|
||||
android:endX="63.6"
|
||||
android:type="linear">
|
||||
<item android:offset="0.158" android:color="#FFF4A223"/>
|
||||
<item android:offset="0.333" android:color="#FFF7C02B"/>
|
||||
<item android:offset="0.807" android:color="#00FDE030"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M111.49,29.67c5.33,8.6 8.11,18.84 8.11,30.23c0,16.9 -6.1,31.2 -17.6,41.4c-10.6,9.3 -25,14.5 -40.4,14.5c-18.06,0 -37.04,-7.35 -48.18,-22.94c10.76,17.66 30.99,25.94 50.18,25.94c15.4,0 29.8,-5.2 40.4,-14.5c11.5,-10.2 17.6,-24.5 17.6,-41.4c0,-12.74 -3.47,-24.06 -10.11,-33.23z"
|
||||
android:fillColor="#eb8f00"/>
|
||||
<path
|
||||
android:pathData="M29.7,64.1m-16.3,0a16.3,16.3 0,1 1,32.6 0a16.3,16.3 0,1 1,-32.6 0"
|
||||
android:strokeAlpha="0.8"
|
||||
android:fillAlpha="0.8">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="17.73736"
|
||||
android:centerX="29.6654"
|
||||
android:centerY="64.10675"
|
||||
android:type="radial">
|
||||
<item android:offset="0" android:color="#FFED7770"/>
|
||||
<item android:offset="0.9" android:color="#00ED7770"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M96.9,51.5m-16.3,0a16.3,16.3 0,1 1,32.6 0a16.3,16.3 0,1 1,-32.6 0"
|
||||
android:strokeAlpha="0.8"
|
||||
android:fillAlpha="0.8">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="17.735403"
|
||||
android:centerX="96.90706"
|
||||
android:centerY="51.534607"
|
||||
android:type="radial">
|
||||
<item android:offset="0" android:color="#FFED7770"/>
|
||||
<item android:offset="0.9" android:color="#00ED7770"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M96.9,49.4C89.2,56.5 79,62.1 67.2,64.8c-11.8,2.7 -23.4,2.2 -33.4,-0.9c-1.8,-0.6 -3.1,1.8 -1.5,3c10.1,7.5 23.6,10.7 37.1,7.5c13.5,-3.1 24.3,-11.8 30.1,-23c1,-1.7 -1.2,-3.3 -2.6,-2z"
|
||||
android:fillColor="#422b0d"/>
|
||||
<path
|
||||
android:pathData="M48.5,47.2l-0.2,-0.2c-0.1,-0.1 -0.3,-0.2 -0.5,-0.4c-0.2,-0.1 -0.4,-0.3 -0.6,-0.4c-0.2,-0.2 -0.5,-0.4 -0.8,-0.6c-0.3,-0.2 -0.6,-0.4 -0.9,-0.5c-0.3,-0.2 -0.6,-0.3 -0.9,-0.3c-0.3,-0.1 -0.5,-0.1 -0.6,-0.1h-0.2h-0.1h0.2l-0.5,0.1c-0.1,0 0,0 0,0h0.1c0.1,0 0,0 0,0h-0.1c-0.1,0.1 -0.3,0.2 -0.5,0.3c-0.2,0.2 -0.5,0.4 -0.7,0.6c-0.2,0.2 -0.4,0.5 -0.6,0.8c-0.4,0.6 -0.7,1.1 -0.9,1.5s-0.4,0.6 -0.4,0.6l-0.2,0.4c-1,1.7 -3.2,2.4 -5,1.5c-1.2,-0.6 -1.9,-1.7 -2,-2.9v-1.1c0.1,-0.7 0.2,-1.7 0.6,-2.9s1.1,-2.7 2.4,-4.1c0.7,-0.7 1.5,-1.5 2.5,-2c0.2,-0.2 0.5,-0.3 0.8,-0.4c0.3,-0.1 0.5,-0.3 0.9,-0.4l0.5,-0.2c0.2,-0.1 0.4,-0.1 0.5,-0.1l0.5,-0.1l0.3,-0.1h0.4l0.5,-0.1h0.9c0.6,0 1.2,0 1.8,0.1c1.2,0.2 2.2,0.6 3.1,1c1.8,0.9 2.9,2 3.8,2.9c0.4,0.5 0.8,0.9 1,1.3c0.3,0.4 0.5,0.8 0.7,1.1s0.2,0.5 0.3,0.6c0,0.1 0.1,0.2 0.1,0.2c0.7,1.8 -0.3,3.8 -2.3,4.5c-1.4,0.5 -2.9,0.2 -3.9,-0.6z"
|
||||
android:fillColor="#422b0d"/>
|
||||
<path
|
||||
android:pathData="M82.8,40.5l-0.2,-0.2c-0.1,-0.1 -0.3,-0.2 -0.5,-0.4c-0.2,-0.1 -0.4,-0.3 -0.6,-0.4c-0.2,-0.2 -0.5,-0.4 -0.8,-0.6c-0.3,-0.2 -0.6,-0.4 -0.9,-0.5c-0.3,-0.2 -0.6,-0.3 -0.9,-0.3c-0.2,-0.1 -0.4,-0.1 -0.5,-0.1H78.1h0.2l-0.5,0.1c-0.1,0 0,0 0,0h0.1c0.1,0 0,0 0,0h-0.1c-0.1,0.1 -0.3,0.2 -0.6,0.3c-0.2,0.2 -0.4,0.4 -0.7,0.6c-0.2,0.2 -0.4,0.5 -0.6,0.8c-0.4,0.6 -0.7,1.1 -0.9,1.5c-0.2,0.4 -0.4,0.6 -0.4,0.6l-0.2,0.3c-1,1.7 -3.2,2.4 -5,1.5c-1.2,-0.6 -1.9,-1.7 -2,-2.9c0,0 0,-0.4 0.1,-1.1c0.1,-0.7 0.2,-1.7 0.6,-2.9s1.1,-2.7 2.4,-4.1c0.7,-0.7 1.5,-1.5 2.5,-2c0.2,-0.1 0.5,-0.3 0.8,-0.4s0.5,-0.3 0.9,-0.4l0.6,-0.2c0.2,-0.1 0.4,-0.1 0.5,-0.1l0.5,-0.1l0.3,-0.1h0.4l0.5,-0.1h0.9c0.6,0 1.2,0 1.8,0.1c1.2,0.2 2.2,0.6 3.1,1c1.8,0.9 2.9,2 3.8,2.9c0.4,0.5 0.8,0.9 1,1.3c0.3,0.4 0.5,0.8 0.7,1.1s0.2,0.5 0.3,0.6c0.1,0.1 0.1,0.2 0.1,0.2c0.7,1.8 -0.3,3.8 -2.3,4.5c-1.6,0.7 -3.1,0.4 -4.1,-0.5z"
|
||||
android:fillColor="#422b0d"/>
|
||||
<path
|
||||
android:pathData="M38.87,84.46C37.78,81.38 37,113.28 37,113.28c2.92,1.23 5.87,2.31 8.93,3.13c1.21,-2.73 2.79,-7.76 1.96,-13.46c-1.34,-9.11 -6.78,-12.14 -9.02,-18.49z"
|
||||
android:strokeAlpha="0.66"
|
||||
android:fillAlpha="0.66">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="128.189"
|
||||
android:startX="42.556"
|
||||
android:endY="97.296"
|
||||
android:endX="42.556"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFBF360C"/>
|
||||
<item android:offset="1" android:color="#33BF360C"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M39.5,81.23c-0.49,-1.33 -1.11,-2.32 -2.68,-2.32c-0.72,0 -1.94,0.18 -2.59,1.17c-1.28,1.92 -0.78,5.52 0.2,8.65c0.55,1.77 0.4,2.42 0.4,2.42c-2.72,-0.17 -5.88,-3.88 -9.03,-7.58c-1.93,-2.27 -3.05,-4.36 -4.93,-6.68c-1.14,-1.41 -2.81,-2.83 -4.77,-2.11c-5.37,1.99 1.27,9.72 2.93,11.84c1.89,2.41 3.76,4.72 3.76,4.72c-1.76,-0.41 -2.87,-0.88 -5.97,-4.06c-2.65,-2.72 -4.62,-6.23 -6.06,-7.54c-3.27,-2.97 -5.27,-2 -6.14,-0.02c-0.81,1.84 0.53,5.09 1.44,6.61c4.51,7.48 10.27,10.41 10.27,10.41c-4.74,-0.21 -8.48,-5.29 -9.99,-6.83c-4.38,-4.45 -6.38,1.06 -3.99,5.05c1.1,1.83 2.2,3.12 4.21,5.19c3.25,3.33 6.31,4.84 6.31,4.84s-1.31,0.2 -2.63,-0.49c-1.78,-0.93 -3.77,-2.33 -4.8,-3.13c-2.95,-2.27 -5.76,-0.32 -4.63,2.7c1.31,3.49 6.09,6.45 8.98,8.6c5.62,4.16 12.63,7.49 19.73,7.26c7.9,-0.25 13.34,-5.68 15.48,-13.31c1.24,-4.44 -1.72,-11.5 -2.47,-13.28c-2.47,-5.76 -2.54,-10.78 -3.03,-12.11z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="38.904"
|
||||
android:centerX="29.186"
|
||||
android:centerY="93.866"
|
||||
android:type="radial">
|
||||
<item android:offset="0.33" android:color="#FFFFF176"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M23.62,99.08c0.14,8.38 5.4,11.88 9.48,12.65a13.771,13.771 0,0 1,-2.39 -8.2c0.1,-2.9 1.22,-6.51 2.88,-8.47c1.66,-1.96 1.24,-3.89 1.24,-3.89s-1.12,-0.09 -1.54,-0.34c-1.34,-0.82 -2.88,-1.48 -4.04,-2.55c-0.85,-0.79 -5.77,2.42 -5.63,10.8z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="11.727577"
|
||||
android:centerX="36.794704"
|
||||
android:centerY="102.449486"
|
||||
android:type="radial">
|
||||
<item android:offset="0" android:color="#FFFFC709"/>
|
||||
<item android:offset="1" android:color="#00FFC709"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M6.4,78.54a2.84,2.84 0,0 0,-1.28 2.35c-0.02,0.91 0.25,1.82 0.58,2.68c0.7,1.73 1.8,3.28 2.93,4.77c1.16,1.48 2.41,2.91 3.71,4.29c1.3,1.39 2.63,2.76 3.98,4.13c-1.67,-0.98 -3.22,-2.16 -4.65,-3.46c-1.42,-1.31 -2.77,-2.73 -3.91,-4.31c-1.17,-1.57 -2.1,-3.32 -2.71,-5.2c-0.28,-0.94 -0.48,-1.95 -0.31,-2.95c0.12,-0.99 0.76,-1.94 1.66,-2.3z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="95.255"
|
||||
android:startX="18.517"
|
||||
android:endY="82.775"
|
||||
android:endX="5.827"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFB7537A"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M0.55,102.67c0.03,-0.28 0.07,-0.52 0.12,-0.73c-0.07,0.23 -0.11,0.47 -0.12,0.73z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="38.904"
|
||||
android:centerX="29.186"
|
||||
android:centerY="93.866"
|
||||
android:type="radial">
|
||||
<item android:offset="0.33" android:color="#FFFFF176"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M4.31,106.78c-0.72,-0.69 -1.21,-1.27 -1.58,-1.88c-0.42,-0.7 -0.66,-1.44 -0.68,-2.13c-0.02,-0.77 0.05,-1.34 0.78,-1.9c0.36,-0.28 1.37,-0.18 1.35,-0.19c-0.01,-0.01 -0.03,-0.01 -0.04,-0.02c-0.61,-0.24 -1.18,-0.3 -1.69,-0.22l0.01,-0.01l-0.01,0.01c-0.86,0.14 -1.52,0.69 -1.78,1.5c-0.05,0.21 -0.09,0.45 -0.12,0.73c-0.01,0.43 0.06,0.91 0.25,1.42c0.29,0.77 0.76,1.52 1.33,2.24c0.34,0.52 0.74,1.01 1.11,1.48c0.59,0.7 1.19,1.39 1.86,2.01a26.45,26.45 0,0 0,4.3 3.38c0.75,0.46 8.17,5.01 14.67,6.26c0.02,-0.02 -12.39,-5.14 -19.76,-12.68z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="109.935"
|
||||
android:startX="0.549"
|
||||
android:endY="109.935"
|
||||
android:endX="24.074"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFDA6727"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M13.25,105.53c-0.07,0 -0.15,-0.01 -0.21,-0.04c-1.58,-0.66 -3.15,-1.6 -4.81,-2.88a27.08,27.08 0,0 1,-4.01 -3.91c-0.64,-0.74 -1.21,-1.55 -1.67,-2.24l-0.14,-0.22c-0.47,-0.73 -0.95,-1.48 -1.25,-2.36c-0.39,-1.04 -0.49,-2.11 -0.27,-3.1c0.25,-1.19 0.93,-2 1.88,-2.41c1.36,-0.59 2.88,0.91 2.79,0.87c-1.05,-0.47 -1.97,-0.21 -2.21,-0.05c-0.55,0.38 -0.9,1.04 -0.97,1.81c-0.05,0.69 0.11,1.45 0.46,2.19c0.3,0.65 0.72,1.28 1.37,2.04c0.56,0.72 1.14,1.38 1.71,2.03a57.36,57.36 0,0 0,3.68 3.77c1.07,0.99 2.49,2.28 4.02,3.5c0.22,0.17 0.28,0.48 0.13,0.72c-0.12,0.18 -0.3,0.28 -0.5,0.28z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="96.876"
|
||||
android:startX="0.779"
|
||||
android:endY="96.876"
|
||||
android:endX="13.816"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFDA6727"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M16.78,96.91c0.19,-0.21 0.15,-0.34 -0.05,-0.54a218.71,218.71 0,0 1,-3.97 -4.12a61.13,61.13 0,0 1,-3.67 -4.24C8,86.58 6.9,85.04 6.22,83.37c-0.26,-0.68 -0.56,-1.58 -0.54,-2.47c0,-0.4 0.11,-0.77 0.29,-1.1c0.17,-0.31 0.42,-0.58 0.73,-0.78c0.01,0 0.01,-0.01 0.01,-0.01c0.51,-0.31 1.34,-0.54 2.67,-0.31c-2.33,-1.57 -4.08,-0.68 -4.82,1.01c-0.81,1.84 0.57,5.13 1.49,6.65c4.51,7.48 10.02,10.47 10.02,10.47c0.27,0.17 0.57,0.22 0.71,0.08z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="87.433"
|
||||
android:startX="4.074"
|
||||
android:endY="87.433"
|
||||
android:endX="16.901"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFDA6727"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M23.54,91.61c-1.69,-2.24 -8.47,-9.35 -8.3,-14c0.05,-1.49 2.43,-3.02 4.81,-1.63c0.03,0.02 -1.74,-2 -3.96,-1.21c-1.93,0.68 -2.21,2.46 -2.23,2.78c-0.1,1.01 0.18,1.98 0.45,2.72c0.56,1.4 1.27,2.76 2.29,4.39c0.76,1.24 1.65,2.5 2.79,3.99c0.63,0.81 1.27,1.55 1.92,2.25c0.44,0.48 0.89,0.94 1.35,1.39c0.11,0.1 0.25,0.16 0.39,0.16c0.12,0 0.24,-0.04 0.34,-0.11c0.21,-0.18 0.31,-0.51 0.15,-0.73z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="83.513"
|
||||
android:startX="13.586"
|
||||
android:endY="83.513"
|
||||
android:endX="23.62"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFDA6727"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M36.82,78.92c-0.72,0 -1.94,0.18 -2.59,1.17c-1.28,1.92 -0.78,5.52 0.2,8.65c0.16,0.51 0.26,0.92 0.32,1.26a0.794,0.794 0,0 1,-1.06 0.9c-2.45,-0.92 -5.17,-4.13 -7.89,-7.32l-0.41,-0.5c0.03,0.46 1.59,2.96 3.72,5.65c0.57,0.73 4.36,4.86 6.38,3.68c1.25,-0.72 -0.79,-6.32 -0.81,-7.77c-0.01,-0.79 -0.12,-1.81 0.13,-2.75c0.05,-0.24 0.2,-0.49 0.39,-0.74c1.09,-1.46 3.35,-1.52 4.28,0.05l0.03,0.04c-0.5,-1.34 -1.12,-2.32 -2.69,-2.32z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="85.763"
|
||||
android:startX="25.394"
|
||||
android:endY="85.763"
|
||||
android:endX="40.599"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFFC400"/>
|
||||
<item android:offset="0.873" android:color="#FFDA6727"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M88.31,84.46c1.09,-3.08 1.87,28.82 1.87,28.82c-2.92,1.23 -5.87,2.31 -8.93,3.13c-1.21,-2.73 -2.79,-7.76 -1.96,-13.46c1.34,-9.11 6.77,-12.14 9.02,-18.49z"
|
||||
android:strokeAlpha="0.66"
|
||||
android:fillAlpha="0.66">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="128.189"
|
||||
android:startX="84.62198"
|
||||
android:endY="97.296"
|
||||
android:endX="84.62198"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFBF360C"/>
|
||||
<item android:offset="1" android:color="#33BF360C"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M87.68,81.23c0.49,-1.33 1.11,-2.32 2.68,-2.32c0.72,0 1.94,0.18 2.59,1.17c1.28,1.92 0.78,5.52 -0.2,8.65c-0.55,1.77 -0.4,2.42 -0.4,2.42c2.72,-0.17 5.88,-3.88 9.03,-7.58c1.93,-2.27 3.05,-4.36 4.93,-6.68c1.14,-1.41 2.81,-2.83 4.77,-2.11c5.37,1.99 -1.27,9.72 -2.93,11.84c-1.89,2.41 -3.76,4.72 -3.76,4.72c1.76,-0.41 2.87,-0.88 5.97,-4.06c2.65,-2.72 4.62,-6.23 6.06,-7.54c3.27,-2.97 5.27,-2 6.14,-0.02c0.81,1.84 -0.53,5.09 -1.44,6.61c-4.51,7.48 -10.27,10.41 -10.27,10.41c4.74,-0.21 8.48,-5.29 9.99,-6.83c4.38,-4.45 6.38,1.06 3.99,5.05c-1.1,1.83 -2.2,3.12 -4.21,5.19c-3.25,3.33 -6.31,4.84 -6.31,4.84s1.31,0.2 2.63,-0.49c1.78,-0.93 3.77,-2.33 4.8,-3.13c2.95,-2.27 5.76,-0.32 4.63,2.7c-1.31,3.49 -6.09,6.45 -8.98,8.6c-5.62,4.16 -12.63,7.49 -19.73,7.26c-7.9,-0.25 -13.34,-5.68 -15.48,-13.31c-1.24,-4.44 1.72,-11.5 2.47,-13.28c2.46,-5.76 2.54,-10.78 3.03,-12.11z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="38.904"
|
||||
android:centerX="97.99198"
|
||||
android:centerY="93.866"
|
||||
android:type="radial">
|
||||
<item android:offset="0.33" android:color="#FFFFF176"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M103.56,99.08c-0.14,8.38 -5.4,11.88 -9.48,12.65c1.64,-2.39 2.49,-5.31 2.39,-8.2c-0.1,-2.9 -1.22,-6.51 -2.88,-8.47c-1.66,-1.96 -1.24,-3.89 -1.24,-3.89s1.12,-0.09 1.54,-0.34c1.34,-0.82 2.88,-1.48 4.04,-2.55c0.84,-0.79 5.77,2.42 5.63,10.8z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="11.727577"
|
||||
android:centerX="90.41754"
|
||||
android:centerY="102.50223"
|
||||
android:type="radial">
|
||||
<item android:offset="0" android:color="#FFFFC709"/>
|
||||
<item android:offset="1" android:color="#00FFC709"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M120.77,78.54a2.84,2.84 0,0 1,1.28 2.35c0.03,0.92 -0.24,1.82 -0.57,2.68c-0.7,1.73 -1.8,3.28 -2.93,4.77a62.626,62.626 0,0 1,-3.71 4.29c-1.3,1.39 -2.63,2.76 -3.98,4.13c1.67,-0.98 3.22,-2.16 4.65,-3.46c1.42,-1.31 2.77,-2.73 3.91,-4.31c1.17,-1.57 2.1,-3.32 2.71,-5.2c0.28,-0.94 0.48,-1.95 0.31,-2.95c-0.13,-0.99 -0.76,-1.94 -1.67,-2.3z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="95.255"
|
||||
android:startX="108.66098"
|
||||
android:endY="82.775"
|
||||
android:endX="121.35098"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFB7537A"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M126.63,102.67c-0.03,-0.28 -0.07,-0.52 -0.12,-0.73c0.06,0.23 0.11,0.47 0.12,0.73z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="38.904"
|
||||
android:centerX="97.99198"
|
||||
android:centerY="93.866"
|
||||
android:type="radial">
|
||||
<item android:offset="0.33" android:color="#FFFFF176"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M122.86,106.78c0.72,-0.69 1.21,-1.27 1.58,-1.88c0.42,-0.7 0.66,-1.44 0.68,-2.13c0.02,-0.77 -0.05,-1.34 -0.78,-1.9c-0.36,-0.28 -1.37,-0.18 -1.35,-0.19c0.01,-0.01 0.03,-0.01 0.04,-0.02c0.61,-0.24 1.18,-0.3 1.69,-0.22l-0.01,-0.01l0.01,0.01c0.86,0.14 1.52,0.69 1.78,1.5c0.05,0.21 0.09,0.45 0.12,0.73c0.01,0.43 -0.06,0.91 -0.25,1.42c-0.29,0.77 -0.76,1.52 -1.33,2.24c-0.34,0.52 -0.74,1.01 -1.11,1.48c-0.59,0.7 -1.19,1.39 -1.86,2.01a26.45,26.45 0,0 1,-4.3 3.38c-0.75,0.46 -8.17,5.01 -14.67,6.26c-0.01,-0.02 12.4,-5.14 19.76,-12.68z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="109.935"
|
||||
android:startX="126.62798"
|
||||
android:endY="109.935"
|
||||
android:endX="103.10398"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFDA6727"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M113.92,105.53c0.07,0 0.15,-0.01 0.21,-0.04c1.58,-0.66 3.15,-1.6 4.81,-2.88a27.08,27.08 0,0 0,4.01 -3.91c0.64,-0.74 1.21,-1.55 1.67,-2.24l0.14,-0.22c0.47,-0.73 0.95,-1.48 1.25,-2.36c0.39,-1.04 0.49,-2.11 0.27,-3.1c-0.25,-1.19 -0.93,-2 -1.88,-2.41c-1.36,-0.59 -2.88,0.91 -2.79,0.87c1.05,-0.47 1.97,-0.21 2.21,-0.05c0.55,0.38 0.91,1.04 0.98,1.81c0.05,0.69 -0.11,1.45 -0.46,2.19c-0.3,0.65 -0.72,1.28 -1.37,2.04c-0.56,0.72 -1.14,1.38 -1.71,2.03a57.36,57.36 0,0 1,-3.68 3.77c-1.07,0.99 -2.49,2.28 -4.02,3.5c-0.22,0.17 -0.28,0.48 -0.13,0.72c0.11,0.18 0.3,0.28 0.49,0.28z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="96.876"
|
||||
android:startX="126.39898"
|
||||
android:endY="96.876"
|
||||
android:endX="113.361984"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFDA6727"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M110.4,96.91c-0.19,-0.21 -0.15,-0.34 0.05,-0.54c1.49,-1.51 2.79,-2.86 3.97,-4.12a61.13,61.13 0,0 0,3.67 -4.24c1.08,-1.42 2.18,-2.96 2.86,-4.63c0.26,-0.68 0.56,-1.58 0.54,-2.47c0,-0.4 -0.11,-0.77 -0.29,-1.1c-0.17,-0.31 -0.42,-0.58 -0.73,-0.78c-0.01,0 -0.01,-0.01 -0.01,-0.01c-0.51,-0.31 -1.34,-0.54 -2.67,-0.31c2.33,-1.57 4.08,-0.68 4.82,1.01c0.81,1.84 -0.57,5.13 -1.49,6.65c-4.51,7.48 -10.02,10.47 -10.02,10.47c-0.26,0.16 -0.57,0.21 -0.7,0.07z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="87.433"
|
||||
android:startX="123.10298"
|
||||
android:endY="87.433"
|
||||
android:endX="110.275986"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFDA6727"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M103.64,91.61c1.69,-2.24 8.47,-9.35 8.3,-14c-0.05,-1.49 -2.43,-3.02 -4.81,-1.63c-0.03,0.02 1.74,-2 3.96,-1.21c1.93,0.68 2.21,2.46 2.23,2.78c0.1,1.01 -0.18,1.98 -0.45,2.72c-0.56,1.4 -1.27,2.76 -2.29,4.39c-0.76,1.24 -1.65,2.5 -2.79,3.99c-0.63,0.81 -1.27,1.55 -1.92,2.25c-0.44,0.48 -0.89,0.94 -1.35,1.39a0.59,0.59 0,0 1,-0.39 0.16c-0.12,0 -0.24,-0.04 -0.34,-0.11c-0.22,-0.18 -0.32,-0.51 -0.15,-0.73z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="83.513"
|
||||
android:startX="113.59198"
|
||||
android:endY="83.513"
|
||||
android:endX="103.55798"
|
||||
android:type="linear">
|
||||
<item android:offset="0.127" android:color="#FFDA6727"/>
|
||||
<item android:offset="1" android:color="#FFFFC400"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M90.36,78.92c0.72,0 1.94,0.18 2.59,1.17c1.28,1.92 0.78,5.52 -0.2,8.65c-0.16,0.51 -0.26,0.92 -0.32,1.26a0.794,0.794 0,0 0,1.06 0.9c2.45,-0.92 5.17,-4.13 7.89,-7.32l0.41,-0.5c-0.03,0.46 -1.59,2.96 -3.72,5.65c-0.57,0.73 -4.36,4.86 -6.38,3.68c-1.25,-0.72 0.79,-6.32 0.81,-7.77c0.01,-0.79 0.12,-1.81 -0.13,-2.75c-0.05,-0.24 -0.2,-0.49 -0.39,-0.74c-1.09,-1.46 -3.35,-1.52 -4.28,0.05l-0.03,0.04c0.5,-1.34 1.11,-2.32 2.69,-2.32z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="85.763"
|
||||
android:startX="101.78398"
|
||||
android:endY="85.763"
|
||||
android:endX="86.57798"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFFC400"/>
|
||||
<item android:offset="0.873" android:color="#FFDA6727"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M0,0h128v128h-128z"
|
||||
android:fillColor="#00000000"/>
|
||||
</vector>
|
BIN
app/src/main/res/drawable/games_achievements.png
Normal file
BIN
app/src/main/res/drawable/games_achievements.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
app/src/main/res/drawable/games_controller.png
Normal file
BIN
app/src/main/res/drawable/games_controller.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/res/drawable/games_leaderboards.png
Normal file
BIN
app/src/main/res/drawable/games_leaderboards.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -5,7 +5,6 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
|
|
47
app/src/main/res/layout/dialog_payments.xml
Normal file
47
app/src/main/res/layout/dialog_payments.xml
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
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="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/emoji"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:srcCompat="@drawable/emoji_hugging_face"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/supportText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/support"
|
||||
android:layout_marginTop="24dp"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/emoji"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/supportDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/support_description"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:gravity="center"
|
||||
android:textSize="16sp"
|
||||
android:paddingBottom="8dp"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/supportText"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -13,7 +13,7 @@
|
|||
android:paddingRight="48dp"
|
||||
android:paddingStart="32dp"
|
||||
android:paddingEnd="48dp"
|
||||
android:paddingVertical="24dp">
|
||||
android:paddingVertical="12dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/radio"
|
||||
|
@ -35,15 +35,6 @@
|
|||
tools:text="">
|
||||
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:textStyle="bold"
|
||||
tools:text="Title" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow android:paddingVertical="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/firstAction"
|
||||
tools:text="First Action" />
|
||||
|
@ -62,7 +53,6 @@
|
|||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/secondAction"
|
||||
tools:text="Long Tap" />
|
||||
|
|
34
app/src/main/res/layout/view_play_games_button.xml
Normal file
34
app/src/main/res/layout/view_play_games_button.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:drawablePadding="10dp"
|
||||
app:tint="@android:color/tab_indicator_text"
|
||||
app:tintMode="src_in"
|
||||
tools:src="@drawable/games_achievements" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:text="Text" />
|
||||
|
||||
</LinearLayout>
|
|
@ -71,6 +71,14 @@
|
|||
android:title="@string/settings" />
|
||||
</group>
|
||||
|
||||
<group android:id="@+id/play_games_group">
|
||||
<item
|
||||
android:id="@+id/play_games"
|
||||
android:checkable="false"
|
||||
android:icon="@drawable/games_controller"
|
||||
android:title="@string/google_play_games" />
|
||||
</group>
|
||||
|
||||
<group
|
||||
android:id="@+id/install_group"
|
||||
android:visible="false"
|
||||
|
|
|
@ -16,7 +16,7 @@ class ControlViewModelTest : IntentViewModelTest() {
|
|||
val rule = InstantTaskExecutorRule()
|
||||
|
||||
private fun ControlViewModel.selectedControlStyle() = singleState().let {
|
||||
it.gameControls[it.selectedId].controlStyle
|
||||
it.gameControls[it.selectedIndex].controlStyle
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -35,5 +35,5 @@ class TestLevelModule {
|
|||
fun provideMinefieldRepository(): IMinefieldRepository = FixedMinefieldRepository()
|
||||
|
||||
@Provides
|
||||
fun provideHapticFeedbackInteractor(): IHapticFeedbackManager = DisabledHapticFeedbackManager()
|
||||
fun provideHapticFeedbackManager(): IHapticFeedbackManager = DisabledHapticFeedbackManager()
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ buildscript {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
||||
classpath 'com.android.tools.build:gradle:4.0.1'
|
||||
classpath 'com.google.gms:google-services:4.3.3'
|
||||
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28.1-alpha'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72'
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
// versionCode and versionName must be hardcoded to support F-droid
|
||||
versionCode 800041
|
||||
versionName '8.0.4'
|
||||
versionCode 800051
|
||||
versionName '8.0.5'
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
@ -41,6 +41,7 @@ dependencies {
|
|||
// Dependencies must be hardcoded to support F-droid
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(':external')
|
||||
|
||||
// AndroidX
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
|
@ -61,8 +62,8 @@ dependencies {
|
|||
kapt 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
|
||||
testImplementation 'com.google.dagger:hilt-android-testing:2.28.1-alpha'
|
||||
kaptTest 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
|
||||
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01"
|
||||
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha01"
|
||||
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02"
|
||||
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha02"
|
||||
|
||||
// Room
|
||||
api 'androidx.room:room-runtime:2.2.5'
|
||||
|
|
|
@ -61,10 +61,6 @@ class AreaAdapter(
|
|||
return AreaViewHolder(view).apply {
|
||||
view.setOnDoubleClickListener(object : GestureDetector.OnDoubleTapListener {
|
||||
override fun onDoubleTap(e: MotionEvent?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
|
||||
val position = adapterPosition
|
||||
return when {
|
||||
position == RecyclerView.NO_POSITION -> {
|
||||
|
@ -83,8 +79,13 @@ class AreaAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
|
||||
if (preferencesRepository.controlStyle() == ControlStyle.DoubleClick) {
|
||||
val style = preferencesRepository.controlStyle()
|
||||
if (style == ControlStyle.DoubleClick || style == ControlStyle.DoubleClickInverted) {
|
||||
val position = adapterPosition
|
||||
if (position == RecyclerView.NO_POSITION) {
|
||||
Log.d(TAG, "Item no longer exists.")
|
||||
|
@ -115,7 +116,8 @@ class AreaAdapter(
|
|||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
if (preferencesRepository.controlStyle() != ControlStyle.DoubleClick) {
|
||||
val style = preferencesRepository.controlStyle()
|
||||
if (style != ControlStyle.DoubleClick && style != ControlStyle.DoubleClickInverted) {
|
||||
val position = adapterPosition
|
||||
if (position == RecyclerView.NO_POSITION) {
|
||||
Log.d(TAG, "Item no longer exists.")
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package dev.lucasnlm.antimine.common.level.view
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
|
@ -39,9 +42,7 @@ abstract class CommonLevelFragment(@LayoutRes val contentLayoutId: Int) : Fragme
|
|||
val height = requireView().measuredHeight
|
||||
val recyclerViewHeight = (dimensionRepository.areaSize() * boardHeight)
|
||||
val separatorsHeight = (2 * dimensionRepository.areaSeparator() * (boardHeight - 1))
|
||||
|
||||
val calculatedHeight = (height - recyclerViewHeight - separatorsHeight)
|
||||
|
||||
return (calculatedHeight / 2).coerceAtLeast(0.0f).toInt()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -208,7 +208,7 @@ class GameViewModel @ViewModelInject constructor(
|
|||
}
|
||||
|
||||
suspend fun saveGame() {
|
||||
if (initialized && gameController.hasMines) {
|
||||
if (gameController.hasMines) {
|
||||
val id = savesRepository.saveGame(
|
||||
gameController.getSaveState(elapsedTimeSeconds.value ?: 0L, currentDifficulty)
|
||||
)
|
||||
|
|
|
@ -5,12 +5,18 @@ import android.util.Log
|
|||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
|
||||
class DebugAnalyticsManager : IAnalyticsManager {
|
||||
override fun setup(context: Context, userProperties: Map<String, String>) {
|
||||
Log.d(TAG, "Setup Analytics using $userProperties")
|
||||
override fun setup(context: Context, properties: Map<String, String>) {
|
||||
if (properties.isNotEmpty()) {
|
||||
Log.d(TAG, "Setup Analytics using $properties")
|
||||
}
|
||||
}
|
||||
|
||||
override fun sentEvent(event: Analytics) {
|
||||
Log.d(TAG, "Sent event: '${event.title}' with ${event.extra}")
|
||||
if (event.extra.isNotEmpty()) {
|
||||
Log.d(TAG, "Sent event: '${event.name}' with ${event.extra}")
|
||||
} else {
|
||||
Log.d(TAG, "Sent event: '${event.name}'")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -4,6 +4,6 @@ import android.content.Context
|
|||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
|
||||
interface IAnalyticsManager {
|
||||
fun setup(context: Context, userProperties: Map<String, String>)
|
||||
fun setup(context: Context, properties: Map<String, String>)
|
||||
fun sentEvent(event: Analytics)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package dev.lucasnlm.antimine.core.analytics
|
||||
|
||||
import android.content.Context
|
||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
import dev.lucasnlm.external.IExternalAnalyticsWrapper
|
||||
|
||||
class ProdAnalyticsManager(
|
||||
private val analyticsWrapper: IExternalAnalyticsWrapper
|
||||
) : IAnalyticsManager{
|
||||
override fun setup(context: Context, properties: Map<String, String>) {
|
||||
analyticsWrapper.setup(context, properties)
|
||||
}
|
||||
|
||||
override fun sentEvent(event: Analytics) {
|
||||
analyticsWrapper.sendEvent(event.name, event.extra)
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import dev.lucasnlm.antimine.common.level.models.Score
|
|||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
|
||||
sealed class Analytics(
|
||||
val title: String,
|
||||
val name: String,
|
||||
val extra: Map<String, String> = mapOf()
|
||||
) {
|
||||
object Open : Analytics("Open game")
|
||||
|
|
|
@ -26,7 +26,8 @@ data class Actions(
|
|||
enum class ControlStyle {
|
||||
Standard,
|
||||
DoubleClick,
|
||||
FastFlag
|
||||
FastFlag,
|
||||
DoubleClickInverted,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,12 +81,27 @@ sealed class GameControl(
|
|||
)
|
||||
)
|
||||
|
||||
object DoubleClickInverted : GameControl(
|
||||
id = ControlStyle.DoubleClickInverted,
|
||||
onCovered = Actions(
|
||||
singleClick = ActionResponse.OpenTile,
|
||||
longPress = null,
|
||||
doubleClick = ActionResponse.SwitchMark
|
||||
),
|
||||
onOpen = Actions(
|
||||
singleClick = ActionResponse.HighlightNeighbors,
|
||||
longPress = null,
|
||||
doubleClick = ActionResponse.OpenNeighbors
|
||||
)
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun fromControlType(controlStyle: ControlStyle): GameControl {
|
||||
return when (controlStyle) {
|
||||
ControlStyle.Standard -> Standard
|
||||
ControlStyle.DoubleClick -> DoubleClick
|
||||
ControlStyle.FastFlag -> FastFlag
|
||||
ControlStyle.DoubleClickInverted -> DoubleClickInverted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import dagger.Provides
|
|||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ApplicationComponent
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dev.lucasnlm.antimine.common.BuildConfig
|
||||
import dev.lucasnlm.antimine.common.level.repository.DimensionRepository
|
||||
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
|
||||
import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.DebugAnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.ProdAnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
import dev.lucasnlm.antimine.core.preferences.PreferencesManager
|
||||
import dev.lucasnlm.antimine.core.preferences.PreferencesRepository
|
||||
|
@ -17,6 +19,7 @@ import dev.lucasnlm.antimine.core.sound.ISoundManager
|
|||
import dev.lucasnlm.antimine.core.sound.SoundManager
|
||||
import dev.lucasnlm.antimine.core.themes.repository.IThemeRepository
|
||||
import dev.lucasnlm.antimine.core.themes.repository.ThemeRepository
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ApplicationComponent::class)
|
||||
|
@ -28,6 +31,7 @@ class CommonModule {
|
|||
): IDimensionRepository =
|
||||
DimensionRepository(context, preferencesRepository)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun providePreferencesRepository(
|
||||
preferencesManager: PreferencesManager
|
||||
|
@ -38,9 +42,6 @@ class CommonModule {
|
|||
@ApplicationContext context: Context
|
||||
): PreferencesManager = PreferencesManager(context)
|
||||
|
||||
@Provides
|
||||
fun provideAnalyticsManager(): IAnalyticsManager = DebugAnalyticsManager()
|
||||
|
||||
@Provides
|
||||
fun provideSoundManager(
|
||||
@ApplicationContext context: Context
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
<string name="size">Size</string>
|
||||
<string name="system">System</string>
|
||||
<string name="rating">Feedback</string>
|
||||
<string name="support">Help supporting us!</string>
|
||||
<string name="support_description">It will allow us building new features and to keep our project active.</string>
|
||||
<string name="rating_message">If you like this game, please give us a feedback. It will help us a lot.</string>
|
||||
<string name="used_software_text">This game uses the following third parties software:</string>
|
||||
<string name="translators_text">This game was translated by the following people:</string>
|
||||
|
@ -58,7 +60,6 @@
|
|||
<string name="use_question_mark">Use Question Mark</string>
|
||||
<string name="control">Controls</string>
|
||||
<string name="arrow">➞</string>
|
||||
<string name="flag_first">Flag First</string>
|
||||
<string name="single_click">Single Click</string>
|
||||
<string name="double_click">Double Click</string>
|
||||
<string name="long_press">Long Press</string>
|
||||
|
@ -71,6 +72,8 @@
|
|||
<string name="cancel">Cancel</string>
|
||||
<string name="resume">Resume</string>
|
||||
<string name="yes">Yes</string>
|
||||
<string name="unlock">Unlock</string>
|
||||
<string name="achievements">Achievements</string>
|
||||
<string name="rating_button_no">No</string>
|
||||
<string name="general">General</string>
|
||||
<string name="source_code">Source Code</string>
|
||||
|
|
|
@ -91,13 +91,13 @@ class GameControllerTest {
|
|||
|
||||
val firstMine = controller.field.first { it.hasMine }
|
||||
assertEquals(
|
||||
listOf(4, 5, 13, 33, 27, 36, 38, 41, 19, 29, 39, 49, 67, 59, 70, 86, 81, 87, 92, 91),
|
||||
listOf(3, 4, 32, 42, 36, 45, 52, 28, 55, 47, 65, 39, 73, 74, 59, 85, 91, 95, 88, 90),
|
||||
controller.takeExplosionRadius(firstMine).map { it.id }.toList()
|
||||
)
|
||||
|
||||
val midMine = controller.field.filter { it.hasMine }.take(controller.getMinesCount() / 2).last()
|
||||
assertEquals(
|
||||
listOf(39, 29, 38, 49, 19, 59, 27, 36, 67, 5, 87, 4, 86, 33, 13, 41, 92, 81, 70, 91),
|
||||
listOf(52, 42, 32, 73, 74, 55, 45, 65, 91, 85, 36, 90, 95, 3, 47, 4, 28, 88, 59, 39),
|
||||
controller.takeExplosionRadius(midMine).map { it.id }.toList()
|
||||
)
|
||||
}
|
||||
|
@ -313,6 +313,34 @@ class GameControllerTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testControlFirstActionWithInvertedDoubleClick() {
|
||||
withGameController { controller ->
|
||||
controller.run {
|
||||
updateGameControl(GameControl.fromControlType(ControlStyle.DoubleClickInverted))
|
||||
assertTrue(at(3).isCovered)
|
||||
fakeDoubleClick(3)
|
||||
assertTrue(at(3).isCovered)
|
||||
assertTrue(at(3).mark.isFlag())
|
||||
fakeDoubleClick(3)
|
||||
assertFalse(at(3).mark.isFlag())
|
||||
assertTrue(at(3).isCovered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testControlSecondActionWithInvertedDoubleClick() {
|
||||
withGameController { controller ->
|
||||
controller.run {
|
||||
updateGameControl(GameControl.fromControlType(ControlStyle.DoubleClickInverted))
|
||||
assertTrue(at(3).isCovered)
|
||||
fakeSingleClick(3)
|
||||
assertFalse(at(3).isCovered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testControlFastFlagOpenMultiple() {
|
||||
withGameController { controller ->
|
||||
|
|
1
external/.gitignore
vendored
Normal file
1
external/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
40
external/build.gradle
vendored
Normal file
40
external/build.gradle
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'kotlin-android'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName '1.0'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Dependencies must be hardcoded to support F-droid
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
// Kotlin Lib
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72'
|
||||
}
|
5
external/src/main/AndroidManifest.xml
vendored
Normal file
5
external/src/main/AndroidManifest.xml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="dev.lucasnlm.external">
|
||||
|
||||
</manifest>
|
8
external/src/main/java/dev/lucasnlm/external/BillingManager.kt
vendored
Normal file
8
external/src/main/java/dev/lucasnlm/external/BillingManager.kt
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.app.Activity
|
||||
|
||||
interface IBillingManager {
|
||||
fun start()
|
||||
suspend fun charge(activity: Activity)
|
||||
}
|
8
external/src/main/java/dev/lucasnlm/external/ExternalAnalyticsWrapper.kt
vendored
Normal file
8
external/src/main/java/dev/lucasnlm/external/ExternalAnalyticsWrapper.kt
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.content.Context
|
||||
|
||||
interface IExternalAnalyticsWrapper {
|
||||
fun setup(context: Context, properties: Map<String, String>)
|
||||
fun sendEvent(name: String, content: Map<String, String>)
|
||||
}
|
11
external/src/main/java/dev/lucasnlm/external/InstantAppManager.kt
vendored
Normal file
11
external/src/main/java/dev/lucasnlm/external/InstantAppManager.kt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
interface IInstantAppManager {
|
||||
fun isInstantAppSupported(context: Context): Boolean
|
||||
fun isInAppPaymentsSupported(context: Context): Boolean
|
||||
fun showInstallPrompt(activity: Activity, intent: Intent?, requestCode: Int, referrer: String?): Boolean
|
||||
}
|
14
external/src/main/java/dev/lucasnlm/external/PlayGamesManager.kt
vendored
Normal file
14
external/src/main/java/dev/lucasnlm/external/PlayGamesManager.kt
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
|
||||
interface IPlayGamesManager {
|
||||
fun hasGooglePlayGames(): Boolean
|
||||
fun silentLogin(activity: Activity)
|
||||
fun getLoginIntent(): Intent?
|
||||
fun handleLoginResult(data: Intent?)
|
||||
fun isLogged(): Boolean
|
||||
fun openAchievements(activity: Activity)
|
||||
fun openLeaderboards(activity: Activity)
|
||||
}
|
|
@ -6,8 +6,8 @@ android {
|
|||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
versionCode 800041 // MMmmPPv
|
||||
versionName '8.0.4'
|
||||
versionCode 800051 // MMmmPPv
|
||||
versionName '8.0.5'
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
}
|
||||
|
@ -24,4 +24,5 @@ dependencies {
|
|||
// Dependencies must be hardcoded to support F-droid
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(':external')
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<manifest
|
||||
package="dev.lucasnlm.foss" />
|
||||
|
|
16
foss/src/main/java/dev/lucasnlm/external/BillingManager.kt
vendored
Normal file
16
foss/src/main/java/dev/lucasnlm/external/BillingManager.kt
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
|
||||
class BillingManager(
|
||||
private val context: Context
|
||||
) : IBillingManager {
|
||||
override fun start() {
|
||||
// Void
|
||||
}
|
||||
|
||||
override suspend fun charge(activity: Activity) {
|
||||
// Void
|
||||
}
|
||||
}
|
|
@ -5,9 +5,11 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
class InstantAppWrapper {
|
||||
class ProprietaryAppWrapper {
|
||||
// FOSS build doesn't support Instant App
|
||||
fun isEnabled(context: Context): Boolean = false
|
||||
fun isInstantAppSupported(context: Context): Boolean = false
|
||||
|
||||
fun isInAppPaymentsSupported(context: Context) = false
|
||||
|
||||
fun showInstallPrompt(activity: Activity, intent: Intent?, requestCode: Int, referrer: String?) {
|
||||
// Empty
|
|
@ -1,13 +1,14 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
versionCode 800041 // MMmmPPv
|
||||
versionName '8.0.4'
|
||||
versionCode 800051
|
||||
versionName '8.0.5'
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
}
|
||||
|
@ -24,7 +25,17 @@ dependencies {
|
|||
// Dependencies must be hardcoded to support F-droid
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(':external')
|
||||
|
||||
// Google
|
||||
implementation 'com.android.billingclient:billing-ktx:3.0.0'
|
||||
implementation 'com.google.android.gms:play-services-instantapps:17.0.0'
|
||||
implementation 'com.google.android.gms:play-services-games:19.0.0'
|
||||
implementation 'com.google.android.gms:play-services-auth:18.1.0'
|
||||
|
||||
// Firebase
|
||||
implementation 'com.google.firebase:firebase-analytics:17.5.0'
|
||||
|
||||
// Kotlin Lib
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72'
|
||||
}
|
||||
|
|
|
@ -1,2 +1,15 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="dev.lucasnlm.proprietary" />
|
||||
package="dev.lucasnlm.antimine">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="com.android.vending.BILLING"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<application>
|
||||
<meta-data android:name="com.google.android.gms.games.APP_ID"
|
||||
android:value="@string/app_id"/>
|
||||
<meta-data android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
59
proprietary/src/main/java/dev/lucasnlm/external/BillingManager.kt
vendored
Normal file
59
proprietary/src/main/java/dev/lucasnlm/external/BillingManager.kt
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import com.android.billingclient.api.BillingClient
|
||||
import com.android.billingclient.api.BillingClientStateListener
|
||||
import com.android.billingclient.api.BillingResult
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener
|
||||
import com.android.billingclient.api.SkuDetailsParams
|
||||
import com.android.billingclient.api.querySkuDetails
|
||||
|
||||
class BillingManager(
|
||||
private val context: Context
|
||||
) : IBillingManager {
|
||||
private val purchaseUpdateListener =
|
||||
PurchasesUpdatedListener { billingResult, purchases ->
|
||||
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
|
||||
// The BillingClient is ready. You can query purchases here.
|
||||
}
|
||||
}
|
||||
|
||||
private val billingClient by lazy {
|
||||
BillingClient.newBuilder(context)
|
||||
.setListener(purchaseUpdateListener)
|
||||
.enablePendingPurchases()
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
billingClient.startConnection(object : BillingClientStateListener {
|
||||
override fun onBillingSetupFinished(billingResult: BillingResult) {
|
||||
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
|
||||
// The BillingClient is ready. You can query purchases here.
|
||||
}
|
||||
}
|
||||
override fun onBillingServiceDisconnected() {
|
||||
// Try to restart the connection on the next request to
|
||||
// Google Play by calling the startConnection() method.
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override suspend fun charge(activity: Activity) {
|
||||
val skuDetailsParams = SkuDetailsParams.newBuilder()
|
||||
.setSkusList(listOf(BASIC_SUPPORT))
|
||||
.setType(BillingClient.SkuType.INAPP)
|
||||
.build()
|
||||
|
||||
val details = billingClient.querySkuDetails(skuDetailsParams)
|
||||
|
||||
print(details.toString())
|
||||
|
||||
//billingClient.launchBillingFlow(activity, flowParams)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BASIC_SUPPORT = "unlock_0"
|
||||
}
|
||||
}
|
30
proprietary/src/main/java/dev/lucasnlm/external/ExternalAnalyticsWrapper.kt
vendored
Normal file
30
proprietary/src/main/java/dev/lucasnlm/external/ExternalAnalyticsWrapper.kt
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
|
||||
class ExternalAnalyticsWrapper(
|
||||
private val context: Context
|
||||
) : IExternalAnalyticsWrapper {
|
||||
private val firebaseAnalytics by lazy {
|
||||
FirebaseAnalytics.getInstance(context)
|
||||
}
|
||||
|
||||
override fun setup(context: Context, properties: Map<String, String>) {
|
||||
properties.forEach { (key, value) ->
|
||||
firebaseAnalytics.setUserProperty(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendEvent(name: String, content: Map<String, String>) {
|
||||
val bundle = Bundle().apply {
|
||||
putString(FirebaseAnalytics.Param.ITEM_NAME, name)
|
||||
content.forEach { (key, value) ->
|
||||
this.putString(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle)
|
||||
}
|
||||
}
|
15
proprietary/src/main/java/dev/lucasnlm/external/InstantAppManager.kt
vendored
Normal file
15
proprietary/src/main/java/dev/lucasnlm/external/InstantAppManager.kt
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.google.android.gms.instantapps.InstantApps
|
||||
|
||||
class InstantAppManager : IInstantAppManager {
|
||||
override fun isInstantAppSupported(context: Context): Boolean = InstantApps.getPackageManagerCompat(context).isInstantApp
|
||||
|
||||
override fun isInAppPaymentsSupported(context: Context): Boolean = true
|
||||
|
||||
override fun showInstallPrompt(activity: Activity, intent: Intent?, requestCode: Int, referrer: String?): Boolean =
|
||||
InstantApps.showInstallPrompt(activity, intent, requestCode, referrer)
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.google.android.gms.instantapps.InstantApps
|
||||
|
||||
class InstantAppWrapper {
|
||||
fun isEnabled(context: Context): Boolean = InstantApps.getPackageManagerCompat(context).isInstantApp
|
||||
|
||||
fun showInstallPrompt(activity: Activity, intent: Intent?, requestCode: Int, referrer: String?) =
|
||||
InstantApps.showInstallPrompt(activity, intent, requestCode, referrer)
|
||||
}
|
85
proprietary/src/main/java/dev/lucasnlm/external/PlayGamesManager.kt
vendored
Normal file
85
proprietary/src/main/java/dev/lucasnlm/external/PlayGamesManager.kt
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
package dev.lucasnlm.external
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import com.google.android.gms.auth.api.Auth
|
||||
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
|
||||
|
||||
|
||||
class PlayGamesManager(
|
||||
private val context: Context
|
||||
) : IPlayGamesManager {
|
||||
private var account: GoogleSignInAccount? = null
|
||||
|
||||
private fun setupPopUp(activity: Activity, account: GoogleSignInAccount) {
|
||||
Games.getGamesClient(context, account).setViewForPopups(activity.findViewById(android.R.id.content))
|
||||
}
|
||||
|
||||
override fun hasGooglePlayGames(): Boolean = true
|
||||
|
||||
override fun silentLogin(activity: Activity) {
|
||||
val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN).build()
|
||||
val lastAccount: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
|
||||
|
||||
if (lastAccount != null) {
|
||||
account = lastAccount.also { setupPopUp(activity, it) }
|
||||
} else {
|
||||
GoogleSignIn
|
||||
.getClient(context, signInOptions)
|
||||
.silentSignIn()
|
||||
.addOnCompleteListener { task ->
|
||||
if (task.isSuccessful) {
|
||||
account = task.result?.also { setupPopUp(activity, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLoginIntent(): Intent? {
|
||||
val signInClient = GoogleSignIn.getClient(context, GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
|
||||
return signInClient.signInIntent
|
||||
}
|
||||
|
||||
override fun handleLoginResult(data: Intent?) {
|
||||
if (data != null) {
|
||||
Auth.GoogleSignInApi.getSignInResultFromIntent(data)?.let { result ->
|
||||
if (result.isSuccess) {
|
||||
account = result.signInAccount
|
||||
} else {
|
||||
result.status.statusMessage?.let { message ->
|
||||
if (message.isNotBlank()) {
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isLogged(): Boolean = account != null
|
||||
|
||||
override fun openAchievements(activity: Activity) {
|
||||
account?.let {
|
||||
Games.getAchievementsClient(context, it)
|
||||
.achievementsIntent
|
||||
.addOnSuccessListener { intent ->
|
||||
activity.startActivityForResult(intent, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun openLeaderboards(activity: Activity) {
|
||||
account?.let {
|
||||
Games.getLeaderboardsClient(context, it)
|
||||
.allLeaderboardsIntent
|
||||
.addOnSuccessListener { intent ->
|
||||
activity.startActivityForResult(intent, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
4
proprietary/src/main/res/values/values.xml
Normal file
4
proprietary/src/main/res/values/values.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_id">0</string>
|
||||
</resources>
|
|
@ -1 +1,2 @@
|
|||
include ':app', ':wear', ':common', ':proprietary', ':foss'
|
||||
include ':external'
|
||||
|
|
|
@ -9,8 +9,8 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
// versionCode and versionName must be hardcoded to support F-droid
|
||||
versionCode 800041
|
||||
versionName '8.0.4'
|
||||
versionCode 800051
|
||||
versionName '8.0.5'
|
||||
applicationId 'dev.lucasnlm.antimine'
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 30
|
||||
|
@ -67,7 +67,7 @@ dependencies {
|
|||
|
||||
// Dagger
|
||||
implementation 'com.google.dagger:hilt-android:2.28.1-alpha'
|
||||
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
|
||||
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
|
||||
kapt 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
|
||||
testImplementation 'com.google.dagger:hilt-android-testing:2.28.1-alpha'
|
||||
kaptTest 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
|
||||
|
|
Loading…
Reference in a new issue