Move hilt to koin

This commit is contained in:
Lucas Lima 2020-08-19 16:11:09 -03:00
parent c68e172946
commit d60a50e6a4
No known key found for this signature in database
GPG key ID: 049CCC5A365B00D2
47 changed files with 252 additions and 383 deletions

View file

@ -1,7 +1,6 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'kotlin-kapt'
android {
@ -85,10 +84,6 @@ kapt {
correctErrorTypes true
}
hilt {
enableTransformForLocalTests true
}
dependencies {
// Dependencies must be hardcoded to support F-droid
@ -120,15 +115,9 @@ dependencies {
// Google
implementation 'com.google.android.material:material:1.2.0'
// Dagger
implementation 'com.google.dagger:hilt-android:2.28.1-alpha'
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'
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.28.1-alpha'
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.28.1-alpha'
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
// Koin
implementation 'org.koin:koin-android:2.1.6'
testImplementation 'org.koin:koin-test:2.1.6'
// Kotlin
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8'

View file

@ -24,7 +24,6 @@ import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.about.AboutActivity
import dev.lucasnlm.antimine.common.level.models.Difficulty
import dev.lucasnlm.antimine.common.level.models.Event
@ -43,7 +42,7 @@ 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.share.ShareManager
import dev.lucasnlm.antimine.stats.StatsActivity
import dev.lucasnlm.antimine.theme.ThemeActivity
import dev.lucasnlm.external.IPlayGamesManager
@ -54,27 +53,22 @@ import kotlinx.android.synthetic.main.activity_tv_game.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import javax.inject.Inject
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.OnDismissListener {
@Inject
lateinit var preferencesRepository: IPreferencesRepository
private val preferencesRepository: IPreferencesRepository by inject()
@Inject
lateinit var analyticsManager: IAnalyticsManager
private val analyticsManager: IAnalyticsManager by inject()
@Inject
lateinit var instantAppManager: InstantAppManager
private val instantAppManager: InstantAppManager by inject()
@Inject
lateinit var savesRepository: ISavesRepository
private val savesRepository: ISavesRepository by inject()
@Inject
lateinit var playGamesManager: IPlayGamesManager
private val playGamesManager: IPlayGamesManager by inject()
val viewModel: GameViewModel by viewModels()
private val shareViewModel: ShareViewModel by viewModels()
private val shareViewModel: ShareManager by inject()
val gameViewModel: GameViewModel by inject()
override val noActionBar: Boolean = true
@ -110,7 +104,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
}
}
private fun bindViewModel() = viewModel.apply {
private fun bindViewModel() = gameViewModel.apply {
var lastEvent: Event? = null // TODO use distinctUntilChanged when available
eventObserver.observe(
@ -127,7 +121,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
this@GameActivity,
Observer {
lifecycleScope.launch {
viewModel.retryGame(currentSaveId.toInt())
gameViewModel.retryGame(currentSaveId.toInt())
}
}
)
@ -189,7 +183,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
when {
drawer.isDrawerOpen(GravityCompat.START) -> {
drawer.closeDrawer(GravityCompat.START)
viewModel.resumeGame()
gameViewModel.resumeGame()
}
status == Status.Running && instantAppManager.isEnabled() -> showQuitConfirmation {
super.onBackPressed()
@ -204,7 +198,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
if (!willReset) {
if (status == Status.Running) {
viewModel.run {
gameViewModel.run {
refreshUserPreferences()
resumeGame()
}
@ -220,7 +214,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
super.onPause()
if (status == Status.Running) {
viewModel.pauseGame()
gameViewModel.pauseGame()
}
if (isFinishing) {
@ -254,12 +248,12 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
if (confirmResign) {
newGameConfirmation {
GlobalScope.launch {
viewModel.startNewGame()
gameViewModel.startNewGame()
}
}
} else {
GlobalScope.launch {
viewModel.startNewGame()
gameViewModel.startNewGame()
}
}
}
@ -296,13 +290,13 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
}
override fun onDrawerOpened(drawerView: View) {
viewModel.pauseGame()
gameViewModel.pauseGame()
analyticsManager.sentEvent(Analytics.OpenDrawer)
}
override fun onDrawerClosed(drawerView: View) {
if (hasNoOtherFocusedDialog()) {
viewModel.resumeGame()
gameViewModel.resumeGame()
}
analyticsManager.sentEvent(Analytics.CloseDrawer)
@ -448,7 +442,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
}
private fun showControlDialog() {
viewModel.pauseGame()
gameViewModel.pauseGame()
if (supportFragmentManager.findFragmentByTag(CustomLevelDialogFragment.TAG) == null) {
ControlDialogFragment().apply {
@ -510,13 +504,13 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
}
private fun waitAndShowEndGameDialog(victory: Boolean, await: Boolean) {
if (await && viewModel.explosionDelay() != 0L) {
if (await && gameViewModel.explosionDelay() != 0L) {
postDelayed(
Handler(),
{
showEndGameDialog(victory)
},
null, (viewModel.explosionDelay() * 0.3).toLong()
null, (gameViewModel.explosionDelay() * 0.3).toLong()
)
} else {
showEndGameDialog(victory)
@ -526,12 +520,12 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
private fun changeDifficulty(newDifficulty: Difficulty) {
if (status == Status.PreGame) {
GlobalScope.launch {
viewModel.startNewGame(newDifficulty)
gameViewModel.startNewGame(newDifficulty)
}
} else {
newGameConfirmation {
GlobalScope.launch {
viewModel.startNewGame(newDifficulty)
gameViewModel.startNewGame(newDifficulty)
}
}
}
@ -549,7 +543,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
}
Event.Resume, Event.Running -> {
status = Status.Running
viewModel.runClock()
gameViewModel.runClock()
refreshNewGameButton()
keepScreenOn(true)
}
@ -560,9 +554,9 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
totalArea
)
status = Status.Over(currentTime, score)
viewModel.stopClock()
viewModel.revealAllEmptyAreas()
viewModel.victory()
gameViewModel.stopClock()
gameViewModel.revealAllEmptyAreas()
gameViewModel.victory()
refreshNewGameButton()
keepScreenOn(false)
waitAndShowEndGameDialog(
@ -580,10 +574,10 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
status = Status.Over(currentTime, score)
refreshNewGameButton()
keepScreenOn(false)
viewModel.stopClock()
gameViewModel.stopClock()
GlobalScope.launch(context = Dispatchers.Main) {
viewModel.gameOver(isResuming)
gameViewModel.gameOver(isResuming)
waitAndShowEndGameDialog(
victory = false,
await = true
@ -607,10 +601,10 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
}
private fun shareCurrentGame() {
val levelSetup = viewModel.levelSetup.value
val field = viewModel.field.value
GlobalScope.launch {
shareViewModel.share(levelSetup, field)
val levelSetup = gameViewModel.levelSetup.value
val field = gameViewModel.field.value
lifecycleScope.launch {
shareViewModel.shareField(levelSetup, field)
}
}
@ -660,7 +654,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
}
override fun onDismiss(dialog: DialogInterface?) {
viewModel.run {
gameViewModel.run {
refreshUserPreferences()
resumeGame()
}

View file

@ -1,18 +1,26 @@
package dev.lucasnlm.antimine
import androidx.multidex.MultiDexApplication
import dagger.hilt.android.HiltAndroidApp
import dev.lucasnlm.antimine.common.level.di.LevelModule
import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager
import dev.lucasnlm.antimine.core.analytics.models.Analytics
import javax.inject.Inject
import dev.lucasnlm.antimine.core.di.CommonModule
import dev.lucasnlm.antimine.di.AppModule
import dev.lucasnlm.antimine.di.ViewModelModule
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
@HiltAndroidApp
open class MainApplication : MultiDexApplication() {
@Inject
lateinit var analyticsManager: IAnalyticsManager
private val analyticsManager: IAnalyticsManager by inject()
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(applicationContext)
modules(AppModule, CommonModule, LevelModule, ViewModelModule)
}
analyticsManager.apply {
setup(applicationContext, mapOf())
sentEvent(Analytics.Open)

View file

@ -5,14 +5,11 @@ import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatActivity
import dev.lucasnlm.antimine.core.themes.model.AppTheme
import dev.lucasnlm.antimine.core.themes.repository.IThemeRepository
import javax.inject.Inject
import org.koin.android.ext.android.inject
open class ThematicActivity : AppCompatActivity {
constructor() : super()
constructor(@LayoutRes contentLayoutId: Int) : super(contentLayoutId)
open class ThematicActivity(@LayoutRes contentLayoutId: Int) : AppCompatActivity(contentLayoutId) {
@Inject
lateinit var themeRepository: IThemeRepository
private val themeRepository: IThemeRepository by inject()
protected open val noActionBar: Boolean = false

View file

@ -5,14 +5,12 @@ import android.os.Bundle
import android.os.Handler
import android.text.format.DateUtils
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.HandlerCompat
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.Observer
import androidx.preference.PreferenceManager
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.about.AboutActivity
import dev.lucasnlm.antimine.common.level.models.Difficulty
import dev.lucasnlm.antimine.common.level.models.Event
@ -26,10 +24,10 @@ import kotlinx.android.synthetic.main.activity_tv_game.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class TvGameActivity : AppCompatActivity() {
private val viewModel: GameViewModel by viewModels()
private val gameViewModel: GameViewModel by inject()
private var status: Status = Status.PreGame
private var totalMines: Int = 0
@ -49,7 +47,7 @@ class TvGameActivity : AppCompatActivity() {
loadGameFragment()
}
private fun bindViewModel() = viewModel.apply {
private fun bindViewModel() = gameViewModel.apply {
eventObserver.observe(
this@TvGameActivity,
Observer {
@ -99,7 +97,7 @@ class TvGameActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
if (status == Status.Running) {
viewModel.resumeGame()
gameViewModel.resumeGame()
}
}
@ -107,7 +105,7 @@ class TvGameActivity : AppCompatActivity() {
super.onPause()
if (status == Status.Running) {
viewModel.pauseGame()
gameViewModel.pauseGame()
}
}
@ -174,7 +172,7 @@ class TvGameActivity : AppCompatActivity() {
setCancelable(false)
setPositiveButton(R.string.new_game) { _, _ ->
GlobalScope.launch {
viewModel.startNewGame()
gameViewModel.startNewGame()
}
}
setNegativeButton(R.string.cancel, null)
@ -193,7 +191,7 @@ class TvGameActivity : AppCompatActivity() {
setMessage(R.string.new_game_request)
setPositiveButton(R.string.yes) { _, _ ->
GlobalScope.launch {
viewModel.startNewGame()
gameViewModel.startNewGame()
}
}
setNegativeButton(R.string.cancel, null)
@ -217,7 +215,7 @@ class TvGameActivity : AppCompatActivity() {
setMessage(R.string.new_game_request)
setPositiveButton(R.string.yes) { _, _ ->
GlobalScope.launch {
viewModel.startNewGame()
gameViewModel.startNewGame()
}
}
setNegativeButton(R.string.cancel, null)
@ -231,12 +229,12 @@ class TvGameActivity : AppCompatActivity() {
private fun changeDifficulty(newDifficulty: Difficulty) {
if (status == Status.PreGame) {
GlobalScope.launch {
viewModel.startNewGame(newDifficulty)
gameViewModel.startNewGame(newDifficulty)
}
} else {
newGameConfirmation {
GlobalScope.launch {
viewModel.startNewGame(newDifficulty)
gameViewModel.startNewGame(newDifficulty)
}
}
}
@ -253,7 +251,7 @@ class TvGameActivity : AppCompatActivity() {
}
Event.Resume, Event.Running -> {
status = Status.Running
viewModel.runClock()
gameViewModel.runClock()
invalidateOptionsMenu()
}
Event.Victory -> {
@ -263,8 +261,8 @@ class TvGameActivity : AppCompatActivity() {
totalArea
)
status = Status.Over(currentTime, score)
viewModel.stopClock()
viewModel.revealAllEmptyAreas()
gameViewModel.stopClock()
gameViewModel.revealAllEmptyAreas()
invalidateOptionsMenu()
showVictory()
}
@ -276,10 +274,10 @@ class TvGameActivity : AppCompatActivity() {
)
status = Status.Over(currentTime, score)
invalidateOptionsMenu()
viewModel.stopClock()
gameViewModel.stopClock()
GlobalScope.launch(context = Dispatchers.Main) {
viewModel.gameOver(false)
gameViewModel.gameOver(false)
waitAndShowGameOverConfirmNewGame()
}
}
@ -291,7 +289,7 @@ class TvGameActivity : AppCompatActivity() {
)
status = Status.Over(currentTime, score)
invalidateOptionsMenu()
viewModel.stopClock()
gameViewModel.stopClock()
waitAndShowConfirmNewGame()
}

View file

@ -1,11 +1,9 @@
package dev.lucasnlm.antimine.about
import android.os.Bundle
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.ThematicActivity
import dev.lucasnlm.antimine.about.viewmodel.AboutEvent
@ -14,10 +12,10 @@ import dev.lucasnlm.antimine.about.views.AboutInfoFragment
import dev.lucasnlm.antimine.about.views.licenses.LicensesFragment
import dev.lucasnlm.antimine.about.views.translators.TranslatorsFragment
import kotlinx.coroutines.flow.collect
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class AboutActivity : ThematicActivity(R.layout.activity_empty) {
private val aboutViewModel: AboutViewModel by viewModels()
private val aboutViewModel: AboutViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -25,6 +23,8 @@ class AboutActivity : ThematicActivity(R.layout.activity_empty) {
replaceFragment(AboutInfoFragment(), null)
lifecycleScope.launchWhenCreated {
aboutViewModel.sendEvent(AboutEvent.Info)
aboutViewModel.observeEvent().collect { event ->
when (event) {
AboutEvent.ThirdPartyLicenses -> {

View file

@ -1,6 +1,7 @@
package dev.lucasnlm.antimine.about.viewmodel
enum class AboutEvent {
Info,
ThirdPartyLicenses,
SourceCode,
Translators

View file

@ -3,13 +3,11 @@ package dev.lucasnlm.antimine.about.viewmodel
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.hilt.lifecycle.ViewModelInject
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.core.viewmodel.IntentViewModel
class AboutViewModel @ViewModelInject constructor(
@ApplicationContext private val context: Context
class AboutViewModel(
private val context: Context
) : IntentViewModel<AboutEvent, AboutState>() {
override fun onEvent(event: AboutEvent) {

View file

@ -3,15 +3,15 @@ package dev.lucasnlm.antimine.about.views
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import dev.lucasnlm.antimine.BuildConfig
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.about.viewmodel.AboutEvent
import dev.lucasnlm.antimine.about.viewmodel.AboutViewModel
import kotlinx.android.synthetic.main.fragment_about_info.*
import org.koin.android.ext.android.inject
class AboutInfoFragment : Fragment(R.layout.fragment_about_info) {
private val aboutViewModel: AboutViewModel by activityViewModels()
private val aboutViewModel: AboutViewModel by inject()
override fun onResume() {
super.onResume()

View file

@ -11,9 +11,10 @@ import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.about.viewmodel.AboutViewModel
import kotlinx.android.synthetic.main.fragment_licenses.*
import kotlinx.coroutines.flow.collect
import org.koin.android.ext.android.inject
class LicensesFragment : Fragment(R.layout.fragment_licenses) {
private val aboutViewModel: AboutViewModel by activityViewModels()
private val aboutViewModel: AboutViewModel by inject()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View file

@ -4,7 +4,6 @@ import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
@ -14,9 +13,10 @@ import dev.lucasnlm.antimine.about.viewmodel.AboutViewModel
import kotlinx.android.synthetic.main.fragment_translators.*
import kotlinx.android.synthetic.main.view_translator.view.*
import kotlinx.coroutines.flow.collect
import org.koin.android.ext.android.inject
internal class TranslatorsFragment : Fragment(R.layout.fragment_translators) {
private val aboutViewModel: AboutViewModel by activityViewModels()
private val aboutViewModel: AboutViewModel by inject()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View file

@ -8,16 +8,14 @@ import android.view.ViewGroup
import android.widget.BaseAdapter
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.fragment.app.activityViewModels
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.control.view.ControlItemView
import dev.lucasnlm.antimine.control.viewmodel.ControlEvent
import dev.lucasnlm.antimine.control.viewmodel.ControlViewModel
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class ControlDialogFragment : AppCompatDialogFragment() {
private val controlViewModel by activityViewModels<ControlViewModel>()
private val controlViewModel: ControlViewModel by inject()
private val adapter by lazy { ControlListAdapter(controlViewModel) }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

View file

@ -1,6 +1,5 @@
package dev.lucasnlm.antimine.control.viewmodel
import androidx.hilt.lifecycle.ViewModelInject
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.control.models.ControlDetails
import dev.lucasnlm.antimine.core.control.ControlStyle
@ -8,7 +7,7 @@ import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.core.viewmodel.IntentViewModel
import kotlinx.coroutines.flow.flow
class ControlViewModel @ViewModelInject constructor(
class ControlViewModel(
private val preferencesRepository: IPreferencesRepository
) : IntentViewModel<ControlEvent, ControlState>() {

View file

@ -9,19 +9,17 @@ import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.fragment.app.activityViewModels
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.common.level.models.Difficulty
import dev.lucasnlm.antimine.common.level.models.Minefield
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
import dev.lucasnlm.antimine.custom.viewmodel.CreateGameViewModel
import dev.lucasnlm.antimine.custom.viewmodel.CustomEvent
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class CustomLevelDialogFragment : AppCompatDialogFragment() {
private val viewModel by activityViewModels<GameViewModel>()
private val createGameViewModel by activityViewModels<CreateGameViewModel>()
private val gameViewModel: GameViewModel by inject()
private val createGameViewModel: CreateGameViewModel by inject()
private lateinit var mapWidth: TextView
private lateinit var mapHeight: TextView
@ -62,7 +60,7 @@ class CustomLevelDialogFragment : AppCompatDialogFragment() {
setPositiveButton(R.string.start) { _, _ ->
val minefield = getSelectedMinefield()
createGameViewModel.sendEvent(CustomEvent.UpdateCustomGameEvent(minefield))
viewModel.startNewGame(Difficulty.Custom)
gameViewModel.startNewGame(Difficulty.Custom)
}
}.create()
}

View file

@ -1,11 +1,10 @@
package dev.lucasnlm.antimine.custom.viewmodel
import androidx.hilt.lifecycle.ViewModelInject
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.core.viewmodel.IntentViewModel
import kotlinx.coroutines.flow.flow
class CreateGameViewModel @ViewModelInject constructor(
class CreateGameViewModel(
private val preferencesRepository: IPreferencesRepository
) : IntentViewModel<CustomEvent, CustomState>() {

View file

@ -1,53 +1,33 @@
package dev.lucasnlm.antimine.di
import android.content.Context
import dagger.Module
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.antimine.share.ShareManager
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
import org.koin.dsl.bind
import org.koin.dsl.module
@Module
@InstallIn(ApplicationComponent::class)
class AppModule {
@Singleton
@Provides
fun provideInstantAppManager(
@ApplicationContext context: Context
): InstantAppManager = InstantAppManager(context)
val AppModule = module {
single { InstantAppManager(get()) }
@Singleton
@Provides
fun provideBillingManager(
@ApplicationContext context: Context
): IBillingManager = BillingManager(context)
single { BillingManager(get()) } bind IBillingManager::class
@Singleton
@Provides
fun providePlayGamesManager(
@ApplicationContext context: Context
): IPlayGamesManager = PlayGamesManager(context)
single { PlayGamesManager(get()) } bind IPlayGamesManager::class
@Singleton
@Provides
fun provideAnalyticsManager(
@ApplicationContext context: Context
): IAnalyticsManager {
return if (BuildConfig.DEBUG) {
single { ShareManager(get()) }
single {
if (BuildConfig.DEBUG) {
DebugAnalyticsManager()
} else {
ProdAnalyticsManager(ExternalAnalyticsWrapper(context))
ProdAnalyticsManager(ExternalAnalyticsWrapper(get()))
}
}
} bind IAnalyticsManager::class
}

View file

@ -0,0 +1,28 @@
package dev.lucasnlm.antimine.di
import dev.lucasnlm.antimine.about.viewmodel.AboutViewModel
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
import dev.lucasnlm.antimine.control.viewmodel.ControlViewModel
import dev.lucasnlm.antimine.custom.viewmodel.CreateGameViewModel
import dev.lucasnlm.antimine.history.viewmodel.HistoryViewModel
import dev.lucasnlm.antimine.level.viewmodel.EndGameDialogViewModel
import dev.lucasnlm.antimine.playgames.viewmodel.PlayGamesViewModel
import dev.lucasnlm.antimine.stats.viewmodel.StatsViewModel
import dev.lucasnlm.antimine.text.viewmodel.TextViewModel
import dev.lucasnlm.antimine.theme.viewmodel.ThemeViewModel
import org.koin.dsl.module
val ViewModelModule = module {
single { AboutViewModel(get()) }
factory { ControlViewModel(get()) }
factory { CreateGameViewModel(get()) }
factory { HistoryViewModel(get(), get()) }
factory { EndGameDialogViewModel(get()) }
factory { PlayGamesViewModel(get(), get()) }
factory { StatsViewModel(get(), get()) }
factory { TextViewModel(get()) }
factory { ThemeViewModel(get()) }
single {
GameViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
}
}

View file

@ -1,12 +1,10 @@
package dev.lucasnlm.antimine.history
import android.os.Bundle
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.ThematicActivity
import dev.lucasnlm.antimine.history.views.HistoryFragment
@AndroidEntryPoint
class HistoryActivity : ThematicActivity(R.layout.activity_empty) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -3,15 +3,13 @@ package dev.lucasnlm.antimine.history.viewmodel
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.hilt.lifecycle.ViewModelInject
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.lucasnlm.antimine.DeepLink
import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
import dev.lucasnlm.antimine.core.viewmodel.IntentViewModel
import kotlinx.coroutines.flow.flow
class HistoryViewModel @ViewModelInject constructor(
@ApplicationContext private val context: Context,
class HistoryViewModel(
private val context: Context,
private val savesRepository: ISavesRepository
) : IntentViewModel<HistoryEvent, HistoryState>() {

View file

@ -3,20 +3,18 @@ package dev.lucasnlm.antimine.history.views
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
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.coroutines.flow.collect
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class HistoryFragment : Fragment(R.layout.fragment_history) {
private val historyViewModel: HistoryViewModel by activityViewModels()
private val historyViewModel: HistoryViewModel by inject()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View file

@ -9,24 +9,20 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
import dev.lucasnlm.antimine.instant.InstantAppManager
import dev.lucasnlm.antimine.level.viewmodel.EndGameDialogEvent
import dev.lucasnlm.antimine.level.viewmodel.EndGameDialogViewModel
import kotlinx.coroutines.flow.collect
import javax.inject.Inject
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class EndGameDialogFragment : AppCompatDialogFragment() {
@Inject
lateinit var instantAppManager: InstantAppManager
private val instantAppManager: InstantAppManager by inject()
private val endGameViewModel by activityViewModels<EndGameDialogViewModel>()
private val viewModel by activityViewModels<GameViewModel>()
private val endGameViewModel: EndGameDialogViewModel by inject()
private val gameViewModel: GameViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -79,12 +75,12 @@ class EndGameDialogFragment : AppCompatDialogFragment() {
}
state.isVictory == true -> {
setNeutralButton(R.string.share) { _, _ ->
viewModel.shareObserver.postValue(Unit)
gameViewModel.shareObserver.postValue(Unit)
}
}
else -> {
setNeutralButton(R.string.retry) { _, _ ->
viewModel.retryObserver.postValue(Unit)
gameViewModel.retryObserver.postValue(Unit)
}
}
}
@ -95,7 +91,7 @@ class EndGameDialogFragment : AppCompatDialogFragment() {
setView(view)
setPositiveButton(R.string.new_game) { _, _ ->
viewModel.startNewGame()
gameViewModel.startNewGame()
}
}.create()

View file

@ -6,7 +6,6 @@ import android.view.View
import androidx.core.view.doOnLayout
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.DeepLink
import dev.lucasnlm.antimine.common.R
import dev.lucasnlm.antimine.common.level.models.Difficulty
@ -18,12 +17,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@AndroidEntryPoint
open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) {
override fun onPause() {
super.onPause()
lifecycleScope.launch {
viewModel.saveGame()
gameViewModel.saveGame()
}
}
@ -37,10 +35,10 @@ open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) {
val retryDeepLink = checkRetryGameDeepLink()
val levelSetup = when {
loadGameUid != null -> viewModel.loadGame(loadGameUid)
newGameDeepLink != null -> viewModel.startNewGame(newGameDeepLink)
retryDeepLink != null -> viewModel.retryGame(retryDeepLink)
else -> viewModel.loadLastGame()
loadGameUid != null -> gameViewModel.loadGame(loadGameUid)
newGameDeepLink != null -> gameViewModel.startNewGame(newGameDeepLink)
retryDeepLink != null -> gameViewModel.retryGame(retryDeepLink)
else -> gameViewModel.loadLastGame()
}
withContext(Dispatchers.Main) {
@ -53,7 +51,7 @@ open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) {
}
}
viewModel.run {
gameViewModel.run {
field.observe(
viewLifecycleOwner,
Observer {

View file

@ -1,14 +1,12 @@
package dev.lucasnlm.antimine.level.viewmodel
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.IntentViewModel
import kotlinx.coroutines.flow.flow
class EndGameDialogViewModel @ViewModelInject constructor(
@ApplicationContext private val context: Context
class EndGameDialogViewModel(
private val context: Context
) : IntentViewModel<EndGameDialogEvent, EndGameDialogState>() {
private fun List<Int>.safeRandomEmoji(

View file

@ -12,17 +12,15 @@ 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 org.koin.android.ext.android.inject
@AndroidEntryPoint
class PlayGamesDialogFragment : DialogFragment() {
private val playGamesViewModel by viewModels<PlayGamesViewModel>()
private val playGamesViewModel: PlayGamesViewModel by inject()
private val adapter by lazy { PlayGamesAdapter(playGamesViewModel) }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

View file

@ -2,15 +2,13 @@ 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,
class PlayGamesViewModel(
private val context: Context,
private val playGamesManager: IPlayGamesManager
) : StatelessViewModel<PlayGamesEvent>() {

View file

@ -3,11 +3,9 @@ package dev.lucasnlm.antimine.preferences
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.ThematicActivity
@AndroidEntryPoint
class PreferencesActivity : ThematicActivity(R.layout.activity_empty) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -9,6 +9,7 @@ import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.Typeface
import android.widget.Toast
import androidx.core.content.FileProvider
import dev.lucasnlm.antimine.BuildConfig
import dev.lucasnlm.antimine.R
@ -22,12 +23,12 @@ import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
class ShareBuilder(
class ShareManager(
context: Context
) {
private val context: Context = context.applicationContext
suspend fun share(minefield: Minefield, field: List<Area>): Boolean {
private suspend fun share(minefield: Minefield, field: List<Area>): Boolean {
val file = createImage(minefield, field)
return if (file != null) {
@ -129,4 +130,16 @@ class ShareBuilder(
false
}
}
suspend fun shareField(minefield: Minefield?, field: List<Area>?) {
val result = if (minefield != null && field != null && field.count() != 0) {
share(minefield, field)
} else {
false
}
if (!result) {
Toast.makeText(context, context.getString(R.string.fail_to_share), Toast.LENGTH_SHORT).show()
}
}
}

View file

@ -1,27 +0,0 @@
package dev.lucasnlm.antimine.share.viewmodel
import android.content.Context
import android.widget.Toast
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.ViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.common.level.models.Area
import dev.lucasnlm.antimine.common.level.models.Minefield
import dev.lucasnlm.antimine.share.ShareBuilder
class ShareViewModel @ViewModelInject constructor(
@ApplicationContext private val context: Context
) : ViewModel() {
suspend fun share(minefield: Minefield?, field: List<Area>?) {
val result = if (minefield != null && field != null && field.count() != 0) {
ShareBuilder(context).share(minefield, field)
} else {
false
}
if (!result) {
Toast.makeText(context, context.getString(R.string.fail_to_share), Toast.LENGTH_SHORT).show()
}
}
}

View file

@ -3,10 +3,8 @@ package dev.lucasnlm.antimine.stats
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.ThematicActivity
import dev.lucasnlm.antimine.stats.model.StatsModel
@ -16,19 +14,19 @@ import kotlinx.android.synthetic.main.activity_stats.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class StatsActivity : ThematicActivity(R.layout.activity_stats) {
private val viewModel: StatsViewModel by viewModels()
private val statsViewModel: StatsViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
refreshStats(StatsViewModel.emptyStats)
lifecycleScope.launchWhenResumed {
viewModel.sendEvent(StatsEvent.LoadStats)
statsViewModel.sendEvent(StatsEvent.LoadStats)
viewModel.observeState().collect {
statsViewModel.observeState().collect {
refreshStats(it)
}
}
@ -61,7 +59,7 @@ class StatsActivity : ThematicActivity(R.layout.activity_stats) {
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
viewModel.singleState().let {
statsViewModel.singleState().let {
if (it.totalGames > 0) {
menuInflater.inflate(R.menu.stats_menu, menu)
}
@ -85,7 +83,7 @@ class StatsActivity : ThematicActivity(R.layout.activity_stats) {
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.delete_all) { _, _ ->
GlobalScope.launch {
viewModel.sendEvent(StatsEvent.DeleteStats)
statsViewModel.sendEvent(StatsEvent.DeleteStats)
}
}
.show()

View file

@ -1,13 +1,12 @@
package dev.lucasnlm.antimine.stats.viewmodel
import androidx.hilt.lifecycle.ViewModelInject
import dev.lucasnlm.antimine.common.level.repository.IStatsRepository
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.core.viewmodel.IntentViewModel
import dev.lucasnlm.antimine.stats.model.StatsModel
import kotlinx.coroutines.flow.flow
class StatsViewModel @ViewModelInject constructor(
class StatsViewModel(
private val statsRepository: IStatsRepository,
private val preferenceRepository: IPreferencesRepository
) : IntentViewModel<StatsEvent, StatsModel>() {

View file

@ -5,16 +5,13 @@ 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
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class SupportAppDialogFragment : AppCompatDialogFragment() {
@Inject
lateinit var billingManager: IBillingManager
private val billingManager: IBillingManager by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -4,10 +4,8 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.viewModels
import androidx.annotation.RawRes
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.ThematicActivity
import dev.lucasnlm.antimine.text.viewmodel.TextEvent
@ -16,10 +14,10 @@ import kotlinx.android.synthetic.main.activity_text.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class TextActivity : ThematicActivity(R.layout.activity_text) {
private val viewModel: TextViewModel by viewModels()
private val textViewModel: TextViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -28,7 +26,7 @@ class TextActivity : ThematicActivity(R.layout.activity_text) {
title = bundle.getString(TEXT_TITLE)
lifecycleScope.launchWhenCreated {
viewModel.sendEvent(
textViewModel.sendEvent(
TextEvent.LoadText(
title = bundle.getString(TEXT_TITLE, ""),
rawFileRes = bundle.getInt(TEXT_PATH, -1)
@ -39,7 +37,7 @@ class TextActivity : ThematicActivity(R.layout.activity_text) {
progressBar.visibility = View.VISIBLE
}
viewModel.observeState().collect {
textViewModel.observeState().collect {
textView.text = it.body
progressBar.visibility = View.GONE
}

View file

@ -2,16 +2,14 @@ package dev.lucasnlm.antimine.text.viewmodel
import android.content.Context
import androidx.annotation.RawRes
import androidx.hilt.lifecycle.ViewModelInject
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.lucasnlm.antimine.core.viewmodel.IntentViewModel
import dev.lucasnlm.antimine.text.models.TextState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
class TextViewModel @ViewModelInject constructor(
@ApplicationContext private val context: Context
class TextViewModel(
private val context: Context
) : IntentViewModel<TextEvent, TextState>() {
private suspend fun loadText(@RawRes rawFile: Int): String {

View file

@ -4,7 +4,6 @@ import android.os.Bundle
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.ThematicActivity
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
@ -14,14 +13,12 @@ import dev.lucasnlm.antimine.theme.view.ThemeAdapter
import dev.lucasnlm.antimine.theme.viewmodel.ThemeViewModel
import kotlinx.android.synthetic.main.activity_theme.*
import kotlinx.coroutines.flow.collect
import javax.inject.Inject
import org.koin.android.ext.android.inject
@AndroidEntryPoint
class ThemeActivity : ThematicActivity(R.layout.activity_theme) {
@Inject
lateinit var dimensionRepository: IDimensionRepository
private val dimensionRepository: IDimensionRepository by inject()
private val viewModel by viewModels<ThemeViewModel>()
private val themeViewModel: ThemeViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -33,10 +30,10 @@ class ThemeActivity : ThematicActivity(R.layout.activity_theme) {
addItemDecoration(SpaceItemDecoration(R.dimen.theme_divider))
setHasFixedSize(true)
layoutManager = GridLayoutManager(context, 3)
adapter = ThemeAdapter(viewModel, areaSize)
adapter = ThemeAdapter(themeViewModel, areaSize)
}
viewModel.observeState().collect {
themeViewModel.observeState().collect {
if (usingTheme.id != it.current.id) {
recreate()
}

View file

@ -1,12 +1,11 @@
package dev.lucasnlm.antimine.theme.viewmodel
import androidx.hilt.lifecycle.ViewModelInject
import dev.lucasnlm.antimine.core.themes.model.AppTheme
import dev.lucasnlm.antimine.core.themes.repository.IThemeRepository
import dev.lucasnlm.antimine.core.viewmodel.IntentViewModel
import kotlinx.coroutines.flow.flow
class ThemeViewModel @ViewModelInject constructor(
class ThemeViewModel(
private val themeRepository: IThemeRepository
) : IntentViewModel<ThemeEvent, ThemeState>() {
private fun setTheme(theme: AppTheme) {

View file

@ -11,7 +11,6 @@ import dev.lucasnlm.antimine.common.level.di.LevelModule
import dev.lucasnlm.antimine.level.view.EndGameDialogFragment
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@ -31,7 +30,6 @@ class GameActivityTest {
var rule = HiltAndroidRule(this)
@Test
@Ignore("Dagger hilt issue")
fun testShowGameOverWhenTapAMine() {
launchActivity<GameActivity>().onActivity { activity ->
ShadowLooper.runUiThreadTasks()
@ -57,7 +55,6 @@ class GameActivityTest {
}
@Test
@Ignore("Dagger hilt issue")
fun testShowVictoryWhenTapAllSafeAreas() {
launchActivity<GameActivity>().onActivity { activity ->
ShadowLooper.runUiThreadTasks()
@ -70,7 +67,7 @@ class GameActivityTest {
ShadowLooper.runUiThreadTasks()
// Tap on safe places
activity.viewModel.field
activity.gameViewModel.field
.value!!
.filter { !it.hasMine && it.isCovered }
.forEach {

View file

@ -7,7 +7,6 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28.1-alpha'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72'
if (System.getenv('IS_GOOGLE_BUILD')) {

View file

@ -2,7 +2,6 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
android {
compileSdkVersion 30
@ -33,10 +32,6 @@ kapt {
correctErrorTypes true
}
hilt {
enableTransformForLocalTests true
}
dependencies {
// Dependencies must be hardcoded to support F-droid
@ -57,13 +52,9 @@ dependencies {
api 'android.arch.lifecycle:extensions:1.1.1'
implementation 'android.arch.lifecycle:viewmodel:1.1.1'
// Dagger
implementation 'com.google.dagger:hilt-android:2.28.1-alpha'
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-alpha02"
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha02"
// Koin
implementation 'org.koin:koin-android:2.1.6'
testImplementation 'org.koin:koin-test:2.1.6'
// Room
api 'androidx.room:room-runtime:2.2.5'

View file

@ -30,4 +30,8 @@ import dev.lucasnlm.antimine.common.level.database.models.Stats
abstract class AppDataBase : RoomDatabase() {
abstract fun saveDao(): SaveDao
abstract fun statsDao(): StatsDao
companion object {
const val DATA_BASE_NAME = "saves-db"
}
}

View file

@ -1,15 +1,7 @@
package dev.lucasnlm.antimine.common.level.di
import android.content.Context
import androidx.room.Room
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.lucasnlm.antimine.common.level.database.AppDataBase
import dev.lucasnlm.antimine.common.level.database.dao.SaveDao
import dev.lucasnlm.antimine.common.level.database.dao.StatsDao
import dev.lucasnlm.antimine.common.level.repository.IMinefieldRepository
import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
import dev.lucasnlm.antimine.common.level.repository.IStatsRepository
@ -19,51 +11,39 @@ import dev.lucasnlm.antimine.common.level.repository.StatsRepository
import dev.lucasnlm.antimine.common.level.utils.Clock
import dev.lucasnlm.antimine.common.level.utils.HapticFeedbackManager
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager
import org.koin.dsl.bind
import org.koin.dsl.module
@Module
@InstallIn(ActivityComponent::class)
open class LevelModule {
@Provides
fun provideAppDataBase(
@ApplicationContext context: Context
): AppDataBase {
return Room.databaseBuilder(context, AppDataBase::class.java, DATA_BASE_NAME)
.fallbackToDestructiveMigration()
.build()
val LevelModule = module {
single { Room.databaseBuilder(get(), AppDataBase::class.java, AppDataBase.DATA_BASE_NAME)
.fallbackToDestructiveMigration()
.build() }
single {
get<AppDataBase>(AppDataBase::class).saveDao()
}
@Provides
fun provideSavesDao(
appDataBase: AppDataBase
): SaveDao = appDataBase.saveDao()
@Provides
fun provideStatsDao(
appDataBase: AppDataBase
): StatsDao = appDataBase.statsDao()
@Provides
open fun provideClock(): Clock = Clock()
@Provides
open fun provideSavesRepository(
savesDao: SaveDao
): ISavesRepository = SavesRepository(savesDao)
@Provides
open fun provideStatsRepository(
statsDao: StatsDao
): IStatsRepository = StatsRepository(statsDao)
@Provides
open fun provideMinefieldRepository(): IMinefieldRepository = MinefieldRepository()
@Provides
open fun provideHapticFeedbackInteractor(
@ApplicationContext context: Context
): IHapticFeedbackManager = HapticFeedbackManager(context)
companion object {
private const val DATA_BASE_NAME = "saves-db"
single {
get<AppDataBase>(AppDataBase::class).statsDao()
}
single {
Clock()
}
single {
SavesRepository(get())
} bind ISavesRepository::class
single {
StatsRepository(get())
} bind IStatsRepository::class
single {
MinefieldRepository()
} bind IMinefieldRepository::class
single {
HapticFeedbackManager(get())
} bind IHapticFeedbackManager::class
}

View file

@ -2,7 +2,6 @@ package dev.lucasnlm.antimine.common.level.repository
import dev.lucasnlm.antimine.common.level.database.dao.SaveDao
import dev.lucasnlm.antimine.common.level.database.models.Save
import javax.inject.Inject
interface ISavesRepository {
suspend fun getAllSaves(): List<Save>
@ -12,7 +11,7 @@ interface ISavesRepository {
fun setLimit(maxSavesStorage: Int)
}
class SavesRepository @Inject constructor(
class SavesRepository(
private val savesDao: SaveDao,
private var maxSavesStorage: Int = MAX_STORAGE
) : ISavesRepository {

View file

@ -2,24 +2,19 @@ package dev.lucasnlm.antimine.common.level.view
import androidx.annotation.LayoutRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.RecyclerView
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
import dev.lucasnlm.antimine.common.level.widget.FixedGridLayoutManager
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import javax.inject.Inject
import org.koin.android.ext.android.inject
abstract class CommonLevelFragment(@LayoutRes val contentLayoutId: Int) : Fragment(contentLayoutId) {
@Inject
lateinit var dimensionRepository: IDimensionRepository
@Inject
lateinit var preferencesRepository: IPreferencesRepository
protected val viewModel: GameViewModel by activityViewModels()
private val dimensionRepository: IDimensionRepository by inject()
private val preferencesRepository: IPreferencesRepository by inject()
protected val gameViewModel: GameViewModel by inject()
protected val areaAdapter by lazy {
AreaAdapter(requireContext(), viewModel, preferencesRepository, dimensionRepository)
AreaAdapter(requireContext(), gameViewModel, preferencesRepository, dimensionRepository)
}
protected lateinit var recyclerGrid: RecyclerView

View file

@ -1,13 +1,11 @@
package dev.lucasnlm.antimine.common.level.viewmodel
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import dev.lucasnlm.antimine.common.R
import dev.lucasnlm.antimine.common.level.GameController
import dev.lucasnlm.antimine.common.level.database.models.FirstOpen
import dev.lucasnlm.antimine.common.level.database.models.Save
import dev.lucasnlm.antimine.common.level.logic.MinefieldHandler
import dev.lucasnlm.antimine.common.level.models.Area
import dev.lucasnlm.antimine.common.level.models.Difficulty
import dev.lucasnlm.antimine.common.level.models.Event
@ -34,7 +32,7 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class GameViewModel @ViewModelInject constructor(
class GameViewModel(
private val savesRepository: ISavesRepository,
private val statsRepository: IStatsRepository,
private val dimensionRepository: IDimensionRepository,

View file

@ -1,13 +1,8 @@
package dev.lucasnlm.antimine.core.di
import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ApplicationComponent
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.lucasnlm.antimine.common.level.repository.DimensionRepository
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
import dev.lucasnlm.antimine.core.preferences.IPreferencesManager
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.core.preferences.PreferencesManager
import dev.lucasnlm.antimine.core.preferences.PreferencesRepository
@ -15,37 +10,17 @@ 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
import org.koin.dsl.bind
import org.koin.dsl.module
@Module
@InstallIn(ApplicationComponent::class)
class CommonModule {
@Provides
fun provideDimensionRepository(
@ApplicationContext context: Context,
preferencesRepository: IPreferencesRepository
): IDimensionRepository =
DimensionRepository(context, preferencesRepository)
val CommonModule = module {
single { PreferencesManager(get()) } bind IPreferencesManager::class
@Singleton
@Provides
fun providePreferencesRepository(
preferencesManager: PreferencesManager
): IPreferencesRepository = PreferencesRepository(preferencesManager)
single { DimensionRepository(get(), get()) } bind IDimensionRepository::class
@Provides
fun providePreferencesInteractor(
@ApplicationContext context: Context
): PreferencesManager = PreferencesManager(context)
single { PreferencesRepository(get()) } bind IPreferencesRepository::class
@Provides
fun provideSoundManager(
@ApplicationContext context: Context
): ISoundManager = SoundManager(context)
single { SoundManager(get()) } bind ISoundManager::class
@Provides
fun provideThemeRepository(
@ApplicationContext context: Context,
preferencesRepository: IPreferencesRepository
): IThemeRepository = ThemeRepository(context, preferencesRepository)
single { ThemeRepository(get(), get()) } bind IThemeRepository::class
}

View file

@ -2,7 +2,6 @@ package dev.lucasnlm.antimine.core.preferences
import android.content.Context
import androidx.preference.PreferenceManager
import javax.inject.Inject
interface IPreferencesManager {
fun getBoolean(key: String, defaultValue: Boolean): Boolean
@ -13,7 +12,7 @@ interface IPreferencesManager {
fun contains(key: String): Boolean
}
class PreferencesManager @Inject constructor(
class PreferencesManager(
private val context: Context
) : IPreferencesManager {
private val preferences by lazy {

View file

@ -2,7 +2,6 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
android {
compileSdkVersion 30
@ -42,10 +41,6 @@ kapt {
correctErrorTypes true
}
hilt {
enableTransformForLocalTests true
}
dependencies {
// Dependencies must be hardcoded to support F-droid
@ -65,12 +60,9 @@ dependencies {
implementation 'com.google.android.support:wearable:2.7.0'
compileOnly 'com.google.android.wearable:wearable:2.7.0'
// Dagger
implementation 'com.google.dagger:hilt-android:2.28.1-alpha'
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'
// Koin
implementation 'org.koin:koin-android:2.1.6'
testImplementation 'org.koin:koin-test:2.1.6'
// Kotlin
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8'

View file

@ -24,7 +24,7 @@ class WatchLevelFragment : CommonLevelFragment(R.layout.fragment_level) {
recyclerGrid = view.findViewById(R.id.recyclerGrid)
GlobalScope.launch {
val levelSetup = viewModel.loadLastGame()
val levelSetup = gameViewModel.loadLastGame()
withContext(Dispatchers.Main) {
recyclerGrid.apply {
@ -45,7 +45,7 @@ class WatchLevelFragment : CommonLevelFragment(R.layout.fragment_level) {
}
}
viewModel.run {
gameViewModel.run {
field.observe(
viewLifecycleOwner,
Observer {