Skip to content
This repository was archived by the owner on Aug 22, 2024. It is now read-only.

Commit 08041b7

Browse files
authored
Merge pull request #103 from amardeshbd/update-view-model-factory-injection
[UPDATE] Using dagger multi binding to inject ViewModels
2 parents 1935c74 + f95d56e commit 08041b7

File tree

9 files changed

+144
-64
lines changed

9 files changed

+144
-64
lines changed

app/src/main/java/com/hossainkhan/android/demo/di/ApplicationModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import dagger.Module
3030
*
3131
* @see DemoApplicationComponent
3232
*/
33-
@Module
33+
@Module(includes = [ViewModelModule::class])
3434
abstract class ApplicationModule {
3535
//expose Application as an injectable context
3636
@Binds
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2019 Hossain Khan
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.hossainkhan.android.demo.di
18+
19+
import androidx.lifecycle.ViewModel
20+
import dagger.MapKey
21+
import kotlin.reflect.KClass
22+
23+
/**
24+
* See https://dagger.dev/multibindings.html
25+
*/
26+
@MustBeDocumented
27+
@Target(
28+
AnnotationTarget.FUNCTION,
29+
AnnotationTarget.PROPERTY_GETTER,
30+
AnnotationTarget.PROPERTY_SETTER
31+
)
32+
@Retention(AnnotationRetention.RUNTIME)
33+
@MapKey
34+
annotation class ViewModelKey(val value: KClass<out ViewModel>)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2019 Hossain Khan
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.hossainkhan.android.demo.di
18+
19+
import androidx.lifecycle.ViewModel
20+
import androidx.lifecycle.ViewModelProvider
21+
import com.hossainkhan.android.demo.ui.browse.LayoutBrowseViewModel
22+
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutInfoViewModel
23+
import com.hossainkhan.android.demo.viewmodel.ViewModelProviderFactory
24+
import dagger.Binds
25+
import dagger.Module
26+
import dagger.multibindings.IntoMap
27+
28+
/**
29+
* Uses dagger multi-binding to provide [ViewModel] instances used in the app.
30+
*
31+
* @see <a href="https://dagger.dev/multibindings">Multibindings</a>
32+
*/
33+
@Suppress("unused")
34+
@Module
35+
abstract class ViewModelModule {
36+
@Binds
37+
@IntoMap
38+
@ViewModelKey(LayoutBrowseViewModel::class)
39+
abstract fun bindLayoutBrowserViewModel(layoutBrowseViewModel: LayoutBrowseViewModel): ViewModel
40+
41+
@Binds
42+
@IntoMap
43+
@ViewModelKey(LayoutInfoViewModel::class)
44+
abstract fun bindLayoutInfoViewModel(layoutInfoViewModel: LayoutInfoViewModel): ViewModel
45+
46+
@Binds
47+
abstract fun bindViewModelFactory(factory: ViewModelProviderFactory): ViewModelProvider.Factory
48+
}

app/src/main/java/com/hossainkhan/android/demo/ui/browse/LayoutBrowseActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import androidx.lifecycle.ViewModelProviders
2222
import androidx.recyclerview.widget.GridLayoutManager
2323
import androidx.recyclerview.widget.RecyclerView
2424
import com.hossainkhan.android.demo.R
25-
import com.hossainkhan.android.demo.viewmodel.LayoutPreviewViewModelFactory
25+
import com.hossainkhan.android.demo.viewmodel.ViewModelProviderFactory
2626
import dagger.android.AndroidInjection
2727
import javax.inject.Inject
2828

@@ -31,7 +31,7 @@ import javax.inject.Inject
3131
*/
3232
class LayoutBrowseActivity : AppCompatActivity() {
3333
@Inject
34-
internal lateinit var viewModelFactory: LayoutPreviewViewModelFactory
34+
internal lateinit var viewModelFactory: ViewModelProviderFactory
3535

3636
private lateinit var recyclerView: RecyclerView
3737
private lateinit var viewAdapter: RecyclerView.Adapter<*>

app/src/main/java/com/hossainkhan/android/demo/ui/browse/LayoutBrowseViewModel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ import com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineBarrierActiv
3030
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineGroupActivity
3131
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutVisibilityGoneActivity
3232
import timber.log.Timber
33+
import javax.inject.Inject
3334

34-
class LayoutBrowseViewModel(
35+
class LayoutBrowseViewModel @Inject constructor(
3536
appDataStore: AppDataStore,
3637
private val browseNavigator: LayoutBrowseNavigator) : ViewModel() {
3738

app/src/main/java/com/hossainkhan/android/demo/ui/layoutpreview/LayoutInfoViewModel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ import androidx.annotation.LayoutRes
2323
import com.hossainkhan.android.demo.data.AppDataStore
2424
import com.hossainkhan.android.demo.data.LayoutInformation
2525
import timber.log.Timber
26+
import javax.inject.Inject
2627

2728
/**
2829
* ViewModel for containing layout information.
2930
*/
30-
class LayoutInfoViewModel(private val appDataStore: AppDataStore) : ViewModel() {
31+
class LayoutInfoViewModel @Inject constructor(private val appDataStore: AppDataStore) : ViewModel() {
3132
private val layoutInfoLiveData: MutableLiveData<LayoutInformation> = MutableLiveData()
3233

3334
val layoutInformation: LiveData<LayoutInformation>

app/src/main/java/com/hossainkhan/android/demo/ui/layoutpreview/LayoutPreviewBaseActivity.kt

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,20 @@
1616

1717
package com.hossainkhan.android.demo.ui.layoutpreview
1818

19-
import androidx.lifecycle.Observer
20-
import androidx.lifecycle.ViewModelProvider
2119
import android.content.Context
2220
import android.content.Intent
23-
import android.net.Uri
2421
import android.os.Bundle
25-
import androidx.annotation.LayoutRes
26-
import androidx.browser.customtabs.CustomTabsIntent
27-
import androidx.core.app.NavUtils
28-
import androidx.appcompat.app.AppCompatActivity
2922
import android.view.Menu
3023
import android.view.MenuItem
24+
import androidx.annotation.LayoutRes
25+
import androidx.appcompat.app.AppCompatActivity
26+
import androidx.core.app.NavUtils
27+
import androidx.lifecycle.Observer
28+
import androidx.lifecycle.ViewModelProvider
3129
import com.hossainkhan.android.demo.R
3230
import com.hossainkhan.android.demo.data.LayoutInformation
3331
import com.hossainkhan.android.demo.ui.dialog.LayoutInfoDialog
34-
import com.hossainkhan.android.demo.viewmodel.LayoutPreviewViewModelFactory
32+
import com.hossainkhan.android.demo.viewmodel.ViewModelProviderFactory
3533
import dagger.android.AndroidInjection
3634
import timber.log.Timber
3735
import javax.inject.Inject
@@ -77,7 +75,7 @@ open class LayoutPreviewBaseActivity : AppCompatActivity() {
7775
}
7876

7977
@Inject
80-
internal lateinit var viewModelFactory: LayoutPreviewViewModelFactory
78+
internal lateinit var viewModelFactory: ViewModelProviderFactory
8179

8280
private lateinit var viewModel: LayoutInfoViewModel
8381

@@ -123,9 +121,7 @@ open class LayoutPreviewBaseActivity : AppCompatActivity() {
123121
Timber.d("Layout info is showing: %s", infoDialog?.isVisible)
124122
if (infoDialog?.isVisible == false) {
125123
if (fromUser || viewModel.isFirstTime) {
126-
infoDialog?.let {
127-
it.show(supportFragmentManager, "dialog")
128-
}
124+
infoDialog?.show(supportFragmentManager, "dialog")
129125
}
130126
} else {
131127
infoDialog?.dismiss()

app/src/main/java/com/hossainkhan/android/demo/viewmodel/LayoutPreviewViewModelFactory.kt

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2018 Hossain Khan
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.hossainkhan.android.demo.viewmodel
18+
19+
import androidx.lifecycle.ViewModel
20+
import androidx.lifecycle.ViewModelProvider
21+
import javax.inject.Inject
22+
import javax.inject.Provider
23+
import javax.inject.Singleton
24+
import com.hossainkhan.android.demo.di.ViewModelModule
25+
/**
26+
* The [ViewModelProvider.Factory] to get instance of all viewmodels using Dagger multi-binding.
27+
*
28+
*
29+
* @see <a href="https://dagger.dev/multibindings">Multibindings</a>
30+
* @see [ViewModelModule]
31+
*/
32+
@Singleton
33+
class ViewModelProviderFactory @Inject constructor(
34+
private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
35+
) : ViewModelProvider.Factory {
36+
override fun <T : ViewModel> create(modelClass: Class<T>): T {
37+
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
38+
modelClass.isAssignableFrom(it.key)
39+
}?.value ?: throw IllegalArgumentException("Unknown view model class $modelClass. Must be added to map first.")
40+
try {
41+
@Suppress("UNCHECKED_CAST")
42+
return creator.get() as T
43+
} catch (e: Exception) {
44+
throw RuntimeException(e)
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)