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

[UPDATE] Using dagger multi binding to inject ViewModels #103

Merged
merged 1 commit into from
Jun 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import dagger.Module
*
* @see DemoApplicationComponent
*/
@Module
@Module(includes = [ViewModelModule::class])
abstract class ApplicationModule {
//expose Application as an injectable context
@Binds
Expand Down
34 changes: 34 additions & 0 deletions app/src/main/java/com/hossainkhan/android/demo/di/ViewModelKey.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2019 Hossain Khan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hossainkhan.android.demo.di

import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass

/**
* See https://dagger.dev/multibindings.html
*/
@MustBeDocumented
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2019 Hossain Khan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hossainkhan.android.demo.di

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.hossainkhan.android.demo.ui.browse.LayoutBrowseViewModel
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutInfoViewModel
import com.hossainkhan.android.demo.viewmodel.ViewModelProviderFactory
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap

/**
* Uses dagger multi-binding to provide [ViewModel] instances used in the app.
*
* @see <a href="https://dagger.dev/multibindings">Multibindings</a>
*/
@Suppress("unused")
@Module
abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(LayoutBrowseViewModel::class)
abstract fun bindLayoutBrowserViewModel(layoutBrowseViewModel: LayoutBrowseViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(LayoutInfoViewModel::class)
abstract fun bindLayoutInfoViewModel(layoutInfoViewModel: LayoutInfoViewModel): ViewModel

@Binds
abstract fun bindViewModelFactory(factory: ViewModelProviderFactory): ViewModelProvider.Factory
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.hossainkhan.android.demo.R
import com.hossainkhan.android.demo.viewmodel.LayoutPreviewViewModelFactory
import com.hossainkhan.android.demo.viewmodel.ViewModelProviderFactory
import dagger.android.AndroidInjection
import javax.inject.Inject

Expand All @@ -31,7 +31,7 @@ import javax.inject.Inject
*/
class LayoutBrowseActivity : AppCompatActivity() {
@Inject
internal lateinit var viewModelFactory: LayoutPreviewViewModelFactory
internal lateinit var viewModelFactory: ViewModelProviderFactory

private lateinit var recyclerView: RecyclerView
private lateinit var viewAdapter: RecyclerView.Adapter<*>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ import com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineBarrierActiv
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutGuidelineGroupActivity
import com.hossainkhan.android.demo.ui.layoutpreview.LayoutVisibilityGoneActivity
import timber.log.Timber
import javax.inject.Inject

class LayoutBrowseViewModel(
class LayoutBrowseViewModel @Inject constructor(
appDataStore: AppDataStore,
private val browseNavigator: LayoutBrowseNavigator) : ViewModel() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import androidx.annotation.LayoutRes
import com.hossainkhan.android.demo.data.AppDataStore
import com.hossainkhan.android.demo.data.LayoutInformation
import timber.log.Timber
import javax.inject.Inject

/**
* ViewModel for containing layout information.
*/
class LayoutInfoViewModel(private val appDataStore: AppDataStore) : ViewModel() {
class LayoutInfoViewModel @Inject constructor(private val appDataStore: AppDataStore) : ViewModel() {
private val layoutInfoLiveData: MutableLiveData<LayoutInformation> = MutableLiveData()

val layoutInformation: LiveData<LayoutInformation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,20 @@

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

import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.annotation.LayoutRes
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.app.NavUtils
import androidx.appcompat.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NavUtils
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.hossainkhan.android.demo.R
import com.hossainkhan.android.demo.data.LayoutInformation
import com.hossainkhan.android.demo.ui.dialog.LayoutInfoDialog
import com.hossainkhan.android.demo.viewmodel.LayoutPreviewViewModelFactory
import com.hossainkhan.android.demo.viewmodel.ViewModelProviderFactory
import dagger.android.AndroidInjection
import timber.log.Timber
import javax.inject.Inject
Expand Down Expand Up @@ -77,7 +75,7 @@ open class LayoutPreviewBaseActivity : AppCompatActivity() {
}

@Inject
internal lateinit var viewModelFactory: LayoutPreviewViewModelFactory
internal lateinit var viewModelFactory: ViewModelProviderFactory

private lateinit var viewModel: LayoutInfoViewModel

Expand Down Expand Up @@ -123,9 +121,7 @@ open class LayoutPreviewBaseActivity : AppCompatActivity() {
Timber.d("Layout info is showing: %s", infoDialog?.isVisible)
if (infoDialog?.isVisible == false) {
if (fromUser || viewModel.isFirstTime) {
infoDialog?.let {
it.show(supportFragmentManager, "dialog")
}
infoDialog?.show(supportFragmentManager, "dialog")
}
} else {
infoDialog?.dismiss()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018 Hossain Khan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hossainkhan.android.demo.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton
import com.hossainkhan.android.demo.di.ViewModelModule
/**
* The [ViewModelProvider.Factory] to get instance of all viewmodels using Dagger multi-binding.
*
*
* @see <a href="https://dagger.dev/multibindings">Multibindings</a>
* @see [ViewModelModule]
*/
@Singleton
class ViewModelProviderFactory @Inject constructor(
private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("Unknown view model class $modelClass. Must be added to map first.")
try {
@Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}