├── lib
├── .gitignore
├── gradle.properties
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── org
│ │ │ └── js
│ │ │ └── cycle
│ │ │ └── android
│ │ │ ├── ComponentProperties.java
│ │ │ ├── Component.java
│ │ │ ├── Driver.java
│ │ │ ├── Source.java
│ │ │ ├── HttpDriver.java
│ │ │ ├── Sink.java
│ │ │ ├── ObservableOnValueChangeListener.java
│ │ │ ├── DomSink.java
│ │ │ ├── ObservableSingleTapGestureDetector.java
│ │ │ ├── HttpSource.java
│ │ │ ├── Sinks.java
│ │ │ ├── HttpSink.java
│ │ │ ├── PropsSource.java
│ │ │ ├── Event.java
│ │ │ ├── DomSelection.java
│ │ │ ├── Sources.java
│ │ │ ├── ObservableTextWatcher.java
│ │ │ ├── DomSource.java
│ │ │ ├── ClickEventInterceptingLayout.java
│ │ │ ├── Cycle.java
│ │ │ ├── Util.java
│ │ │ └── DomDriver.java
│ └── test
│ │ └── java
│ │ └── org
│ │ └── js
│ │ └── cycle
│ │ └── android
│ │ ├── ObservableTextWatcherTest.java
│ │ ├── UtilTest.java
│ │ ├── DomSourceTest.java
│ │ └── CycleTest.java
├── proguard-rules.pro
├── build.gradle
└── gradle-maven-push.gradle
├── sample
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── 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
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ ├── dimens.xml
│ │ │ └── styles.xml
│ │ ├── values-v21
│ │ │ └── styles.xml
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ ├── layout
│ │ │ ├── activity_sample.xml
│ │ │ ├── layout_labeled_slider.xml
│ │ │ ├── vtree_helloworld.xml
│ │ │ ├── vtree_counter.xml
│ │ │ ├── vtree_search_github.xml
│ │ │ ├── activity_bmi.xml
│ │ │ ├── drawer_header.xml
│ │ │ └── content_sample.xml
│ │ └── menu
│ │ │ └── drawer.xml
│ │ ├── java
│ │ └── org
│ │ │ └── js
│ │ │ └── cycle
│ │ │ └── android
│ │ │ └── sample
│ │ │ ├── GithubService.java
│ │ │ ├── SearchResponse.java
│ │ │ ├── SampleApplication.java
│ │ │ ├── HelloWorldActivity.java
│ │ │ ├── LabeledSlider.java
│ │ │ ├── CounterActivity.java
│ │ │ ├── BmiComponent.java
│ │ │ ├── GithubSearchActivity.java
│ │ │ ├── BmiActivity.java
│ │ │ └── SampleActivity.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── CHANGELOG.md
├── .gitignore
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .buildscript
└── deploy_snapshot.sh
├── gradle.properties
├── .travis.yml
├── gradlew.bat
├── README.md
└── gradlew
/lib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':sample', ':lib'
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 0.2.0 (06/21/2016)
2 |
3 | * Initial release
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea
5 | .DS_Store
6 | /build
7 | /captures
--------------------------------------------------------------------------------
/lib/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Cycle.js port for Android
2 | POM_ARTIFACT_ID=cycle-android
3 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs-community/cycle-android/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/ComponentProperties.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | public interface ComponentProperties {
4 | }
5 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs-community/cycle-android/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs-community/cycle-android/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs-community/cycle-android/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/Component.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | public interface Component {
4 | Sinks create(Sources sources);
5 | }
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs-community/cycle-android/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs-community/cycle-android/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/Driver.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import rx.Observable;
4 |
5 | public interface Driver {
6 | void apply(Observable> stream);
7 | }
8 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/Source.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import rx.Observable;
4 |
5 | public interface Source {
6 | String name();
7 | void apply(Observable> stream);
8 | }
9 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Aug 29 23:56:17 PDT 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CycleAndroid
3 | Settings
4 | Open
5 | Close
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/java/org/js/cycle/android/sample/GithubService.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android.sample;
2 |
3 | import retrofit2.Response;
4 | import retrofit2.http.GET;
5 | import retrofit2.http.Query;
6 | import rx.Observable;
7 |
8 | interface GithubService {
9 | @GET("search/repositories") Observable> search(@Query("q") String query);
10 | }
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/java/org/js/cycle/android/sample/SearchResponse.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android.sample;
2 |
3 | import java.util.Collections;
4 | import java.util.List;
5 |
6 | public class SearchResponse {
7 | int total_count;
8 | boolean incomplete_results;
9 | List items = Collections.emptyList();
10 |
11 | static class SearchResponseItem {
12 | long id;
13 | String name;
14 | String full_name;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/HttpDriver.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import rx.Observable;
4 |
5 | @SuppressWarnings("rawtypes")
6 | public final class HttpDriver implements Driver {
7 | private Observable> sink;
8 |
9 | private HttpDriver() {
10 | }
11 |
12 | public static HttpDriver makeHttpDriver() {
13 | return new HttpDriver();
14 | }
15 |
16 | @Override public void apply(Observable> sink) {
17 | this.sink = sink;
18 | }
19 |
20 | public Observable> sink() {
21 | return sink;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/Sink.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import rx.Observable;
4 |
5 | public interface Sink {
6 | String name();
7 | Observable stream();
8 |
9 | class Factory {
10 | public static Sink create(String name, Observable stream) {
11 | return new Sink() {
12 | @Override public String name() {
13 | return name;
14 | }
15 |
16 | @Override public Observable stream() {
17 | return stream;
18 | }
19 | };
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/layout_labeled_slider.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/drawer.xml:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/ObservableOnValueChangeListener.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import android.widget.NumberPicker;
4 |
5 | import rx.Observable;
6 | import rx.subjects.PublishSubject;
7 |
8 | final class ObservableOnValueChangeListener implements NumberPicker.OnValueChangeListener {
9 | private final PublishSubject observable = PublishSubject.create();
10 |
11 | @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
12 | observable.onNext(new Event("change", picker));
13 | }
14 |
15 | public Observable observable() {
16 | return observable;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/DomSink.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import rx.Observable;
4 | import trikita.anvil.Anvil;
5 |
6 | public final class DomSink implements Sink {
7 | private final Observable vtree;
8 |
9 | private DomSink(Observable vtree) {
10 | this.vtree = vtree;
11 | }
12 |
13 | public static DomSink create(Observable vtree) {
14 | return new DomSink(vtree);
15 | }
16 |
17 | @Override public String name() {
18 | return "DOM";
19 | }
20 |
21 | @Override public Observable stream() {
22 | return vtree;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/ObservableSingleTapGestureDetector.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import android.view.GestureDetector;
4 | import android.view.MotionEvent;
5 |
6 | import rx.Observable;
7 | import rx.subjects.PublishSubject;
8 |
9 | final class ObservableSingleTapGestureDetector extends GestureDetector.SimpleOnGestureListener {
10 | private final PublishSubject subject = PublishSubject.create();
11 |
12 | @Override public boolean onSingleTapUp(MotionEvent e) {
13 | subject.onNext(e);
14 | return super.onSingleTapUp(e);
15 | }
16 |
17 | Observable clickStream() {
18 | return subject;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/HttpSource.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import retrofit2.Response;
4 | import rx.Observable;
5 |
6 | /** TODO */
7 | public final class HttpSource implements Source {
8 | private final HttpDriver httpDriver;
9 |
10 | public HttpSource(HttpDriver httpDriver) {
11 | this.httpDriver = httpDriver;
12 | }
13 |
14 | public Observable>> observable() {
15 | //noinspection unchecked
16 | return (Observable>>) httpDriver.sink();
17 | }
18 |
19 | @Override public String name() {
20 | return "HTTP";
21 | }
22 |
23 | @Override public void apply(Observable> source) {
24 | httpDriver.apply(source);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/felipe_lima/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/felipe_lima/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/Sinks.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 |
6 | import rx.Observable;
7 |
8 | @SuppressWarnings("rawtypes")
9 | public final class Sinks extends ArrayList {
10 | private Sinks(Sink... sinks) {
11 | super(Arrays.asList(sinks));
12 | }
13 |
14 | public static Sinks create(Sink... sinks) {
15 | return new Sinks(sinks);
16 | }
17 |
18 | public Observable findStreamOrThrow(String name) {
19 | for (Sink sink : this) {
20 | if (sink.name().equals(name)) {
21 | //noinspection unchecked
22 | return (Observable) sink.stream();
23 | }
24 | }
25 | throw new IllegalArgumentException("Cannot find Sink with name " + name);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/vtree_helloworld.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
12 |
13 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/js/cycle/android/ObservableTextWatcherTest.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import android.widget.EditText;
4 |
5 | import org.junit.Test;
6 |
7 | import rx.observers.TestSubscriber;
8 |
9 | import static org.mockito.Mockito.mock;
10 |
11 | public class ObservableTextWatcherTest {
12 | @Test public void testObservable() {
13 | EditText editText = mock(EditText.class);
14 | ObservableTextWatcher textWatcher = ObservableTextWatcher.create(editText);
15 | TestSubscriber subscriber = new TestSubscriber<>();
16 | textWatcher.observable().subscribe(subscriber);
17 | textWatcher.onTextChanged("foo", 0, 0, 0);
18 | subscriber.assertValue(new Event("input", editText));
19 | subscriber.assertNotCompleted();
20 | subscriber.assertNoErrors();
21 | }
22 | }
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/HttpSink.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import retrofit2.Response;
4 | import rx.Observable;
5 |
6 | public final class HttpSink implements Sink>> {
7 | private final Observable>> requestStream;
8 |
9 | private HttpSink(Observable>> requestStream) {
10 | this.requestStream = requestStream;
11 | }
12 |
13 | @Override public String name() {
14 | return "HTTP";
15 | }
16 |
17 | @Override public Observable>> stream() {
18 | return requestStream;
19 | }
20 |
21 | public static HttpSink create(Observable>> requestStream) {
22 | return new HttpSink(requestStream);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/PropsSource.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import rx.Observable;
4 |
5 | public class PropsSource implements Source {
6 | public static final String NAME = "PROPS";
7 | private Observable extends ComponentProperties> propsObservable;
8 |
9 | public PropsSource(Observable extends ComponentProperties> propsObservable) {
10 | this.propsObservable = propsObservable;
11 | }
12 |
13 | public Observable extends ComponentProperties> observable() {
14 | return propsObservable;
15 | }
16 |
17 | @Override public String name() {
18 | return NAME;
19 | }
20 |
21 | @Override public void apply(Observable> stream) {
22 | //noinspection unchecked
23 | propsObservable = (Observable extends ComponentProperties>) stream;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/vtree_counter.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
12 |
13 |
20 |
21 |
26 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/Event.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import android.view.View;
4 |
5 | public final class Event {
6 | private final String name;
7 | private final View view;
8 |
9 | Event(String name, View view) {
10 | this.name = name;
11 | this.view = view;
12 | }
13 |
14 | public T view() {
15 | //noinspection unchecked
16 | return (T) view;
17 | }
18 |
19 | public String name() {
20 | return name;
21 | }
22 |
23 | @Override public boolean equals(Object o) {
24 | if (this == o) return true;
25 | if (o == null || getClass() != o.getClass()) return false;
26 |
27 | Event event = (Event) o;
28 |
29 | if (!name.equals(event.name)) return false;
30 | if (!view.equals(event.view)) return false;
31 |
32 | return true;
33 | }
34 |
35 | @Override public int hashCode() {
36 | int result = name.hashCode();
37 | result = 31 * result + view.hashCode();
38 | return result;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/DomSelection.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import android.support.annotation.IdRes;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import rx.Observable;
9 | import rx.subjects.PublishSubject;
10 |
11 | public final class DomSelection {
12 | private final List idSelectors = new ArrayList<>(1);
13 | private final PublishSubject event$ = PublishSubject.create();
14 |
15 | DomSelection(int viewId) {
16 | select(viewId);
17 | }
18 |
19 | public DomSelection select(@IdRes int viewId) {
20 | idSelectors.add(viewId);
21 | return this;
22 | }
23 |
24 | public Observable events(String eventName) {
25 | return event$.filter(e -> e.name().equals(eventName));
26 | }
27 |
28 | public Observable observable() {
29 | return event$;
30 | }
31 |
32 | void emit(Event e) {
33 | event$.onNext(e);
34 | }
35 |
36 | List idSelectors() {
37 | return idSelectors;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/vtree_search_github.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
12 |
13 |
19 |
20 |
25 |
26 |
27 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'me.tatarka.retrolambda'
3 |
4 | android {
5 | compileSdkVersion 23
6 | buildToolsVersion "23.0.2"
7 |
8 | defaultConfig {
9 | applicationId "org.js.cycle.android.sample"
10 | minSdkVersion 16
11 | targetSdkVersion 23
12 | versionCode 1
13 | versionName "1.0"
14 | }
15 | compileOptions {
16 | sourceCompatibility JavaVersion.VERSION_1_8
17 | targetCompatibility JavaVersion.VERSION_1_8
18 | }
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | lintOptions {
26 | disable 'InvalidPackage'
27 | }
28 | }
29 |
30 | dependencies {
31 | compile project(':lib')
32 | retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.3.0'
33 | compile 'com.android.support:appcompat-v7:23.3.0'
34 | compile 'com.android.support:design:23.3.0'
35 | compile 'com.squareup.okhttp3:logging-interceptor:3.3.0'
36 | }
37 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_bmi.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
27 |
28 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/Sources.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | public class Sources {
7 | private final List sources;
8 |
9 | private Sources(Source... sources) {
10 | this.sources = Arrays.asList(sources);
11 | }
12 |
13 | public static Sources create(Source... sources) {
14 | return new Sources(sources);
15 | }
16 |
17 | public DomSource dom() {
18 | return (DomSource) findSourceByName("DOM");
19 | }
20 |
21 | public HttpSource http() {
22 | return (HttpSource) findSourceByName("HTTP");
23 | }
24 |
25 | public List list() {
26 | return sources;
27 | }
28 |
29 | private Source findSourceByName(String name) {
30 | for (Source source : sources) {
31 | if (source.name().equals(name)) {
32 | return source;
33 | }
34 | }
35 | throw new IllegalArgumentException("Source not found with name=" + name);
36 | }
37 |
38 | public PropsSource props() {
39 | return (PropsSource) findSourceByName("PROPS");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/ObservableTextWatcher.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import android.text.Editable;
4 | import android.text.TextWatcher;
5 | import android.widget.EditText;
6 |
7 | import rx.Observable;
8 | import rx.subjects.PublishSubject;
9 |
10 | class ObservableTextWatcher implements TextWatcher {
11 | private final PublishSubject textChanged$ = PublishSubject.create();
12 | private final EditText editText;
13 |
14 | private ObservableTextWatcher(EditText editText) {
15 | this.editText = editText;
16 | }
17 |
18 | static ObservableTextWatcher create(EditText editText) {
19 | return new ObservableTextWatcher(editText);
20 | }
21 |
22 | @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {
23 | }
24 |
25 | @Override public void onTextChanged(CharSequence s, int start, int before, int count) {
26 | textChanged$.onNext(editText);
27 | }
28 |
29 | @Override public void afterTextChanged(Editable s) {
30 | }
31 |
32 | Observable observable() {
33 | return textChanged$.map(e -> new Event("input", editText));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/.buildscript/deploy_snapshot.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo.
4 | #
5 | # Adapted from https://coderwall.com/p/9b_lfq and
6 | # http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/
7 |
8 | SLUG="cyclejs-community/cycle-android"
9 | JDK="oraclejdk8"
10 | BRANCH="master"
11 |
12 | set -e
13 |
14 | if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then
15 | echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'."
16 | elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then
17 | echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'."
18 | elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
19 | echo "Skipping snapshot deployment: was pull request."
20 | elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then
21 | echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'."
22 | else
23 | echo "Deploying snapshot..."
24 | ./gradlew uploadArchives -PnexusUsername="${CI_DEPLOY_USERNAME}" -PnexusPassword="${CI_DEPLOY_PASSWORD}" -PCI="${CI}"
25 | echo "Snapshot deployed!"
26 | fi
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/DomSource.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import android.support.annotation.IdRes;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import rx.Observable;
9 |
10 | /** TODO */
11 | public final class DomSource implements Source {
12 | private final DomDriver domDriver;
13 | private final List selections = new ArrayList<>();
14 |
15 | public DomSource(DomDriver domDriver) {
16 | this.domDriver = domDriver;
17 | domDriver.events().subscribe(this::onDomEvent);
18 | }
19 |
20 | public DomSelection select(@IdRes int viewId) {
21 | DomSelection selection = new DomSelection(viewId);
22 | selections.add(selection);
23 | return selection;
24 | }
25 |
26 | @Override public String name() {
27 | return "DOM";
28 | }
29 |
30 | private void onDomEvent(Event event) {
31 | for (DomSelection selection : selections) {
32 | if (Util.viewMatchesSelector(event.view(), selection.idSelectors())) {
33 | selection.emit(event);
34 | }
35 | }
36 | }
37 |
38 | @Override public void apply(Observable> stream) {
39 | domDriver.apply(stream);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/drawer_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
22 |
23 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/sample/src/main/java/org/js/cycle/android/sample/SampleApplication.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android.sample;
2 |
3 | import android.app.Application;
4 |
5 | import okhttp3.OkHttpClient;
6 | import okhttp3.logging.HttpLoggingInterceptor;
7 | import retrofit2.Retrofit;
8 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
9 | import retrofit2.converter.moshi.MoshiConverterFactory;
10 |
11 | public class SampleApplication extends Application {
12 | private final HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
13 | private GithubService githubService;
14 |
15 | @Override public void onCreate() {
16 | super.onCreate();
17 | logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
18 | OkHttpClient client = new OkHttpClient.Builder()
19 | .addInterceptor(logging)
20 | .build();
21 | Retrofit retrofit = new Retrofit.Builder()
22 | .baseUrl("https://api.github.com")
23 | .client(client)
24 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
25 | .addConverterFactory(MoshiConverterFactory.create())
26 | .build();
27 | githubService = retrofit.create(GithubService.class);
28 | }
29 |
30 | public GithubService githubService() {
31 | return githubService;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/ClickEventInterceptingLayout.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.view.GestureDetector;
6 | import android.view.MotionEvent;
7 | import android.widget.FrameLayout;
8 |
9 | import rx.Observable;
10 |
11 | import static org.js.cycle.android.Util.getClickTarget;
12 |
13 | @SuppressLint("ViewConstructor")
14 | class ClickEventInterceptingLayout extends FrameLayout {
15 | private final GestureDetector gestureDetector;
16 | private final Observable click$;
17 |
18 | ClickEventInterceptingLayout(Context context) {
19 | super(context);
20 | ObservableSingleTapGestureDetector singleTapDetector = new ObservableSingleTapGestureDetector();
21 | gestureDetector = new GestureDetector(getContext(), singleTapDetector);
22 | click$ = singleTapDetector.clickStream()
23 | .map(e -> getClickTarget(this, e))
24 | .map(v -> new Event("click", v));
25 | }
26 |
27 | @Override public boolean onInterceptTouchEvent(MotionEvent ev) {
28 | gestureDetector.onTouchEvent(ev);
29 | return super.onInterceptTouchEvent(ev);
30 | }
31 |
32 | Observable clickEventsObservable() {
33 | return click$;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/sample/src/main/java/org/js/cycle/android/sample/HelloWorldActivity.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android.sample;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.widget.EditText;
6 |
7 | import org.js.cycle.android.DomSink;
8 | import org.js.cycle.android.Sinks;
9 | import org.js.cycle.android.Sources;
10 |
11 | import static trikita.anvil.BaseDSL.withId;
12 | import static trikita.anvil.BaseDSL.xml;
13 | import static trikita.anvil.DSL.text;
14 |
15 | public class HelloWorldActivity extends SampleActivity {
16 | @Override protected Sinks main(Sources sources) {
17 | DomSink domSink = DomSink.create(sources.dom()
18 | .select(R.id.editName)
19 | .events("input")
20 | .map(ev -> ev.view().getText().toString())
21 | .startWith("")
22 | .map(name -> () ->
23 | xml(R.layout.vtree_helloworld, () ->
24 | withId(R.id.txtHelloWorld, () ->
25 | text("Hello, " + name)))));
26 |
27 | return Sinks.create(domSink);
28 | }
29 |
30 | @Override protected int menuItemId() {
31 | return R.id.nav_hello_world;
32 | }
33 |
34 | static Intent newIntent(Context context) {
35 | return new Intent(context, HelloWorldActivity.class);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'me.tatarka.retrolambda'
3 | apply from: 'gradle-maven-push.gradle'
4 |
5 | android {
6 | compileSdkVersion 24
7 | buildToolsVersion "24.0.2"
8 |
9 | defaultConfig {
10 | minSdkVersion 16
11 | targetSdkVersion 24
12 | }
13 | compileOptions {
14 | sourceCompatibility JavaVersion.VERSION_1_8
15 | targetCompatibility JavaVersion.VERSION_1_8
16 | }
17 | lintOptions {
18 | disable 'InvalidPackage'
19 | }
20 | }
21 |
22 | dependencies {
23 | retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.3.0'
24 | compile 'io.reactivex:rxjava:1.1.9'
25 | compile 'com.squareup.retrofit2:retrofit:2.1.0'
26 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
27 | compile 'com.squareup.retrofit2:converter-moshi:2.1.0'
28 | compile 'io.reactivex:rxandroid:1.2.1'
29 | compile 'com.android.support:support-annotations:24.2.0'
30 | compile 'com.android.support:appcompat-v7:24.2.0'
31 | compile 'com.android.support:recyclerview-v7:24.2.0'
32 | compile 'co.trikita:anvil-sdk15:0.4.0'
33 | compile 'com.github.trikita:anvil-recyclerview:1.2'
34 |
35 | testCompile 'junit:junit:4.12'
36 | testCompile 'org.mockito:mockito-core:1.10.19'
37 | testCompile 'org.assertj:assertj-core:1.7.1'
38 | }
39 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
27 |
31 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/sample/src/main/java/org/js/cycle/android/sample/LabeledSlider.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android.sample;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.IdRes;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.widget.LinearLayout;
8 | import android.widget.NumberPicker;
9 | import android.widget.TextView;
10 |
11 | public class LabeledSlider extends LinearLayout {
12 | @IdRes static final int NUMBER_PICKER_VIEW_ID = R.id.labeled_slider_picker;
13 | private final TextView label;
14 | private final NumberPicker picker;
15 |
16 | public LabeledSlider(Context context) {
17 | this(context, null);
18 | }
19 |
20 | public LabeledSlider(Context context, AttributeSet attrs) {
21 | this(context, attrs, 0);
22 | }
23 |
24 | public LabeledSlider(Context context, AttributeSet attrs, int defStyleAttr) {
25 | super(context, attrs, defStyleAttr);
26 | LayoutInflater.from(context).inflate(R.layout.layout_labeled_slider, this);
27 | setOrientation(LinearLayout.VERTICAL);
28 | label = (TextView) findViewById(R.id.labeled_slider_label);
29 | picker = (NumberPicker) findViewById(R.id.labeled_slider_picker);
30 | }
31 |
32 | void setLabel(String newLabel) {
33 | label.setText(newLabel);
34 | // TODO: use the BmiComponent parameters to set the min/max here
35 | picker.setMinValue(0);
36 | picker.setMaxValue(200);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/sample/src/main/java/org/js/cycle/android/sample/CounterActivity.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android.sample;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import org.js.cycle.android.DomSink;
7 | import org.js.cycle.android.Sinks;
8 | import org.js.cycle.android.Sources;
9 |
10 | import rx.Observable;
11 | import trikita.anvil.Anvil;
12 |
13 | import static trikita.anvil.BaseDSL.withId;
14 | import static trikita.anvil.BaseDSL.xml;
15 | import static trikita.anvil.DSL.text;
16 |
17 | public class CounterActivity extends SampleActivity {
18 | @Override protected Sinks main(Sources sources) {
19 | Observable action$ = Observable.merge(
20 | sources.dom().select(R.id.btnIncrement).events("click").map(ev -> 1),
21 | sources.dom().select(R.id.btnDecrement).events("click").map(ev -> -1));
22 |
23 | DomSink domSink = DomSink.create(action$
24 | .startWith(0)
25 | .scan((x, y) -> x + y)
26 | .map(String::valueOf)
27 | .map(c -> (Anvil.Renderable) () ->
28 | xml(R.layout.vtree_counter, () ->
29 | withId(R.id.txtCount, () ->
30 | text(c)))));
31 |
32 | return Sinks.create(domSink);
33 | }
34 |
35 | @Override protected int menuItemId() {
36 | return R.id.nav_counter;
37 | }
38 |
39 | static Intent newIntent(Context context) {
40 | return new Intent(context, CounterActivity.class);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | org.gradle.parallel=true
19 | VERSION_CODE=1
20 | VERSION_NAME=0.3.0-SNAPSHOT
21 | GROUP=com.felipecsl
22 | POM_DESCRIPTION=A Cycle.js port for Android
23 | POM_URL=https://github.com/felipecsl/cycle-android
24 | POM_SCM_URL=https://github.com/felipecsl/cycle-android
25 | POM_SCM_CONNECTION=scm:git@github.com:felipecsl/cycle-android.git
26 | POM_SCM_DEV_CONNECTION=scm:git@github.com:felipecsl/cycle-android.git
27 | POM_LICENSE_NAME=Apache v2
28 | POM_LICENSE_URL=https://github.com/felipecsl/cycle-android/blob/master/LICENSE
29 | POM_LICENSE_DIST=repo
30 | POM_DEVELOPER_ID=felipecsl
31 | POM_DEVELOPER_NAME=felipecsl
--------------------------------------------------------------------------------
/lib/src/main/java/org/js/cycle/android/Cycle.java:
--------------------------------------------------------------------------------
1 | package org.js.cycle.android;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import rx.Observer;
7 | import rx.functions.Func1;
8 | import rx.subjects.ReplaySubject;
9 |
10 | public final class Cycle {
11 | public static void run(Func1 main, Source... sourceList) {
12 | Sources sources = Sources.create(sourceList);
13 | Map> sinkProxies = createAndApplySinkProxies(sources);
14 | Sinks sinks = main.call(sources);
15 | subscribeProxies(sinkProxies, sinks);
16 | }
17 |
18 | private static void subscribeProxies(Map> sinkProxies, Sinks sinks) {
19 | for (Sink> sink : sinks) {
20 | //noinspection rawtypes
21 | Observer proxy = sinkProxies.get(sink.name());
22 | // If there was not a Source matching the provided sink, the proxy will be null here, so we
23 | // just skip it.
24 | if (proxy != null) {
25 | //noinspection unchecked
26 | sink.stream().subscribe(proxy);
27 | }
28 | }
29 | }
30 |
31 | private static Map> createAndApplySinkProxies(Sources sources) {
32 | Map> sinkProxies = new HashMap<>();
33 | for (Source source : sources.list()) {
34 | ReplaySubject