diff --git a/android-mvvm/.gitignore b/android-mvvm-conductor/.gitignore similarity index 100% rename from android-mvvm/.gitignore rename to android-mvvm-conductor/.gitignore diff --git a/android-mvvm-conductor/build.gradle b/android-mvvm-conductor/build.gradle new file mode 100644 index 0000000..1b12a2b --- /dev/null +++ b/android-mvvm-conductor/build.gradle @@ -0,0 +1,54 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +buildscript { + ext.kotlin_version = '1.1.2-2' + ext.moduleArtifact = 'conductor' + repositories { + jcenter() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.3" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + dataBinding { + enabled = true + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + provided project(path: ':android-mvvm-core') + provided project(path: ':android-mvvm-kotlin') + + testCompile 'junit:junit:4.12' + + compile 'com.bluelinelabs:conductor:2.1.2' + compile 'com.bluelinelabs:conductor-rxlifecycle2:2.1.2' + compile 'com.trello.rxlifecycle2:rxlifecycle-kotlin:2.0.1' + compile "com.android.support:support-v4:$support_library_version" + provided "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" + +} + +apply from: rootProject.file('gradle/bintray-push.gradle') \ No newline at end of file diff --git a/android-mvvm-conductor/proguard-rules.pro b/android-mvvm-conductor/proguard-rules.pro new file mode 100644 index 0000000..5f245cb --- /dev/null +++ b/android-mvvm-conductor/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/gabor/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/android-mvvm-conductor/src/main/AndroidManifest.xml b/android-mvvm-conductor/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3b5d76a --- /dev/null +++ b/android-mvvm-conductor/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/android-mvvm-conductor/src/main/java/com/manaschaudhari/android_mvvm/conductor/ConductorExtensions.kt b/android-mvvm-conductor/src/main/java/com/manaschaudhari/android_mvvm/conductor/ConductorExtensions.kt new file mode 100644 index 0000000..94cef58 --- /dev/null +++ b/android-mvvm-conductor/src/main/java/com/manaschaudhari/android_mvvm/conductor/ConductorExtensions.kt @@ -0,0 +1,43 @@ +package com.manaschaudhari.android_mvvm.conductor + +import com.bluelinelabs.conductor.rxlifecycle2.ControllerEvent +import com.trello.rxlifecycle2.LifecycleProvider +import com.trello.rxlifecycle2.kotlin.bindUntilEvent +import io.reactivex.BackpressureStrategy +import io.reactivex.Completable +import io.reactivex.Flowable +import io.reactivex.Observable + +/** + * Created by gabor on 2017. 05. 01.. + */ + +typealias ControllerLifecycleProvider = LifecycleProvider + +/** + * Convenience function for [Observable]s to bind to a controller's lifecycle. This makes it possible + * to terminate a stream when the specified [end] controller event occurs. Also, this function takes care + * of resubscribing to the Observable when the specified [start] lifecycle event occurs. + * + * @param lp The lifecycle provider instance, usually the Controller itself (in case of an [RxController]). + */ +fun Observable.attachToLifecycle(lp: ControllerLifecycleProvider, start: ControllerEvent = ControllerEvent.ATTACH, end: ControllerEvent = ControllerEvent.DETACH): Observable = lp.lifecycle() + .filter { it == start } + .switchMap { this.bindUntilEvent(lp, end) } + .bindUntilEvent(lp, ControllerEvent.DESTROY) + +/*fun Single.attachToLifecycle(lp: ControllerLifecycleProvider, start: ControllerEvent = ControllerEvent.ATTACH, end: ControllerEvent = ControllerEvent.DETACH): Single = lp.lifecycle() + .filter { it == start } + .flatMapSingle { this.bindUntilEvent(lp, end) } + .bindUntilEvent(lp, ControllerEvent.DESTROY)*/ + +fun Flowable.attachToLifecycle(lp: ControllerLifecycleProvider, start: ControllerEvent = ControllerEvent.ATTACH, end: ControllerEvent = ControllerEvent.DETACH): Flowable = lp.lifecycle() + .toFlowable(BackpressureStrategy.LATEST) + .filter { it == start } + .switchMap { this.bindUntilEvent(lp, end) } + .bindUntilEvent(lp, ControllerEvent.DESTROY) + +fun Completable.attachToLifecycle(lp: ControllerLifecycleProvider, start: ControllerEvent = ControllerEvent.ATTACH, end: ControllerEvent = ControllerEvent.DETACH): Completable = lp.lifecycle() + .filter { it == start } + .flatMapCompletable { this.bindUntilEvent(lp, end) } + .bindUntilEvent(lp, ControllerEvent.DESTROY) diff --git a/android-mvvm-conductor/src/main/java/com/manaschaudhari/android_mvvm/conductor/MvvmController.kt b/android-mvvm-conductor/src/main/java/com/manaschaudhari/android_mvvm/conductor/MvvmController.kt new file mode 100644 index 0000000..014069f --- /dev/null +++ b/android-mvvm-conductor/src/main/java/com/manaschaudhari/android_mvvm/conductor/MvvmController.kt @@ -0,0 +1,100 @@ +package com.manaschaudhari.android_mvvm.conductor + +import android.databinding.DataBindingUtil +import android.databinding.ViewDataBinding +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.bluelinelabs.conductor.Controller +import com.bluelinelabs.conductor.rxlifecycle2.RxController +import com.manaschaudhari.android_mvvm.adapters.ViewModelBinder +import com.manaschaudhari.android_mvvm.utils.BindingUtils +import com.trello.rxlifecycle2.internal.Preconditions + +/** + * A [Controller] that uses DataBinding to inflate a layout, and asks implementations + * to create a [ViewModel] instance when necessary. This class is designed to integrate + * with Conductor, and to take care of retaining the ViewModel across orientation changes. + * + * With that, it is possible for ViewModels to hold a reference to the [Controller] (either + * directly, or indirectly), and not cause any memory leaks. This is possible + * because the Controller will be retained across orientation changes as well. + * + * Created by Gabor Szanto on 2017. 04. 29.. + */ +abstract class MvvmController : RxController { + constructor() : super() + constructor(args: Bundle?) : super(args) + + /** + * The [ViewModel] instance that will be held by implementations. This instance will be + * constructed once, and persisted across orientation changes. + */ + abstract var viewModel: ViewModel + + /** + * The generic [Binding] instance. This will be recreated whenever the Controller decides + * to recreate its View. + */ + lateinit var binding: Binding + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View { + val vm = this::class.java.getDeclaredField("viewModel") + vm.isAccessible = true + if (vm.get(this) == null) + createViewModel() + + binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false) + return binding.root + } + + override fun onDestroyView(view: View) { + val binding = this::class.java.getField("binding") + binding.isAccessible = true + binding.set(this, null) + super.onDestroyView(view) + } + + private val defaultBinder: com.manaschaudhari.android_mvvm.adapters.ViewModelBinder + get() { + val defaultBinder = BindingUtils.getDefaultBinder() + Preconditions.checkNotNull(defaultBinder, "Default Binder") + return defaultBinder!! + } + + override fun onAttach(view: View) { + super.onAttach(view) + defaultBinder.bind(binding, viewModel) + } + + override fun onDetach(view: View) { + defaultBinder.bind(binding, null) + binding.executePendingBindings() + super.onDetach(view) + } + + /** + * The [Controller] instance is about to be destroyed, so we need to clear out the + * reference to the [ViewModel]. + * + * Note: we need to use reflection here to keep Kotlin happy about the nullability. :) + */ + override fun onDestroy() { + val viewModel = this::class.java.getField("viewModel") + viewModel.isAccessible = true + viewModel.set(this, null) + super.onDestroy() + } + + /** + * Implementations should return the ID of the layout to be inflated. + */ + abstract fun getLayoutId(): Int + + /** + * Implementations should create the ViewModel in this method, either manually, or + * by using dependency injection (e.g. Dagger 2) + */ + abstract fun createViewModel() +} \ No newline at end of file diff --git a/android-mvvm-core/.gitignore b/android-mvvm-core/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/android-mvvm-core/.gitignore @@ -0,0 +1 @@ +/build diff --git a/android-mvvm/build.gradle b/android-mvvm-core/build.gradle similarity index 69% rename from android-mvvm/build.gradle rename to android-mvvm-core/build.gradle index 9ca6f69..b772e80 100644 --- a/android-mvvm/build.gradle +++ b/android-mvvm-core/build.gradle @@ -1,16 +1,20 @@ apply plugin: 'com.android.library' -def isCi() { +buildscript { + ext.moduleArtifact = 'core' +} + +static def isCi() { return "true".equals(System.getenv('CI')) } android { - compileSdkVersion 24 - buildToolsVersion "24.0.0" + compileSdkVersion 25 + buildToolsVersion "25.0.3" defaultConfig { - minSdkVersion 9 - targetSdkVersion 24 + minSdkVersion 16 + targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -60,13 +64,19 @@ if (isCi()) { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' + compile "io.reactivex.rxjava2:rxjava:$rx_version" + + compile "com.android.support:recyclerview-v7:$support_library_version" + compile "com.android.support:support-v4:$support_library_version" + androidTestCompile 'com.android.support.test:runner:0.5' androidTestCompile 'com.android.support.test:rules:0.5' - compile 'io.reactivex:rxjava:1.1.7' - compile 'com.android.support:recyclerview-v7:24.2.1' - compile 'com.android.support:appcompat-v7:24.2.1' + androidTestCompile "com.android.support:recyclerview-v7:$support_library_version" } -apply from: rootProject.file('gradle/bintray-push.gradle') \ No newline at end of file +apply from: rootProject.file('gradle/bintray-push.gradle') +repositories { + mavenCentral() + jcenter() +} diff --git a/android-mvvm-core/gradle/wrapper/gradle-wrapper.jar b/android-mvvm-core/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/android-mvvm-core/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android-mvvm-core/gradle/wrapper/gradle-wrapper.properties b/android-mvvm-core/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9a778d6 --- /dev/null +++ b/android-mvvm-core/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Dec 28 10:00:20 PST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/android-mvvm-core/gradlew b/android-mvvm-core/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/android-mvvm-core/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android-mvvm-core/pom.xml b/android-mvvm-core/pom.xml new file mode 100644 index 0000000..3368922 --- /dev/null +++ b/android-mvvm-core/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + com.apptive + android-mvvm + 0.3.0 + aar + Android MVVM + Tools for implementing MVVM on Android + https://github.com/manas-chaudhari/android-mvvm + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + manas-chaudhari + Manas Chaudhari + chaudhari.manas@gmail.com + + + + https://github.com/manas-chaudhari/android-mvvm.git + https://github.com/manas-chaudhari/android-mvvm.git + https://github.com/manas-chaudhari/android-mvvm + + + + com.android.support + recyclerview-v7 + 25.3.0 + compile + + + com.android.support + appcompat-v7 + 25.3.0 + compile + + + com.trello + navi + 1.0 + compile + + + diff --git a/android-mvvm/proguard-rules.pro b/android-mvvm-core/proguard-rules.pro similarity index 100% rename from android-mvvm/proguard-rules.pro rename to android-mvvm-core/proguard-rules.pro diff --git a/android-mvvm-core/settings.gradle b/android-mvvm-core/settings.gradle new file mode 100644 index 0000000..6a12b63 --- /dev/null +++ b/android-mvvm-core/settings.gradle @@ -0,0 +1 @@ +include ':android-mvvm-core' diff --git a/android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/ApplicationTest.java b/android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/ApplicationTest.java similarity index 100% rename from android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/ApplicationTest.java rename to android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/ApplicationTest.java diff --git a/android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/BindingTestViewModel.java b/android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/BindingTestViewModel.java similarity index 97% rename from android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/BindingTestViewModel.java rename to android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/BindingTestViewModel.java index 892f9c0..2fc42a9 100644 --- a/android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/BindingTestViewModel.java +++ b/android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/BindingTestViewModel.java @@ -21,7 +21,7 @@ import java.util.List; -import rx.Observable; +import io.reactivex.Observable; public class BindingTestViewModel implements ViewModel { public List subclassList; diff --git a/android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapterTest.java b/android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapterTest.java similarity index 100% rename from android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapterTest.java rename to android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapterTest.java diff --git a/android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/TestViewModel.java b/android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/TestViewModel.java similarity index 100% rename from android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/TestViewModel.java rename to android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/TestViewModel.java diff --git a/android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/TestViewModelBinder.java b/android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/TestViewModelBinder.java similarity index 100% rename from android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/TestViewModelBinder.java rename to android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/TestViewModelBinder.java diff --git a/android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapterTest.java b/android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapterTest.java similarity index 100% rename from android-mvvm/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapterTest.java rename to android-mvvm-core/src/androidTest/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapterTest.java diff --git a/android-mvvm/src/androidTest/res/layout/layout_binding_tests.xml b/android-mvvm-core/src/androidTest/res/layout/layout_binding_tests.xml similarity index 100% rename from android-mvvm/src/androidTest/res/layout/layout_binding_tests.xml rename to android-mvvm-core/src/androidTest/res/layout/layout_binding_tests.xml diff --git a/android-mvvm/src/androidTest/res/layout/layout_test.xml b/android-mvvm-core/src/androidTest/res/layout/layout_test.xml similarity index 100% rename from android-mvvm/src/androidTest/res/layout/layout_test.xml rename to android-mvvm-core/src/androidTest/res/layout/layout_test.xml diff --git a/android-mvvm-core/src/main/AndroidManifest.xml b/android-mvvm-core/src/main/AndroidManifest.xml new file mode 100644 index 0000000..448d0aa --- /dev/null +++ b/android-mvvm-core/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/FieldUtils.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/FieldUtils.java new file mode 100644 index 0000000..1ad2863 --- /dev/null +++ b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/FieldUtils.java @@ -0,0 +1,275 @@ +/* + * Copyright 2016 Manas Chaudhari + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.manaschaudhari.android_mvvm; + +import android.databinding.Observable.OnPropertyChangedCallback; +import android.databinding.ObservableBoolean; +import android.databinding.ObservableDouble; +import android.databinding.ObservableField; +import android.databinding.ObservableFloat; +import android.databinding.ObservableInt; +import android.databinding.ObservableList; +import android.databinding.ObservableLong; +import android.support.annotation.NonNull; +import android.util.Log; + +import java.util.List; + +import io.reactivex.Observable; +import io.reactivex.ObservableEmitter; +import io.reactivex.ObservableOnSubscribe; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Cancellable; +import io.reactivex.functions.Consumer; + +public class FieldUtils { + @NonNull + public static Observable toObservable(@NonNull final ObservableField field) { + return Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(final ObservableEmitter subscriber) { + if (field.get() != null) + subscriber.onNext(field.get()); + + final OnPropertyChangedCallback callback = new OnPropertyChangedCallback() { + @Override + public void onPropertyChanged(android.databinding.Observable observable, int i) { + if (field.get() == null) { + Log.w("FieldUtils", "Null value received in ObservableField. Since Observables cannot emit null, ignoring this value."); + return; + } + + subscriber.onNext(field.get()); + } + }; + field.addOnPropertyChangedCallback(callback); + + subscriber.setCancellable(new Cancellable() { + @Override + public void cancel() throws Exception { + field.removeOnPropertyChangedCallback(callback); + } + }); + } + }); + } + + @NonNull + public static Observable toObservable(@NonNull final ObservableInt field) { + return Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(final ObservableEmitter subscriber) { + subscriber.onNext(field.get()); + final OnPropertyChangedCallback callback = new OnPropertyChangedCallback() { + @Override + public void onPropertyChanged(android.databinding.Observable observable, int i) { + subscriber.onNext(field.get()); + } + }; + field.addOnPropertyChangedCallback(callback); + + subscriber.setCancellable(new Cancellable() { + @Override + public void cancel() throws Exception { + field.removeOnPropertyChangedCallback(callback); + } + }); + } + }); + } + + @NonNull + public static Observable toObservable(@NonNull final ObservableBoolean field) { + return Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(final ObservableEmitter subscriber) { + subscriber.onNext(field.get()); + final OnPropertyChangedCallback callback = new OnPropertyChangedCallback() { + @Override + public void onPropertyChanged(android.databinding.Observable observable, int i) { + subscriber.onNext(field.get()); + } + }; + field.addOnPropertyChangedCallback(callback); + subscriber.setCancellable(new Cancellable() { + @Override + public void cancel() throws Exception { + field.removeOnPropertyChangedCallback(callback); + } + }); + } + }); + } + + @NonNull + public static Observable toObservable(@NonNull final ObservableFloat field) { + return Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(final ObservableEmitter subscriber) { + subscriber.onNext(field.get()); + final OnPropertyChangedCallback callback = new OnPropertyChangedCallback() { + @Override + public void onPropertyChanged(android.databinding.Observable observable, int i) { + subscriber.onNext(field.get()); + } + }; + field.addOnPropertyChangedCallback(callback); + subscriber.setCancellable(new Cancellable() { + @Override + public void cancel() throws Exception { + field.removeOnPropertyChangedCallback(callback); + } + }); + } + }); + } + + @NonNull + public static Observable toObservable(@NonNull final ObservableDouble field) { + return Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(final ObservableEmitter subscriber) { + subscriber.onNext(field.get()); + final OnPropertyChangedCallback callback = new OnPropertyChangedCallback() { + @Override + public void onPropertyChanged(android.databinding.Observable observable, int i) { + subscriber.onNext(field.get()); + } + }; + field.addOnPropertyChangedCallback(callback); + subscriber.setCancellable(new Cancellable() { + @Override + public void cancel() throws Exception { + field.removeOnPropertyChangedCallback(callback); + } + }); + } + }); + } + + @NonNull + public static Observable toObservable(@NonNull final ObservableLong field) { + return Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(final ObservableEmitter subscriber) { + subscriber.onNext(field.get()); + final OnPropertyChangedCallback callback = new OnPropertyChangedCallback() { + @Override + public void onPropertyChanged(android.databinding.Observable observable, int i) { + subscriber.onNext(field.get()); + } + }; + field.addOnPropertyChangedCallback(callback); + subscriber.setCancellable(new Cancellable() { + @Override + public void cancel() throws Exception { + field.removeOnPropertyChangedCallback(callback); + } + }); + } + }); + } + + @NonNull + public static Observable> toObservable(@NonNull final ObservableList field) { + return Observable.create(new ObservableOnSubscribe>() { + @Override + public void subscribe(final ObservableEmitter> subscriber) { + subscriber.onNext(field); + final ObservableList.OnListChangedCallback> callback = new ObservableList.OnListChangedCallback>() { + @Override + public void onChanged(ObservableList observableList) { + subscriber.onNext(field); + } + + @Override + public void onItemRangeChanged(ObservableList observableList, int i, int i1) { + onChanged(observableList); + } + + @Override + public void onItemRangeInserted(ObservableList observableList, int i, int i1) { + onChanged(observableList); + } + + @Override + public void onItemRangeMoved(ObservableList observableList, int i, int i1, int i2) { + onChanged(observableList); + } + + @Override + public void onItemRangeRemoved(ObservableList observableList, int i, int i1) { + onChanged(observableList); + } + }; + field.addOnListChangedCallback(callback); + subscriber.setCancellable(new Cancellable() { + @Override + public void cancel() throws Exception { + field.removeOnListChangedCallback(callback); + } + }); + } + }); + } + + /** + * A convenient wrapper for {@code ReadOnlyField#create(Observable)} + * + * @return DataBinding field created from the specified Observable + */ + @NonNull + public static ReadOnlyField toField(@NonNull final Observable observable) { + return ReadOnlyField.create(observable); + } + + @NonNull + public static Disposable bindTo(@NonNull final Observable observable, @NonNull final ObservableField field) { + return observable.subscribe(new Consumer() { + @Override + public void accept(@io.reactivex.annotations.NonNull T t) throws Exception { + field.set(t); + } + }, new Consumer() { + @Override + public void accept(@io.reactivex.annotations.NonNull Throwable throwable) throws Exception { + Log.e("FieldUtils", "onError in source observable", throwable); + } + }); + } + +/* @NonNull + public static Disposable bindTo(@NonNull final ObservableSource observable, @NonNull final Observer observer) { + observable.subscribe(observer); + return observable.subscribe(new Consumer() { + @Override + public void accept(@io.reactivex.annotations.NonNull T t) throws Exception { + field.onNext(t); + } + }, new Consumer() { + @Override + public void accept(@io.reactivex.annotations.NonNull Throwable throwable) throws Exception { + Log.e("FieldUtils", "onError in source observable", throwable); + } + }); + }*/ + + @NonNull + public static , U> ReadOnlyList toList(@NonNull final Observable observable) { + return ReadOnlyList.create(observable); + } +} \ No newline at end of file diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/ReadOnlyField.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/ReadOnlyField.java similarity index 79% rename from android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/ReadOnlyField.java rename to android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/ReadOnlyField.java index cbd07e3..4e609d1 100644 --- a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/ReadOnlyField.java +++ b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/ReadOnlyField.java @@ -22,13 +22,13 @@ import java.util.HashMap; -import rx.Observable; -import rx.Subscription; -import rx.functions.Action1; +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; public class ReadOnlyField extends ObservableField { final Observable source; - final HashMap subscriptions = new HashMap<>(); + final HashMap subscriptions = new HashMap<>(); public static ReadOnlyField create(@NonNull Observable source) { return new ReadOnlyField<>(source); @@ -37,15 +37,15 @@ public static ReadOnlyField create(@NonNull Observable source) { protected ReadOnlyField(@NonNull Observable source) { super(); this.source = source - .doOnNext(new Action1() { + .doOnNext(new Consumer() { @Override - public void call(T t) { + public void accept(T t) { ReadOnlyField.super.set(t); } }) - .doOnError(new Action1() { + .doOnError(new Consumer() { @Override - public void call(Throwable throwable) { + public void accept(Throwable throwable) { Log.e("ReadOnlyField", "onError in source observable", throwable); } }) @@ -70,9 +70,9 @@ public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback @Override public synchronized void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) { super.removeOnPropertyChangedCallback(callback); - Subscription subscription = subscriptions.remove(callback); - if (subscription != null && !subscription.isUnsubscribed()) { - subscription.unsubscribe(); + Disposable subscription = subscriptions.remove(callback); + if (subscription != null && !subscription.isDisposed()) { + subscription.dispose(); } } } diff --git a/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/ReadOnlyList.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/ReadOnlyList.java new file mode 100644 index 0000000..00689ec --- /dev/null +++ b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/ReadOnlyList.java @@ -0,0 +1,95 @@ +/* + * Copyright 2016 Manas Chaudhari + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.manaschaudhari.android_mvvm; + +import android.databinding.ObservableArrayList; +import android.support.annotation.NonNull; +import android.util.Log; + +import java.util.Collection; +import java.util.List; + +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; + +public class ReadOnlyList, U> extends ObservableArrayList { + final Observable source; + private Disposable subscription; + private int listenerCount = 0; + + public static , X> ReadOnlyList create(@NonNull Observable source) { + return new ReadOnlyList<>(source); + } + + protected ReadOnlyList(@NonNull Observable source) { + super(); + this.source = source; + subscribe(); + } + + /** + * @deprecated Setter of ReadOnlyList does nothing. Merge with the source Observable instead. + * See Documentation/ObservablesAndSetters.md + */ + @Override + public boolean add(U object) { + return false; + } + + @Override + public void clear() { + } + + @Override + public boolean addAll(Collection collection) { + return false; + } + + @Override + public void addOnListChangedCallback(OnListChangedCallback listener) { + super.addOnListChangedCallback(listener); + listenerCount++; + if (subscription == null || subscription.isDisposed()) { + subscribe(); + } + } + + private void subscribe() { + subscription = this.source.subscribe(new Consumer() { + @Override + public void accept(T us) { + ReadOnlyList.super.clear(); + ReadOnlyList.super.addAll(us); + } + }, new Consumer() { + @Override + public void accept(Throwable throwable) { + Log.e("ReadOnlyList", "onError in source observable", throwable); + } + }); + } + + @Override + public void removeOnListChangedCallback(OnListChangedCallback listener) { + super.removeOnListChangedCallback(listener); + if (--listenerCount == 0 && subscription != null && !subscription.isDisposed()) { + subscription.dispose(); + subscription = null; + } + } +} diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/ViewModel.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/ViewModel.java similarity index 99% rename from android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/ViewModel.java rename to android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/ViewModel.java index e041ba8..af44815 100644 --- a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/ViewModel.java +++ b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/ViewModel.java @@ -17,4 +17,4 @@ package com.manaschaudhari.android_mvvm; public interface ViewModel { -} +} \ No newline at end of file diff --git a/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/Connectable.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/Connectable.java new file mode 100644 index 0000000..e0202a6 --- /dev/null +++ b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/Connectable.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016 Manas Chaudhari + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.manaschaudhari.android_mvvm.adapters; + +import io.reactivex.disposables.Disposable; + +/** + * A {@link com.manaschaudhari.android_mvvm.ViewModel} can implement this + * interface to return a Subscription which will eventually be + * unsubscribed from when the ViewModel's View is detached from the + * view hierarchy. + */ +public interface Connectable { + Disposable connect(); +} diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapter.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapter.java similarity index 86% rename from android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapter.java rename to android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapter.java index fa0f769..6baf1e0 100644 --- a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapter.java +++ b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/RecyclerViewAdapter.java @@ -31,16 +31,17 @@ import java.util.HashMap; import java.util.List; -import rx.Observable; -import rx.Subscription; -import rx.functions.Action1; +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; + public class RecyclerViewAdapter extends RecyclerView.Adapter { private @NonNull List latestViewModels = new ArrayList<>(); private final @NonNull ViewProvider viewProvider; private final @NonNull ViewModelBinder binder; private final @NonNull Observable> source; - private final @NonNull HashMap subscriptions = new HashMap<>(); + private final @NonNull HashMap subscriptions = new HashMap<>(); public RecyclerViewAdapter(@NonNull Observable> viewModels, @NonNull ViewProvider viewProvider, @@ -48,16 +49,16 @@ public RecyclerViewAdapter(@NonNull Observable> viewModels, this.viewProvider = viewProvider; this.binder = viewModelBinder; source = viewModels - .doOnNext(new Action1>() { + .doOnNext(new Consumer>() { @Override - public void call(@Nullable List viewModels) { + public void accept(@Nullable List viewModels) { latestViewModels = viewModels != null ? viewModels : new ArrayList(); notifyDataSetChanged(); } }) - .doOnError(new Action1() { + .doOnError(new Consumer() { @Override - public void call(Throwable throwable) { + public void accept(Throwable throwable) { Log.e("RecyclerViewAdapter", "onError in source observable", throwable); } }) @@ -101,9 +102,9 @@ public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observe @Override public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) { super.unregisterAdapterDataObserver(observer); - Subscription subscription = subscriptions.remove(observer); - if (subscription != null && !subscription.isUnsubscribed()) { - subscription.unsubscribe(); + Disposable subscription = subscriptions.remove(observer); + if (subscription != null && !subscription.isDisposed()) { + subscription.dispose(); } } diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewModelBinder.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewModelBinder.java similarity index 100% rename from android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewModelBinder.java rename to android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewModelBinder.java diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapter.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapter.java similarity index 79% rename from android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapter.java rename to android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapter.java index 1074920..538352e 100644 --- a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapter.java +++ b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerAdapter.java @@ -31,9 +31,9 @@ import java.util.ArrayList; import java.util.List; -import rx.Observable; -import rx.Subscription; -import rx.functions.Action1; +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; public class ViewPagerAdapter extends PagerAdapter implements Connectable { @@ -44,23 +44,23 @@ public class ViewPagerAdapter extends PagerAdapter implements Connectable { private final Observable> source; @NonNull - private final ViewProvider viewProvider; + private final ViewPagerViewProvider viewProvider; @NonNull private final ViewModelBinder binder; - public ViewPagerAdapter(@NonNull Observable> viewModels, @NonNull ViewProvider viewProvider, @NonNull ViewModelBinder binder) { + public ViewPagerAdapter(@NonNull Observable> viewModels, @NonNull ViewPagerViewProvider viewProvider, @NonNull ViewModelBinder binder) { source = viewModels - .doOnNext(new Action1>() { + .doOnNext(new Consumer>() { @Override - public void call(@Nullable List viewModels) { + public void accept(@Nullable List viewModels) { latestViewModels = (viewModels != null) ? viewModels : new ArrayList(); notifyDataSetChanged(); } }) - .doOnError(new Action1() { + .doOnError(new Consumer() { @Override - public void call(Throwable throwable) { + public void accept(Throwable throwable) { Log.e("ViewPagerAdapter", "Error in source observable", throwable); } }) @@ -103,11 +103,17 @@ public int getCount() { @Override public boolean isViewFromObject(View view, Object object) { - return ((ViewDataBinding)object).getRoot() == view; + return ((ViewDataBinding) object).getRoot() == view; } @Override - public Subscription connect() { + public CharSequence getPageTitle(int position) { + ViewModel vm = latestViewModels.get(position); + return viewProvider.getTitle(vm); + } + + @Override + public Disposable connect() { return source.subscribe(); } } diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/Connectable.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerViewProvider.java similarity index 81% rename from android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/Connectable.java rename to android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerViewProvider.java index b99cc3e..146f43e 100644 --- a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/Connectable.java +++ b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewPagerViewProvider.java @@ -16,8 +16,8 @@ package com.manaschaudhari.android_mvvm.adapters; -import rx.Subscription; +import com.manaschaudhari.android_mvvm.ViewModel; -public interface Connectable { - Subscription connect(); +public interface ViewPagerViewProvider extends ViewProvider { + String getTitle(ViewModel vm); } diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewProvider.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewProvider.java similarity index 100% rename from android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewProvider.java rename to android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/adapters/ViewProvider.java diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/utils/BindingUtils.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/utils/BindingUtils.java similarity index 58% rename from android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/utils/BindingUtils.java rename to android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/utils/BindingUtils.java index 110188d..bf4284e 100644 --- a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/utils/BindingUtils.java +++ b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/utils/BindingUtils.java @@ -18,13 +18,18 @@ import android.databinding.BindingAdapter; import android.databinding.BindingConversion; +import android.databinding.adapters.ListenerUtil; +import android.os.Build; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.view.View; import com.manaschaudhari.android_mvvm.R; import com.manaschaudhari.android_mvvm.ViewModel; @@ -32,14 +37,17 @@ import com.manaschaudhari.android_mvvm.adapters.RecyclerViewAdapter; import com.manaschaudhari.android_mvvm.adapters.ViewModelBinder; import com.manaschaudhari.android_mvvm.adapters.ViewPagerAdapter; +import com.manaschaudhari.android_mvvm.adapters.ViewPagerViewProvider; import com.manaschaudhari.android_mvvm.adapters.ViewProvider; import java.util.ArrayList; import java.util.List; -import rx.Observable; -import rx.Subscription; -import rx.functions.Func1; +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Function; +import io.reactivex.processors.PublishProcessor; +import io.reactivex.subjects.PublishSubject; @SuppressWarnings("unused") public class BindingUtils { @@ -62,9 +70,9 @@ public static void bindAdapter(@NonNull ViewPager viewPager, @Nullable PagerAdap // Disconnect previous adapter if its Connectable if (oldAdapter != null && oldAdapter instanceof Connectable) { - Subscription subscription = (Subscription) viewPager.getTag(R.integer.tag_subscription); - if (subscription != null && !subscription.isUnsubscribed()) { - subscription.unsubscribe(); + Disposable subscription = (Disposable) viewPager.getTag(R.integer.tag_subscription); + if (subscription != null && !subscription.isDisposed()) { + subscription.dispose(); } viewPager.setTag(R.integer.tag_subscription, null); } @@ -92,7 +100,7 @@ public static void bindAdapterWithDefaultBinder(@NonNull RecyclerView recyclerVi } @BindingAdapter({"items", "view_provider"}) - public static void bindAdapterWithDefaultBinder(@NonNull ViewPager viewPager, @Nullable Observable> items, @Nullable ViewProvider viewProvider) { + public static void bindAdapterWithDefaultBinder(@NonNull ViewPager viewPager, @Nullable Observable> items, @Nullable ViewPagerViewProvider viewProvider) { ViewPagerAdapter adapter = null; if (items != null && viewProvider != null) { Preconditions.checkNotNull(defaultBinder, "Default Binder"); @@ -115,9 +123,9 @@ public int getView(ViewModel vm) { @BindingConversion @Nullable public static Observable> toGenericList(@Nullable Observable> specificList) { - return specificList == null ? null : specificList.map(new Func1, List>() { + return specificList == null ? null : specificList.map(new Function, List>() { @Override - public List call(List ts) { + public List apply(List ts) { return new ArrayList(ts); } }); @@ -127,7 +135,7 @@ public List call(List ts) { @Nullable public static Observable> toListObservable(@Nullable List specificList) { return specificList == null ? null : - Observable.just((List)new ArrayList(specificList)); + Observable.just((List) new ArrayList(specificList)); } // Extra Utilities @@ -137,4 +145,85 @@ public static void bindLayoutManager(@NonNull RecyclerView recyclerView, boolean int orientation = vertical ? RecyclerView.VERTICAL : RecyclerView.HORIZONTAL; recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext(), orientation, false)); } -} + + @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1) + @BindingAdapter("lifecycle") + public static void bindLifecycle(View view, final Connectable connectable) { + View.OnAttachStateChangeListener newListener = null; + + if (connectable != null) { + newListener = new View.OnAttachStateChangeListener() { + private Disposable subscription; + + @Override + public void onViewAttachedToWindow(View v) { + subscription = connectable.connect(); + } + + @Override + public void onViewDetachedFromWindow(View v) { + subscription.dispose(); + } + }; + + View.OnAttachStateChangeListener oldValue = ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener); + if (oldValue != null) { + view.removeOnAttachStateChangeListener(oldValue); + } + } + + if (newListener != null) { + if (ViewCompat.isAttachedToWindow(view)) { + newListener.onViewAttachedToWindow(view); + } + + view.addOnAttachStateChangeListener(newListener); + } + } + + private static final Object CLICK_OBJECT = new Object(); + + @BindingConversion + public static View.OnClickListener toOnClickListener(final PublishSubject click) { + if (click != null) { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + click.onNext(CLICK_OBJECT); + } + }; + } else { + return null; + } + } + + @BindingConversion + public static View.OnLongClickListener toOnLongClickListener(final PublishSubject click) { + if (click != null) { + return new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + click.onNext(CLICK_OBJECT); + return true; + } + }; + } else { + return null; + } + } + + @BindingConversion + public static View.OnLongClickListener toOnLongClickListener(final PublishProcessor click) { + if (click != null) { + return new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + click.onNext(CLICK_OBJECT); + return true; + } + }; + } else { + return null; + } + } +} \ No newline at end of file diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/utils/Preconditions.java b/android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/utils/Preconditions.java similarity index 100% rename from android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/utils/Preconditions.java rename to android-mvvm-core/src/main/java/com/manaschaudhari/android_mvvm/utils/Preconditions.java diff --git a/android-mvvm/src/main/res/values/integers.xml b/android-mvvm-core/src/main/res/values/integers.xml similarity index 100% rename from android-mvvm/src/main/res/values/integers.xml rename to android-mvvm-core/src/main/res/values/integers.xml diff --git a/android-mvvm/src/sharedTest/java/com/manaschaudhari/android_mvvm/testutils/SubscriptionCounter.java b/android-mvvm-core/src/sharedTest/java/com/manaschaudhari/android_mvvm/testutils/SubscriptionCounter.java similarity index 60% rename from android-mvvm/src/sharedTest/java/com/manaschaudhari/android_mvvm/testutils/SubscriptionCounter.java rename to android-mvvm-core/src/sharedTest/java/com/manaschaudhari/android_mvvm/testutils/SubscriptionCounter.java index b0660ce..72cb0a0 100644 --- a/android-mvvm/src/sharedTest/java/com/manaschaudhari/android_mvvm/testutils/SubscriptionCounter.java +++ b/android-mvvm-core/src/sharedTest/java/com/manaschaudhari/android_mvvm/testutils/SubscriptionCounter.java @@ -16,23 +16,27 @@ package com.manaschaudhari.android_mvvm.testutils; -import rx.Observable; -import rx.functions.Action0; +import io.reactivex.Observable; +import io.reactivex.ObservableSource; +import io.reactivex.ObservableTransformer; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Action; +import io.reactivex.functions.Consumer; -public class SubscriptionCounter implements Observable.Transformer { +public class SubscriptionCounter implements ObservableTransformer { public int subscriptions; public int unsubscriptions; @Override - public Observable call(Observable tObservable) { - return tObservable.doOnSubscribe(new Action0() { + public ObservableSource apply(Observable tObservable) { + return tObservable.doOnSubscribe(new Consumer() { @Override - public void call() { + public void accept(Disposable a) { subscriptions++; } - }).doOnUnsubscribe(new Action0() { + }).doOnDispose(new Action() { @Override - public void call() { + public void run() throws Exception { unsubscriptions++; } }); diff --git a/android-mvvm/src/test/java/com/manaschaudhari/android_mvvm/FieldUtilsTest.java b/android-mvvm-core/src/test/java/com/manaschaudhari/android_mvvm/FieldUtilsTest.java similarity index 71% rename from android-mvvm/src/test/java/com/manaschaudhari/android_mvvm/FieldUtilsTest.java rename to android-mvvm-core/src/test/java/com/manaschaudhari/android_mvvm/FieldUtilsTest.java index 67e164b..6982d06 100644 --- a/android-mvvm/src/test/java/com/manaschaudhari/android_mvvm/FieldUtilsTest.java +++ b/android-mvvm-core/src/test/java/com/manaschaudhari/android_mvvm/FieldUtilsTest.java @@ -21,8 +21,10 @@ import org.junit.Before; import org.junit.Test; -import rx.Observable; -import rx.observers.TestSubscriber; +import io.reactivex.Observable; +import io.reactivex.annotations.NonNull; +import io.reactivex.functions.Consumer; +import io.reactivex.subscribers.TestSubscriber; import static com.manaschaudhari.android_mvvm.FieldUtils.toObservable; @@ -42,14 +44,23 @@ public void setUp() throws Exception { @Test public void emitsInitialValue() throws Exception { - sut.subscribe(testSubscriber); - + sut.subscribe(new Consumer() { + @Override + public void accept(@NonNull Integer integer) throws Exception { + testSubscriber.onNext(integer); + } + }); testSubscriber.assertValues(INITIAL_VALUE); } @Test public void emitsUpdates() throws Exception { - sut.subscribe(testSubscriber); + sut.subscribe(new Consumer() { + @Override + public void accept(@NonNull Integer integer) throws Exception { + testSubscriber.onNext(integer); + } + }); observableField.set(3); testSubscriber.assertValues(INITIAL_VALUE, 3); diff --git a/android-mvvm/src/test/java/com/manaschaudhari/android_mvvm/ReadOnlyFieldTests.java b/android-mvvm-core/src/test/java/com/manaschaudhari/android_mvvm/ReadOnlyFieldTests.java similarity index 93% rename from android-mvvm/src/test/java/com/manaschaudhari/android_mvvm/ReadOnlyFieldTests.java rename to android-mvvm-core/src/test/java/com/manaschaudhari/android_mvvm/ReadOnlyFieldTests.java index f2b536c..de43d93 100644 --- a/android-mvvm/src/test/java/com/manaschaudhari/android_mvvm/ReadOnlyFieldTests.java +++ b/android-mvvm-core/src/test/java/com/manaschaudhari/android_mvvm/ReadOnlyFieldTests.java @@ -21,11 +21,12 @@ import org.junit.Before; import org.junit.Test; -import rx.Observable; -import rx.subjects.BehaviorSubject; +import io.reactivex.Observable; +import io.reactivex.subjects.BehaviorSubject; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; /** * To work on unit tests, switch the Test Artifact in the Build Variants view. @@ -38,7 +39,7 @@ public class ReadOnlyFieldTests { @Before public void setUp() throws Exception { - sourceSubject = BehaviorSubject.create(INITIAL_VALUE); + sourceSubject = BehaviorSubject.createDefault(INITIAL_VALUE); subscriptionCounter = new SubscriptionCounter<>(); Observable source = sourceSubject.compose(subscriptionCounter); sut = ReadOnlyField.create(source); diff --git a/android-mvvm-kotlin/.gitignore b/android-mvvm-kotlin/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/android-mvvm-kotlin/.gitignore @@ -0,0 +1 @@ +/build diff --git a/android-mvvm-kotlin/build.gradle b/android-mvvm-kotlin/build.gradle new file mode 100644 index 0000000..2041e57 --- /dev/null +++ b/android-mvvm-kotlin/build.gradle @@ -0,0 +1,43 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +buildscript { + ext.kotlin_version = '1.1.2-2' + ext.moduleArtifact = 'kotlin' + repositories { + jcenter() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.3" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + provided project(path: ':android-mvvm-core') + provided "io.reactivex.rxjava2:rxjava:$rx_version" + testCompile 'junit:junit:4.12' + provided "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" +} + +apply from: rootProject.file('gradle/bintray-push.gradle') \ No newline at end of file diff --git a/android-mvvm-kotlin/proguard-rules.pro b/android-mvvm-kotlin/proguard-rules.pro new file mode 100644 index 0000000..5f245cb --- /dev/null +++ b/android-mvvm-kotlin/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/gabor/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/android-mvvm-kotlin/src/main/AndroidManifest.xml b/android-mvvm-kotlin/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3b5d76a --- /dev/null +++ b/android-mvvm-kotlin/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/android-mvvm-kotlin/src/main/java/com/manaschaudhari/android_mvvm/MvvmRxExtensions.kt b/android-mvvm-kotlin/src/main/java/com/manaschaudhari/android_mvvm/MvvmRxExtensions.kt new file mode 100644 index 0000000..6900bfb --- /dev/null +++ b/android-mvvm-kotlin/src/main/java/com/manaschaudhari/android_mvvm/MvvmRxExtensions.kt @@ -0,0 +1,60 @@ +package com.manaschaudhari.android_mvvm + +import android.databinding.* +import android.support.annotation.CheckResult +import io.reactivex.Observable +import io.reactivex.annotations.CheckReturnValue +import io.reactivex.disposables.Disposable +import io.reactivex.subjects.PublishSubject + +/** + * A set of convenience extension functions for Kotlin. + * + * Example usage: + * someObservableField.asObservable()..... + * Created by Gabor Szanto on 2017. 03. 24.. + */ + +/** + * From ObservableField to Observable + */ +fun ObservableField.asObservable(): Observable = FieldUtils.toObservable(this) + +fun ObservableInt.asObservable(): Observable = FieldUtils.toObservable(this) +fun ObservableFloat.asObservable(): Observable = FieldUtils.toObservable(this) +fun ObservableDouble.asObservable(): Observable = FieldUtils.toObservable(this) +fun ObservableLong.asObservable(): Observable = FieldUtils.toObservable(this) +fun ObservableList.asObservable(): Observable> = FieldUtils.toObservable(this) + +/** + * From Observable to ObservableField + */ +fun Observable.asField(): ObservableField = FieldUtils.toField(this) + +@CheckResult +@CheckReturnValue +fun Observable.bindTo(field: ObservableField): Disposable = FieldUtils.bindTo(this, field) + +/** + * Helper method for creating an Observable that emits true when the specified Observables emit something. + * This is useful for creating a loading Observable, that emits true if a loader should be visible, and false when + * it should be invisible. Finish the call with [endLoading]. + * + * Example: + * val loading = startLoading(button1Tap, button2Tap).endLoading(response1, response2) + */ +@CheckResult +@CheckReturnValue +fun startLoading(start1: Observable<*>, start2: Observable = PublishSubject.create()): Observable { + return Observable.merge(start1.map { true }, start2.map { true }) +} + +@CheckResult +@CheckReturnValue +fun Observable.endLoading(end1: Observable<*>, end2: Observable<*>? = null): Observable { + val end = Observable.merge(end1.map { false }, (end2 ?: Observable.never()).map { false }) +/* val _end2 = end2?.map { Any() } ?: Observable.just(Any()) + + val endCombined: Observable = Observable.combineLatest(end1, _end2, BiFunction { _, _ -> false }) */ + return Observable.merge(this, end) +} \ No newline at end of file diff --git a/android-mvvm-kotlin/src/main/java/com/manaschaudhari/android_mvvm/RxExtensions.kt b/android-mvvm-kotlin/src/main/java/com/manaschaudhari/android_mvvm/RxExtensions.kt new file mode 100644 index 0000000..23cc649 --- /dev/null +++ b/android-mvvm-kotlin/src/main/java/com/manaschaudhari/android_mvvm/RxExtensions.kt @@ -0,0 +1,21 @@ +package com.manaschaudhari.android_mvvm + +import android.support.annotation.CheckResult +import io.reactivex.BackpressureStrategy +import io.reactivex.Observable +import io.reactivex.Single +import io.reactivex.annotations.CheckReturnValue + +@CheckResult +@CheckReturnValue +fun Observable.flatMapFirst(obs: Single): Observable = this + .toFlowable(io.reactivex.BackpressureStrategy.DROP) + .flatMap({ obs.toFlowable() }, 1) + .toObservable() + +@CheckResult +@CheckReturnValue +fun Observable.flatMapFirst(obs: Observable): Observable = this + .toFlowable(io.reactivex.BackpressureStrategy.DROP) + .flatMap({ obs.toFlowable(BackpressureStrategy.DROP) }, 1) + .toObservable() \ No newline at end of file diff --git a/android-mvvm-kotlin/src/main/res/values/strings.xml b/android-mvvm-kotlin/src/main/res/values/strings.xml new file mode 100644 index 0000000..67b44b1 --- /dev/null +++ b/android-mvvm-kotlin/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + kotlin + diff --git a/android-mvvm/src/main/AndroidManifest.xml b/android-mvvm/src/main/AndroidManifest.xml deleted file mode 100644 index 80b5c36..0000000 --- a/android-mvvm/src/main/AndroidManifest.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/FieldUtils.java b/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/FieldUtils.java deleted file mode 100644 index b5bf58b..0000000 --- a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/FieldUtils.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2016 Manas Chaudhari - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.manaschaudhari.android_mvvm; - -import android.databinding.Observable.OnPropertyChangedCallback; -import android.databinding.ObservableField; -import android.support.annotation.NonNull; - -import rx.Observable; -import rx.Subscriber; -import rx.functions.Action0; -import rx.subscriptions.Subscriptions; - -public class FieldUtils { - @NonNull - public static Observable toObservable(@NonNull final ObservableField field) { - return Observable.create(new Observable.OnSubscribe() { - @Override - public void call(final Subscriber subscriber) { - subscriber.onNext(field.get()); - final OnPropertyChangedCallback callback = new OnPropertyChangedCallback() { - @Override - public void onPropertyChanged(android.databinding.Observable observable, int i) { - subscriber.onNext(field.get()); - } - }; - field.addOnPropertyChangedCallback(callback); - subscriber.add(Subscriptions.create(new Action0() { - @Override - public void call() { - field.removeOnPropertyChangedCallback(callback); - } - })); - } - }); - } - - /** - * A convenient wrapper for {@code ReadOnlyField#create(Observable)} - * @return DataBinding field created from the specified Observable - */ - @NonNull - public static ReadOnlyField toField(@NonNull final Observable observable) { - return ReadOnlyField.create(observable); - } -} diff --git a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/MvvmActivity.java b/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/MvvmActivity.java deleted file mode 100644 index 618216a..0000000 --- a/android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/MvvmActivity.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2016 Manas Chaudhari - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.manaschaudhari.android_mvvm; - -import android.databinding.DataBindingUtil; -import android.databinding.ViewDataBinding; -import android.os.Bundle; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; - -import com.manaschaudhari.android_mvvm.adapters.ViewModelBinder; -import com.manaschaudhari.android_mvvm.utils.BindingUtils; -import com.manaschaudhari.android_mvvm.utils.Preconditions; - -/** - * Inflates the provided view and binds the provided ViewModel based on default - * binder provided to the library - */ -public abstract class MvvmActivity extends AppCompatActivity { - private ViewDataBinding binding; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - binding = DataBindingUtil.setContentView(this, getLayoutId()); - getDefaultBinder().bind(binding, createViewModel()); - } - - @Override - protected void onDestroy() { - getDefaultBinder().bind(binding, null); - binding.executePendingBindings(); - super.onDestroy(); - } - - @NonNull - private ViewModelBinder getDefaultBinder() { - ViewModelBinder defaultBinder = BindingUtils.getDefaultBinder(); - Preconditions.checkNotNull(defaultBinder, "Default Binder"); - return defaultBinder; - } - - @NonNull - protected abstract ViewModel createViewModel(); - - @LayoutRes - protected abstract int getLayoutId(); -} diff --git a/android-mvvm/src/main/res/values/strings.xml b/android-mvvm/src/main/res/values/strings.xml deleted file mode 100644 index adae841..0000000 --- a/android-mvvm/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - android-mvvm - diff --git a/build.gradle b/build.gradle index 0adc364..fbd9e09 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,16 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.rx_version = '2.0.9' + ext.support_library_version = '25.3.1' + repositories { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.0' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.1' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' + classpath 'com.android.tools.build:gradle:2.3.1' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/conductor-sample/.gitignore b/conductor-sample/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/conductor-sample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/conductor-sample/build.gradle b/conductor-sample/build.gradle new file mode 100644 index 0000000..a5e3e4c --- /dev/null +++ b/conductor-sample/build.gradle @@ -0,0 +1,43 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.3" + + defaultConfig { + applicationId "com.manaschaudhari.android_mvvm.sample" + minSdkVersion 15 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + } + + dataBinding { + enabled = true + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + lintOptions { + textOutput 'stdout' + textReport true + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + testCompile 'junit:junit:4.12' + compile "com.android.support:appcompat-v7:$support_library_version" + compile project(':android-mvvm') + compile project(':conductor') + + // Leak Canary + debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' + releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' + testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' +} diff --git a/conductor-sample/proguard-rules.pro b/conductor-sample/proguard-rules.pro new file mode 100644 index 0000000..af20b2d --- /dev/null +++ b/conductor-sample/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/manas/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/conductor-sample/src/androidTest/java/com/manaschaudhari/android_mvvm/ApplicationTest.java b/conductor-sample/src/androidTest/java/com/manaschaudhari/android_mvvm/ApplicationTest.java new file mode 100644 index 0000000..e1280fb --- /dev/null +++ b/conductor-sample/src/androidTest/java/com/manaschaudhari/android_mvvm/ApplicationTest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016 Manas Chaudhari + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.manaschaudhari.android_mvvm; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/conductor-sample/src/main/AndroidManifest.xml b/conductor-sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0c2a584 --- /dev/null +++ b/conductor-sample/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/conductor-sample/src/main/res/drawable/some_image.png b/conductor-sample/src/main/res/drawable/some_image.png new file mode 100644 index 0000000..52531fa Binary files /dev/null and b/conductor-sample/src/main/res/drawable/some_image.png differ diff --git a/conductor-sample/src/main/res/layout/activity_calculator.xml b/conductor-sample/src/main/res/layout/activity_calculator.xml new file mode 100644 index 0000000..cd856d9 --- /dev/null +++ b/conductor-sample/src/main/res/layout/activity_calculator.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/conductor-sample/src/main/res/layout/activity_data_loading.xml b/conductor-sample/src/main/res/layout/activity_data_loading.xml new file mode 100644 index 0000000..5ccaa65 --- /dev/null +++ b/conductor-sample/src/main/res/layout/activity_data_loading.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/conductor-sample/src/main/res/layout/activity_item_details.xml b/conductor-sample/src/main/res/layout/activity_item_details.xml new file mode 100644 index 0000000..6017f45 --- /dev/null +++ b/conductor-sample/src/main/res/layout/activity_item_details.xml @@ -0,0 +1,12 @@ + + + + diff --git a/conductor-sample/src/main/res/layout/activity_item_list.xml b/conductor-sample/src/main/res/layout/activity_item_list.xml new file mode 100644 index 0000000..37c5fb8 --- /dev/null +++ b/conductor-sample/src/main/res/layout/activity_item_list.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/conductor-sample/src/main/res/layout/activity_main.xml b/conductor-sample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..a05b9ab --- /dev/null +++ b/conductor-sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,46 @@ + + + + + + + + + + +