├── .bazelrc ├── .github └── workflows │ └── android.yml ├── .gitignore ├── BUILD.bazel ├── CHANGELOG.md ├── README.md ├── RELEASING.md ├── WORKSPACE ├── app ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── stepango │ │ └── example │ │ └── MainActivity.kt │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib ├── .gitignore ├── BUILD.bazel ├── VERSION ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── stepango │ │ └── rxdatabindings │ │ ├── DataBindingScheduler.kt │ │ ├── DatabindingsExt.kt │ │ ├── FlowableExt.kt │ │ ├── MaybeExt.kt │ │ ├── ObservableExt.kt │ │ ├── ObservableNumber.kt │ │ ├── ObservableSpanned.kt │ │ ├── ObservableString.kt │ │ └── SingleExt.kt │ └── test │ └── java │ └── com │ └── stepango │ └── test │ ├── NullStringGetter.java │ └── ObservableStringTest.kt └── settings.gradle /.bazelrc: -------------------------------------------------------------------------------- 1 | build --disk_cache=./bazel-cache -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: set up JDK 1.8 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 1.8 16 | - name: Build with Gradle 17 | run: ./gradlew build test 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | *.patch 11 | /bazel-* 12 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stepango/RxDatabindings/538909f9ff936948f6f1a390a52241fd83379939/BUILD.bazel -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | Version 2.0.0 *(2019-09-23)* 5 | 6 | * AndroidX Migration 7 | * Default scheduler is now IO 8 | * Bintray publishing setup 9 | 10 | Version 1.9.0 *()* 11 | 12 | * Explicit return types 13 | 14 | Version 1.8.0 *(2019-06-19)* 15 | 16 | * Default scheduler in now Computation 17 | * safeObserve extension added 18 | 19 | Version 1.7.0 *(2019-06-14)* 20 | 21 | * Specialised extensions for ObservableString, ObservableSpanned, ObservableNumber 22 | * ObservableSpannableString removed 23 | * Dependencies updated 24 | 25 | Version 1.6.0 *(2018-12-06)* 26 | 27 | * Added ObservableSpanned 28 | * ObservableSpannableString - deprecated 29 | 30 | Version 1.5.0 *(2018-10-26)* 31 | 32 | * Added ObservableSpannableString 33 | 34 | Version 1.4.0 *(2018-07-18)* 35 | ---------------------------- 36 | 37 | * Make ObservableString setter null safe 38 | 39 | Version 1.3.0 *(2018-07-13)* 40 | ---------------------------- 41 | 42 | * Removed kOptional 43 | 44 | Version 1.1.1 *(2017-06-01)* 45 | ---------------------------- 46 | 47 | * Fixed kotlin parcelable serialization for ObservableString 48 | * rxJava updated to 2.1.0 49 | * Kotlin updated to 1.1.2-3 50 | * Example updated for orientation changes support 51 | * Gradle updated to v3.3 52 | 53 | Version 1.1.0 *(2017-04-24)* 54 | ---------------------------- 55 | 56 | * `StringObservable` parcelable implementation now compatible with Kotlin 1.1 57 | * `fireInitialValue` flag added 58 | * `invoke` methods removed, breaking change 59 | 60 | Version 1.0.0 *(2017-03-16)* 61 | ---------------------------- 62 | 63 | * `Kotlin` updated to v1.1.1 64 | * `rxJava` updated to v2.0.7 65 | * `Flowable` support added 66 | * `Single` support added 67 | * `Maybe` support added 68 | * `Default` scheduler support added 69 | 70 | Version 0.6.0 *(2017-02-15)* 71 | ---------------------------- 72 | 73 | * `ObservableString` implements Parcelable 74 | * `observeOption` is now `observeOptional` 75 | 76 | Version 0.5.0 *(2017-01-13)* 77 | ---------------------------- 78 | 79 | * `safeSetTo` methods added 80 | * documentation added 81 | * `rxJava` updated to 2.0.4 82 | 83 | Version 0.4.0 *(2016-12-29)* 84 | ---------------------------- 85 | 86 | * setTo(Observable*, transformer) methods added 87 | * `kOptional` updated to v1.1.0 88 | * `Kotlin` updated to v1.0.6 89 | 90 | Version 0.3.0 *(2016-12-21)* 91 | ---------------------------- 92 | 93 | * setTo(Observable*) methods added -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxDatabindings 2 | [![Download](https://api.bintray.com/packages/step-89-g/stepango/rxDataBindings/images/download.svg) ](https://bintray.com/step-89-g/stepango/rxDataBindings/_latestVersion) 3 | ![Android CI](https://github.com/stepango/RxDatabindings/workflows/Android%20CI/badge.svg?branch=master) 4 | 5 | RxJava 2 & 3 extensions for Android Databindings library 6 | 7 | Add RxDataBindings to your build.gradle 8 | ```groovy 9 | implementation "com.stepango.rxdatabindings:rxdatabindings:3.0.0" 10 | ``` 11 | 12 | Basic example of usage: 13 | ```kotlin 14 | interface ViewModelState : Parcelable { 15 | val text: ObservableString 16 | val counter: ObservableInt 17 | } 18 | 19 | @Parcelize 20 | data class ViewModelStateImpl( 21 | override val text: ObservableString = ObservableString(), 22 | override val counter: ObservableInt = ObservableInt() 23 | ) : ViewModelState 24 | 25 | class ViewModel(val state: ViewModelState) : ViewModelState by state { 26 | 27 | init { 28 | counter.observe() 29 | .setTo(text) { "Wow! You count till $it" } 30 | .doOnNext { Log.d("THREAD", Thread.currentThread().name) } 31 | .subscribe() 32 | } 33 | 34 | fun incCounter() = counter.inc(10) 35 | fun decCounter() = counter.dec(0) 36 | 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | ``` 2 | cd lib 3 | ../gradlew clean build generateRelease 4 | ``` 5 | 6 | Then zip/compress the `com` folder and upload to jcenter. 7 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | 3 | RULES_JVM_EXTERNAL_TAG = "3.0" 4 | RULES_JVM_EXTERNAL_SHA = "62133c125bf4109dfd9d2af64830208356ce4ef8b165a6ef15bbff7460b35c3a" 5 | 6 | http_archive( 7 | name = "rules_jvm_external", 8 | strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG, 9 | sha256 = RULES_JVM_EXTERNAL_SHA, 10 | url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG, 11 | ) 12 | 13 | load("@rules_jvm_external//:defs.bzl", "maven_install") 14 | 15 | android_plugin_version = "3.6.1" 16 | rx_java_version = "3.0.0" 17 | junit_version = "4.12" 18 | 19 | maven_install( 20 | artifacts = [ 21 | "io.reactivex.rxjava3:rxjava:" + rx_java_version, 22 | "androidx.databinding:databinding-common:" + android_plugin_version, 23 | "androidx.databinding:databinding-runtime:" + android_plugin_version , 24 | "junit:junit:" + junit_version, 25 | ], 26 | repositories = [ 27 | "https://jcenter.bintray.com/", 28 | "https://maven.google.com", 29 | "https://repo1.maven.org/maven2", 30 | ], 31 | ) 32 | 33 | # Configure Android SDK Path 34 | 35 | http_archive( 36 | name = "build_bazel_rules_android", 37 | urls = ["https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"], 38 | sha256 = "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", 39 | strip_prefix = "rules_android-0.1.1", 40 | ) 41 | 42 | load("@build_bazel_rules_android//android:rules.bzl", "android_sdk_repository") 43 | 44 | android_sdk_repository( 45 | name = "androidsdk", 46 | ) 47 | 48 | # Kotlin rules 49 | 50 | rules_kotlin_version = "legacy-1.3.0" 51 | rules_kotlin_sha = "4fd769fb0db5d3c6240df8a9500515775101964eebdf85a3f9f0511130885fde" 52 | http_archive( 53 | name = "io_bazel_rules_kotlin", 54 | urls = ["https://github.com/cgruber/rules_kotlin/archive/%s.zip" % rules_kotlin_version], 55 | type = "zip", 56 | strip_prefix = "rules_kotlin-%s" % rules_kotlin_version, 57 | sha256 = rules_kotlin_sha, 58 | ) 59 | 60 | load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kotlin_repositories", "kt_register_toolchains") 61 | kotlin_repositories() # if you want the default. Otherwise see custom kotlinc distribution below 62 | kt_register_toolchains() # to use the default toolchain, otherwise see toolchains below -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-android-extensions' 5 | id 'kotlin-kapt' 6 | } 7 | 8 | android { 9 | 10 | compileSdkVersion 29 11 | 12 | defaultConfig { 13 | applicationId "com.stepango.example" 14 | 15 | minSdkVersion 16 16 | targetSdkVersion 29 17 | 18 | versionCode 1 19 | versionName "1.0" 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android.txt') 26 | } 27 | } 28 | 29 | dataBinding.enabled = true 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | } 37 | 38 | androidExtensions { 39 | features = ["parcelize"] 40 | } 41 | 42 | dependencies { 43 | implementation project(':lib') 44 | 45 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 46 | implementation "io.reactivex.rxjava3:rxjava:$rxJava_version" 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/stepango/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.stepango.example 2 | 3 | import android.app.Activity 4 | import android.os.Bundle 5 | import android.os.Parcelable 6 | import android.text.SpannableString 7 | import android.text.style.AbsoluteSizeSpan 8 | import android.util.Log 9 | import androidx.databinding.DataBindingUtil 10 | import androidx.databinding.ObservableInt 11 | import com.stepango.example.databinding.ActivityMainBinding 12 | import com.stepango.rxdatabindings.ObservableSpanned 13 | import com.stepango.rxdatabindings.ObservableString 14 | import com.stepango.rxdatabindings.observe 15 | import com.stepango.rxdatabindings.setTo 16 | import kotlinx.android.parcel.Parcelize 17 | import kotlin.properties.Delegates 18 | 19 | 20 | class MainActivity : Activity() { 21 | 22 | private var viewModel by Delegates.notNull() 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | val binding = DataBindingUtil.setContentView(this, R.layout.activity_main) 27 | viewModel = ViewModel(savedInstanceState.extract { ViewModelStateImpl() }) 28 | binding.viewModel = viewModel 29 | } 30 | 31 | override fun onSaveInstanceState(outState: Bundle?) { 32 | super.onSaveInstanceState(outState) 33 | outState?.putState(viewModel.state) 34 | } 35 | 36 | } 37 | 38 | interface ViewModelState : Parcelable { 39 | val text: ObservableString 40 | val counter: ObservableInt 41 | val styledText: ObservableSpanned 42 | } 43 | 44 | @Parcelize 45 | data class ViewModelStateImpl( 46 | override val text: ObservableString = ObservableString(), 47 | override val counter: ObservableInt = ObservableInt(), 48 | override val styledText: ObservableSpanned = ObservableSpanned() 49 | ) : ViewModelState 50 | 51 | @Parcelize 52 | class ViewModel(val state: ViewModelState) : ViewModelState by state { 53 | 54 | init { 55 | counter.observe(fireInitialValue = true) 56 | .setTo(text) { "Wow! You counted till $it" } 57 | .setTo(styledText) { 58 | SpannableString("Now that's... awesome!").apply { 59 | setSpan(AbsoluteSizeSpan(it, true), 14, 22, 0) 60 | } 61 | } 62 | .doOnNext { Log.d("THREAD", Thread.currentThread().name) } 63 | .subscribe() 64 | } 65 | 66 | fun incCounter() = counter.set(counter.get().inc()) 67 | fun decCounter() = counter.set(counter.get().dec()) 68 | 69 | } 70 | 71 | inline fun Bundle?.extract(defaultValueProducer: () -> T): T { 72 | this ?: return defaultValueProducer() 73 | val key = T::class.java.name 74 | return if (containsKey(key)) { 75 | Log.d("State::", "restored $key") 76 | getParcelable(key) ?: throw IllegalStateException("null Parcelable") 77 | } else { 78 | defaultValueProducer() 79 | } 80 | } 81 | 82 | fun Bundle.putState(value: T) { 83 | val name = value::class.java.name 84 | putParcelable(name, value) 85 | Log.d("State::", "saved $name") 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 25 | 26 | 30 | 31 | 34 | 35 |