Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GT-1064 Update dagger hilt ViewModel support #1515

Merged
merged 10 commits into from Jan 29, 2021
2 changes: 0 additions & 2 deletions app/build.gradle
Expand Up @@ -165,7 +165,6 @@ dependencies {

implementation "androidx.constraintlayout:constraintlayout:${deps.androidX.constraintLayout}"
implementation "androidx.fragment:fragment-ktx:${deps.androidX.fragment}"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:${deps.androidX.hilt}"
implementation "androidx.lifecycle:lifecycle-extensions:${deps.androidX.lifecycle}"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:${deps.androidX.lifecycle}"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:${deps.androidX.swipeRefreshLayout}"
Expand Down Expand Up @@ -216,7 +215,6 @@ dependencies {
debugImplementation 'com.facebook.soloader:soloader:0.9.0'
debugImplementation "com.squareup.leakcanary:leakcanary-android:${deps.leakcanary}"

kapt "androidx.hilt:hilt-compiler:${deps.androidX.hilt}"
kapt "com.google.dagger:dagger-compiler:${deps.dagger}"
kapt "com.google.dagger:hilt-compiler:${deps.hilt}"
kapt "org.greenrobot:eventbus-annotation-processor:${deps.eventbus}"
Expand Down
Expand Up @@ -2,10 +2,11 @@ package org.cru.godtools.ui.languages

import android.os.Bundle
import androidx.fragment.app.viewModels
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.ViewModel
import androidx.lifecycle.switchMap
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import org.ccci.gto.android.common.androidx.lifecycle.orEmpty
import org.ccci.gto.android.common.db.findLiveData
import org.cru.godtools.R
Expand Down Expand Up @@ -34,7 +35,8 @@ class LanguageSettingsFragment :
override fun editParallelLanguage() = requireActivity().startLanguageSelectionActivity(false)
}

class LanguageSettingsFragmentDataModel @ViewModelInject constructor(dao: GodToolsDao, settings: Settings) :
@HiltViewModel
class LanguageSettingsFragmentDataModel @Inject constructor(dao: GodToolsDao, settings: Settings) :
ViewModel() {
val primaryLanguage = settings.primaryLanguageLiveData.switchMap { dao.findLiveData<Language>(it) }
val parallelLanguage =
Expand Down
@@ -1,17 +1,17 @@
package org.cru.godtools.ui.languages

import android.content.Context
import androidx.hilt.Assisted
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.switchMap
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import java.text.Collator
import java.util.Locale
import javax.inject.Inject
import org.ccci.gto.android.common.androidx.lifecycle.combineWith
import org.ccci.gto.android.common.db.Query
import org.ccci.gto.android.common.db.getAsLiveData
Expand All @@ -23,11 +23,12 @@ import org.keynote.godtools.android.db.GodToolsDao
private const val KEY_QUERY = "query"
private const val KEY_IS_SEARCH_VIEW_OPEN = "isSearchViewOpen"

class LanguagesFragmentViewModel @ViewModelInject constructor(
@HiltViewModel
class LanguagesFragmentViewModel @Inject constructor(
@ApplicationContext context: Context,
dao: GodToolsDao,
settings: Settings,
@Assisted private val savedState: SavedStateHandle
private val savedState: SavedStateHandle
) : ViewModel() {
val isPrimary = MutableLiveData(true)

Expand Down
Expand Up @@ -2,9 +2,10 @@ package org.cru.godtools.ui.profile

import android.os.Bundle
import androidx.fragment.app.viewModels
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.ViewModel
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import org.ccci.gto.android.common.db.findLiveData
import org.ccci.gto.android.common.sync.swiperefreshlayout.widget.SwipeRefreshSyncHelper
import org.cru.godtools.R
Expand All @@ -28,6 +29,7 @@ class GlobalActivityFragment :
}
}

class GlobalActivityFragmentViewModel @ViewModelInject constructor(dao: GodToolsDao) : ViewModel() {
@HiltViewModel
class GlobalActivityFragmentViewModel @Inject constructor(dao: GodToolsDao) : ViewModel() {
val globalActivity = dao.findLiveData<GlobalActivityAnalytics>(1)
}
@@ -1,12 +1,13 @@
package org.cru.godtools.ui.tooldetails

import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map
import androidx.lifecycle.switchMap
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import org.ccci.gto.android.common.androidx.lifecycle.emptyLiveData
import org.ccci.gto.android.common.androidx.lifecycle.orEmpty
import org.ccci.gto.android.common.androidx.lifecycle.switchCombineWith
Expand All @@ -23,7 +24,8 @@ import org.cru.godtools.shortcuts.GodToolsShortcutManager
import org.keynote.godtools.android.db.Contract.TranslationTable
import org.keynote.godtools.android.db.GodToolsDao

class ToolDetailsFragmentDataModel @ViewModelInject constructor(
@HiltViewModel
class ToolDetailsFragmentDataModel @Inject constructor(
private val dao: GodToolsDao,
private val downloadManager: GodToolsDownloadManager,
manifestManager: ManifestManager,
Expand Down
20 changes: 9 additions & 11 deletions app/src/main/java/org/cru/godtools/ui/tools/ToolsAdapter.kt
Expand Up @@ -17,16 +17,16 @@ import org.ccci.gto.android.common.recyclerview.advrecyclerview.draggable.Simple
import org.cru.godtools.databinding.ToolsListItemToolBinding
import org.cru.godtools.model.Tool

private const val VIEW_MODEL_KEY_PREFIX =
"org.cru.godtools.ui.tools.ToolsAdapter:org.cru.godtools.ui.tools.ToolsAdapterToolViewModel"
private typealias VH = DataBindingDraggableItemViewHolder<ToolsListItemToolBinding>

class ToolsAdapter(lifecycleOwner: LifecycleOwner, private val viewModelProvider: ViewModelProvider) :
class ToolsAdapter(lifecycleOwner: LifecycleOwner, viewModelProvider: ViewModelProvider) :
SimpleDataBindingDraggableItemAdapter<ToolsListItemToolBinding>(lifecycleOwner), Observer<List<Tool>> {
init {
setHasStableIds(true)
}

private val viewModel = viewModelProvider.get(ToolsAdapterViewModel::class.java)

val callbacks = ObservableField<ToolsAdapterCallbacks>()
private var tools: List<Tool>? = null
set(value) {
Expand Down Expand Up @@ -54,16 +54,14 @@ class ToolsAdapter(lifecycleOwner: LifecycleOwner, private val viewModelProvider

override fun onBindViewDataBinding(binding: ToolsListItemToolBinding, position: Int) {
val tool = getItem(position)
val code = tool?.code
val viewModel = viewModelProvider.get("$VIEW_MODEL_KEY_PREFIX:$code", ToolsAdapterToolViewModel::class.java)
.also { it.toolCode.value = code }
val toolViewModel = tool?.code?.let { viewModel.getToolViewModel(it) }

binding.tool = tool
binding.setDownloadProgress(viewModel.downloadProgress)
binding.setBanner(viewModel.banner)
binding.primaryTranslation = viewModel.firstTranslation
binding.parallelTranslation = viewModel.parallelTranslation
binding.parallelLanguage = viewModel.parallelLanguage
binding.setDownloadProgress(toolViewModel?.downloadProgress)
binding.setBanner(toolViewModel?.banner)
binding.primaryTranslation = toolViewModel?.firstTranslation
binding.parallelTranslation = toolViewModel?.parallelTranslation
binding.parallelLanguage = toolViewModel?.parallelLanguage
}

override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
Expand Down

This file was deleted.

@@ -0,0 +1,65 @@
package org.cru.godtools.ui.tools

import androidx.lifecycle.ViewModel
import androidx.lifecycle.map
import androidx.lifecycle.switchMap
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import org.ccci.gto.android.common.androidx.lifecycle.combineWith
import org.ccci.gto.android.common.androidx.lifecycle.emptyLiveData
import org.ccci.gto.android.common.androidx.lifecycle.orEmpty
import org.ccci.gto.android.common.androidx.lifecycle.switchCombineWith
import org.ccci.gto.android.common.db.Query
import org.ccci.gto.android.common.db.findLiveData
import org.ccci.gto.android.common.db.getAsLiveData
import org.cru.godtools.base.Settings
import org.cru.godtools.download.manager.GodToolsDownloadManager
import org.cru.godtools.model.Attachment
import org.cru.godtools.model.Language
import org.keynote.godtools.android.db.Contract.AttachmentTable
import org.keynote.godtools.android.db.Contract.ToolTable
import org.keynote.godtools.android.db.GodToolsDao

@HiltViewModel
class ToolsAdapterViewModel @Inject constructor(
private val dao: GodToolsDao,
private val downloadManager: GodToolsDownloadManager,
private val settings: Settings
) : ViewModel() {
private val toolViewModels = mutableMapOf<String, ToolViewModel>()
fun getToolViewModel(tool: String) = toolViewModels.getOrPut(tool) { ToolViewModel(tool) }

inner class ToolViewModel(private val tool: String) {
val banner = Query.select<Attachment>()
.join(AttachmentTable.SQL_JOIN_TOOL)
.where(
ToolTable.FIELD_CODE.eq(tool)
.and(ToolTable.FIELD_BANNER.eq(AttachmentTable.FIELD_ID))
.and(AttachmentTable.SQL_WHERE_DOWNLOADED)
)
.limit(1)
.getAsLiveData(dao)
.map { it.firstOrNull() }

private val primaryTranslation =
settings.primaryLanguageLiveData.switchMap { dao.getLatestTranslationLiveData(tool, it) }
private val defaultTranslation = dao.getLatestTranslationLiveData(tool, Settings.defaultLanguage)
internal val firstTranslation = primaryTranslation.combineWith(defaultTranslation) { p, d -> p ?: d }
internal val parallelTranslation =
settings.parallelLanguageLiveData.switchMap { dao.getLatestTranslationLiveData(tool, it) }

internal val parallelLanguage = parallelTranslation.switchMap { t ->
t?.languageCode?.let { dao.findLiveData<Language>(it) }.orEmpty()
}

internal val downloadProgress =
primaryTranslation.switchCombineWith(defaultTranslation, parallelTranslation) { prim, def, para ->
when {
prim != null -> downloadManager.getDownloadProgressLiveData(tool, prim.languageCode)
def != null -> downloadManager.getDownloadProgressLiveData(tool, def.languageCode)
para != null -> downloadManager.getDownloadProgressLiveData(tool, para.languageCode)
else -> emptyLiveData()
}
}
}
}
@@ -1,11 +1,12 @@
package org.cru.godtools.ui.tools

import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.switchMap
import dagger.hilt.android.lifecycle.HiltViewModel
import java.util.Locale
import javax.inject.Inject
import org.ccci.gto.android.common.androidx.lifecycle.combineWith
import org.ccci.gto.android.common.db.Query
import org.ccci.gto.android.common.db.getAsLiveData
Expand All @@ -20,7 +21,8 @@ import org.cru.godtools.widget.BannerType
import org.keynote.godtools.android.db.Contract.ToolTable
import org.keynote.godtools.android.db.GodToolsDao

class ToolsFragmentDataModel @ViewModelInject constructor(private val dao: GodToolsDao, settings: Settings) :
@HiltViewModel
class ToolsFragmentDataModel @Inject constructor(private val dao: GodToolsDao, settings: Settings) :
ViewModel() {
val mode = MutableLiveData(MODE_ADDED)

Expand Down
Expand Up @@ -17,7 +17,6 @@ import org.cru.godtools.model.Language
import org.cru.godtools.model.Tool
import org.cru.godtools.model.Translation
import org.cru.godtools.ui.tools.ToolsAdapterCallbacks
import org.cru.godtools.ui.tools.ToolsAdapterToolViewModel
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
Expand All @@ -33,7 +32,6 @@ import org.robolectric.annotation.Config
class ToolsListItemToolBindingTest {
private lateinit var binding: ToolsListItemToolBinding
private lateinit var callbacks: ToolsAdapterCallbacks
private lateinit var viewModel: ToolsAdapterToolViewModel
private val tool = Tool().apply {
type = Tool.Type.TRACT
code = "test"
Expand All @@ -56,7 +54,6 @@ class ToolsListItemToolBindingTest {
fun createBinding() {
val activityController = Robolectric.buildActivity(MainActivity::class.java)
callbacks = mock()
viewModel = mock()

binding = ToolsListItemToolBinding.inflate(LayoutInflater.from(activityController.get()), null, false)
binding.lifecycleOwner = activityController.get()
Expand Down
3 changes: 1 addition & 2 deletions build.gradle
Expand Up @@ -18,7 +18,7 @@ buildscript {
gtoSupport : '3.7.2-SNAPSHOT',
guava : '30.1-android',
hamcrest : '2.2',
hilt : '2.30.1-alpha',
hilt : '2.31.2-alpha',
jacoco : '0.8.6',
jsoup : '1.13.1',
junit : '4.13.1',
Expand Down Expand Up @@ -59,7 +59,6 @@ buildscript {
core : '1.3.2',
databinding : deps.gradleAndroidPlugin,
fragment : '1.2.5',
hilt : '1.0.0-alpha02',
lifecycle : '2.2.0',
loader : '1.1.0',
multidex : '2.0.1',
Expand Down
3 changes: 0 additions & 3 deletions library/analytics/build.gradle
Expand Up @@ -30,8 +30,6 @@ dependencies {

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${deps.kotlinCoroutines}"

implementation "androidx.hilt:hilt-lifecycle-viewmodel:${deps.androidX.hilt}"

implementation "com.google.firebase:firebase-core:${deps.firebase.core}"
implementation "com.google.firebase:firebase-messaging:${deps.firebase.messaging}"
implementation "com.google.firebase:firebase-perf:${deps.firebase.perf}"
Expand All @@ -56,7 +54,6 @@ dependencies {

testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:${deps.kotlinCoroutines}"

kapt "androidx.hilt:hilt-compiler:${deps.androidX.hilt}"
kapt "com.google.dagger:dagger-compiler:${deps.dagger}"
kapt "com.google.dagger:hilt-compiler:${deps.hilt}"
kapt "org.greenrobot:eventbus-annotation-processor:${deps.eventbus}"
Expand Down