Skip to content

Commit

Permalink
Merge pull request #10 from etiennelenhart/github-readme
Browse files Browse the repository at this point in the history
GitHub Readme
  • Loading branch information
Etienne Lenhart authored Jan 26, 2018
2 parents 699b263 + fc73b56 commit 3ef712d
Show file tree
Hide file tree
Showing 16 changed files with 498 additions and 50 deletions.
416 changes: 413 additions & 3 deletions README.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions architecture_diagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
buildscript {
apply from: 'version.gradle'

ext.kotlin_version = '1.2.10'
ext.kotlin_version = '1.2.21'
repositories {
google()
jcenter()
Expand Down
4 changes: 2 additions & 2 deletions eiffel/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ android {
defaultConfig {
minSdkVersion 19
targetSdkVersion 27
buildToolsVersion "27.0.2"
buildToolsVersion "27.0.3"
versionCode versionCodeArgument()
versionName versionNameArgument()
setProperty("archivesBaseName", "eiffel-${android.defaultConfig.versionName}")
Expand All @@ -33,7 +33,7 @@ dependencies {
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

implementation "android.arch.lifecycle:extensions:1.0.0"
implementation "android.arch.lifecycle:extensions:1.1.0"
}

task sources(type: Jar) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.etiennelenhart.eiffel

/**
* Type of error that occurred while executing a command or notifying a LiveData value.
* Type of error that occurred while executing a command or before notifying a LiveData value.
*/
interface ErrorType {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.etiennelenhart.eiffel.state.ViewState
/**
* State that adapts a [ViewState] to a bindable state for data binding.
*
* Since [ViewState]s should expose view agnostic properties, any adaptions
* Since [ViewState]s should expose layout agnostic properties, any adaptions
* needed to properly display the current state can be made inside this state's
* [refresh] method.
*
Expand All @@ -14,13 +14,15 @@ import com.etiennelenhart.eiffel.state.ViewState
interface BindingState<in T : ViewState> {

/**
* Perform any adaptions needed to properly display the current state here.
* Make any adaptions needed to properly display the current state here.
* For example, when using observable fields:
*
* ```
* class SampleBindingState(val loading: ObservableBoolean = ObservableBoolean(false)) : BindingState<SampleViewState> {
* fun refresh(state: SampleViewState) {
* loading.set(state.status == Status.PENDING)
* class SampleBindingState : BindingState<SampleViewState> {
* val progressBarVisible = ObservableBoolean(false)
*
* override fun refresh(state: SampleViewState) {
* progressBarVisible.set(state.status == Status.PENDING)
* }
* }
* ```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,16 @@ class ContentViewBinding<out T : ViewDataBinding>(@LayoutRes private val layoutI
return value!!
}
}

/**
* Convenience extension function for the [ContentViewBinding] delegate.
*
* May be used in an [Activity] like this:
* ```
* val binding by contentViewBinding<ActivitySampleBinding>(R.layout.activity_sample)
* ```
*
* @param[T] Type of the provided binding.
* @param[layoutId] ID of the layout to bind and set as content view.
*/
fun <T : ViewDataBinding> Activity.contentViewBinding(@LayoutRes layoutId: Int) = ContentViewBinding<T>(layoutId)
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,19 @@ class NotifyBinding<T : Any>(private var value: T, private val fieldId: Int) : R
thisRef.notifyPropertyChanged(fieldId)
}
}

/**
* Convenience extension function for the [NotifyBinding] delegate.
*
* The delegating property should be annotated with `@get:Bindable` to generate a field in BR.
* May be used in a [BaseObservable] like this:
* ```
* @get:Bindable
* var sampleValue by notifyBinding("", BR.sampleValue)
* ```
*
* @param[T] Type of the notifying property.
* @param[value] Initial value.
* @param[fieldId] The id of the generated BR field.
*/
fun <T : Any> BaseObservable.notifyBinding(value: T, fieldId: Int) = NotifyBinding(value, fieldId)
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@ import com.etiennelenhart.eiffel.Status
* Wrapper class to associate a status to a LiveData value.
*
* @param[T] Type of the LiveData's value.
* @property[status] LiveData value's current status.
* @property[status] Current status of the LiveData value.
* @property[value] LiveData's actual value.
*/
sealed class Resource<out T>(val status: Status, val value: T) {
/**
* Resource variant signaling a successful LiveData operation.
* Resource variant signaling a successful LiveData value.
*
* @param[value] Actual value.
*/
class Success<out T>(value: T) : Resource<T>(Status.SUCCESS, value)

/**
* Resource variant signaling a pending LiveData operation.
* Resource variant signaling a pending LiveData value.
*
* @param[value] Actual value.
*/
class Pending<out T>(value: T) : Resource<T>(Status.PENDING, value)

/**
* Resource variant signaling a failed LiveData operation.
* Resource variant signaling a failed LiveData value.
*
* @param[value] Actual value.
* @param[type] Optional [ErrorType]. Defaults to [ErrorType.Unspecified].
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.etiennelenhart.eiffel.state

/**
* Base class for view model actions that require an Activity.
* Base class for one-off view model actions that require an Activity.
*
* Can be used as a property in [ViewState]s to signal an action to the observer:
*
Expand All @@ -13,7 +13,7 @@ package com.etiennelenhart.eiffel.state
*
* ```
* sealed class SampleViewEvent : ViewEvent() {
* class ActivityAction : SampleViewEvent()
* class ShowSample : SampleViewEvent()
* }
* ```
*
Expand All @@ -23,7 +23,7 @@ package com.etiennelenhart.eiffel.state
* viewModel.state.observe(this, Observer {
* if (!it.event.handled) {
* when (it.event) {
* is SampleViewEvent.ActivityAction -> {
* is SampleViewEvent.ShowSample -> {
* it.event.handled = true
* ...
* }
Expand All @@ -33,11 +33,11 @@ package com.etiennelenhart.eiffel.state
* ```
*
* @param[handled] 'true' when the event is already handled. Defaults to false.
* @property[handled] 'false' when the event has yet to be handled. Set to 'true' when event is handled.
* @property[handled] 'false' when the event has yet to be handled. Set to 'true' when event has been handled.
*/
abstract class ViewEvent(var handled: Boolean = false) {
/**
* Convenience [ViewEvent] to set as an initial event that requires no action.
* Convenience [ViewEvent] to set as an initial event that requires no handling.
*
* This event's 'handled' property is initialized to 'true'.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.etiennelenhart.eiffel.state

import com.etiennelenhart.eiffel.viewmodel.StateViewModel

/**
* Marker interface for data classes that represent a view state.
*
* Only use `val`s and immutable data structures for properties and try
* to avoid view specifics like resource IDs or data binding logic.
* Implementing classes can be used as state in [StateViewModel].
* Implementing classes can be used as a state in [StateViewModel].
*/
interface ViewState
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ abstract class StateViewModel<T : ViewState> : ViewModel() {
}

/**
* Updates the current state by applying the 'update' lambda.
* Updates the current state by applying the supplied lambda expression.
*
* May be used like this:
*
* ```
* updateState { it.copy(sample = true) }
* ```
*
* @param[update] Lambda to update the current view state.
* @param[newState] Lambda expression that should return the new updated state.
* @throws[KotlinNullPointerException] when the state's initial value has not been set.
*/
protected inline fun updateState(update: (currentState: T) -> T) {
(state as MutableLiveData).value = update(currentState)
protected inline fun updateState(newState: (currentState: T) -> T) {
(state as MutableLiveData).value = newState(currentState)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import kotlin.reflect.KProperty
*
* @param[T] Type of the provided view model.
* @param[viewModelClass] Java class of the provided view model.
* @param[factory] Block to lazily get the factory to instantiate the view model.
* @param[factory] Lambda expression to lazily get the factory to instantiate the view model.
*/
class ProvidedFactoryViewModel<out T : ViewModel>(private val viewModelClass: Class<T>,
private val factory: () -> ViewModelProvider.Factory) : ReadOnlyProperty<FragmentActivity, T> {
class ProvidedFactoryViewModel<out T : ViewModel>(
private val viewModelClass: Class<T>,
private val factory: () -> ViewModelProvider.Factory
) : ReadOnlyProperty<FragmentActivity, T> {

private var value: T? = null

Expand All @@ -36,12 +38,11 @@ class ProvidedFactoryViewModel<out T : ViewModel>(private val viewModelClass: Cl
*
* May be used in a [FragmentActivity] like this:
* ```
* val viewModel by providedViewModel<SampleViewModel>(sampleFactory)
* val viewModel by providedViewModel<SampleViewModel> { sampleFactory }
* ```
*
* @param[T] Type of the provided view model.
* @param[factory] Block to lazily get the factory to instantiate the view model.
* @param[factory] Lambda expression to lazily get the factory to instantiate the view model.
*/
inline fun <reified T : ViewModel> FragmentActivity.providedViewModel(noinline factory: () -> ViewModelProvider.Factory): ProvidedFactoryViewModel<T> {
return ProvidedFactoryViewModel(T::class.java, factory)
}
inline fun <reified T : ViewModel> FragmentActivity.providedViewModel(noinline factory: () -> ViewModelProvider.Factory) =
ProvidedFactoryViewModel(T::class.java, factory)
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import kotlin.reflect.KProperty
*
* @param[T] Type of the provided view model.
* @param[viewModelClass] Java class of the provided view model.
* @param[factory] Block to lazily get the factory to instantiate the view model.
* @param[factory] Lambda expression to lazily get the factory to instantiate the view model.
*/
class ProvidedFragmentFactoryViewModel<out T : ViewModel>(private val viewModelClass: Class<T>,
private val factory: () -> ViewModelProvider.Factory) : ReadOnlyProperty<Fragment, T> {
class ProvidedFragmentFactoryViewModel<out T : ViewModel>(
private val viewModelClass: Class<T>,
private val factory: () -> ViewModelProvider.Factory
) : ReadOnlyProperty<Fragment, T> {

private var value: T? = null

Expand All @@ -36,12 +38,11 @@ class ProvidedFragmentFactoryViewModel<out T : ViewModel>(private val viewModelC
*
* May be used in a [Fragment] like this:
* ```
* val viewModel by providedFactoryViewModel<SampleViewModel>(sampleFactory)
* val viewModel by providedFactoryViewModel<SampleViewModel> { sampleFactory }
* ```
*
* @param[T] Type of the provided view model.
* @param[factory] Block to lazily get the factory to instantiate the view model.
* @param[factory] Lambda expression to lazily get the factory to instantiate the view model.
*/
inline fun <reified T : ViewModel> Fragment.providedViewModel(noinline factory: () -> ViewModelProvider.Factory): ProvidedFragmentFactoryViewModel<T> {
return ProvidedFragmentFactoryViewModel(T::class.java, factory)
}
inline fun <reified T : ViewModel> Fragment.providedViewModel(noinline factory: () -> ViewModelProvider.Factory) =
ProvidedFragmentFactoryViewModel(T::class.java, factory)
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import kotlin.reflect.KProperty
*
* @param[T] Type of the provided view model.
* @param[viewModelClass] Java class of the provided view model.
* @param[factory] Block to lazily get the factory to instantiate the view model.
* @param[factory] Lambda expression to lazily get the factory to instantiate the view model.
*/
class SharedFactoryViewModel<out T : ViewModel>(private val viewModelClass: Class<T>,
private val factory: () -> ViewModelProvider.Factory) : ReadOnlyProperty<Fragment, T> {
class SharedFactoryViewModel<out T : ViewModel>(
private val viewModelClass: Class<T>,
private val factory: () -> ViewModelProvider.Factory
) : ReadOnlyProperty<Fragment, T> {

private var value: T? = null

Expand All @@ -36,12 +38,10 @@ class SharedFactoryViewModel<out T : ViewModel>(private val viewModelClass: Clas
*
* May be used in a [Fragment] like this:
* ```
* val viewModel by sharedViewModel<SampleViewModel>(sampleFactory)
* val viewModel by sharedViewModel<SampleViewModel> { sampleFactory }
* ```
*
* @param[T] Type of the provided view model.
* @param[factory] Block to lazily get the factory to instantiate the view model.
* @param[factory] Lambda expression to lazily get the factory to instantiate the view model.
*/
inline fun <reified T : ViewModel> Fragment.sharedViewModel(noinline factory: () -> ViewModelProvider.Factory): SharedFactoryViewModel<T> {
return SharedFactoryViewModel(T::class.java, factory)
}
inline fun <reified T : ViewModel> Fragment.sharedViewModel(noinline factory: () -> ViewModelProvider.Factory) = SharedFactoryViewModel(T::class.java, factory)
Loading

0 comments on commit 3ef712d

Please sign in to comment.