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

Use screen lifecycle based on #637 #825

Open
wants to merge 3 commits into
base: add-dfm
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions buildSrc/src/main/java/dependencies/Dep.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ object Dep {
val fragment = "androidx.fragment:fragment:1.1.0-alpha03"

val lifecycleExtensions = "androidx.lifecycle:lifecycle-extensions:2.0.0"
val viewModelKtx = "androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0"
val lifecycleLiveData = "androidx.lifecycle:lifecycle-livedata:2.0.0"

object Room {
Expand Down
2 changes: 2 additions & 0 deletions corecomponent/androidcomponent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ dependencies {
api Dep.AndroidX.Navigation.runtimeKtx
api Dep.AndroidX.Navigation.fragmentKtx
api Dep.AndroidX.Navigation.uiKtx
implementation Dep.AndroidX.viewModelKtx
implementation Dep.AndroidX.lifecycleExtensions

api Dep.liveDataKtx

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.droidkaigi.confsched2019.di

import androidx.fragment.app.Fragment
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProviders
import io.github.droidkaigi.confsched2019.widget.PageViewModel

interface PageComponent

inline fun <reified T : PageComponent> getPageComponent(
fragment: Fragment,
noinline pageComponentCreator: (pageLifecycleOwner: LifecycleOwner) -> T
): T {
val viewModel = ViewModelProviders
.of(fragment, PageViewModel.Factory(pageComponentCreator))
.get(PageViewModel::class.java)
return viewModel.pageComponent as T
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,24 @@ import kotlinx.coroutines.launch
}
}

@MainThread fun <T> ReceiveChannel<T>.toLiveData(
coroutineScope: CoroutineScope,
defaultValue: T? = null
): LiveData<T> {
return object : LiveData<T>(), CoroutineScope by GlobalScope {
init {
if (defaultValue != null) {
value = defaultValue
}
coroutineScope.launch {
for (element in this@toLiveData) {
postValue(element)
}
}
}
}
}

@MainThread fun <T> ReceiveChannel<T>.toLiveData(
store: Store,
defaultValue: T? = null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.github.droidkaigi.confsched2019.widget

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.github.droidkaigi.confsched2019.di.PageComponent

class PageViewModel : ViewModel(), LifecycleOwner {
lateinit var pageComponent: PageComponent

private val lifecycle = LifecycleRegistry(this).apply {
handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}

override fun getLifecycle(): Lifecycle = lifecycle

override fun onCleared() {
super.onCleared()
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}

class Factory(val pageComponentCreator: (LifecycleOwner) -> PageComponent) :
ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val pageViewModel = PageViewModel()
pageViewModel.pageComponent = pageComponentCreator(pageViewModel)
@Suppress("UNCHECKED_CAST")
return pageViewModel as T
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,28 @@ import androidx.fragment.app.Fragment
import com.xwray.groupie.GroupAdapter
import com.xwray.groupie.Item
import com.xwray.groupie.ViewHolder
import dagger.Component
import io.github.droidkaigi.confsched2019.App
import io.github.droidkaigi.confsched2019.di.AppComponent
import io.github.droidkaigi.confsched2019.di.PageComponent
import io.github.droidkaigi.confsched2019.di.PageScope
import io.github.droidkaigi.confsched2019.di.getPageComponent
import io.github.droidkaigi.confsched2019.ext.changed
import io.github.droidkaigi.confsched2019.ext.requireValue
import io.github.droidkaigi.confsched2019.staff_dfm.R
import io.github.droidkaigi.confsched2019.staff_dfm.databinding.FragmentStaffSearchBinding
import io.github.droidkaigi.confsched2019.staff_dfm.ui.actioncreator.StaffSearchActionCreator
import io.github.droidkaigi.confsched2019.staff_dfm.ui.di.DaggerStaffComponent
import io.github.droidkaigi.confsched2019.staff_dfm.ui.di.StaffModule
import io.github.droidkaigi.confsched2019.staff_dfm.ui.di.PageModule
import io.github.droidkaigi.confsched2019.staff_dfm.ui.item.StaffItem
import io.github.droidkaigi.confsched2019.staff_dfm.ui.store.StaffSearchStore
import me.tatarka.injectedvmprovider.InjectedViewModelProviders
import javax.inject.Inject
import javax.inject.Provider

class StaffSearchFragment : Fragment() {
private lateinit var binding: FragmentStaffSearchBinding

@Inject lateinit var searchActionCreator: StaffSearchActionCreator
@Inject lateinit var searchStore: StaffSearchStore
private var searchView: SearchView? = null
@Inject lateinit var searchStoreProvider: Provider<StaffSearchStore>
private val searchStore: StaffSearchStore by lazy {
InjectedViewModelProviders.of(requireActivity()).get(searchStoreProvider)
}

private val groupAdapter = GroupAdapter<ViewHolder>()

Expand All @@ -60,13 +59,13 @@ class StaffSearchFragment : Fragment() {

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)

val appComponent = (requireContext().applicationContext as App).appCmponent
val component = DaggerStaffComponent.builder()
.appComponent(appComponent)
.staffModule(StaffModule(this))
.build()
component.inject(this)
getPageComponent(this) { pageLifecycleOwner ->
DaggerStaffPageComponent.builder()
.appComponent(appComponent)
.pageModule(PageModule(pageLifecycleOwner))
.build()
}.inject(this)

binding.searchRecycler.adapter = groupAdapter

Expand Down Expand Up @@ -132,3 +131,19 @@ class StaffSearchFragment : Fragment() {
imm?.hideSoftInputFromWindow(view?.windowToken, 0)
}
}

@PageScope
@Component(
modules = [PageModule::class],
dependencies = [AppComponent::class]
)
interface StaffPageComponent : PageComponent {
@Component.Builder
interface Builder {
fun pageModule(pageModule: PageModule): Builder
fun appComponent(appComponent: AppComponent): Builder
fun build(): StaffPageComponent
}

fun inject(fragment: StaffSearchFragment)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package io.github.droidkaigi.confsched2019.staff_dfm.ui.actioncreator

import androidx.lifecycle.Lifecycle
import io.github.droidkaigi.confsched2019.action.Action
import io.github.droidkaigi.confsched2019.data.repository.StaffRepository
import io.github.droidkaigi.confsched2019.di.PageScope
import io.github.droidkaigi.confsched2019.dispatcher.Dispatcher
import io.github.droidkaigi.confsched2019.ext.coroutineScope
import io.github.droidkaigi.confsched2019.model.LoadingState
import io.github.droidkaigi.confsched2019.model.StaffContents
import io.github.droidkaigi.confsched2019.model.StaffSearchResult
Expand All @@ -18,8 +16,8 @@ import javax.inject.Inject
class StaffSearchActionCreator @Inject constructor(
override val dispatcher: Dispatcher,
private val staffRepository: StaffRepository,
@PageScope private val lifecycle: Lifecycle
) : CoroutineScope by lifecycle.coroutineScope, ErrorHandler {
@PageScope private val coroutineScope: CoroutineScope
) : CoroutineScope by coroutineScope, ErrorHandler {

fun load() = launch {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.github.droidkaigi.confsched2019.staff_dfm.ui.di

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import dagger.Module
import dagger.Provides
import io.github.droidkaigi.confsched2019.di.PageScope
import io.github.droidkaigi.confsched2019.ext.coroutineScope
import kotlinx.coroutines.CoroutineScope

@Module
class PageModule(val lifecycleOwner: LifecycleOwner) {

@Provides
@PageScope
fun providesLifecycle(): Lifecycle {
return lifecycleOwner.lifecycle
}

@Provides
@PageScope
fun provideCoroutineScope(): CoroutineScope {
return lifecycleOwner.lifecycle.coroutineScope
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
package io.github.droidkaigi.confsched2019.staff_dfm.ui.store

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import io.github.droidkaigi.confsched2019.action.Action
import io.github.droidkaigi.confsched2019.di.PageScope
import io.github.droidkaigi.confsched2019.dispatcher.Dispatcher
import io.github.droidkaigi.confsched2019.ext.toLiveData
import io.github.droidkaigi.confsched2019.model.LoadingState
import io.github.droidkaigi.confsched2019.model.StaffContents
import io.github.droidkaigi.confsched2019.model.StaffSearchResult
import io.github.droidkaigi.confsched2019.store.Store
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.map
import javax.inject.Inject

@PageScope
class StaffSearchStore @Inject constructor(
dispatcher: Dispatcher
) : Store() {
dispatcher: Dispatcher,
@PageScope coroutineScope: CoroutineScope
) {
val query get() = searchResult.value?.query

val loadingState: LiveData<LoadingState> = dispatcher
.subscribe<Action.StaffSearchLoadingStateChanged>()
.map { it.loadingState }
.toLiveData(this, LoadingState.LOADING)
.toLiveData(coroutineScope, LoadingState.LOADING)
val searchResult = dispatcher
.subscribe<Action.StaffSearchResultLoaded>()
.map { it.searchResult }
.toLiveData(this, StaffSearchResult.EMPTY)
.toLiveData(coroutineScope, StaffSearchResult.EMPTY)
val staffContents = dispatcher
.subscribe<Action.StaffLoaded>()
.map { it.staffContents }
.toLiveData(this, StaffContents.EMPTY)
.toLiveData(coroutineScope, StaffContents.EMPTY)
}