1
0
Fork 0

Fix ViewModel States (#2457)

Co-authored-by: Jeff Boek <jeff@jeffboek.com>
master
Colin Lee 2019-05-14 22:49:02 +02:00 committed by Jeff Boek
parent a739d5a9e5
commit 49ac62ab85
13 changed files with 83 additions and 90 deletions

View File

@ -25,7 +25,8 @@
tools:ignore="UnusedAttribute">
<activity android:name=".HomeActivity"
android:launchMode="singleInstance"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|locale|layoutDirection|smallestScreenSize|screenLayout">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

View File

@ -67,9 +67,9 @@ class CollectionCreationComponent(
override fun initView() = CollectionCreationUIView(container, actionEmitter, changesObservable)
override fun render(): Observable<CollectionCreationState> =
ViewModelProvider(owner, CollectionCreationViewModel.Factory(initialState, changesObservable)).get(
ViewModelProvider(owner, CollectionCreationViewModel.Factory(initialState)).get(
CollectionCreationViewModel::class.java
).render(uiView)
).render(changesObservable, uiView)
init {
render()
@ -77,22 +77,19 @@ class CollectionCreationComponent(
}
class CollectionCreationViewModel(
initialState: CollectionCreationState,
changesObservable: Observable<CollectionCreationChange>
initialState: CollectionCreationState
) :
UIComponentViewModel<CollectionCreationState, CollectionCreationAction, CollectionCreationChange>(
initialState,
changesObservable,
reducer
) {
class Factory(
private val initialState: CollectionCreationState,
private val changesObservable: Observable<CollectionCreationChange>
private val initialState: CollectionCreationState
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
CollectionCreationViewModel(initialState, changesObservable) as T
CollectionCreationViewModel(initialState) as T
}
companion object {

View File

@ -53,7 +53,7 @@ class ToolbarComponent(
override fun render(): Observable<SearchState> =
ViewModelProviders.of(owner, ToolbarViewModel.Factory(initialState, changesObservable))
.get(ToolbarViewModel::class.java).render(uiView)
.get(ToolbarViewModel::class.java).render(changesObservable, uiView)
init {
render()
@ -99,8 +99,8 @@ sealed class SearchChange : Change {
data class SearchShortcutEngineSelected(val engine: SearchEngine) : SearchChange()
}
class ToolbarViewModel(initialState: SearchState, changesObservable: Observable<SearchChange>) :
UIComponentViewModel<SearchState, SearchAction, SearchChange>(initialState, changesObservable, reducer) {
class ToolbarViewModel(initialState: SearchState) :
UIComponentViewModel<SearchState, SearchAction, SearchChange>(initialState, reducer) {
class Factory(
private val initialState: SearchState,
@ -108,7 +108,7 @@ class ToolbarViewModel(initialState: SearchState, changesObservable: Observable<
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
ToolbarViewModel(initialState, changesObservable) as T
ToolbarViewModel(initialState) as T
}
companion object {

View File

@ -33,8 +33,8 @@ class ExceptionsComponent(
) {
override fun render(): Observable<ExceptionsState> =
ViewModelProviders.of(owner, ExceptionsViewModel.Factory(initialState, changesObservable))
.get(ExceptionsViewModel::class.java).render(uiView)
ViewModelProviders.of(owner, ExceptionsViewModel.Factory(initialState))
.get(ExceptionsViewModel::class.java).render(changesObservable, uiView)
override fun initView() = ExceptionsUIView(container, actionEmitter, changesObservable)
@ -56,20 +56,18 @@ sealed class ExceptionsChange : Change {
data class Change(val list: List<ExceptionsItem>) : ExceptionsChange()
}
class ExceptionsViewModel(initialState: ExceptionsState, changesObservable: Observable<ExceptionsChange>) :
class ExceptionsViewModel(initialState: ExceptionsState) :
UIComponentViewModel<ExceptionsState, ExceptionsAction, ExceptionsChange>(
initialState,
changesObservable,
reducer
) {
class Factory(
private val initialState: ExceptionsState,
private val changesObservable: Observable<ExceptionsChange>
private val initialState: ExceptionsState
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
ExceptionsViewModel(initialState, changesObservable) as T
ExceptionsViewModel(initialState) as T
}
companion object {

View File

@ -155,33 +155,37 @@ class HomeFragment : Fragment(), CoroutineScope {
privateBrowsingButton.setOnClickListener {
val browsingModeManager = (activity as HomeActivity).browsingModeManager
browsingModeManager.mode = when (browsingModeManager.mode) {
val newMode = when (browsingModeManager.mode) {
BrowsingModeManager.Mode.Normal -> BrowsingModeManager.Mode.Private
BrowsingModeManager.Mode.Private -> BrowsingModeManager.Mode.Normal
}
val mode = if (newMode == BrowsingModeManager.Mode.Private) Mode.Private else Mode.Normal
getManagedEmitter<SessionControlChange>().onNext(SessionControlChange.ModeChange(mode))
browsingModeManager.mode = newMode
}
// We need the shadow to be above the components.
homeDividerShadow.bringToFront()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// This can get called before onCreateView, before the component is defined
view?.let {
val state = sessionControlComponent.stateObservable.blockingFirst()
val modeInt = if (state.mode is Mode.Private) 0 else 1
outState.putInt(KEY_MODE, modeInt)
}
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
if (savedInstanceState != null) {
emitSessionChanges()
val mode = if (savedInstanceState.getInt(KEY_MODE) == 0) Mode.Private else Mode.Normal
getManagedEmitter<SessionControlChange>().onNext(
SessionControlChange.ModeChange(mode)
SessionControlChange.TabsChange(
(savedInstanceState.getParcelableArrayList<Tab>(
KEY_TABS
) ?: arrayListOf()).toList()
)
)
getManagedEmitter<SessionControlChange>().onNext(
SessionControlChange.CollectionsChange(
(savedInstanceState.getParcelableArrayList<TabCollection>(
KEY_COLLECTIONS
) ?: arrayListOf()).toList()
)
)
}
}
@ -210,6 +214,9 @@ class HomeFragment : Fragment(), CoroutineScope {
}
}
val mode = if ((activity as HomeActivity).browsingModeManager.isPrivate) Mode.Private else Mode.Normal
getManagedEmitter<SessionControlChange>().onNext(SessionControlChange.ModeChange(mode))
emitSessionChanges()
sessionObserver = subscribeToSessions()
}

View File

@ -42,9 +42,9 @@ class SessionControlComponent(
get() = uiView.view as RecyclerView
override fun render(): Observable<SessionControlState> {
viewModel = ViewModelProviders.of(owner, SessionControlViewModel.Factory(initialState, changesObservable))
viewModel = ViewModelProviders.of(owner, SessionControlViewModel.Factory(initialState))
.get(SessionControlViewModel::class.java)
return viewModel.render(uiView)
return viewModel.render(changesObservable, uiView)
}
init {
@ -123,20 +123,18 @@ sealed class SessionControlChange : Change {
data class CollectionsChange(val collections: List<TabCollection>) : SessionControlChange()
}
class SessionControlViewModel(initialState: SessionControlState, changesObservable: Observable<SessionControlChange>) :
class SessionControlViewModel(initialState: SessionControlState) :
UIComponentViewModel<SessionControlState, SessionControlAction, SessionControlChange>(
initialState,
changesObservable,
reducer
) {
class Factory(
private val initialState: SessionControlState,
private val changesObservable: Observable<SessionControlChange>
private val initialState: SessionControlState
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
SessionControlViewModel(initialState, changesObservable) as T
SessionControlViewModel(initialState) as T
}
companion object {

View File

@ -39,8 +39,8 @@ class BookmarkComponent(
override fun render(): Observable<BookmarkState> {
return ViewModelProvider(
owner,
BookmarkViewModel.Factory(initialState, changesObservable)
).get(BookmarkViewModel::class.java).render(uiView)
BookmarkViewModel.Factory(initialState)
).get(BookmarkViewModel::class.java).render(changesObservable, uiView)
}
init {
@ -81,16 +81,15 @@ operator fun BookmarkNode.contains(item: BookmarkNode): Boolean {
return children?.contains(item) ?: false
}
class BookmarkViewModel(initialState: BookmarkState, changesObservable: Observable<BookmarkChange>) :
UIComponentViewModel<BookmarkState, BookmarkAction, BookmarkChange>(initialState, changesObservable, reducer) {
class BookmarkViewModel(initialState: BookmarkState) :
UIComponentViewModel<BookmarkState, BookmarkAction, BookmarkChange>(initialState, reducer) {
class Factory(
private val initialState: BookmarkState,
private val changesObservable: Observable<BookmarkChange>
private val initialState: BookmarkState
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
BookmarkViewModel(initialState, changesObservable) as T
BookmarkViewModel(initialState) as T
}
companion object {

View File

@ -35,8 +35,8 @@ class SignInComponent(
override fun render(): Observable<SignInState> =
ViewModelProvider(
owner,
SignInViewModel.Factory(initialState, changesObservable)
).get(SignInViewModel::class.java).render(uiView)
SignInViewModel.Factory(initialState)
).get(SignInViewModel::class.java).render(changesObservable, uiView)
init {
render()
@ -54,18 +54,17 @@ sealed class SignInChange : Change {
object SignedOut : SignInChange()
}
class SignInViewModel(initialState: SignInState, changesObservable: Observable<SignInChange>) :
class SignInViewModel(initialState: SignInState) :
UIComponentViewModel<SignInState, SignInAction, SignInChange>(
initialState, changesObservable, reducer
initialState, reducer
) {
class Factory(
private val initialState: SignInState,
private val changesObservable: Observable<SignInChange>
private val initialState: SignInState
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
SignInViewModel(initialState, changesObservable) as T
SignInViewModel(initialState) as T
}
companion object {

View File

@ -36,8 +36,8 @@ class HistoryComponent(
override fun render(): Observable<HistoryState> =
ViewModelProvider(
owner,
HistoryViewModel.Factory(initialState, changesObservable)
).get(HistoryViewModel::class.java).render(uiView)
HistoryViewModel.Factory(initialState)
).get(HistoryViewModel::class.java).render(changesObservable, uiView)
init {
render()
@ -74,16 +74,15 @@ sealed class HistoryChange : Change {
data class RemoveItemForRemoval(val item: HistoryItem) : HistoryChange()
}
class HistoryViewModel(initialState: HistoryState, changesObservable: Observable<HistoryChange>) :
UIComponentViewModel<HistoryState, HistoryAction, HistoryChange>(initialState, changesObservable, reducer) {
class HistoryViewModel(initialState: HistoryState) :
UIComponentViewModel<HistoryState, HistoryAction, HistoryChange>(initialState, reducer) {
class Factory(
private val initialState: HistoryState,
private val changesObservable: Observable<HistoryChange>
private val initialState: HistoryState
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
HistoryViewModel(initialState, changesObservable) as T
HistoryViewModel(initialState) as T
}
companion object {

View File

@ -39,8 +39,8 @@ class QuickActionComponent(
override fun render(): Observable<QuickActionState> =
ViewModelProvider(
owner,
QuickActionViewModel.Factory(initialState, changesObservable)
).get(QuickActionViewModel::class.java).render(uiView)
QuickActionViewModel.Factory(initialState)
).get(QuickActionViewModel::class.java).render(changesObservable, uiView)
init {
render()
@ -71,20 +71,18 @@ sealed class QuickActionChange : Change {
object BounceNeededChange : QuickActionChange()
}
class QuickActionViewModel(initialState: QuickActionState, changesObservable: Observable<QuickActionChange>) :
class QuickActionViewModel(initialState: QuickActionState) :
UIComponentViewModel<QuickActionState, QuickActionAction, QuickActionChange>(
initialState,
changesObservable,
reducer
) {
class Factory(
private val initialState: QuickActionState,
private val changesObservable: Observable<QuickActionChange>
private val initialState: QuickActionState
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
QuickActionViewModel(initialState, changesObservable) as T
QuickActionViewModel(initialState) as T
}
companion object {

View File

@ -51,28 +51,26 @@ class AwesomeBarComponent(
override fun initView() = AwesomeBarUIView(container, actionEmitter, changesObservable)
override fun render(): Observable<AwesomeBarState> =
ViewModelProviders.of(owner, AwesomeBarViewModel.Factory(initialState, changesObservable))
.get(AwesomeBarViewModel::class.java).render(uiView)
ViewModelProviders.of(owner, AwesomeBarViewModel.Factory(initialState))
.get(AwesomeBarViewModel::class.java).render(changesObservable, uiView)
init {
render()
}
}
class AwesomeBarViewModel(initialState: AwesomeBarState, changesObservable: Observable<AwesomeBarChange>) :
class AwesomeBarViewModel(initialState: AwesomeBarState) :
UIComponentViewModel<AwesomeBarState, AwesomeBarAction, AwesomeBarChange>(
initialState,
changesObservable,
reducer
) {
class Factory(
private val initialState: AwesomeBarState,
private val changesObservable: Observable<AwesomeBarChange>
private val initialState: AwesomeBarState
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
AwesomeBarViewModel(initialState, changesObservable) as T
AwesomeBarViewModel(initialState) as T
}
companion object {

View File

@ -40,9 +40,9 @@ class QuickSettingsComponent(
}
override fun render(): Observable<QuickSettingsState> =
ViewModelProvider(owner, QuickSettingsViewModel.Factory(initialState, changesObservable)).get(
ViewModelProvider(owner, QuickSettingsViewModel.Factory(initialState)).get(
QuickSettingsViewModel::class.java
).render(uiView)
).render(changesObservable, uiView)
init {
render()
@ -120,20 +120,18 @@ sealed class QuickSettingsChange : Change {
data class Stored(val phoneFeature: PhoneFeature, val sitePermissions: SitePermissions?) : QuickSettingsChange()
}
class QuickSettingsViewModel(initialState: QuickSettingsState, changesObservable: Observable<QuickSettingsChange>) :
class QuickSettingsViewModel(initialState: QuickSettingsState) :
UIComponentViewModel<QuickSettingsState, QuickSettingsAction, QuickSettingsChange>(
initialState,
changesObservable,
reducer
) {
class Factory(
private val initialState: QuickSettingsState,
private val changesObservable: Observable<QuickSettingsChange>
private val initialState: QuickSettingsState
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
QuickSettingsViewModel(initialState, changesObservable) as T
QuickSettingsViewModel(initialState) as T
}
companion object {

View File

@ -28,32 +28,33 @@ abstract class UIComponent<S : ViewState, A : Action, C : Change>(
}
open class UIComponentViewModel<S : ViewState, A : Action, C : Change>(
private val initialState: S,
val changesObservable: Observable<C>,
reducer: Reducer<S, C>
initialState: S,
private val reducer: Reducer<S, C>
) : ViewModel() {
private val statesObservable: Observable<S> = internalRender(reducer)
private var currentState: S = initialState
private var statesDisposable: Disposable? = null
/**
* Render the ViewState to the View through the Reducer
*/
fun render(uiView: UIView<S, A, C>): Observable<S> {
fun render(changesObservable: Observable<C>, uiView: UIView<S, A, C>): Observable<S> {
val statesObservable = internalRender(changesObservable, reducer)
statesDisposable = statesObservable
.subscribe(uiView.updateView())
return statesObservable
}
@Suppress("MemberVisibilityCanBePrivate")
protected fun internalRender(reducer: Reducer<S, C>): Observable<S> =
protected fun internalRender(changesObservable: Observable<C>, reducer: Reducer<S, C>): Observable<S> =
changesObservable
.scan(initialState, reducer)
.scan(currentState, reducer)
.distinctUntilChanged()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.replay(1)
.autoConnect(0)
.doOnNext { currentState = it }
override fun onCleared() {
super.onCleared()