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

Patch 2 #2

Open
wants to merge 5 commits into
base: master
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
35 changes: 32 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,21 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

androidExtensions {
experimental = true // Enabled for @Parcelize
}

dependencies {
// Arrow
compile "io.arrow-kt:arrow-core:0.8.2"

// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"

Expand All @@ -39,16 +47,33 @@ dependencies {
debugImplementation "com.squareup.leakcanary:leakcanary-support-fragment:1.6.3"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:1.6.3"

// Mobius
implementation "com.spotify.mobius:mobius-core:1.2.0"
implementation "com.spotify.mobius:mobius-android:1.2.0"
implementation "com.spotify.mobius:mobius-rx2:1.2.0"
implementation "com.spotify.mobius:mobius-extras:1.2.0"

// Picasso
implementation 'com.squareup.picasso:picasso:2.71828'

// OkHttp
implementation "com.squareup.okhttp3:logging-interceptor:3.14.1"

// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.5.0"
implementation "com.squareup.retrofit2:converter-moshi:2.5.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.5.0"

// Rx
implementation "io.reactivex.rxjava2:rxjava:2.2.5"
implementation "io.reactivex.rxjava2:rxandroid:2.1.0"
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
implementation "io.reactivex.rxjava2:rxkotlin:2.3.0"

// Support libraries
implementation "com.android.support:appcompat-v7:28.0.0"
implementation "com.android.support.constraint:constraint-layout:1.1.3"
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation "com.android.support:design:28.0.0"
implementation "com.android.support:recyclerview-v7:28.0.0"

// Timber
implementation "com.jakewharton.timber:timber:4.7.1"
Expand All @@ -57,8 +82,12 @@ dependencies {
testImplementation "junit:junit:4.12"
testImplementation "com.google.truth:truth:0.42"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
testImplementation "com.spotify.mobius:mobius-test:1.2.0"

// Android testing
androidTestImplementation "com.android.support.test:runner:1.0.2"
androidTestImplementation "com.android.support.test.espresso:espresso-core:3.0.2"

//picaso
implementation 'com.squareup.picasso:picasso:2.5.2'
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,100 @@
package io.redgreen.benchpress.architecture

import android.os.Bundle
import android.os.Parcelable
import android.support.annotation.LayoutRes
import android.support.v7.app.AppCompatActivity
import com.spotify.mobius.Connectable
import com.spotify.mobius.Connection
import com.spotify.mobius.MobiusLoop
import com.spotify.mobius.Next
import com.spotify.mobius.android.MobiusAndroid
import com.spotify.mobius.extras.Connectables
import com.spotify.mobius.functions.Consumer
import com.spotify.mobius.functions.Function
import com.spotify.mobius.rx2.RxMobius
import io.reactivex.ObservableTransformer
import kotlin.LazyThreadSafetyMode.NONE

abstract class BaseActivity : AppCompatActivity()
abstract class BaseActivity<M : Parcelable, E, F> : AppCompatActivity(), Connectable<M, E> {
companion object {
private const val KEY_MODEL = "model"
}

private lateinit var controller: MobiusLoop.Controller<M, E>

private val loop by lazy(NONE) {
RxMobius
.loop(
{ model: M, event: E -> updateFunction(model, event) },
{ effects -> effects.compose(effectHandler()) }
)
.eventSource(eventSource)
}

protected val eventSource by lazy(NONE) {
DeferredEventSource<E>()
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layoutResId())
controller = MobiusAndroid.controller(loop, resolveDefaultModel(savedInstanceState))
controller.connect(Connectables.contramap(identity(), this))
setup()
}

override fun onResume() {
super.onResume()
controller.start()
}

override fun onStop() {
controller.stop()
super.onStop()
}

override fun onDestroy() {
controller.disconnect()
super.onDestroy()
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(KEY_MODEL, controller.model)
}

override fun connect(output: Consumer<E>): Connection<M> {
return object : Connection<M> {
override fun accept(value: M) {
render(value)
}

override fun dispose() {
/* no-op, nothing to dispose */ // TODO(rj) dispose required?
}
}
}

private fun identity(): Function<M, M> =
Function { it }

private fun resolveDefaultModel(savedInstanceState: Bundle?): M =
savedInstanceState?.getParcelable(KEY_MODEL) ?: initialModel()

@LayoutRes
abstract fun layoutResId(): Int

abstract fun setup()

abstract fun initialModel(): M

abstract fun updateFunction(
model: M,
event: E
): Next<M, F>

abstract fun render(model: M)

abstract fun effectHandler(): ObservableTransformer<F, E>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.redgreen.benchpress.architecture

import com.spotify.mobius.EventSource
import com.spotify.mobius.disposables.Disposable
import com.spotify.mobius.functions.Consumer
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.atomic.AtomicBoolean

class DeferredEventSource<E> : EventSource<E> {
private val events = LinkedBlockingQueue<E>()

override fun subscribe(eventConsumer: Consumer<E>): Disposable {
val run = AtomicBoolean(true)
val thread = Thread {
while (run.get()) {
try {
val event = events.take()
if (run.get()) {
eventConsumer.accept(event)
}
} catch (e: Throwable) {
// TODO(rj) 8/Jan/19 - Log this exception.
}
}
}
thread.start()
return Disposable {
run.set(false)
thread.interrupt()
}
}

@Synchronized
fun notifyEvent(e: E) {
events.offer(e)
}
}
4 changes: 2 additions & 2 deletions app/src/main/java/io/redgreen/benchpress/bmi/BmiActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package io.redgreen.benchpress.bmi
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import io.redgreen.benchpress.R
import io.redgreen.benchpress.architecture.BaseActivity
import kotlinx.android.synthetic.main.bmi_activity.*

class BmiActivity : BaseActivity() {
class BmiActivity :AppCompatActivity() {
companion object {
fun start(context: Context) {
context.startActivity(Intent(context, BmiActivity::class.java))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,55 @@ package io.redgreen.benchpress.counter

import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.spotify.mobius.Next
import io.reactivex.ObservableTransformer
import io.redgreen.benchpress.R
import io.redgreen.benchpress.architecture.BaseActivity
import kotlinx.android.synthetic.main.counter_activity.*

class CounterActivity : BaseActivity() {
class CounterActivity : BaseActivity<ModelCounter, CounterEvent, CounterEffect>(), Interactor {
companion object {
fun start(context: Context) {
context.startActivity(Intent(context, CounterActivity::class.java))
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.counter_activity)
deleteMe()
override fun layoutResId(): Int {
return R.layout.counter_activity
}

private fun deleteMe() {
counterTextView.text = 0.toString()
override fun setup() {
incrementButton.setOnClickListener { eventSource.notifyEvent(IncrementCounterEvent) }
decrementButton.setOnClickListener { eventSource.notifyEvent(DecrementCounterEvent) }
}

override fun initialModel():ModelCounter {
return ModelCounter.ZERO
}

override fun updateFunction(
model: ModelCounter,
event: CounterEvent
): Next<ModelCounter,CounterEffect> {
return CounterLogic.update(model, event)
}

override fun render(model: ModelCounter) {
counterTextView.text = model.counter.toString()
}

override fun effectHandler(): ObservableTransformer<CounterEffect, CounterEvent> {
return CounterEffectHandler().createEffectHandler(
interactor = this@CounterActivity
)
}

override fun showError() {
//show toast
}
}


interface Interactor {
fun showError()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.redgreen.benchpress.counter

sealed class CounterEffect

object ShowErrorEffect : CounterEffect()
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.redgreen.benchpress.counter


import io.reactivex.Observable
import io.reactivex.ObservableSource
import io.reactivex.ObservableTransformer

class CounterEffectHandler<E> : ObservableTransformer<CounterEffect, E> {

fun createEffectHandler(
interact: Interactor
) :ObservableTransformer<CounterEffect, CounterEvent> {

return RxMobius.subTypeEffectHandler<CounterEffect, CounterEvent>()
.addConsumer(
ShowErrorEffect::class.java,
interact.showError(),
AndroidSchedulers.mainThread()
).build()

}


}

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.redgreen.benchpress.counter

sealed class CounterEvent

object IncrementCounterEvent : CounterEvent()
object DecrementCounterEvent : CounterEvent()
27 changes: 27 additions & 0 deletions app/src/main/java/io/redgreen/benchpress/counter/CounterLogic.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.redgreen.benchpress.counter

import com.spotify.mobius.Effects.effects
import com.spotify.mobius.Next
import com.spotify.mobius.Next.*
import com.spotify.mobius.Update

object CounterLogic : Update<ModelCounter, CounterEvent, CounterEffect> {
override fun update(
model: ModelCounter,
event: CounterEvent
): Next<ModelCounter, CounterEffect> {
return if (event is IncrementCounterEvent) {
next(model.increment())
} else if (event is DecrementCounterEvent) {
if (model.counter <= 0) {
next(
setOf(ShowErrorEffect as CounterEffect)
)
} else {
next(model.decrement())
}
} else {
noChange()
}
}
}
18 changes: 18 additions & 0 deletions app/src/main/java/io/redgreen/benchpress/counter/modelCounter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.redgreen.benchpress.counter
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

@Parcelize
data class ModelCounter(
val counter: Int
) : Parcelable {

companion object {
val ZERO = ModelCounter(0)
}

fun increment(): ModelCounter = copy(counter = counter + 1)

fun decrement(): ModelCounter = copy(counter = if (counter == 0) 0 else counter - 1)

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package io.redgreen.benchpress.hellostranger
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import io.redgreen.benchpress.R
import io.redgreen.benchpress.architecture.BaseActivity

class HelloStrangerActivity : BaseActivity() {
class HelloStrangerActivity : AppCompatActivity() {
companion object {
fun start(context: Context) {
context.startActivity(Intent(context, HelloStrangerActivity::class.java))
Expand Down
Loading