Skip to content

Commit

Permalink
Support constructor injection for view models
Browse files Browse the repository at this point in the history
  • Loading branch information
bubelov committed Feb 17, 2018
1 parent 7e750c9 commit 56629d7
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 17 deletions.
45 changes: 39 additions & 6 deletions app/src/main/java/com/bubelov/coins/dagger/AppComponent.kt
@@ -1,3 +1,30 @@
/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <https://unlicense.org>
*/

package com.bubelov.coins.dagger

import android.content.Context
Expand All @@ -10,26 +37,32 @@ import dagger.Component
import dagger.BindsInstance
import dagger.android.AndroidInjectionModule

/**
* @author Igor Bubelov
*/

@Singleton
@Component(modules = [AppModule::class, DatabaseModule::class, AndroidInjectionModule::class, ActivityBuilder::class, FragmentBuilder::class, ServiceBuilder::class])
@Component(
modules = [
AppModule::class,
DatabaseModule::class,
AndroidInjectionModule::class,
ActivityBuilder::class,
FragmentBuilder::class,
ServiceBuilder::class,
ViewModelModule::class
]
)
interface AppComponent {
fun inject(app: App)

fun inject(target: MapViewModel)
fun inject(target: ExchangeRatesViewModel)
fun inject(target: NotificationAreaViewModel)
fun inject(target: PlacesSearchViewModel)
fun inject(target: EditPlaceViewModel)
fun inject(target: SettingsViewModel)

@Component.Builder
interface Builder {
@BindsInstance
fun context(context: Context): Builder

fun build(): AppComponent
}
}
65 changes: 65 additions & 0 deletions app/src/main/java/com/bubelov/coins/dagger/ViewModelFactory.kt
@@ -0,0 +1,65 @@
/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <https://unlicense.org>
*/

package com.bubelov.coins.dagger

import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProvider

import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton

@Singleton
class ViewModelFactory @Inject constructor(
private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {

@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel>? = creators[modelClass]

if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}

if (creator == null) {
throw IllegalArgumentException("unknown model class " + modelClass)
}

try {
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
46 changes: 46 additions & 0 deletions app/src/main/java/com/bubelov/coins/dagger/ViewModelKey.kt
@@ -0,0 +1,46 @@
/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <https://unlicense.org>
*/

package com.bubelov.coins.dagger

import android.arch.lifecycle.ViewModel

import java.lang.annotation.Documented
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target

import dagger.MapKey
import kotlin.reflect.KClass

@Suppress("DEPRECATED_JAVA_ANNOTATION")
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
46 changes: 46 additions & 0 deletions app/src/main/java/com/bubelov/coins/dagger/ViewModelModule.kt
@@ -0,0 +1,46 @@
/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <https://unlicense.org>
*/

package com.bubelov.coins.dagger

import dagger.Module
import android.arch.lifecycle.ViewModel
import dagger.multibindings.IntoMap
import dagger.Binds
import android.arch.lifecycle.ViewModelProvider
import com.bubelov.coins.ui.viewmodel.EditPlaceViewModel

@Module
abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(EditPlaceViewModel::class) // PROVIDE YOUR OWN MODELS HERE
internal abstract fun bindEditPlaceViewModel(dashboardViewModel: EditPlaceViewModel): ViewModel

@Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
Expand Up @@ -28,9 +28,8 @@
package com.bubelov.coins.ui.activity

import android.app.Activity
import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.*
import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
Expand All @@ -49,8 +48,10 @@ import dagger.android.AndroidInjection
import kotlinx.android.synthetic.main.activity_edit_place.*
import org.jetbrains.anko.alert
import java.util.*
import javax.inject.Inject

class EditPlaceActivity : AppCompatActivity() {
@Inject lateinit var modelFactory: ViewModelProvider.Factory
private lateinit var model: EditPlaceViewModel

private val map = MutableLiveData<GoogleMap>()
Expand All @@ -60,7 +61,7 @@ class EditPlaceActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_place)

model = ViewModelProviders.of(this).get(EditPlaceViewModel::class.java)
model = ViewModelProviders.of(this, modelFactory).get(EditPlaceViewModel::class.java)
model.setup(intent.getSerializableExtra(PLACE_EXTRA) as Place?)

toolbar.setNavigationOnClickListener { supportFinishAfterTransition() }
Expand Down
Expand Up @@ -27,11 +27,9 @@

package com.bubelov.coins.ui.viewmodel

import android.app.Application
import android.arch.lifecycle.AndroidViewModel
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import com.bubelov.coins.dagger.Injector
import android.arch.lifecycle.ViewModel
import com.bubelov.coins.model.Place
import com.bubelov.coins.repository.Result
import com.bubelov.coins.repository.place.PlacesRepository
Expand All @@ -42,8 +40,7 @@ import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject

class EditPlaceViewModel(application: Application) : AndroidViewModel(application) {
@Inject lateinit var placesRepository: PlacesRepository
class EditPlaceViewModel @Inject constructor(var placesRepository: PlacesRepository) : ViewModel() {

var place: Place? = null

Expand All @@ -52,7 +49,6 @@ class EditPlaceViewModel(application: Application) : AndroidViewModel(applicatio
val showProgress = MutableLiveData<Boolean>().apply { value = false }

fun setup(place: Place?) {
Injector.appComponent.inject(this)
this.place = place

if (place != null) {
Expand Down Expand Up @@ -85,8 +81,7 @@ class EditPlaceViewModel(application: Application) : AndroidViewModel(applicatio

launch(UI) {
showProgress.value = true
val result = async {
placesRepository.updatePlace(place) }.await()
val result = async { placesRepository.updatePlace(place) }.await()

when (result) {
is Result.Success -> success.value = true
Expand Down

0 comments on commit 56629d7

Please sign in to comment.