Skip to content

Instantly share code, notes, and snippets.

@marcin-kozinski
Last active February 15, 2016 09:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcin-kozinski/8650511284841867e397 to your computer and use it in GitHub Desktop.
Save marcin-kozinski/8650511284841867e397 to your computer and use it in GitHub Desktop.
Half-baked toy Cycle.js implementation on Android, based on first 10 lessons of André Staltz's egghead.io course
import android.app.Activity
import android.os.Bundle
import android.view.View
import android.widget.TextView
import com.jakewharton.rxbinding.view.clicks
import com.jakewharton.rxbinding.widget.text
import rx.Observable
import rx.android.schedulers.AndroidSchedulers.mainThread
import rx.subjects.BehaviorSubject
import java.util.concurrent.TimeUnit.SECONDS
class CycleActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_cycle)
// Logic (functional)
fun main(source: Source) =
source.select(R.id.app, View::clicks)
.startWith(rx.Observable.just<Unit>(null))
.switchMap { rx.Observable.interval(0, 1, SECONDS) }
.map { ViewModel("Seconds elapsed\n" + it) }
// Effects (imperative)
fun androidActivityDriver(viewModelStream: Observable<ViewModel>): (Int) -> View {
(findViewById(R.id.app) as TextView).let { app ->
val textStream = viewModelStream.onBackpressureDrop()
.observeOn(mainThread())
.map { it.label }
.distinctUntilChanged()
textStream.subscribe(app.text())
}
return fun(viewId: Int) = findViewById(viewId)
}
fun <T> run(main: (Source) -> rx.Observable<T>, driver: (rx.Observable<T>) -> (Int) -> View) {
val eventualSelector = BehaviorSubject.create<(Int) -> View>()
val sink = main(ProxySource(eventualSelector.asObservable()))
val selector = driver(sink)
eventualSelector.onNext(selector)
}
run(::main, ::androidActivityDriver)
}
}
private data class ViewModel(val label: String)
private interface Source {
fun <V : View, R> select(id: Int, binding: V.() -> rx.Observable<R>): Observable<R>
}
private class ProxySource(val eventualSelector: Observable<(Int) -> View>) : Source {
override fun <V : View, R> select(id: Int, binding: V.() -> rx.Observable<R>) =
eventualSelector.map { select -> select(id) }
.switchMap { it -> @Suppress("UNCHECKED_CAST") (it as V).binding() }
}
@marcin-kozinski
Copy link
Author

It's very early, just half a Sunday of hacking, following along the lessons from https://egghead.io/series/cycle-js-fundamentals
I only got up to lesson 10, I didn't even try to port the virtual DOM approach to Android and I only have one source and sink for now, because my head hurt when I tried to think how to do it in a type-safe language. But it works! It displays the countdown and resets if you click it, just as in the course.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment