├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── rxanimation-kotlin ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hosaka │ │ └── rxanimation_kotlin │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── reyurnible │ │ └── rxanimation_kotlin │ │ └── animation │ │ ├── RxAnimation.kt │ │ └── RxAnimator.kt │ └── res │ └── values │ └── strings.xml ├── rxanimation ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── reyurnible │ │ └── rxanimation │ │ ├── RecordingObserver.java │ │ ├── RxAnimationTest.java │ │ ├── RxAnimationTestRunner.java │ │ ├── RxAnimatorTest.java │ │ └── TestActivity.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── reyurnible │ │ └── rxanimation │ │ ├── animation │ │ ├── AnimationEvent.java │ │ ├── AnimationEventOnSubscribe.java │ │ └── RxAnimation.java │ │ ├── animator │ │ ├── AnimatorEvent.java │ │ ├── AnimatorEventOnSubscribe.java │ │ ├── AnimatorPauseEvent.java │ │ ├── AnimatorPauseEventOnSubscribe.java │ │ ├── AnimatorUpdateEvent.java │ │ ├── AnimatorUpdateEventOnSubscribe.java │ │ └── RxAnimator.java │ │ └── internal │ │ ├── MainThreadSubscription.java │ │ └── Preconditions.java │ └── res │ └── values │ └── strings.xml ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── reyurnible │ │ └── android │ │ └── sample │ │ └── MainActivity.kt │ └── res │ ├── drawable │ └── bg_form.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Proguard folder generated by Eclipse 12 | proguard/ 13 | 14 | # Log Files 15 | *.log 16 | 17 | # IntelliJ IDEA 18 | .idea 19 | *.iml 20 | annotations 21 | 22 | # Gradle 23 | .gradle 24 | gradlew.bat 25 | build 26 | local.properties 27 | reports 28 | jacoco.exec 29 | 30 | .DS_Store 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Shun Hosaka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Kotlin](https://img.shields.io/badge/kotlin-1.0.5-blue.svg)](http://kotlinlang.org) 2 | [![DUB](https://img.shields.io/dub/l/vibe-d.svg)](https://github.com/mplatvoet/kovenant/blob/master/LICENSE) 3 | [![Kotlin Slack](https://img.shields.io/badge/chat-kotlin%20slack-orange.svg)](http://kotlinslackin.herokuapp.com) 4 | 5 | # RxAnimation 6 | This is Animation wrapping Observable. 7 | You can get event Observable. 8 | And you can call animation chain more simple. 9 | 10 | ## Download 11 | Animation bindings 12 | 13 | ``` 14 | compile 'com.reyurnible.rxanimation:rxanimations:1.0.2' 15 | ``` 16 | 17 | using Kotlin: 18 | 19 | ``` 20 | compile 'com.reyurnible.rxanimation:rxanimations-kotlin:1.0.2' 21 | ``` 22 | 23 | ## Public Classes 24 | - RxAnimation 25 | - RxAnimator 26 | 27 | ## Example 28 | 29 | ### Simple Usage 30 | in Java 31 | 32 | ``` 33 | TextView textView = (TextView) findViewById(R.id.textView); 34 | Animation animation = new ScaleAnimation(0.0f, 0.0f, 1.0f, 1.0f); 35 | RxAnimation.events(animation, textView) 36 | .subscribe(new Subscriber() { 37 | @Override public void onCompleted() { 38 | 39 | } 40 | 41 | @Override public void onError(Throwable e) { 42 | 43 | } 44 | 45 | @Override public void onNext(AnimationEvent animationEvent) { 46 | switch (animationEvent.kind()) { 47 | case START: 48 | break; 49 | case REPEAT: 50 | break; 51 | case END: 52 | break; 53 | } 54 | } 55 | }); 56 | ``` 57 | 58 | in Kotlin 59 | 60 | ``` 61 | val view: TextView = findViewById(R.id.textView) as TextView 62 | val animation: Animation = ScaleAnimation(0.0f, 0.0f, 1.0f, 1.0f) 63 | animation.bindView(view).subscribe(object : Subscriber() { 64 | override fun onCompleted() { 65 | 66 | } 67 | 68 | override fun onNext(t: AnimationEvent?) { 69 | when(t!!.kind()) { 70 | AnimationEvent.Kind.START -> { 71 | // START 72 | } 73 | AnimationEvent.Kind.END -> { 74 | // END 75 | } 76 | AnimationEvent.Kind.REPEAT -> { 77 | // REPEAT 78 | } 79 | } 80 | } 81 | 82 | override fun onError(e: Throwable?) { 83 | 84 | } 85 | }) 86 | ``` 87 | 88 | ### Combine Usage 89 | 90 | ``` 91 | private fun startAnimations() { 92 | val alphaAnimation: AlphaAnimation = AlphaAnimation(0.0f, 1.0f) 93 | alphaAnimation.duration = 3000 94 | val alphaAnimationObserver = alphaAnimation.bindView(formLayout).map { 95 | Log.d(TAG, it.toString()) 96 | it.kind() == AnimationEvent.Kind.END 97 | } 98 | 99 | val translationAnimator: ObjectAnimator = ObjectAnimator.ofFloat(formLayout, "translationY", -1000f, 0f); 100 | translationAnimator.setDuration(3000); 101 | val translateAnimatorObserver = translationAnimator.bind().map { 102 | Log.d(TAG, it.toString()) 103 | it.kind() == AnimatorEvent.Kind.END 104 | } 105 | 106 | Observable.combineLatest(translateAnimatorObserver, alphaAnimationObserver, { t1, t2 -> 107 | Log.d(TAG, "T1:" + t1.toString()) 108 | Log.d(TAG, "T2:" + t2.toString()) 109 | t1.and(t2) 110 | }).filter { it }.subscribe { validate -> 111 | // post event 112 | Log.d(TAG, "End All Animation") 113 | } 114 | } 115 | ``` 116 | 117 | ## Licences 118 | 119 | ``` 120 | Copyright (c) 2015 Shun Hosaka 121 | 122 | Permission is hereby granted, free of charge, to any person obtaining a copy 123 | of this software and associated documentation files (the "Software"), to deal 124 | in the Software without restriction, including without limitation the rights 125 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 126 | copies of the Software, and to permit persons to whom the Software is 127 | furnished to do so, subject to the following conditions: 128 | 129 | The above copyright notice and this permission notice shall be included in 130 | all copies or substantial portions of the Software. 131 | 132 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 133 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 134 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 135 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 136 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 137 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 138 | THE SOFTWARE. 139 | ``` 140 | 141 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | subprojects { 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | jcenter() 17 | } 18 | } 19 | 20 | task clean(type: Delete) { 21 | delete rootProject.buildDir 22 | } 23 | 24 | ext { 25 | androidPlugin = 'com.android.tools.build:gradle:1.3.1' 26 | minSdkVersion = 14 27 | compileSdkVersion = 23 28 | buildToolsVersion = '23.0.1' 29 | 30 | kotlinVersion = '1.0.5-3' 31 | kotlinPlugin = 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.0-beta-2422' 32 | kotlinStdlib = 'org.jetbrains.kotlin:kotlin-stdlib:1.0.0-beta-2422' 33 | 34 | supportAnnotations = 'com.android.support:support-annotations:23.0.1' 35 | supportV4 = 'com.android.support:support-v4:23.0.1' 36 | supportAppCompat = 'com.android.support:appcompat-v7:23.0.1' 37 | 38 | supportTestRunner = 'com.android.support.test:runner:0.4' 39 | supportTestRules = 'com.android.support.test:rules:0.4' 40 | supportTestEspresso = 'com.android.support.test.espresso:espresso-core:2.2.1' 41 | supportTestEspressoContrib = 'com.android.support.test.espresso:espresso-contrib:2.2.1' 42 | junit = 'junit:junit:4.12' 43 | truth = 'com.google.truth:truth:0.27' 44 | 45 | rxJava = 'io.reactivex:rxjava:1.2.5' 46 | rxAndroid = 'io.reactivex:rxandroid:1.2.1' 47 | 48 | bintrayRelease = 'com.novoda:bintray-release:0.3.4' 49 | 50 | siteUrl = 'https://github.com/Reyurnible/RxAnimation' 51 | gitUrl = 'https://github.com/Reyurnible/RxAnimation.git' 52 | issueTrackingUrl = 'https://github.com/Reyurnible/RxAnimation/issues' 53 | } 54 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | GROUP_ID=com.reyurnible.rxanimation 2 | VERSION_NAME=1.0.2 3 | 4 | # Project-wide Gradle settings. 5 | 6 | # IDE (e.g. Android Studio) users: 7 | # Gradle settings configured through the IDE *will override* 8 | # any settings specified in this file. 9 | 10 | # For more details on how to configure your build environment visit 11 | # http://www.gradle.org/docs/current/userguide/build_environment.html 12 | 13 | # Specifies the JVM arguments used for the daemon process. 14 | # The setting is particularly useful for tweaking memory settings. 15 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 16 | org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 17 | 18 | # When configured, Gradle will run in incubating parallel mode. 19 | # This option should only be used with decoupled projects. More details, visit 20 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 21 | org.gradle.parallel=true 22 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hosshan/RxAnimation/a4fdb1ca9724d35461b1b6fc6b1c9381c5bdb64c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Oct 08 01:34:34 JST 2015 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.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /rxanimation-kotlin/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /rxanimation-kotlin/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | dependencies { 3 | classpath rootProject.ext.androidPlugin 4 | classpath rootProject.ext.kotlinPlugin 5 | classpath rootProject.ext.bintrayRelease 6 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion" 7 | } 8 | } 9 | 10 | apply plugin: 'com.android.library' 11 | apply plugin: 'kotlin-android' 12 | apply plugin: 'com.novoda.bintray-release' 13 | 14 | dependencies { 15 | compile project(':rxanimation') 16 | compile rootProject.ext.kotlinStdlib 17 | compile "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.ext.kotlinVersion" 18 | } 19 | 20 | android { 21 | compileSdkVersion rootProject.ext.compileSdkVersion 22 | buildToolsVersion rootProject.ext.buildToolsVersion 23 | 24 | defaultConfig { 25 | minSdkVersion rootProject.ext.minSdkVersion 26 | } 27 | 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_7 30 | targetCompatibility JavaVersion.VERSION_1_7 31 | } 32 | 33 | lintOptions { 34 | textReport true 35 | textOutput 'stdout' 36 | } 37 | 38 | buildTypes { 39 | debug { 40 | testCoverageEnabled true 41 | } 42 | } 43 | 44 | packagingOptions { 45 | exclude 'LICENSE.txt' 46 | } 47 | 48 | sourceSets { 49 | main.java.srcDirs += 'src/main/kotlin' 50 | } 51 | } 52 | 53 | afterEvaluate{ 54 | tasks.findByPath(":rxanimation-kotlin:mavenAndroidJavadocs").enabled = false 55 | } 56 | 57 | publish { 58 | // Bintray Properties 59 | // Run Command ./gradlew clean build bintrayUpload -PdryRun=false 60 | bintrayUser = BINTRAY_USER 61 | bintrayKey = BINTRAY_API_KEY 62 | 63 | userOrg = BINTRAY_USER 64 | groupId = GROUP_ID 65 | uploadName = 'RxAnimationsKotlin' 66 | artifactId = 'rxanimations-kotlin' 67 | publishVersion = VERSION_NAME 68 | description = 'Android observable animation binding kotlin extention.' 69 | website = rootProject.ext.siteUrl 70 | } 71 | -------------------------------------------------------------------------------- /rxanimation-kotlin/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/shunhosaka/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 | -------------------------------------------------------------------------------- /rxanimation-kotlin/src/androidTest/java/com/hosaka/rxanimation_kotlin/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.hosaka.rxanimation_kotlin; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /rxanimation-kotlin/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /rxanimation-kotlin/src/main/kotlin/com/reyurnible/rxanimation_kotlin/animation/RxAnimation.kt: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation_kotlin.animation 2 | 3 | import android.view.View 4 | import android.view.animation.Animation 5 | import com.reyurnible.rxanimation.animation.AnimationEvent 6 | import com.reyurnible.rxanimation.animation.RxAnimation 7 | import rx.Observable 8 | 9 | fun Animation.events(view: View): Observable = RxAnimation.events(this, view) 10 | -------------------------------------------------------------------------------- /rxanimation-kotlin/src/main/kotlin/com/reyurnible/rxanimation_kotlin/animation/RxAnimator.kt: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation_kotlin.animation 2 | 3 | import android.animation.Animator 4 | import android.animation.ValueAnimator 5 | import com.reyurnible.rxanimation.animator.AnimatorEvent 6 | import com.reyurnible.rxanimation.animator.AnimatorPauseEvent 7 | import com.reyurnible.rxanimation.animator.AnimatorUpdateEvent 8 | import com.reyurnible.rxanimation.animator.RxAnimator 9 | import rx.Observable 10 | 11 | fun Animator.events(): Observable = RxAnimator.events(this) 12 | 13 | fun Animator.pauseEvents(): Observable = RxAnimator.pauseEvents(this) 14 | 15 | fun ValueAnimator.updateEvents(): Observable = RxAnimator.updateEvents(this) 16 | -------------------------------------------------------------------------------- /rxanimation-kotlin/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | rxanimation-kotlin 3 | 4 | -------------------------------------------------------------------------------- /rxanimation/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /rxanimation/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | dependencies { 3 | classpath rootProject.ext.androidPlugin 4 | classpath rootProject.ext.bintrayRelease 5 | } 6 | } 7 | 8 | apply plugin: 'com.android.library' 9 | apply plugin: 'com.novoda.bintray-release' 10 | 11 | dependencies { 12 | compile rootProject.ext.rxJava 13 | compile rootProject.ext.supportAnnotations 14 | 15 | androidTestCompile rootProject.ext.supportTestRunner 16 | androidTestCompile rootProject.ext.supportTestRules 17 | androidTestCompile rootProject.ext.rxAndroid 18 | androidTestCompile rootProject.ext.truth 19 | testCompile rootProject.ext.junit 20 | } 21 | 22 | android { 23 | compileSdkVersion rootProject.ext.compileSdkVersion 24 | buildToolsVersion rootProject.ext.buildToolsVersion 25 | 26 | defaultConfig { 27 | minSdkVersion rootProject.ext.minSdkVersion 28 | 29 | testInstrumentationRunner 'com.reyurnible.rxanimation.RxAnimationTestRunner' 30 | } 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_7 34 | targetCompatibility JavaVersion.VERSION_1_7 35 | } 36 | 37 | lintOptions { 38 | textReport true 39 | textOutput 'stdout' 40 | } 41 | 42 | buildTypes { 43 | debug { 44 | testCoverageEnabled true 45 | } 46 | } 47 | 48 | packagingOptions { 49 | exclude 'LICENSE.txt' 50 | } 51 | } 52 | 53 | publish { 54 | // Bintray Properties 55 | // Run Command ./gradlew clean build bintrayUpload -PdryRun=false 56 | bintrayUser = BINTRAY_USER 57 | bintrayKey = BINTRAY_API_KEY 58 | 59 | userOrg = BINTRAY_USER 60 | groupId = GROUP_ID 61 | uploadName = 'RxAnimations' 62 | artifactId = 'rxanimations' 63 | publishVersion = VERSION_NAME 64 | description = 'Android animation observable binding.' 65 | website = rootProject.ext.siteUrl 66 | } 67 | -------------------------------------------------------------------------------- /rxanimation/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/shunhosaka/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 | -------------------------------------------------------------------------------- /rxanimation/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /rxanimation/src/androidTest/java/com/reyurnible/rxanimation/RecordingObserver.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation; 2 | 3 | import android.util.Log; 4 | 5 | import java.util.NoSuchElementException; 6 | import java.util.concurrent.BlockingDeque; 7 | import java.util.concurrent.LinkedBlockingDeque; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import rx.Observer; 11 | 12 | import static com.google.common.truth.Truth.assertThat; 13 | 14 | public final class RecordingObserver implements Observer { 15 | private static final String TAG = "RecordingObserver"; 16 | 17 | private final BlockingDeque events = new LinkedBlockingDeque<>(); 18 | 19 | @Override 20 | public void onCompleted() { 21 | Log.v(TAG, "onCompleted"); 22 | events.addLast(new OnCompleted()); 23 | } 24 | 25 | @Override 26 | public void onError(Throwable e) { 27 | Log.v(TAG, "onError", e); 28 | events.addLast(new OnError(e)); 29 | } 30 | 31 | @Override 32 | public void onNext(T t) { 33 | Log.v(TAG, "onNext " + t); 34 | events.addLast(new OnNext(t)); 35 | } 36 | 37 | private E takeEvent(Class wanted) { 38 | Object event; 39 | try { 40 | event = events.pollFirst(1, TimeUnit.SECONDS); 41 | } catch (InterruptedException e) { 42 | throw new RuntimeException(e); 43 | } 44 | if (event == null) { 45 | throw new NoSuchElementException( 46 | "No event found while waiting for " + wanted.getSimpleName()); 47 | } 48 | assertThat(event).isInstanceOf(wanted); 49 | return wanted.cast(event); 50 | } 51 | 52 | public T takeNext() { 53 | OnNext event = takeEvent(OnNext.class); 54 | return event.value; 55 | } 56 | 57 | public Throwable takeError() { 58 | return takeEvent(OnError.class).throwable; 59 | } 60 | 61 | public void assertOnCompleted() { 62 | takeEvent(OnCompleted.class); 63 | } 64 | 65 | public void assertNoMoreEvents() { 66 | try { 67 | Object event = takeEvent(Object.class); 68 | throw new IllegalStateException("Expected no more events but got " + event); 69 | } catch (NoSuchElementException ignored) { 70 | } 71 | } 72 | 73 | private final class OnNext { 74 | final T value; 75 | 76 | private OnNext(T value) { 77 | this.value = value; 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return "OnNext[" + value + "]"; 83 | } 84 | } 85 | 86 | private final class OnCompleted { 87 | @Override 88 | public String toString() { 89 | return "OnCompleted"; 90 | } 91 | } 92 | 93 | private final class OnError { 94 | private final Throwable throwable; 95 | 96 | private OnError(Throwable throwable) { 97 | this.throwable = throwable; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "OnError[" + throwable + "]"; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /rxanimation/src/androidTest/java/com/reyurnible/rxanimation/RxAnimationTest.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation; 2 | 3 | import android.app.Instrumentation; 4 | import android.os.SystemClock; 5 | import android.support.test.InstrumentationRegistry; 6 | import android.support.test.rule.ActivityTestRule; 7 | import android.support.test.runner.AndroidJUnit4; 8 | import android.view.View; 9 | import android.view.animation.AlphaAnimation; 10 | 11 | import com.reyurnible.rxanimation.animation.AnimationEvent; 12 | import com.reyurnible.rxanimation.animation.RxAnimation; 13 | 14 | import org.junit.Before; 15 | import org.junit.Rule; 16 | import org.junit.Test; 17 | import org.junit.runner.RunWith; 18 | 19 | import rx.Subscription; 20 | import rx.android.schedulers.AndroidSchedulers; 21 | 22 | import static com.google.common.truth.Truth.assertThat; 23 | 24 | @RunWith(AndroidJUnit4.class) 25 | public final class RxAnimationTest { 26 | @Rule 27 | public final ActivityTestRule activityRule = 28 | new ActivityTestRule<>(TestActivity.class); 29 | 30 | private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 31 | private View child; 32 | 33 | @Before 34 | public void setUp() { 35 | TestActivity activity = activityRule.getActivity(); 36 | child = activity.child; 37 | } 38 | 39 | @Test 40 | public void events() throws InterruptedException { 41 | RecordingObserver o = new RecordingObserver<>(); 42 | 43 | final AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f); 44 | alphaAnimation.setStartOffset(0); 45 | alphaAnimation.setDuration(100); 46 | 47 | final Subscription subscription = RxAnimation.events(alphaAnimation, child).subscribeOn(AndroidSchedulers.mainThread()).subscribe(o); 48 | { // ロードが完了するまで待つ 49 | waitForLoadingFinished(500); 50 | // Stack Start event 51 | assertThat(o.takeNext().kind() == AnimationEvent.Kind.START); 52 | // Stack End event 53 | assertThat(o.takeNext().kind() == AnimationEvent.Kind.END); 54 | } 55 | // Checking complete 56 | { 57 | waitForLoadingFinished(2000); 58 | o.assertOnCompleted(); 59 | } 60 | subscription.unsubscribe(); 61 | o.assertNoMoreEvents(); 62 | } 63 | 64 | @Test 65 | public void repeat() throws InterruptedException { 66 | RecordingObserver o = new RecordingObserver<>(); 67 | 68 | final AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f); 69 | alphaAnimation.setStartOffset(0); 70 | alphaAnimation.setDuration(100); 71 | alphaAnimation.setRepeatCount(2); 72 | final Subscription subscription = RxAnimation.events(alphaAnimation, child).subscribeOn(AndroidSchedulers.mainThread()).subscribe(o); 73 | { // ロードが完了するまで待つ 74 | waitForLoadingFinished(500); 75 | // Stack Start event 76 | assertThat(o.takeNext().kind() == AnimationEvent.Kind.START); 77 | // Stack Repeat event 78 | assertThat(o.takeNext().kind() == AnimationEvent.Kind.REPEAT); 79 | // One more repeat 80 | assertThat(o.takeNext().kind() == AnimationEvent.Kind.REPEAT); 81 | // Stack End event 82 | assertThat(o.takeNext().kind() == AnimationEvent.Kind.END); 83 | } 84 | // Checking complete 85 | { 86 | waitForLoadingFinished(2000); 87 | o.assertOnCompleted(); 88 | } 89 | subscription.unsubscribe(); 90 | o.assertNoMoreEvents(); 91 | } 92 | 93 | public static void waitForLoadingFinished(int timeout) { 94 | long t = SystemClock.elapsedRealtime(); 95 | do { 96 | SystemClock.sleep(100); 97 | } while (SystemClock.elapsedRealtime() - t < timeout); 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /rxanimation/src/androidTest/java/com/reyurnible/rxanimation/RxAnimationTestRunner.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation; 2 | 3 | import android.app.KeyguardManager; 4 | import android.content.Context; 5 | import android.os.PowerManager; 6 | import android.support.test.runner.AndroidJUnitRunner; 7 | 8 | import static android.content.Context.KEYGUARD_SERVICE; 9 | import static android.content.Context.POWER_SERVICE; 10 | import static android.os.PowerManager.ACQUIRE_CAUSES_WAKEUP; 11 | import static android.os.PowerManager.FULL_WAKE_LOCK; 12 | import static android.os.PowerManager.ON_AFTER_RELEASE; 13 | 14 | public final class RxAnimationTestRunner extends AndroidJUnitRunner { 15 | private PowerManager.WakeLock wakeLock; 16 | 17 | @Override 18 | public void onStart() { 19 | Context app = getTargetContext().getApplicationContext(); 20 | 21 | String name = RxAnimationTestRunner.class.getSimpleName(); 22 | // Unlock the device so that the tests can input keystrokes. 23 | KeyguardManager keyguard = (KeyguardManager) app.getSystemService(KEYGUARD_SERVICE); 24 | keyguard.newKeyguardLock(name).disableKeyguard(); 25 | // Wake up the screen. 26 | PowerManager power = (PowerManager) app.getSystemService(POWER_SERVICE); 27 | wakeLock = power.newWakeLock(FULL_WAKE_LOCK | ACQUIRE_CAUSES_WAKEUP | ON_AFTER_RELEASE, name); 28 | wakeLock.acquire(); 29 | 30 | super.onStart(); 31 | } 32 | 33 | @Override 34 | public void onDestroy() { 35 | super.onDestroy(); 36 | 37 | wakeLock.release(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rxanimation/src/androidTest/java/com/reyurnible/rxanimation/RxAnimatorTest.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.animation.ValueAnimator; 5 | import android.app.Instrumentation; 6 | import android.os.SystemClock; 7 | import android.support.test.InstrumentationRegistry; 8 | import android.support.test.rule.ActivityTestRule; 9 | import android.support.test.runner.AndroidJUnit4; 10 | import android.view.View; 11 | 12 | import com.reyurnible.rxanimation.animator.AnimatorEvent; 13 | import com.reyurnible.rxanimation.animator.AnimatorUpdateEvent; 14 | import com.reyurnible.rxanimation.animator.RxAnimator; 15 | 16 | import org.junit.Before; 17 | import org.junit.Rule; 18 | import org.junit.Test; 19 | import org.junit.runner.RunWith; 20 | 21 | import rx.Subscription; 22 | import rx.android.schedulers.AndroidSchedulers; 23 | 24 | import static com.google.common.truth.Truth.assertThat; 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public final class RxAnimatorTest { 28 | @Rule 29 | public final ActivityTestRule activityRule = 30 | new ActivityTestRule<>(TestActivity.class); 31 | 32 | private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 33 | private View child; 34 | 35 | @Before 36 | public void setUp() { 37 | TestActivity activity = activityRule.getActivity(); 38 | child = activity.child; 39 | } 40 | 41 | @Test 42 | public void events() throws InterruptedException { 43 | RecordingObserver o = new RecordingObserver<>(); 44 | 45 | ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f); 46 | alphaAnimator.setDuration(100); 47 | 48 | final Subscription subscription = RxAnimator.events(alphaAnimator).subscribeOn(AndroidSchedulers.mainThread()).subscribe(o); 49 | { // ロードが完了するまで待つ 50 | waitForLoadingFinished(500); 51 | // Stack Start event 52 | assertThat(o.takeNext().kind() == AnimatorEvent.Kind.START); 53 | // Stack End event 54 | assertThat(o.takeNext().kind() == AnimatorEvent.Kind.END); 55 | } 56 | // Checking complete 57 | { 58 | waitForLoadingFinished(2000); 59 | o.assertOnCompleted(); 60 | } 61 | subscription.unsubscribe(); 62 | o.assertNoMoreEvents(); 63 | } 64 | 65 | @Test 66 | public void updates() throws InterruptedException { 67 | RecordingObserver o = new RecordingObserver<>(); 68 | 69 | ValueAnimator animator = ValueAnimator.ofInt(0, 3); 70 | animator.setDuration(100); 71 | 72 | final Subscription subscription = RxAnimator.updateEvents(animator).subscribeOn(AndroidSchedulers.mainThread()).subscribe(o); 73 | { // ロードが完了するまで待つ 74 | waitForLoadingFinished(500); 75 | // Event 0 76 | assertThat(o.takeNext() != null); 77 | // Event 1 78 | assertThat(o.takeNext() != null); 79 | // Event 2 80 | assertThat(o.takeNext() != null); 81 | } 82 | subscription.unsubscribe(); 83 | } 84 | 85 | 86 | public static void waitForLoadingFinished(int timeout) { 87 | long t = SystemClock.elapsedRealtime(); 88 | do { 89 | SystemClock.sleep(100); 90 | } while (SystemClock.elapsedRealtime() - t < timeout); 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /rxanimation/src/androidTest/java/com/reyurnible/rxanimation/TestActivity.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.FrameLayout; 7 | 8 | public class TestActivity extends Activity { 9 | 10 | View child; 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | FrameLayout parent = new FrameLayout(this); 16 | child = new View(this); 17 | parent.addView(child); 18 | setContentView(parent); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rxanimation/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animation/AnimationEvent.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animation; 2 | 3 | import android.support.annotation.CheckResult; 4 | import android.support.annotation.NonNull; 5 | import android.view.animation.Animation; 6 | 7 | /** 8 | * A android animation event occurred. 9 | */ 10 | public class AnimationEvent { 11 | 12 | /** 13 | * Animation event kinds. 14 | */ 15 | public enum Kind { 16 | START, END, REPEAT 17 | } 18 | 19 | @CheckResult 20 | @NonNull 21 | public static AnimationEvent create(@NonNull Animation animation, @NonNull Kind kind) { 22 | return new AnimationEvent(animation, kind); 23 | } 24 | 25 | private final Kind kind; 26 | private Animation animation; 27 | 28 | private AnimationEvent(Animation animation, Kind kind) { 29 | this.animation = animation; 30 | this.kind = kind; 31 | } 32 | 33 | @NonNull 34 | public Animation animation() { 35 | return animation; 36 | } 37 | 38 | @NonNull 39 | public Kind kind() { 40 | return kind; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object object) { 45 | if (object == this) return true; 46 | if (!(object instanceof AnimationEvent)) return false; 47 | AnimationEvent other = (AnimationEvent) object; 48 | return other.animation() == animation() 49 | && other.kind() == kind(); 50 | } 51 | 52 | @Override 53 | public int hashCode() { 54 | int result = 17; 55 | result = result * 37 + animation().hashCode(); 56 | result = result * 37 + kind().hashCode(); 57 | return result; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return "AnimationEvent{animation=" 63 | + animation() 64 | + ", kind=" 65 | + kind() 66 | + "}"; 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animation/AnimationEventOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animation; 2 | 3 | import android.view.View; 4 | import android.view.animation.Animation; 5 | 6 | import com.reyurnible.rxanimation.internal.MainThreadSubscription; 7 | 8 | import rx.Observable; 9 | import rx.Subscriber; 10 | 11 | import static com.reyurnible.rxanimation.internal.Preconditions.checkUiThread; 12 | 13 | /** 14 | * A animation event observer. 15 | */ 16 | final class AnimationEventOnSubscribe implements Observable.OnSubscribe { 17 | private final Animation animation; 18 | private final View view; 19 | 20 | AnimationEventOnSubscribe(Animation animation, View view) { 21 | this.animation = animation; 22 | this.view = view; 23 | } 24 | 25 | @Override 26 | public void call(final Subscriber subscriber) { 27 | checkUiThread(); 28 | 29 | Animation.AnimationListener listener = new Animation.AnimationListener() { 30 | @Override 31 | public void onAnimationStart(Animation animation) { 32 | if (!subscriber.isUnsubscribed()) { 33 | subscriber.onNext(AnimationEvent.create(animation, AnimationEvent.Kind.START)); 34 | } 35 | } 36 | 37 | @Override 38 | public void onAnimationEnd(Animation animation) { 39 | if (!subscriber.isUnsubscribed()) { 40 | subscriber.onNext(AnimationEvent.create(animation, AnimationEvent.Kind.END)); 41 | subscriber.onCompleted(); 42 | } 43 | } 44 | 45 | @Override 46 | public void onAnimationRepeat(Animation animation) { 47 | if (!subscriber.isUnsubscribed()) { 48 | subscriber.onNext(AnimationEvent.create(animation, AnimationEvent.Kind.REPEAT)); 49 | } 50 | } 51 | }; 52 | animation.start(); 53 | animation.setAnimationListener(listener); 54 | // subscribeするタイミングはユーザーが決められるのでsetではなくstartにする 55 | view.startAnimation(animation); 56 | 57 | subscriber.add(new MainThreadSubscription() { 58 | @Override 59 | protected void onUnSubscribe() { 60 | animation.setAnimationListener(null); 61 | } 62 | }); 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animation/RxAnimation.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animation; 2 | 3 | import android.support.annotation.CheckResult; 4 | import android.support.annotation.NonNull; 5 | import android.view.View; 6 | import android.view.animation.Animation; 7 | 8 | import rx.Observable; 9 | 10 | /** 11 | * Static factory methods for animation event observable. 12 | */ 13 | public final class RxAnimation { 14 | 15 | /** 16 | * Create an animation event observable. 17 | */ 18 | @CheckResult 19 | @NonNull 20 | public static Observable events(@NonNull Animation animation, @NonNull View view) { 21 | return Observable.create(new AnimationEventOnSubscribe(animation, view)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animator/AnimatorEvent.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animator; 2 | 3 | import android.animation.Animator; 4 | import android.support.annotation.CheckResult; 5 | import android.support.annotation.NonNull; 6 | 7 | /** 8 | * A android animator event occurred. 9 | */ 10 | public class AnimatorEvent { 11 | 12 | public enum Kind { 13 | START, END, CANCEL, REPEAT 14 | } 15 | 16 | @CheckResult 17 | @NonNull 18 | public static AnimatorEvent create(@NonNull Animator animation, @NonNull Kind kind) { 19 | return new AnimatorEvent(animation, kind); 20 | } 21 | 22 | private final Kind kind; 23 | private Animator animation; 24 | 25 | private AnimatorEvent(Animator animation, Kind kind) { 26 | this.animation = animation; 27 | this.kind = kind; 28 | } 29 | 30 | @NonNull 31 | public Animator animation() { 32 | return animation; 33 | } 34 | 35 | @NonNull 36 | public Kind kind() { 37 | return kind; 38 | } 39 | 40 | @Override 41 | public boolean equals(Object object) { 42 | if (object == this) return true; 43 | if (!(object instanceof AnimatorEvent)) return false; 44 | AnimatorEvent other = (AnimatorEvent) object; 45 | return other.animation() == animation() 46 | && other.kind() == kind(); 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | int result = 17; 52 | result = result * 37 + animation().hashCode(); 53 | result = result * 37 + kind().hashCode(); 54 | return result; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return "AnimatorEvent{animation=" 60 | + animation() 61 | + ", kind=" 62 | + kind() 63 | + "}"; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animator/AnimatorEventOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animator; 2 | 3 | import android.animation.Animator; 4 | import android.support.annotation.NonNull; 5 | 6 | import com.reyurnible.rxanimation.internal.MainThreadSubscription; 7 | 8 | import rx.Observable; 9 | import rx.Subscriber; 10 | 11 | import static com.reyurnible.rxanimation.internal.Preconditions.checkUiThread; 12 | 13 | /** 14 | * A animator event observer. 15 | */ 16 | final class AnimatorEventOnSubscribe implements Observable.OnSubscribe { 17 | private final Animator animation; 18 | 19 | AnimatorEventOnSubscribe(@NonNull Animator animation) { 20 | this.animation = animation; 21 | } 22 | 23 | @Override 24 | public void call(final Subscriber subscriber) { 25 | checkUiThread(); 26 | 27 | final Animator.AnimatorListener listener = new Animator.AnimatorListener() { 28 | @Override 29 | public void onAnimationStart(Animator animation) { 30 | if (!subscriber.isUnsubscribed()) { 31 | subscriber.onNext(AnimatorEvent.create(animation, AnimatorEvent.Kind.START)); 32 | } 33 | } 34 | 35 | @Override 36 | public void onAnimationEnd(Animator animation) { 37 | if (!subscriber.isUnsubscribed()) { 38 | subscriber.onNext(AnimatorEvent.create(animation, AnimatorEvent.Kind.END)); 39 | subscriber.onCompleted(); 40 | } 41 | } 42 | 43 | @Override 44 | public void onAnimationCancel(Animator animation) { 45 | if (!subscriber.isUnsubscribed()) { 46 | subscriber.onNext(AnimatorEvent.create(animation, AnimatorEvent.Kind.CANCEL)); 47 | } 48 | } 49 | 50 | @Override 51 | public void onAnimationRepeat(Animator animation) { 52 | if (!subscriber.isUnsubscribed()) { 53 | subscriber.onNext(AnimatorEvent.create(animation, AnimatorEvent.Kind.REPEAT)); 54 | } 55 | } 56 | }; 57 | animation.addListener(listener); 58 | 59 | animation.start(); 60 | 61 | subscriber.add(new MainThreadSubscription() { 62 | @Override 63 | protected void onUnSubscribe() { 64 | animation.removeListener(listener); 65 | } 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animator/AnimatorPauseEvent.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animator; 2 | 3 | import android.animation.Animator; 4 | import android.support.annotation.CheckResult; 5 | import android.support.annotation.NonNull; 6 | 7 | /** 8 | * A android animator pause event occurred. 9 | */ 10 | public class AnimatorPauseEvent { 11 | 12 | public enum Kind { 13 | PAUSE, RESUME 14 | } 15 | 16 | @CheckResult @NonNull 17 | public static AnimatorPauseEvent create(@NonNull Animator animation, @NonNull Kind kind) { 18 | return new AnimatorPauseEvent(animation, kind); 19 | } 20 | 21 | private final Kind kind; 22 | private Animator animation; 23 | 24 | private AnimatorPauseEvent(Animator animation, Kind kind) { 25 | this.animation = animation; 26 | this.kind = kind; 27 | } 28 | 29 | @NonNull 30 | public Animator animation() { 31 | return animation; 32 | } 33 | 34 | @NonNull 35 | public Kind kind() { 36 | return kind; 37 | } 38 | 39 | @Override public boolean equals(Object object) { 40 | if (object == this) return true; 41 | if (!(object instanceof AnimatorPauseEvent)) return false; 42 | AnimatorPauseEvent other = (AnimatorPauseEvent) object; 43 | return other.animation() == animation() 44 | && other.kind() == kind(); 45 | } 46 | 47 | @Override public int hashCode() { 48 | int result = 17; 49 | result = result * 37 + animation().hashCode(); 50 | result = result * 37 + kind().hashCode(); 51 | return result; 52 | } 53 | 54 | @Override public String toString() { 55 | return "AnimatorPauseEvent{animation=" 56 | + animation() 57 | + ", kind=" 58 | + kind() 59 | + "}"; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animator/AnimatorPauseEventOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animator; 2 | 3 | import android.animation.Animator; 4 | import android.annotation.TargetApi; 5 | import android.os.Build; 6 | 7 | import com.reyurnible.rxanimation.internal.MainThreadSubscription; 8 | 9 | import rx.Observable; 10 | import rx.Subscriber; 11 | 12 | import static com.reyurnible.rxanimation.internal.Preconditions.checkUiThread; 13 | 14 | /** 15 | * A animator pause event observer. 16 | */ 17 | @TargetApi(Build.VERSION_CODES.KITKAT) 18 | public class AnimatorPauseEventOnSubscribe implements Observable.OnSubscribe { 19 | private final Animator animation; 20 | 21 | AnimatorPauseEventOnSubscribe(Animator animation) { 22 | this.animation = animation; 23 | } 24 | 25 | @Override 26 | public void call(final Subscriber subscriber) { 27 | checkUiThread(); 28 | 29 | final Animator.AnimatorPauseListener listener = new Animator.AnimatorPauseListener() { 30 | @Override 31 | public void onAnimationPause(Animator animation) { 32 | if (!subscriber.isUnsubscribed()) { 33 | subscriber.onNext(AnimatorPauseEvent.create(animation, AnimatorPauseEvent.Kind.PAUSE)); 34 | } 35 | } 36 | 37 | @Override 38 | public void onAnimationResume(Animator animation) { 39 | if (!subscriber.isUnsubscribed()) { 40 | subscriber.onNext(AnimatorPauseEvent.create(animation, AnimatorPauseEvent.Kind.RESUME)); 41 | } 42 | } 43 | }; 44 | animation.addPauseListener(listener); 45 | 46 | animation.start(); 47 | 48 | subscriber.add(new MainThreadSubscription() { 49 | @Override 50 | protected void onUnSubscribe() { 51 | animation.removePauseListener(listener); 52 | } 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animator/AnimatorUpdateEvent.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animator; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.support.annotation.NonNull; 6 | 7 | /** 8 | * A android animation update event occurred. 9 | */ 10 | public class AnimatorUpdateEvent { 11 | 12 | public static AnimatorUpdateEvent create(@NonNull ValueAnimator animation) { 13 | return new AnimatorUpdateEvent(animation); 14 | } 15 | 16 | private ValueAnimator animation; 17 | 18 | public AnimatorUpdateEvent(ValueAnimator animation) { 19 | this.animation = animation; 20 | } 21 | 22 | @NonNull 23 | public Animator animation() { 24 | return animation; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) return true; 30 | if (o == null || getClass() != o.getClass()) return false; 31 | AnimatorUpdateEvent that = (AnimatorUpdateEvent) o; 32 | return animation != null ? animation.equals(that.animation) : that.animation == null; 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return animation != null ? animation.hashCode() : 0; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "AnimatorUpdateEvent{" + 43 | "animation=" + animation + 44 | '}'; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animator/AnimatorUpdateEventOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animator; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.support.annotation.NonNull; 5 | 6 | import com.reyurnible.rxanimation.internal.MainThreadSubscription; 7 | 8 | import rx.Observable; 9 | import rx.Subscriber; 10 | 11 | import static com.reyurnible.rxanimation.internal.Preconditions.checkUiThread; 12 | 13 | /** 14 | * A animation update event observer. 15 | */ 16 | final class AnimatorUpdateEventOnSubscribe implements Observable.OnSubscribe { 17 | private final ValueAnimator animation; 18 | 19 | AnimatorUpdateEventOnSubscribe(@NonNull ValueAnimator animation) { 20 | this.animation = animation; 21 | } 22 | 23 | @Override 24 | public void call(final Subscriber subscriber) { 25 | checkUiThread(); 26 | 27 | final ValueAnimator.AnimatorUpdateListener listener = new ValueAnimator.AnimatorUpdateListener() { 28 | @Override 29 | public void onAnimationUpdate(ValueAnimator animation) { 30 | if (!subscriber.isUnsubscribed()) { 31 | subscriber.onNext(AnimatorUpdateEvent.create(animation)); 32 | } 33 | } 34 | }; 35 | animation.addUpdateListener(listener); 36 | animation.start(); 37 | 38 | subscriber.add(new MainThreadSubscription() { 39 | @Override 40 | protected void onUnSubscribe() { 41 | animation.removeUpdateListener(listener); 42 | } 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/animator/RxAnimator.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.animator; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.annotation.TargetApi; 6 | import android.os.Build; 7 | import android.support.annotation.CheckResult; 8 | import android.support.annotation.NonNull; 9 | 10 | import rx.Observable; 11 | 12 | /** 13 | * Static factory methods for animator event observable. 14 | */ 15 | public final class RxAnimator { 16 | 17 | @CheckResult 18 | @NonNull 19 | public static Observable events(@NonNull Animator animation) { 20 | return Observable.create(new AnimatorEventOnSubscribe(animation)); 21 | } 22 | 23 | @TargetApi(Build.VERSION_CODES.KITKAT) 24 | @CheckResult 25 | @NonNull 26 | public static Observable pauseEvents(@NonNull Animator animation) { 27 | return Observable.create(new AnimatorPauseEventOnSubscribe(animation)); 28 | } 29 | 30 | @CheckResult 31 | @NonNull 32 | public static Observable updateEvents(@NonNull ValueAnimator animation) { 33 | return Observable.create(new AnimatorUpdateEventOnSubscribe(animation)); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/internal/MainThreadSubscription.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.internal; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.support.annotation.Keep; 6 | 7 | import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; 8 | 9 | import rx.Subscription; 10 | 11 | /** 12 | * Checking main thread subscription helper. 13 | */ 14 | public abstract class MainThreadSubscription implements Subscription, Runnable { 15 | private static final Handler mainThread = new Handler(Looper.getMainLooper()); 16 | 17 | @Keep 18 | @SuppressWarnings("unused") // Updated by 'unsubscribedUpdater' object. 19 | private volatile int unsubscribed; 20 | private static final AtomicIntegerFieldUpdater unsubscribedUpdater = 21 | AtomicIntegerFieldUpdater.newUpdater(MainThreadSubscription.class, "unsubscribed"); 22 | 23 | @Override 24 | public final boolean isUnsubscribed() { 25 | return unsubscribed != 0; 26 | } 27 | 28 | @Override 29 | public final void unsubscribe() { 30 | if (unsubscribedUpdater.compareAndSet(this, 0, 1)) { 31 | if (Looper.getMainLooper() == Looper.myLooper()) { 32 | onUnSubscribe(); 33 | } else { 34 | mainThread.post(this); 35 | } 36 | } 37 | } 38 | 39 | @Override 40 | public final void run() { 41 | onUnSubscribe(); 42 | } 43 | 44 | protected abstract void onUnSubscribe(); 45 | } 46 | -------------------------------------------------------------------------------- /rxanimation/src/main/java/com/reyurnible/rxanimation/internal/Preconditions.java: -------------------------------------------------------------------------------- 1 | package com.reyurnible.rxanimation.internal; 2 | 3 | import android.os.Looper; 4 | 5 | /** 6 | * Check condition static function. 7 | */ 8 | public final class Preconditions { 9 | public static void checkArgument(boolean assertion, String message) { 10 | if (!assertion) { 11 | throw new IllegalArgumentException(message); 12 | } 13 | } 14 | 15 | public static void checkUiThread() { 16 | if (Looper.getMainLooper() != Looper.myLooper()) { 17 | throw new IllegalStateException( 18 | "Must be called from the main thread. Was: " + Thread.currentThread()); 19 | } 20 | } 21 | 22 | private Preconditions() { 23 | throw new AssertionError("No instance"); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /rxanimation/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RxAnimation 3 | 4 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | dependencies { 3 | classpath rootProject.ext.androidPlugin 4 | classpath rootProject.ext.kotlinPlugin 5 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion" 6 | } 7 | repositories { 8 | mavenCentral() 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.application' 13 | apply plugin: 'kotlin-android' 14 | 15 | android { 16 | compileSdkVersion rootProject.ext.compileSdkVersion 17 | buildToolsVersion rootProject.ext.buildToolsVersion 18 | 19 | defaultConfig { 20 | applicationId "com.reyurnible.android.sample" 21 | minSdkVersion 14 22 | targetSdkVersion 23 23 | versionCode 1 24 | versionName "1.0" 25 | } 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/kotlin' 34 | } 35 | } 36 | 37 | dependencies { 38 | compile fileTree(dir: 'libs', include: ['*.jar']) 39 | testCompile 'junit:junit:4.12' 40 | compile 'com.android.support:appcompat-v7:23.1.1' 41 | // Kotlin 42 | compile rootProject.ext.kotlinStdlib 43 | // KotterKnife 44 | compile 'com.jakewharton:kotterknife:0.1.0-SNAPSHOT' 45 | // RxAnimation 46 | compile project(":rxanimation-kotlin") 47 | compile "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.ext.kotlinVersion" 48 | } 49 | repositories { 50 | mavenCentral() 51 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } 52 | } 53 | -------------------------------------------------------------------------------- /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/shunhosaka/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/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sample/src/main/java/com/reyurnible/android/sample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.reyurnible.android.sample 2 | 3 | import android.animation.ObjectAnimator 4 | import android.os.Bundle 5 | import android.support.v7.app.AppCompatActivity 6 | import android.util.Log 7 | import android.view.View 8 | import android.view.animation.AlphaAnimation 9 | import android.widget.Button 10 | import android.widget.EditText 11 | import android.widget.RelativeLayout 12 | import android.widget.TextView 13 | import butterknife.bindView 14 | import com.reyurnible.rxanimation.animation.AnimationEvent 15 | import com.reyurnible.rxanimation.animator.AnimatorEvent 16 | import com.reyurnible.rxanimation_kotlin.animation.events 17 | import rx.Observable 18 | 19 | class MainActivity : AppCompatActivity() { 20 | 21 | companion object { 22 | const val TAG = "MainActivity" 23 | } 24 | 25 | val loginButton: Button by bindView(R.id.main_button_login) 26 | val formLayout: RelativeLayout by bindView(R.id.main_layout_form) 27 | val mailEditText: EditText by bindView(R.id.main_edittext_mail) 28 | val passEditText: EditText by bindView(R.id.main_edittext_pass) 29 | val signupTextView: TextView by bindView(R.id.main_textview_signup) 30 | val forgotTextView: TextView by bindView(R.id.main_textview_forgot) 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | super.onCreate(savedInstanceState) 34 | setContentView(R.layout.activity_main) 35 | // initialize invisible 36 | formLayout.visibility = View.INVISIBLE 37 | loginButton.setOnClickListener { startAnimations() } 38 | } 39 | 40 | private fun startAnimations() { 41 | formLayout.visibility = View.VISIBLE 42 | mailEditText.visibility = View.INVISIBLE 43 | passEditText.visibility = View.INVISIBLE 44 | signupTextView.visibility = View.INVISIBLE 45 | forgotTextView.visibility = View.INVISIBLE 46 | 47 | val alphaAnimation: AlphaAnimation = AlphaAnimation(0.0f, 1.0f) 48 | alphaAnimation.duration = 3000 49 | val alphaAnimationObserver = alphaAnimation.events(formLayout).map { 50 | Log.d(TAG, it.toString()) 51 | it.kind() == AnimationEvent.Kind.END 52 | } 53 | 54 | val translationAnimator: ObjectAnimator = ObjectAnimator.ofFloat(formLayout, "translationY", -1000f, 0f) 55 | translationAnimator.duration = 3000 56 | val translateAnimatorObserver = translationAnimator.events().map { 57 | Log.d(TAG, it.toString()) 58 | it.kind() == AnimatorEvent.Kind.END 59 | } 60 | 61 | Observable.combineLatest(translateAnimatorObserver, alphaAnimationObserver, { t1, t2 -> 62 | Log.d(TAG, "T1:" + t1.toString()) 63 | Log.d(TAG, "T2:" + t2.toString()) 64 | t1.and(t2) 65 | }).filter { it }.subscribe { validate -> 66 | // post event 67 | Log.d(TAG, "End All Animation") 68 | mailEditText.visibility = View.VISIBLE 69 | passEditText.visibility = View.VISIBLE 70 | signupTextView.visibility = View.VISIBLE 71 | forgotTextView.visibility = View.VISIBLE 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/bg_form.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 |