├── .buckconfig
├── .gitignore
├── .travis.yml
├── 01.gif
├── BUCK
├── LICENSE
├── PATENTS
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── project.properties
├── rebound-android-example
├── BUCK
├── build.gradle
├── debug.keystore
├── debug.keystore.properties
├── proguard-project.txt
├── project.properties
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── facebook
│ │ └── rebound
│ │ └── example
│ │ └── MainActivity.java
│ └── res
│ ├── drawable-hdpi
│ ├── landscape.png
│ └── rebound_icon.png
│ ├── drawable-mdpi
│ ├── landscape.png
│ └── rebound_icon.png
│ ├── drawable-xhdpi
│ ├── landscape.png
│ └── rebound_icon.png
│ ├── drawable-xxhdpi
│ ├── landscape.png
│ └── rebound_icon.png
│ └── layout
│ └── main.xml
├── rebound-android-playground
├── BUCK
├── build.gradle
├── debug.keystore
├── debug.keystore.properties
├── gradle.properties
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── facebook
│ │ └── rebound
│ │ └── playground
│ │ ├── app
│ │ ├── ExampleContainerView.java
│ │ ├── PlaygroundActivity.java
│ │ ├── RowView.java
│ │ └── Util.java
│ │ └── examples
│ │ ├── BallExample.java
│ │ ├── OrigamiExample.java
│ │ ├── PhotoGalleryExample.java
│ │ ├── SimpleExample.java
│ │ ├── SpringChainExample.java
│ │ ├── SpringScrollViewExample.java
│ │ └── scrollview
│ │ ├── ExampleRowView.java
│ │ ├── SpringOverScroller.java
│ │ └── SpringScrollView.java
│ └── res
│ ├── drawable-hdpi
│ ├── ic_launcher.png
│ ├── landscape.png
│ └── rebound_icon.png
│ ├── drawable-mdpi
│ ├── ic_launcher.png
│ ├── landscape.png
│ └── rebound_icon.png
│ ├── drawable-nodpi
│ ├── d1.jpg
│ ├── d10.jpg
│ ├── d11.jpg
│ ├── d12.jpg
│ ├── d2.jpg
│ ├── d3.jpg
│ ├── d4.jpg
│ ├── d5.jpg
│ ├── d6.jpg
│ ├── d7.jpg
│ ├── d8.jpg
│ └── d9.jpg
│ ├── drawable-xhdpi
│ ├── feedback.png
│ ├── grid.png
│ ├── ic_launcher.png
│ ├── landscape.png
│ ├── rebound_icon.png
│ └── selected_photo.png
│ ├── drawable-xxhdpi
│ ├── ic_launcher.png
│ ├── landscape.png
│ └── rebound_icon.png
│ ├── drawable
│ ├── rebound_tiles.xml
│ └── row_background.xml
│ ├── layout
│ ├── activity_playground.xml
│ ├── cascade_effect.xml
│ ├── example_row_view.xml
│ ├── origami_example.xml
│ ├── photo_scale_example.xml
│ ├── row_view.xml
│ └── spring_scroll_view_example.xml
│ ├── menu
│ └── playground.xml
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── rebound-android
├── BUCK
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── facebook
│ └── rebound
│ ├── AndroidSpringLooperFactory.java
│ ├── AnimationQueue.java
│ ├── ChoreographerCompat.java
│ ├── SpringChain.java
│ ├── SpringSystem.java
│ └── ui
│ ├── SpringConfiguratorView.java
│ └── Util.java
├── rebound-core
├── BUCK
├── build.gradle
├── libs
│ ├── hamcrest-core-1.3.jar
│ ├── junit-4.11.jar
│ └── mockito-all-1.9.5.jar
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── facebook
│ │ └── rebound
│ │ ├── BaseSpringSystem.java
│ │ ├── BouncyConversion.java
│ │ ├── OrigamiValueConverter.java
│ │ ├── SimpleSpringListener.java
│ │ ├── Spring.java
│ │ ├── SpringConfig.java
│ │ ├── SpringConfigRegistry.java
│ │ ├── SpringListener.java
│ │ ├── SpringLooper.java
│ │ ├── SpringSystemListener.java
│ │ ├── SpringUtil.java
│ │ ├── SteppingLooper.java
│ │ └── SynchronousLooper.java
│ └── test
│ └── java
│ └── com
│ └── facebook
│ └── rebound
│ ├── SpringConfigRegistryTest.java
│ ├── SpringSystemTest.java
│ └── SpringTest.java
├── release_process.txt
└── settings.gradle
/.buckconfig:
--------------------------------------------------------------------------------
1 | [alias]
2 | android = //rebound-android:src
3 | app = //rebound-android-example:bin
4 | core = //rebound-core:src
5 | playground = //rebound-android-playground:bin
6 | rebound = //:rebound
7 | test = //rebound-core:test
8 |
9 | [project]
10 | ignore = .git
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea
3 | .gradle
4 | gen
5 | local.properties
6 | buck-*
7 | .buckd
8 | out
9 | build
10 | .DS_Store
11 | _site
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk: oraclejdk7
3 | env:
4 | matrix:
5 | - ANDROID_SDKS=android-22,sysimg-22 ANDROID_TARGET=android-22 ANDROID_ABI=armeabi-v7a
6 | before_install:
7 | # Install base Android SDK.
8 | - sudo apt-get update -qq
9 | - if [ `uname -m` = x86_64 ]; then sudo apt-get install -qq --force-yes libgd2-xpm ia32-libs ia32-libs-multiarch > /dev/null; fi
10 | - wget http://dl.google.com/android/android-sdk_r24.3.3-linux.tgz
11 | - tar xzf android-sdk_r24.3.3-linux.tgz
12 | - export ANDROID_HOME=$PWD/android-sdk-linux
13 | - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
14 |
15 | # Gradle
16 | - wget http://services.gradle.org/distributions/gradle-2.4-bin.zip
17 | - unzip gradle-2.4-bin.zip
18 | - export GRADLE_HOME=$PWD/gradle-2.4
19 | - export PATH=$GRADLE_HOME/bin:$PATH
20 |
21 | # install android build tools.
22 | - wget https://dl-ssl.google.com/android/repository/build-tools_r22.0.1-linux.zip
23 | - unzip build-tools_r22.0.1-linux.zip -d $ANDROID_HOME
24 | - mkdir -p $ANDROID_HOME/build-tools/
25 | - mv $ANDROID_HOME/android-5.1 $ANDROID_HOME/build-tools/22.0.1
26 | - ls -la $ANDROID_HOME/build-tools/22.0.1
27 | - echo $ANDROID_HOME
28 |
29 | # Install required components.
30 | # For a full list, run `android list sdk -a --extended`
31 | # Note that sysimg-22 downloads the ARM, x86 and MIPS images (we should optimize this).
32 | # Other relevant API's
33 | - echo yes | android update sdk --filter platform-tools --no-ui --force > /dev/null
34 | - echo yes | android update sdk --filter android-18 --no-ui --force > /dev/null
35 | - echo yes | android update sdk --filter android-22 --no-ui --force > /dev/null
36 | - echo yes | android update sdk --filter sysimg-22 --no-ui --force > /dev/null
37 | - echo yes | android update sdk --filter extra-android-support --no-ui --force > /dev/null
38 | - echo yes | android update sdk --filter extra-android-m2repository --no-ui --force > /dev/null
39 |
40 | script: ./gradlew smokeTest --stacktrace
41 |
--------------------------------------------------------------------------------
/01.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/01.gif
--------------------------------------------------------------------------------
/BUCK:
--------------------------------------------------------------------------------
1 | # A special build that depends on rebound-android:src and packages into a jar file for distribution.
2 | java_binary(
3 | name = 'rebound',
4 | deps = ['//rebound-android:src'],
5 | visibility = ['PUBLIC'],
6 | )
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD License
2 |
3 | For Rebound software
4 |
5 | Copyright (c) 2013, Facebook, Inc.
6 | All rights reserved.
7 |
8 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
11 | disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
14 | disclaimer in the documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
17 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/PATENTS:
--------------------------------------------------------------------------------
1 | Additional Grant of Patent Rights Version 2
2 |
3 | "Software means the Rebound software distributed by Facebook, Inc.
4 |
5 | Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
6 | ("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
7 | (subject to the termination provision below) license under any Necessary Claims,
8 | to make, have made, use, sell, offer to sell, import, and otherwise transfer the
9 | Software. For avoidance of doubt, no license is granted under Facebook’s rights
10 | in any patent claims that are infringed by (i) modifications to the Software
11 | made by you or any third party or (ii) the Software in combination with any
12 | software or other technology.
13 |
14 | The license granted hereunder will terminate, automatically and without notice,
15 | if you (or any of your subsidiaries, corporate affiliates or agents) initiate
16 | directly or indirectly, or take a direct financial interest in, any Patent
17 | Assertion: (i) against Facebook or any of its subsidiaries or corporate
18 | affiliates, (ii) against any party if such Patent Assertion arises in whole or
19 | in part from any software, technology, product or service of Facebook or any of
20 | its subsidiaries or corporate affiliates, or (iii) against any party relating to
21 | the Software. Notwithstanding the foregoing, if Facebook or any of its
22 | subsidiaries or corporate affiliates files a lawsuit alleging patent
23 | infringement against you in the first instance, and you respond by filing a
24 | patent infringement counterclaim in that lawsuit against that party that is
25 | unrelated to the Software, the license granted hereunder will not terminate
26 | under section (i) of this paragraph due to such counterclaim.
27 |
28 | A "Necessary Claim" is a claim of a patent owned by Facebook that is necessarily
29 | infringed by the Software standing alone.
30 |
31 | A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
32 | or contributory infringement or inducement to infringe any patent, including a
33 | cross-claim or counterclaim.
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## facebook的Rebound动库,弹性效果挺好的,估计太老了,之前一直跑不起来,修改了各种gradle配置后总算可以了
3 |
4 | GIF效果有效掉帧,类似这种弹性好过挺好的:
5 |
6 | 
7 |
8 | #Rebound
9 | [](https://travis-ci.org/facebook/rebound)
10 | [](https://android-arsenal.com/details/1/30)
11 |
12 | ##About
13 |
14 | Rebound is a java library that
15 | models spring dynamics. Rebound spring models can be used to create animations
16 | that feel natural by introducing real world physics to your application.
17 |
18 | Rebound is not a general purpose physics library; however, spring dynamics
19 | can be used to drive a wide variety of animations. The simplicity of Rebound
20 | makes it easy to integrate and use as a building block for creating more
21 | complex components like pagers, toggles, and scrollers.
22 |
23 | For examples and usage instructions head over to:
24 |
25 | [facebook.github.io/rebound](http://facebook.github.io/rebound)
26 |
27 | If you are looking to build springy animations for the web, check out the [Javascript version](https://github.com/facebook/rebound-js).
28 |
29 | ##License
30 |
31 | BSD License
32 |
33 | For Rebound software
34 |
35 | Copyright (c) 2013, Facebook, Inc.
36 | All rights reserved.
37 |
38 | Redistribution and use in source and binary forms, with or without
39 | modification, are permitted provided that the following conditions are met:
40 |
41 | * Redistributions of source code must retain the above copyright notice,
42 | this list of conditions and the following disclaimer.
43 |
44 | * Redistributions in binary form must reproduce the above copyright notice,
45 | this list of conditions and the following disclaimer in the documentation
46 | and/or other materials provided with the distribution.
47 |
48 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
49 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
52 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58 | POSSIBILITY OF SUCH DAMAGE.
59 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.2.2'
8 | }
9 | }
10 |
11 | apply plugin: 'com.android.library'
12 | import com.android.builder.core.BuilderConstants
13 |
14 | android {
15 | compileOptions {
16 | sourceCompatibility JavaVersion.VERSION_1_7
17 | targetCompatibility JavaVersion.VERSION_1_7
18 | }
19 |
20 | compileSdkVersion Integer.parseInt(project.ANDROID_COMPILE_SDK_VERSION)
21 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
22 |
23 | defaultConfig {
24 | minSdkVersion 11
25 | targetSdkVersion Integer.parseInt(project.ANDROID_TARGET_SDK_VERSION)
26 | }
27 |
28 | sourceSets {
29 | main {
30 | manifest.srcFile 'rebound-android/src/main/AndroidManifest.xml'
31 | java.srcDirs = ['rebound-core/src/main/java', 'rebound-android/src/main/java']
32 | }
33 | }
34 | }
35 |
36 | // Create jar for distribution.
37 | android.libraryVariants.all { variant ->
38 | def name = variant.buildType.name
39 | if (name.equals(BuilderConstants.RELEASE)) {
40 | // Distribution Jar Task
41 | def distJarTask = task reboundDistJar(type: Jar, dependsOn: variant.javaCompile) {
42 | from variant.javaCompile.destinationDir
43 | }
44 | artifacts.add('archives', distJarTask)
45 |
46 | // Javadocs Task
47 | def javadocsTask = task androidJavadocs(type: Javadoc, dependsOn: variant.javaCompile) {
48 | source = variant.javaCompile.source
49 | def androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
50 | classpath = files(variant.javaCompile.classpath.files, androidJar)
51 | options {
52 | links "http://docs.oracle.com/javase/7/docs/api/"
53 | linksOffline "http://d.android.com/reference","${android.sdkDirectory}/docs/reference"
54 | }
55 | exclude '**/BuildConfig.java'
56 | exclude '**/R.java'
57 | }
58 | def javadocsJarTask = task androidJavadocsJar(type: Jar, dependsOn: javadocsTask) {
59 | classifier = 'javadoc'
60 | from javadocsTask.destinationDir
61 | }
62 | artifacts.add('archives', javadocsJarTask)
63 |
64 | // Sources Jar Task
65 | def sourcesJarTask = task androidSourcesJar(type: Jar) {
66 | classifier = 'sources'
67 | from variant.javaCompile.source
68 | }
69 | artifacts.add('archives', sourcesJarTask)
70 | }
71 | }
72 |
73 | task installExample(dependsOn: 'rebound-android-example:installDebug')
74 | task installPlayground(dependsOn: 'rebound-android-playground:installDebug')
75 | task smokeTest(dependsOn: [
76 | 'rebound-core:assemble',
77 | 'rebound-core:check',
78 | 'rebound-android:assemble',
79 | 'rebound-android:check',
80 | 'rebound-android-example:assembleDebug',
81 | 'rebound-android-playground:assembleDebug',
82 | 'reboundDistJar'
83 | ])
84 |
85 | // Configure gradle.properties to do maven builds
86 | if (!project.hasProperty('sonatypeRepo') ||
87 | !project.hasProperty('sonatypeUsername') ||
88 | !project.hasProperty('sonatypePassword')) {
89 | return;
90 | }
91 |
92 | // Maven Push
93 |
94 | apply plugin: 'maven'
95 | apply plugin: 'signing'
96 |
97 | version = "0.3.8"
98 | group = "com.facebook.rebound"
99 |
100 | signing {
101 | required { has("release") && gradle.taskGraph.hasTask("uploadArchives") }
102 | sign configurations.archives
103 | }
104 |
105 | uploadArchives {
106 | configuration = configurations.archives
107 | repositories.mavenDeployer {
108 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
109 |
110 | repository(url: sonatypeRepo) {
111 | authentication(userName: sonatypeUsername, password: sonatypePassword)
112 | }
113 |
114 | pom.project {
115 | name 'Rebound'
116 | packaging 'jar'
117 | description 'Rebound is a simple spring dynamics animation library for Java and Android applications.'
118 | url 'http://facebook.github.io/rebound'
119 |
120 | scm {
121 | url 'scm:git@github.com:facebook/rebound.git'
122 | connection 'scm:git@github.com:facebook/rebound.git'
123 | developerConnection 'scm:git@github.com:facebook/rebound.git'
124 | }
125 |
126 | licenses {
127 | license {
128 | name 'BSD 2-Clause License'
129 | url 'https://github.com/facebook/rebound/blob/master/LICENSE'
130 | distribution 'repo'
131 | }
132 | }
133 |
134 | developers {
135 | developer {
136 | id 'will.bailey'
137 | name 'Will Bailey'
138 | email 'will.bailey@gmail.com'
139 | }
140 | }
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | VERSION_NAME=1.0
2 | VERSION_CODE=1
3 |
4 | ANDROID_BUILD_TOOLS_VERSION=22.0.1
5 | ANDROID_COMPILE_SDK_VERSION=22
6 | ANDROID_TARGET_SDK_VERSION=22
7 | ANDROID_MIN_SDK=14
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Nov 25 11:58:14 GMT+08:00 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | android.library=false
14 | # Project target.
15 | target=android-10
16 |
--------------------------------------------------------------------------------
/rebound-android-example/BUCK:
--------------------------------------------------------------------------------
1 | keystore(
2 | name = 'debug_keystore',
3 | store = 'debug.keystore',
4 | properties = 'debug.keystore.properties',
5 | )
6 |
7 | android_resource(
8 | name = 'res',
9 | res = 'src/main/res',
10 | package = 'com.facebook.rebound.example',
11 | visibility = ['//rebound-android-example:src'],
12 | )
13 |
14 | android_library(
15 | name = 'src',
16 | srcs = glob(['src/main/java/**/*.java']),
17 | deps = [
18 | ':res',
19 | '//rebound-core:src',
20 | '//rebound-android:src',
21 | ],
22 | )
23 |
24 | android_binary(
25 | name = 'bin',
26 | manifest = 'src/main/AndroidManifest.xml',
27 | target = 'Google Inc.:Google APIs:19',
28 | keystore = ':debug_keystore',
29 | deps = [
30 | ':res',
31 | ':src',
32 | ],
33 | )
34 |
35 | project_config(
36 | src_target = ':bin',
37 | src_roots = ['src/main/java'],
38 | )
39 |
--------------------------------------------------------------------------------
/rebound-android-example/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | dependencies {
4 | compile project(':rebound-android')
5 | }
6 |
7 | android {
8 |
9 | compileOptions {
10 | sourceCompatibility JavaVersion.VERSION_1_7
11 | targetCompatibility JavaVersion.VERSION_1_7
12 | }
13 |
14 | compileSdkVersion Integer.parseInt(project.ANDROID_COMPILE_SDK_VERSION)
15 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
16 |
17 | defaultConfig {
18 | minSdkVersion Integer.parseInt(project.ANDROID_MIN_SDK)
19 | targetSdkVersion Integer.parseInt(project.ANDROID_TARGET_SDK_VERSION)
20 | }
21 |
22 | signingConfigs {
23 | debug {
24 | storeFile file("debug.keystore")
25 | storePassword "android"
26 | keyAlias "androiddebugkey"
27 | keyPassword "android"
28 | }
29 | }
30 |
31 | buildTypes {
32 | debug {
33 | signingConfig signingConfigs.debug
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/rebound-android-example/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-example/debug.keystore
--------------------------------------------------------------------------------
/rebound-android-example/debug.keystore.properties:
--------------------------------------------------------------------------------
1 | key.store=debug.keystore
2 | key.alias=androiddebugkey
3 | key.store.password=android
4 | key.alias.password=android
5 |
--------------------------------------------------------------------------------
/rebound-android-example/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/rebound-android-example/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 | android.library.reference.1=../rebound-android
16 |
--------------------------------------------------------------------------------
/rebound-android-example/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
9 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/rebound-android-example/src/main/java/com/facebook/rebound/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.example;
14 |
15 | import android.app.Activity;
16 | import android.os.Bundle;
17 | import android.view.MotionEvent;
18 | import android.view.View;
19 | import android.widget.FrameLayout;
20 |
21 | import com.facebook.rebound.BaseSpringSystem;
22 | import com.facebook.rebound.SimpleSpringListener;
23 | import com.facebook.rebound.Spring;
24 | import com.facebook.rebound.SpringSystem;
25 | import com.facebook.rebound.SpringUtil;
26 |
27 | /**
28 | * This Activity presents an ImageView that scales down when pressed and returns to full size when
29 | * released. This demonstrates a very simple integrates a very Simple integration of a Rebound
30 | * Spring model to drive a bouncy animation as the photo scales up and down. You can control the
31 | * Spring configuration by tapping on the blue nub at the bottom of the screen to reveal the
32 | * SpringConfiguratorView. From this view you can adjust the tension and friction of the animation
33 | * spring and observe the effect these values have on the animation.
34 | */
35 | public class MainActivity extends Activity {
36 |
37 | private final BaseSpringSystem mSpringSystem = SpringSystem.create();
38 | private final ExampleSpringListener mSpringListener = new ExampleSpringListener();
39 | private FrameLayout mRootView;
40 | private Spring mScaleSpring;
41 | private View mImageView;
42 |
43 | @Override
44 | public void onCreate(Bundle savedInstanceState) {
45 | super.onCreate(savedInstanceState);
46 |
47 | setContentView(R.layout.main);
48 | mRootView = (FrameLayout) findViewById(R.id.root_view);
49 | mImageView = mRootView.findViewById(R.id.image_view);
50 |
51 | // Create the animation spring.
52 | mScaleSpring = mSpringSystem.createSpring();
53 |
54 | // Add an OnTouchListener to the root view.
55 | mRootView.setOnTouchListener(new View.OnTouchListener() {
56 | @Override
57 | public boolean onTouch(View v, MotionEvent event) {
58 | switch (event.getAction()) {
59 | case MotionEvent.ACTION_DOWN:
60 | // When pressed start solving the spring to 1.
61 | mScaleSpring.setEndValue(1);
62 | break;
63 | case MotionEvent.ACTION_UP:
64 | case MotionEvent.ACTION_CANCEL:
65 | // When released start solving the spring to 0.
66 | mScaleSpring.setEndValue(0);
67 | break;
68 | }
69 | return true;
70 | }
71 | });
72 | }
73 |
74 | @Override
75 | public void onResume() {
76 | super.onResume();
77 | // Add a listener to the spring when the Activity resumes.
78 | mScaleSpring.addListener(mSpringListener);
79 | }
80 |
81 | @Override
82 | public void onPause() {
83 | super.onPause();
84 | // Remove the listener to the spring when the Activity pauses.
85 | mScaleSpring.removeListener(mSpringListener);
86 | }
87 |
88 | private class ExampleSpringListener extends SimpleSpringListener {
89 | @Override
90 | public void onSpringUpdate(Spring spring) {
91 | // On each update of the spring value, we adjust the scale of the image view to match the
92 | // springs new value. We use the SpringUtil linear interpolation function mapValueFromRangeToRange
93 | // to translate the spring's 0 to 1 scale to a 100% to 50% scale range and apply that to the View
94 | // with setScaleX/Y. Note that rendering is an implementation detail of the application and not
95 | // Rebound itself. If you need Gingerbread compatibility consider using NineOldAndroids to update
96 | // your view properties in a backwards compatible manner.
97 | float mappedValue = (float) SpringUtil.mapValueFromRangeToRange(spring.getCurrentValue(), 0, 1, 1, 0.5);
98 | mImageView.setScaleX(mappedValue);
99 | mImageView.setScaleY(mappedValue);
100 | }
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/rebound-android-example/src/main/res/drawable-hdpi/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-example/src/main/res/drawable-hdpi/landscape.png
--------------------------------------------------------------------------------
/rebound-android-example/src/main/res/drawable-hdpi/rebound_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-example/src/main/res/drawable-hdpi/rebound_icon.png
--------------------------------------------------------------------------------
/rebound-android-example/src/main/res/drawable-mdpi/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-example/src/main/res/drawable-mdpi/landscape.png
--------------------------------------------------------------------------------
/rebound-android-example/src/main/res/drawable-mdpi/rebound_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-example/src/main/res/drawable-mdpi/rebound_icon.png
--------------------------------------------------------------------------------
/rebound-android-example/src/main/res/drawable-xhdpi/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-example/src/main/res/drawable-xhdpi/landscape.png
--------------------------------------------------------------------------------
/rebound-android-example/src/main/res/drawable-xhdpi/rebound_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-example/src/main/res/drawable-xhdpi/rebound_icon.png
--------------------------------------------------------------------------------
/rebound-android-example/src/main/res/drawable-xxhdpi/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-example/src/main/res/drawable-xxhdpi/landscape.png
--------------------------------------------------------------------------------
/rebound-android-example/src/main/res/drawable-xxhdpi/rebound_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-example/src/main/res/drawable-xxhdpi/rebound_icon.png
--------------------------------------------------------------------------------
/rebound-android-example/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
15 |
21 |
22 |
--------------------------------------------------------------------------------
/rebound-android-playground/BUCK:
--------------------------------------------------------------------------------
1 | keystore(
2 | name = 'debug_keystore',
3 | store = 'debug.keystore',
4 | properties = 'debug.keystore.properties',
5 | )
6 |
7 | android_resource(
8 | name = 'res',
9 | res = 'src/main/res',
10 | package = 'com.facebook.rebound.playground',
11 | visibility = ['//rebound-android-playground:src'],
12 | )
13 |
14 | android_library(
15 | name = 'src',
16 | srcs = glob(['src/main/java/**/*.java']),
17 | deps = [
18 | ':res',
19 | '//rebound-core:src',
20 | '//rebound-android:src',
21 | ],
22 | )
23 |
24 | android_binary(
25 | name = 'bin',
26 | manifest = 'src/main/AndroidManifest.xml',
27 | target = 'Google Inc.:Google APIs:19',
28 | keystore = ':debug_keystore',
29 | deps = [
30 | ':res',
31 | ':src',
32 | ],
33 | )
34 |
35 | project_config(
36 | src_target = ':bin',
37 | src_roots = ['src/main/java'],
38 | )
39 |
--------------------------------------------------------------------------------
/rebound-android-playground/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | dependencies {
4 | compile fileTree(dir: 'libs', include: ['*.jar'])
5 | compile project(':rebound-android')
6 | }
7 |
8 | android {
9 | compileOptions {
10 | sourceCompatibility JavaVersion.VERSION_1_7
11 | targetCompatibility JavaVersion.VERSION_1_7
12 | }
13 | compileSdkVersion Integer.parseInt(project.ANDROID_COMPILE_SDK_VERSION)
14 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
15 |
16 | defaultConfig {
17 | applicationId "com.facebook.rebound.androidplayground"
18 | versionCode Integer.parseInt(project.VERSION_CODE)
19 | versionName project.VERSION_NAME
20 | minSdkVersion 19
21 | targetSdkVersion Integer.parseInt(project.ANDROID_TARGET_SDK_VERSION)
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/rebound-android-playground/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/debug.keystore
--------------------------------------------------------------------------------
/rebound-android-playground/debug.keystore.properties:
--------------------------------------------------------------------------------
1 | key.store=debug.keystore
2 | key.alias=androiddebugkey
3 | key.store.password=android
4 | key.alias.password=android
5 |
--------------------------------------------------------------------------------
/rebound-android-playground/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/app/ExampleContainerView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.app;
14 |
15 | import android.content.Context;
16 | import android.graphics.Color;
17 | import android.util.AttributeSet;
18 | import android.view.ViewGroup;
19 | import android.view.ViewTreeObserver;
20 | import android.widget.FrameLayout;
21 |
22 | import com.facebook.rebound.Spring;
23 | import com.facebook.rebound.SpringListener;
24 | import com.facebook.rebound.SpringSystem;
25 | import com.facebook.rebound.SpringUtil;
26 |
27 | public class ExampleContainerView extends FrameLayout implements SpringListener {
28 | private final SpringSystem mSpringSystem;
29 | private final Spring mTransitionSpring;
30 | private Callback mCallback;
31 |
32 | public void clearCallback() {
33 | mCallback = null;
34 | }
35 |
36 | public interface Callback {
37 | void onProgress(double progress);
38 | void onEnd();
39 | }
40 |
41 | public ExampleContainerView(Context context) {
42 | this(context, null);
43 | }
44 |
45 | public ExampleContainerView(Context context, AttributeSet attrs) {
46 | this(context, attrs, 0);
47 | }
48 |
49 | public ExampleContainerView(Context context, AttributeSet attrs, int defStyle) {
50 | super(context, attrs, defStyle);
51 | mSpringSystem = SpringSystem.create();
52 | mTransitionSpring = mSpringSystem.createSpring();
53 | setClickable(true);
54 | setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
55 | getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
56 | @Override
57 | public void onGlobalLayout() {
58 | mTransitionSpring.setCurrentValue(1).setAtRest();
59 | getViewTreeObserver().removeOnGlobalLayoutListener(this);
60 | }
61 | });
62 | setBackgroundColor(Color.WHITE);
63 | }
64 |
65 | @Override
66 | protected void onAttachedToWindow() {
67 | super.onAttachedToWindow();
68 | mTransitionSpring.addListener(this);
69 | }
70 |
71 | @Override
72 | protected void onDetachedFromWindow() {
73 | super.onDetachedFromWindow();
74 | mTransitionSpring.removeListener(this);
75 | }
76 |
77 | public void reveal(boolean animated, Callback callback) {
78 | if (animated) {
79 | mTransitionSpring.setEndValue(0);
80 | } else {
81 | mTransitionSpring.setCurrentValue(0).setAtRest();
82 | }
83 | mCallback = callback;
84 | }
85 |
86 | public void hide(boolean animated, Callback callback) {
87 | if (animated) {
88 | mTransitionSpring.setEndValue(1);
89 | } else {
90 | mTransitionSpring.setCurrentValue(1).setAtRest();
91 | }
92 | mCallback = callback;
93 | }
94 |
95 | @Override
96 | public void onSpringUpdate(Spring spring) {
97 | double val = spring.getCurrentValue();
98 | float xlat = (float) SpringUtil.mapValueFromRangeToRange(val, 0, 1, 0, getWidth());
99 | setTranslationX(xlat);
100 | if (mCallback != null) {
101 | mCallback.onProgress(spring.getCurrentValue());
102 | }
103 | }
104 |
105 | @Override
106 | public void onSpringAtRest(Spring spring) {
107 | if (mCallback != null) {
108 | mCallback.onEnd();
109 | }
110 | }
111 |
112 | @Override
113 | public void onSpringActivate(Spring spring) {
114 | }
115 |
116 | @Override
117 | public void onSpringEndStateChange(Spring spring) {
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/app/PlaygroundActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.app;
14 |
15 | import android.app.Activity;
16 | import android.content.Context;
17 | import android.database.DataSetObserver;
18 | import android.os.Bundle;
19 | import android.view.LayoutInflater;
20 | import android.view.Menu;
21 | import android.view.MenuItem;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.AdapterView;
25 | import android.widget.ListAdapter;
26 | import android.widget.ListView;
27 |
28 | import com.facebook.rebound.SpringUtil;
29 | import com.facebook.rebound.playground.R;
30 | import com.facebook.rebound.playground.examples.BallExample;
31 | import com.facebook.rebound.playground.examples.PhotoGalleryExample;
32 | import com.facebook.rebound.playground.examples.SpringChainExample;
33 | import com.facebook.rebound.playground.examples.OrigamiExample;
34 | import com.facebook.rebound.playground.examples.SimpleExample;
35 | import com.facebook.rebound.playground.examples.SpringScrollViewExample;
36 |
37 | import java.lang.reflect.Constructor;
38 | import java.lang.reflect.InvocationTargetException;
39 | import java.util.ArrayList;
40 | import java.util.List;
41 |
42 | public class PlaygroundActivity extends Activity implements AdapterView.OnItemClickListener {
43 |
44 | private static final List SAMPLES = new ArrayList();
45 |
46 | static {
47 | SAMPLES.add(new Sample(SimpleExample.class, "Simple Example", "Scale a photo when you press and release"));
48 | SAMPLES.add(new Sample(SpringScrollViewExample.class, "Scroll View", "A scroll view with spring physics"));
49 | SAMPLES.add(new Sample(SpringChainExample.class, "SpringChain", "Drag any row in the list."));
50 | SAMPLES.add(new Sample(PhotoGalleryExample.class, "Photo Gallery", "Tap on a photo to enlarge or minimize."));
51 | SAMPLES.add(new Sample(BallExample.class, "Inertia Ball", "Toss the ball around the screen and watch it settle"));
52 | SAMPLES.add(new Sample(OrigamiExample.class, "Origami Example", "Rebound port of an Origami composition"));
53 | }
54 |
55 | private ListView mListView;
56 | private View mRootContainer;
57 | private ExampleListAdapter mAdapter;
58 | private ViewGroup mRootView;
59 | private ExampleContainerView mCurrentExample;
60 | private boolean mAnimating;
61 | private LayoutInflater mInflater;
62 |
63 | @Override
64 | protected void onCreate(Bundle savedInstanceState) {
65 | super.onCreate(savedInstanceState);
66 | setContentView(R.layout.activity_playground);
67 | mInflater = LayoutInflater.from(this);
68 | mRootView = (ViewGroup) findViewById(R.id.root);
69 | mRootContainer = findViewById(R.id.root_container);
70 | mListView = (ListView) findViewById(R.id.list_view);
71 | mAdapter = new ExampleListAdapter();
72 | mListView.setAdapter(mAdapter);
73 |
74 | mListView.setOnItemClickListener(this);
75 | }
76 |
77 | @Override
78 | public boolean onCreateOptionsMenu(Menu menu) {
79 | getMenuInflater().inflate(R.menu.playground, menu);
80 | return true;
81 | }
82 |
83 | @Override
84 | public boolean onOptionsItemSelected(MenuItem item) {
85 | int id = item.getItemId();
86 | if (id == R.id.action_settings) {
87 | return true;
88 | }
89 | return super.onOptionsItemSelected(item);
90 | }
91 |
92 | @Override
93 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
94 | if (mAnimating) {
95 | return;
96 | }
97 |
98 | Class extends View> clazz = SAMPLES.get(position).viewClass;
99 | View sampleView = null;
100 | try {
101 | Constructor extends View> ctor = clazz.getConstructor(Context.class);
102 | sampleView = ctor.newInstance(PlaygroundActivity.this);
103 | } catch (NoSuchMethodException e) {
104 | e.printStackTrace();
105 | } catch (InvocationTargetException e) {
106 | e.printStackTrace();
107 | } catch (InstantiationException e) {
108 | e.printStackTrace();
109 | } catch (IllegalAccessException e) {
110 | e.printStackTrace();
111 | }
112 |
113 | if (sampleView == null) {
114 | return;
115 | }
116 | mAnimating = true;
117 |
118 | mCurrentExample = new ExampleContainerView(this);
119 | mCurrentExample.addView(sampleView);
120 | mRootView.addView(mCurrentExample);
121 |
122 | mCurrentExample.postDelayed(new Runnable() {
123 | @Override
124 | public void run() {
125 | mCurrentExample.reveal(true, new ExampleContainerView.Callback() {
126 | @Override
127 | public void onProgress(double progress) {
128 | float scale = (float) SpringUtil.mapValueFromRangeToRange(progress, 0, 1, 0.8, 1);
129 | mRootContainer.setScaleX(scale);
130 | mRootContainer.setScaleY(scale);
131 | mRootContainer.setAlpha((float) progress);
132 | }
133 |
134 | @Override
135 | public void onEnd() {
136 | mAnimating = false;
137 | }
138 | });
139 | }
140 | }, 100);
141 | }
142 |
143 | @Override
144 | public void onBackPressed() {
145 | if (mAnimating || mCurrentExample == null) {
146 | return;
147 | }
148 | mAnimating = true;
149 | mCurrentExample.hide(true, new ExampleContainerView.Callback() {
150 | @Override
151 | public void onProgress(double progress) {
152 | float scale = (float) SpringUtil.mapValueFromRangeToRange(progress, 0, 1, 0.8, 1);
153 | mRootContainer.setScaleX(scale);
154 | mRootContainer.setScaleY(scale);
155 | mRootContainer.setAlpha((float) progress);
156 | }
157 |
158 | @Override
159 | public void onEnd() {
160 | mAnimating = false;
161 | mCurrentExample.clearCallback();
162 | mRootView.removeView(mCurrentExample);
163 | mCurrentExample = null;
164 | }
165 | });
166 | }
167 |
168 | private class ExampleListAdapter implements ListAdapter {
169 |
170 | @Override
171 | public void registerDataSetObserver(DataSetObserver observer) {}
172 |
173 | @Override
174 | public void unregisterDataSetObserver(DataSetObserver observer) {
175 | }
176 |
177 | @Override
178 | public int getCount() {
179 | return SAMPLES.size();
180 | }
181 |
182 | @Override
183 | public Object getItem(int position) {
184 | return SAMPLES.get(position);
185 | }
186 |
187 | @Override
188 | public long getItemId(int position) {
189 | return position;
190 | }
191 |
192 | @Override
193 | public boolean hasStableIds() {
194 | return true;
195 | }
196 |
197 | @Override
198 | public View getView(int position, View convertView, ViewGroup parent) {
199 | RowView rowView;
200 | if (convertView != null) {
201 | rowView = (RowView) convertView;
202 | } else {
203 | rowView = new RowView(PlaygroundActivity.this);
204 | }
205 | rowView.setText(SAMPLES.get(position).text);
206 | rowView.setSubtext(SAMPLES.get(position).subtext);
207 | return rowView;
208 | }
209 |
210 | @Override
211 | public int getItemViewType(int position) {
212 | return 0;
213 | }
214 |
215 | @Override
216 | public int getViewTypeCount() {
217 | return 1;
218 | }
219 |
220 | @Override
221 | public boolean isEmpty() {
222 | return SAMPLES.isEmpty();
223 | }
224 |
225 | @Override
226 | public boolean areAllItemsEnabled() {
227 | return true;
228 | }
229 |
230 | @Override
231 | public boolean isEnabled(int position) {
232 | return true;
233 | }
234 | }
235 |
236 | private static class Sample {
237 | public Class extends View> viewClass;
238 | public String text;
239 | public String subtext;
240 |
241 | public Sample(Class extends View> viewClass, String text, String subtext) {
242 | this.viewClass = viewClass;
243 | this.text = text;
244 | this.subtext = subtext;
245 | }
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/app/RowView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.app;
14 |
15 | import android.content.Context;
16 | import android.util.AttributeSet;
17 | import android.view.LayoutInflater;
18 | import android.view.View;
19 | import android.widget.FrameLayout;
20 | import android.widget.TextView;
21 |
22 | import com.facebook.rebound.playground.R;
23 |
24 | public class RowView extends FrameLayout{
25 | private final TextView mTextView;
26 | private final TextView mSubTextView;
27 |
28 | public RowView(Context context) {
29 | this(context, null);
30 | }
31 |
32 | public RowView(Context context, AttributeSet attrs) {
33 | this(context, attrs, 0);
34 | }
35 |
36 | public RowView(Context context, AttributeSet attrs, int defStyle) {
37 | super(context, attrs, defStyle);
38 | LayoutInflater inflater = LayoutInflater.from(context);
39 | View view = inflater.inflate(R.layout.row_view, this, false);
40 | mTextView = (TextView) view.findViewById(R.id.text_view);
41 | mSubTextView = (TextView) view.findViewById(R.id.subtext_view);
42 | setBackgroundResource(R.drawable.row_background);
43 | addView(view);
44 | }
45 |
46 | public void setText(String text) {
47 | mTextView.setText(text);
48 | }
49 |
50 | public void setSubtext(String text) {
51 | mSubTextView.setText(text);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/app/Util.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.app;
14 |
15 | import android.graphics.Color;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.Random;
20 |
21 | public abstract class Util {
22 |
23 | public static final List COLORS = new ArrayList();
24 | static {
25 | for (int i = 0; i < 10; i++) {
26 | COLORS.add(randomColor());
27 | }
28 | }
29 |
30 | public static int randomColor() {
31 | Random random = new Random();
32 | return Color.argb(
33 | 255,
34 | random.nextInt(255),
35 | random.nextInt(255),
36 | random.nextInt(255));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/examples/BallExample.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.examples;
14 |
15 | import android.animation.ArgbEvaluator;
16 | import android.content.Context;
17 | import android.graphics.Canvas;
18 | import android.graphics.Color;
19 | import android.graphics.Paint;
20 | import android.graphics.PointF;
21 | import android.util.AttributeSet;
22 | import android.view.MotionEvent;
23 | import android.view.VelocityTracker;
24 | import android.view.ViewTreeObserver;
25 | import android.widget.FrameLayout;
26 |
27 | import com.facebook.rebound.BaseSpringSystem;
28 | import com.facebook.rebound.Spring;
29 | import com.facebook.rebound.SpringConfig;
30 | import com.facebook.rebound.SpringListener;
31 | import com.facebook.rebound.SpringSystem;
32 | import com.facebook.rebound.SpringSystemListener;
33 | import com.facebook.rebound.playground.app.Util;
34 |
35 | import java.util.ArrayList;
36 | import java.util.List;
37 |
38 | public class BallExample extends FrameLayout implements SpringListener, SpringSystemListener {
39 |
40 | private final Spring xSpring;
41 | private final Spring ySpring;
42 | private final SpringSystem springSystem;
43 | private final SpringConfig COASTING;
44 | private float x;
45 | private float y;
46 | private Paint paint;
47 | private boolean dragging;
48 | private float radius = 100;
49 | private float downX;
50 | private float downY;
51 | private float lastX;
52 | private float lastY;
53 | private VelocityTracker velocityTracker;
54 | private float centerX;
55 | private float centerY;
56 | private float attractionThreshold = 200;
57 | private SpringConfig CONVERGING = SpringConfig.fromOrigamiTensionAndFriction(20, 3);
58 | private List points = new ArrayList();
59 | private ArgbEvaluator colorEvaluator = new ArgbEvaluator();
60 | private Integer startColor = Color.argb(255, 0, 255, 48);
61 | private Integer endColor = Color.argb(255, 0, 228, 255);
62 |
63 | public BallExample(Context context) {
64 | this(context, null);
65 | }
66 |
67 | public BallExample(Context context, AttributeSet attrs) {
68 | this(context, attrs, 0);
69 | }
70 |
71 | public BallExample(Context context, AttributeSet attrs, int defStyle) {
72 | super(context, attrs, defStyle);
73 | COASTING = SpringConfig.fromOrigamiTensionAndFriction(0, 0.5);
74 | COASTING.tension = 0;
75 | setBackgroundColor(Color.WHITE);
76 |
77 | springSystem = SpringSystem.create();
78 | springSystem.addListener(this);
79 | xSpring = springSystem.createSpring();
80 | ySpring = springSystem.createSpring();
81 | xSpring.addListener(this);
82 | ySpring.addListener(this);
83 | paint = new Paint(Paint.ANTI_ALIAS_FLAG);
84 |
85 | getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
86 | @Override
87 | public void onGlobalLayout() {
88 | centerX = getWidth() / 2f;
89 | centerY = getHeight() / 2f;
90 |
91 | xSpring.setCurrentValue(centerX).setAtRest();
92 | ySpring.setCurrentValue(centerY).setAtRest();
93 | getViewTreeObserver().removeOnGlobalLayoutListener(this);
94 |
95 | int offsetH = (int) ((getHeight() - (2 * radius)) % 800) / 2;
96 | int offsetW = (int) ((getWidth() - (2 * radius)) % 800) / 2;
97 | for (float i = offsetH + radius; i < getHeight() - offsetH; i += 400) {
98 | for (float j = offsetW + radius; j < getWidth() - offsetW; j += 400) {
99 | points.add(new PointF(j, i));
100 | }
101 | }
102 | }
103 | });
104 | }
105 |
106 | @Override
107 | protected void onDraw(Canvas canvas) {
108 | super.onDraw(canvas);
109 |
110 | int bg = Color.argb(255, 240, 240, 240);
111 | canvas.drawColor(bg);
112 |
113 | int i = 0;
114 | for (PointF point : points) {
115 | paint.setColor(Color.argb(255, 255, 255, 255));
116 | paint.setStyle(Paint.Style.FILL);
117 | canvas.drawCircle(point.x, point.y, attractionThreshold - 80, paint);
118 | Integer color = (Integer) colorEvaluator.evaluate(
119 | (i + 1) / (float) points.size(), startColor, endColor);
120 | paint.setColor(color);
121 | paint.setStyle(Paint.Style.STROKE);
122 | paint.setStrokeWidth(20);
123 | canvas.drawCircle(point.x, point.y, attractionThreshold - 80, paint);
124 | i++;
125 | }
126 |
127 | paint.setColor(Color.argb(200, 255, 0, 0));
128 | paint.setStyle(Paint.Style.FILL);
129 | canvas.drawCircle(x, y, radius, paint);
130 | paint.setColor(Color.WHITE);
131 | paint.setTextSize(36);
132 | paint.setTextAlign(Paint.Align.CENTER);
133 | canvas.drawText("TOUCH", x, y + 10, paint);
134 | }
135 |
136 | @Override
137 | public boolean onTouchEvent(MotionEvent event) {
138 | float touchX = event.getRawX();
139 | float touchY = event.getRawY();
140 | boolean ret = false;
141 |
142 | switch (event.getAction()) {
143 | case MotionEvent.ACTION_DOWN:
144 | downX = touchX;
145 | downY = touchY;
146 | lastX = downX;
147 | lastY = downY;
148 | velocityTracker = VelocityTracker.obtain();
149 | velocityTracker.addMovement(event);
150 | if (downX > x - radius && downX < x + radius && downY > y - radius && downY < y + radius) {
151 | dragging = true;
152 | ret = true;
153 | }
154 | break;
155 | case MotionEvent.ACTION_MOVE:
156 | if (!dragging) {
157 | break;
158 | }
159 | velocityTracker.addMovement(event);
160 | float offsetX = lastX - touchX;
161 | float offsetY = lastY - touchY;
162 | xSpring.setCurrentValue(xSpring.getCurrentValue() - offsetX).setAtRest();
163 | ySpring.setCurrentValue(ySpring.getCurrentValue() - offsetY).setAtRest();
164 | checkConstraints();
165 | ret = true;
166 | break;
167 | case MotionEvent.ACTION_UP:
168 | case MotionEvent.ACTION_CANCEL:
169 | if (!dragging) {
170 | break;
171 | }
172 | velocityTracker.addMovement(event);
173 | velocityTracker.computeCurrentVelocity(1000);
174 | dragging = false;
175 | ySpring.setSpringConfig(COASTING);
176 | xSpring.setSpringConfig(COASTING);
177 | downX = 0;
178 | downY = 0;
179 | xSpring.setVelocity(velocityTracker.getXVelocity());
180 | ySpring.setVelocity(velocityTracker.getYVelocity());
181 | ret = true;
182 | }
183 |
184 | lastX = touchX;
185 | lastY = touchY;
186 | return ret;
187 | }
188 |
189 | @Override
190 | public void onSpringUpdate(Spring spring) {
191 | x = (float) xSpring.getCurrentValue();
192 | y = (float) ySpring.getCurrentValue();
193 | invalidate();
194 | }
195 |
196 | @Override
197 | public void onSpringAtRest(Spring spring) {
198 |
199 | }
200 |
201 | @Override
202 | public void onSpringActivate(Spring spring) {
203 |
204 | }
205 |
206 | @Override
207 | public void onSpringEndStateChange(Spring spring) {
208 |
209 | }
210 |
211 | @Override
212 | public void onBeforeIntegrate(BaseSpringSystem springSystem) {
213 | }
214 |
215 | @Override
216 | public void onAfterIntegrate(BaseSpringSystem springSystem) {
217 | checkConstraints();
218 | }
219 |
220 | private void checkConstraints() {
221 | if (x + radius >= getWidth()) {
222 | xSpring.setVelocity(-xSpring.getVelocity());
223 | xSpring.setCurrentValue(xSpring.getCurrentValue() - (x + radius - getWidth()), false);
224 | }
225 | if (x - radius <= 0) {
226 | xSpring.setVelocity(-xSpring.getVelocity());
227 | xSpring.setCurrentValue(xSpring.getCurrentValue() - (x - radius), false);
228 | }
229 | if (y + radius >= getHeight()) {
230 | ySpring.setVelocity(-ySpring.getVelocity());
231 | ySpring.setCurrentValue(ySpring.getCurrentValue() - (y + radius - getHeight()), false);
232 | }
233 | if (y - radius <= 0) {
234 | ySpring.setVelocity(-ySpring.getVelocity());
235 | ySpring.setCurrentValue(ySpring.getCurrentValue() - (y - radius), false);
236 | }
237 |
238 | for (PointF point : points) {
239 | if (dist(x, y, point.x, point.y) < attractionThreshold &&
240 | Math.abs(xSpring.getVelocity()) < 900 &&
241 | Math.abs(ySpring.getVelocity()) < 900 &&
242 | !dragging) {
243 | xSpring.setSpringConfig(CONVERGING);
244 | xSpring.setEndValue(point.x);
245 | ySpring.setSpringConfig(CONVERGING);
246 | ySpring.setEndValue(point.y);
247 | }
248 | }
249 | }
250 |
251 | private float dist(double posX, double posY, double pos2X, double pos2Y) {
252 | return (float) Math.sqrt(Math.pow(pos2X - posX, 2) + Math.pow(pos2Y - posY, 2));
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/examples/OrigamiExample.java:
--------------------------------------------------------------------------------
1 | package com.facebook.rebound.playground.examples;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.view.ViewTreeObserver;
10 | import android.widget.FrameLayout;
11 |
12 | import com.facebook.rebound.SimpleSpringListener;
13 | import com.facebook.rebound.Spring;
14 | import com.facebook.rebound.SpringConfig;
15 | import com.facebook.rebound.SpringConfigRegistry;
16 | import com.facebook.rebound.SpringSystem;
17 | import com.facebook.rebound.SpringUtil;
18 | import com.facebook.rebound.playground.R;
19 | import com.facebook.rebound.ui.SpringConfiguratorView;
20 | import com.facebook.rebound.ui.Util;
21 |
22 | public class OrigamiExample extends FrameLayout {
23 |
24 | // Create a spring configuration based on Origami values from the Photo Grid example.
25 | private static final SpringConfig ORIGAMI_SPRING_CONFIG = SpringConfig.fromOrigamiTensionAndFriction(40, 7);
26 |
27 | private final Spring mSpring;
28 | private final View mSelectedPhoto;
29 | private final View mPhotoGrid;
30 | private final View mFeedbackBar;
31 | private final SpringConfiguratorView mSpringConfiguratorView;
32 |
33 | public OrigamiExample(Context context) {
34 | this(context, null);
35 | }
36 |
37 | public OrigamiExample(Context context, AttributeSet attrs) {
38 | this(context, attrs, 0);
39 | }
40 |
41 | public OrigamiExample(Context context, AttributeSet attrs, int defStyle) {
42 | super(context, attrs, defStyle);
43 |
44 | // Inflate the layout.
45 | LayoutInflater inflater = LayoutInflater.from(context);
46 | ViewGroup root = (ViewGroup) inflater.inflate(R.layout.origami_example, this, false);
47 | addView(root);
48 |
49 | // Listen for clicks on the root view.
50 | root.setOnClickListener(new OnClickListener() {
51 | @Override
52 | public void onClick(View v) {
53 | handleClick(v);
54 | }
55 | });
56 |
57 | // Get references to our views.
58 | mPhotoGrid = root.findViewById(R.id.grid);
59 | mSelectedPhoto = root.findViewById(R.id.selection);
60 | mFeedbackBar = root.findViewById(R.id.feedback);
61 | mSpringConfiguratorView = (SpringConfiguratorView) root.findViewById(R.id.spring_configurator);
62 |
63 | // Setup the Spring by creating a SpringSystem adding a SimpleListener that renders the
64 | // animation whenever the spring is updated.
65 | mSpring = SpringSystem
66 | .create()
67 | .createSpring()
68 | .setSpringConfig(ORIGAMI_SPRING_CONFIG)
69 | .addListener(new SimpleSpringListener() {
70 | @Override
71 | public void onSpringUpdate(Spring spring) {
72 | // Just tell the UI to update based on the springs current state.
73 | render();
74 | }
75 | });
76 |
77 |
78 | // Here we just wait until the first layout pass finishes and call our render method to update
79 | // the animation to the initial resting state of the spring.
80 | mPhotoGrid.getViewTreeObserver().addOnGlobalLayoutListener(
81 | new ViewTreeObserver.OnGlobalLayoutListener() {
82 | @Override
83 | public void onGlobalLayout() {
84 | render();
85 | mPhotoGrid.getViewTreeObserver().removeOnGlobalLayoutListener(this);
86 | }
87 | });
88 |
89 | /** Optional - Live Spring Tuning **/
90 |
91 | // Put our config into a registry. This is optional, but it gives you the ability to live tune
92 | // the spring using the SpringConfiguratorView which will show up at the bottom of the screen.
93 | SpringConfigRegistry.getInstance().addSpringConfig(ORIGAMI_SPRING_CONFIG, "origami animation spring");
94 | // Tell the SpringConfiguratorView that we've updated the registry to allow you to live tune the animation spring.
95 | mSpringConfiguratorView.refreshSpringConfigurations();
96 |
97 | // Uncomment this line to actually show the SpringConfiguratorView allowing you to live tune
98 | // the Spring constants as you manipulate the UI.
99 | mSpringConfiguratorView.setVisibility(View.VISIBLE);
100 | }
101 |
102 | /**
103 | * On click we just move the springs end state from 0 to 1. This allows the Spring to act much
104 | * like an Origami switch.
105 | */
106 | public void handleClick(View view) {
107 | if (mSpring.getEndValue() == 0) {
108 | mSpring.setEndValue(1);
109 | } else {
110 | mSpring.setEndValue(0);
111 | }
112 | }
113 |
114 | /**
115 | * This method takes the current state of the spring and maps it to all the values for each UI
116 | * element that is animated on this spring. This allows the Spring to act as a common timing
117 | * function for the animation ensuring that all element transitions are synchronized.
118 | *
119 | * You can think of these mappings as similiar to Origami transitions.
120 | * SpringUtil#mapValueFromRangeToRange converts the spring's 0 to 1 transition and maps it to the
121 | * range of animation for a property on a view such as translation, scale, rotation, and alpha.
122 | */
123 | private void render() {
124 | Resources resources = getResources();
125 | // Get the current spring value.
126 | double value = mSpring.getCurrentValue();
127 |
128 | // Map the spring to the feedback bar position so that its hidden off screen and bounces in on tap.
129 | float barPosition =
130 | (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, mFeedbackBar.getHeight(), 0);
131 | mFeedbackBar.setTranslationY(barPosition);
132 |
133 | // Map the spring to the selected photo scale as it moves into and out of the grid.
134 | float selectedPhotoScale =
135 | (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, 0.33, 1);
136 | selectedPhotoScale = Math.max(selectedPhotoScale, 0); // Clamp the value so we don't go below 0.
137 | mSelectedPhoto.setScaleX(selectedPhotoScale);
138 | mSelectedPhoto.setScaleY(selectedPhotoScale);
139 |
140 | // Map the spring to the selected photo translation from its position in the grid
141 | float selectedPhotoTranslateX = (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, Util.dpToPx(-106.667f, resources), 0);
142 | float selectedPhotoTranslateY = (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, Util.dpToPx(46.667f, resources), 0);
143 | mSelectedPhoto.setTranslationX(selectedPhotoTranslateX);
144 | mSelectedPhoto.setTranslationY(selectedPhotoTranslateY);
145 |
146 | // Map the spring to the photo grid alpha as it fades to black when the photo is selected.
147 | float gridAlpha =
148 | (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, 1, 0);
149 | mPhotoGrid.setAlpha(gridAlpha);
150 |
151 |
152 | // Map the spring to the photo grid scale so that it scales down slightly as the selected photo // zooms in.
153 | float gridScale =
154 | (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, 1, 0.95);
155 | gridScale = Math.max(gridScale, 0); // Clamp the value so we don't go below 0.
156 | mPhotoGrid.setScaleX(gridScale);
157 | mPhotoGrid.setScaleY(gridScale);
158 | }
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/examples/PhotoGalleryExample.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.examples;
14 |
15 | import android.content.Context;
16 | import android.content.res.Resources;
17 | import android.graphics.Point;
18 | import android.util.TypedValue;
19 | import android.view.View;
20 | import android.view.ViewTreeObserver;
21 | import android.widget.FrameLayout;
22 | import android.widget.ImageView;
23 |
24 | import com.facebook.rebound.SimpleSpringListener;
25 | import com.facebook.rebound.Spring;
26 | import com.facebook.rebound.SpringChain;
27 | import com.facebook.rebound.SpringConfig;
28 | import com.facebook.rebound.SpringListener;
29 | import com.facebook.rebound.SpringSystem;
30 | import com.facebook.rebound.SpringUtil;
31 | import com.facebook.rebound.playground.app.Util;
32 |
33 | import java.util.ArrayList;
34 | import java.util.List;
35 |
36 | public class PhotoGalleryExample extends FrameLayout implements SpringListener {
37 |
38 | private static final int ROWS = 5;
39 | private static final int COLS = 4;
40 |
41 | private final List mImageViews = new ArrayList();
42 | private final List mPositions = new ArrayList();
43 | private final SpringChain mSpringChain = SpringChain.create();
44 | private final Spring mSpring = SpringSystem
45 | .create()
46 | .createSpring()
47 | .addListener(this)
48 | .setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(40, 6));
49 |
50 | private int mActiveIndex;
51 | private int mPadding;
52 |
53 | public PhotoGalleryExample(Context context) {
54 | super(context);
55 |
56 | int viewCount = ROWS * COLS;
57 |
58 | for (int i = 0; i < viewCount; i++) {
59 | final int j = i;
60 |
61 | // Create the View.
62 | final ImageView imageView = new ImageView(context);
63 | mImageViews.add(imageView);
64 | addView(imageView);
65 | imageView.setAlpha(0f);
66 | imageView.setBackgroundColor(Util.randomColor());
67 | imageView.setLayerType(LAYER_TYPE_HARDWARE, null);
68 |
69 | // Add an image for each view.
70 | int res = getResources().getIdentifier("d" + (i % 11 + 1), "drawable", context.getPackageName());
71 | imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
72 | imageView.setImageResource(res);
73 |
74 | // Add a click listener to handle scaling up the view.
75 | imageView.setOnClickListener(new OnClickListener() {
76 | @Override
77 | public void onClick(View v) {
78 | int endValue = mSpring.getEndValue() == 0 ? 1 : 0;
79 | imageView.bringToFront();
80 | mActiveIndex = j;
81 | mSpring.setEndValue(endValue);
82 | }
83 | });
84 |
85 | // Add a spring to the SpringChain to do an entry animation.
86 | mSpringChain.addSpring(new SimpleSpringListener() {
87 | @Override
88 | public void onSpringUpdate(Spring spring) {
89 | render();
90 | }
91 | });
92 | }
93 |
94 | // Wait for layout.
95 | getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
96 | @Override
97 | public void onGlobalLayout() {
98 | layout();
99 | getViewTreeObserver().removeOnGlobalLayoutListener(this);
100 |
101 | postOnAnimationDelayed(new Runnable() {
102 | @Override
103 | public void run() {
104 | mSpringChain.setControlSpringIndex(0).getControlSpring().setEndValue(1);
105 | }
106 | }, 500);
107 | }
108 | });
109 |
110 | }
111 |
112 | private void render() {
113 | for (int i = 0; i < mImageViews.size(); i++) {
114 | ImageView imageView = mImageViews.get(i);
115 | if (mSpring.isAtRest() && mSpring.getCurrentValue() == 0) {
116 | // Performing the initial entry transition animation.
117 | Spring spring = mSpringChain.getAllSprings().get(i);
118 | float val = (float) spring.getCurrentValue();
119 | imageView.setScaleX(val);
120 | imageView.setScaleY(val);
121 | imageView.setAlpha(val);
122 | Point pos = mPositions.get(i);
123 | imageView.setTranslationX(pos.x);
124 | imageView.setTranslationY(pos.y);
125 | } else {
126 | // Scaling up a photo to fullscreen size.
127 | Point pos = mPositions.get(i);
128 | if (i == mActiveIndex) {
129 | float ww = imageView.getWidth();
130 | float hh = imageView.getHeight();
131 | float sx = getWidth() / ww;
132 | float sy = getHeight() / hh;
133 | float s = sx > sy ? sx : sy;
134 | float xlatX = (float) SpringUtil.mapValueFromRangeToRange(mSpring.getCurrentValue(), 0, 1, pos.x, 0);
135 | float xlatY = (float) SpringUtil.mapValueFromRangeToRange(mSpring.getCurrentValue(), 0, 1, pos.y, 0);
136 | imageView.setPivotX(0);
137 | imageView.setPivotY(0);
138 | imageView.setTranslationX(xlatX);
139 | imageView.setTranslationY(xlatY);
140 |
141 | float ss = (float) SpringUtil.mapValueFromRangeToRange(mSpring.getCurrentValue(), 0, 1, 1, s);
142 | imageView.setScaleX(ss);
143 | imageView.setScaleY(ss);
144 | } else {
145 | float val = (float) Math.max(0, 1 - mSpring.getCurrentValue());
146 | imageView.setAlpha(val);
147 | }
148 | }
149 | }
150 | }
151 |
152 | private void layout() {
153 | float width = getWidth();
154 | float height = getHeight();
155 |
156 | // Determine the size for each image given the screen dimensions.
157 | Resources res = getResources();
158 | mPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, res.getDisplayMetrics());
159 | int colWidth = (int) Math.ceil((width - 2 * mPadding) / COLS) - 2 * mPadding;
160 | int rowHeight = (int) Math.ceil((height - 2 * mPadding) / ROWS) - 2 * mPadding;
161 |
162 | // Determine the resting position for each view.
163 | int k = 0;
164 | int py = 0;
165 | for (int i = 0; i < ROWS; i++) {
166 | int px = 0;
167 | py += mPadding * 2;
168 | for (int j = 0; j < COLS; j++) {
169 | px += mPadding * 2;
170 | ImageView imageView = mImageViews.get(k);
171 | imageView.setLayoutParams(new LayoutParams(colWidth, rowHeight));
172 | mPositions.add(new Point(px, py));
173 | px += colWidth;
174 | k++;
175 | }
176 | py += rowHeight;
177 | }
178 | }
179 |
180 | @Override
181 | public void onSpringUpdate(Spring spring) {
182 | render();
183 | }
184 |
185 | @Override
186 | public void onSpringAtRest(Spring spring) {
187 |
188 | }
189 |
190 | @Override
191 | public void onSpringActivate(Spring spring) {
192 |
193 | }
194 |
195 | @Override
196 | public void onSpringEndStateChange(Spring spring) {
197 |
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/examples/SimpleExample.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.examples;
14 |
15 | import android.content.Context;
16 | import android.util.AttributeSet;
17 | import android.view.LayoutInflater;
18 | import android.view.MotionEvent;
19 | import android.view.View;
20 | import android.widget.FrameLayout;
21 |
22 | import com.facebook.rebound.BaseSpringSystem;
23 | import com.facebook.rebound.SimpleSpringListener;
24 | import com.facebook.rebound.Spring;
25 | import com.facebook.rebound.SpringConfig;
26 | import com.facebook.rebound.SpringSystem;
27 | import com.facebook.rebound.SpringUtil;
28 | import com.facebook.rebound.playground.R;
29 |
30 | public class SimpleExample extends FrameLayout {
31 |
32 | private final BaseSpringSystem mSpringSystem = SpringSystem.create();
33 | private final ExampleSpringListener mSpringListener = new ExampleSpringListener();
34 | private final FrameLayout mRootView;
35 | private final Spring mScaleSpring;
36 | private final View mImageView;
37 |
38 | public SimpleExample(Context context) {
39 | this(context, null);
40 | }
41 |
42 | public SimpleExample(Context context, AttributeSet attrs) {
43 | this(context, attrs, 0);
44 | }
45 |
46 | public SimpleExample(Context context, AttributeSet attrs, int defStyle) {
47 | super(context, attrs, defStyle);
48 | mScaleSpring = mSpringSystem.createSpring();
49 | LayoutInflater inflater = LayoutInflater.from(context);
50 | mRootView = (FrameLayout) inflater.inflate(R.layout.photo_scale_example, this, false);
51 | mImageView = mRootView.findViewById(R.id.image_view);
52 | mRootView.setOnTouchListener(new View.OnTouchListener() {
53 | @Override
54 | public boolean onTouch(View v, MotionEvent event) {
55 | switch (event.getAction()) {
56 | case MotionEvent.ACTION_DOWN:
57 | mScaleSpring.setEndValue(1);
58 | break;
59 | case MotionEvent.ACTION_UP:
60 | case MotionEvent.ACTION_CANCEL:
61 | mScaleSpring.setEndValue(0);
62 | break;
63 | }
64 | return true;
65 | }
66 | });
67 | addView(mRootView);
68 | }
69 |
70 | @Override
71 | protected void onAttachedToWindow() {
72 | mScaleSpring.addListener(mSpringListener);
73 | }
74 |
75 | @Override
76 | protected void onDetachedFromWindow() {
77 | mScaleSpring.removeListener(mSpringListener);
78 | }
79 |
80 | private class ExampleSpringListener extends SimpleSpringListener {
81 | @Override
82 | public void onSpringUpdate(Spring spring) {
83 | float mappedValue = (float) SpringUtil.mapValueFromRangeToRange(spring.getCurrentValue(), 0, 1, 1, 0.5);
84 | mImageView.setScaleX(mappedValue);
85 | mImageView.setScaleY(mappedValue);
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/examples/SpringChainExample.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.examples;
14 |
15 | import android.animation.ArgbEvaluator;
16 | import android.content.Context;
17 | import android.graphics.Color;
18 | import android.view.LayoutInflater;
19 | import android.view.MotionEvent;
20 | import android.view.VelocityTracker;
21 | import android.view.View;
22 | import android.view.ViewGroup;
23 | import android.view.ViewTreeObserver;
24 | import android.widget.FrameLayout;
25 | import android.widget.TableLayout;
26 |
27 | import com.facebook.rebound.SimpleSpringListener;
28 | import com.facebook.rebound.Spring;
29 | import com.facebook.rebound.SpringChain;
30 | import com.facebook.rebound.playground.R;
31 |
32 | import java.util.ArrayList;
33 | import java.util.List;
34 |
35 | public class SpringChainExample extends FrameLayout {
36 |
37 | private final SpringChain mSpringChain = SpringChain.create();
38 |
39 | private final List mViews = new ArrayList();
40 | private float mLastDownX;
41 |
42 | /** Touch handling **/
43 | private View mLastDraggingView;
44 | private float mLastDownXlat;
45 | private int mActivePointerId;
46 | private VelocityTracker mVelocityTracker;
47 |
48 | public SpringChainExample(Context context) {
49 | super(context);
50 |
51 | LayoutInflater inflater = LayoutInflater.from(context);
52 | ViewGroup container = (ViewGroup) inflater.inflate(R.layout.cascade_effect, this, false);
53 | addView(container);
54 | ViewGroup rootView = (ViewGroup) container.findViewById(R.id.root);
55 | int bgColor = Color.argb(255, 17, 148, 231);
56 | setBackgroundColor(bgColor);
57 | rootView.setBackgroundResource(R.drawable.rebound_tiles);
58 |
59 | int startColor = Color.argb(255, 255, 64, 230);
60 | int endColor = Color.argb(255, 255, 230, 64);
61 | ArgbEvaluator evaluator = new ArgbEvaluator();
62 | int viewCount = 10;
63 | for (int i = 0; i < viewCount; i++) {
64 | final View view = new View(context);
65 | view.setLayoutParams(
66 | new TableLayout.LayoutParams(
67 | ViewGroup.LayoutParams.MATCH_PARENT,
68 | ViewGroup.LayoutParams.WRAP_CONTENT,
69 | 1f));
70 | mSpringChain.addSpring(new SimpleSpringListener() {
71 | @Override
72 | public void onSpringUpdate(Spring spring) {
73 | float value = (float) spring.getCurrentValue();
74 | view.setTranslationX(value);
75 | }
76 | });
77 | int color = (Integer) evaluator.evaluate((float) i / (float) viewCount, startColor, endColor);
78 | view.setBackgroundColor(color);
79 | view.setOnTouchListener(new OnTouchListener() {
80 | @Override
81 | public boolean onTouch(View v, MotionEvent event) {
82 | return handleRowTouch(v, event);
83 | }
84 | });
85 | mViews.add(view);
86 | rootView.addView(view);
87 | }
88 |
89 | getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
90 | @Override
91 | public void onGlobalLayout() {
92 | getViewTreeObserver().removeOnGlobalLayoutListener(this);
93 | List springs = mSpringChain.getAllSprings();
94 | for (int i = 0; i < springs.size(); i++) {
95 | springs.get(i).setCurrentValue(-mViews.get(i).getWidth());
96 | }
97 | postDelayed(new Runnable() {
98 | @Override
99 | public void run() {
100 | mSpringChain
101 | .setControlSpringIndex(0)
102 | .getControlSpring()
103 | .setEndValue(0);
104 | }
105 | }, 500);
106 | }
107 | });
108 | }
109 |
110 | private boolean handleRowTouch(View view, MotionEvent event) {
111 | int action = event.getAction();
112 | switch (action & MotionEvent.ACTION_MASK) {
113 | case MotionEvent.ACTION_DOWN:
114 |
115 | mActivePointerId = event.getPointerId(0);
116 | mLastDownXlat = view.getTranslationX();
117 | mLastDraggingView = view;
118 | mLastDownX = event.getRawX();
119 |
120 | mVelocityTracker = VelocityTracker.obtain();
121 | mVelocityTracker.addMovement(event);
122 |
123 | int idx = mViews.indexOf(mLastDraggingView);
124 | mSpringChain
125 | .setControlSpringIndex(idx)
126 | .getControlSpring()
127 | .setCurrentValue(mLastDownXlat);
128 | break;
129 | case MotionEvent.ACTION_MOVE: {
130 | final int pointerIndex = event.findPointerIndex(mActivePointerId);
131 | if (pointerIndex != -1) {
132 | final int location[] = {0, 0};
133 | view.getLocationOnScreen(location);
134 | float x = event.getX(pointerIndex) + location[0];
135 | float offset = x - mLastDownX + mLastDownXlat;
136 | mSpringChain
137 | .getControlSpring()
138 | .setCurrentValue(offset);
139 | mVelocityTracker.addMovement(event);
140 | }
141 | break;
142 | }
143 | case MotionEvent.ACTION_CANCEL:
144 | case MotionEvent.ACTION_UP:
145 | final int pointerIndex = event.findPointerIndex(mActivePointerId);
146 | if (pointerIndex != -1) {
147 | mVelocityTracker.addMovement(event);
148 | mVelocityTracker.computeCurrentVelocity(1000);
149 | mSpringChain
150 | .getControlSpring()
151 | .setVelocity(mVelocityTracker.getXVelocity())
152 | .setEndValue(0);
153 | }
154 | break;
155 | }
156 | return true;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/examples/SpringScrollViewExample.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.examples;
14 |
15 | import android.animation.ArgbEvaluator;
16 | import android.content.Context;
17 | import android.graphics.Color;
18 | import android.view.LayoutInflater;
19 | import android.view.ViewGroup;
20 | import android.widget.FrameLayout;
21 |
22 | import com.facebook.rebound.playground.R;
23 | import com.facebook.rebound.playground.examples.scrollview.ExampleRowView;
24 |
25 | public class SpringScrollViewExample extends FrameLayout {
26 |
27 | private final int ROW_COUNT = 50;
28 |
29 | public SpringScrollViewExample(Context context) {
30 | super(context);
31 |
32 | LayoutInflater inflater = LayoutInflater.from(context);
33 | ViewGroup root = (ViewGroup) inflater.inflate(R.layout.spring_scroll_view_example, this, false);
34 | ViewGroup content = (ViewGroup) root.findViewById(R.id.content_view);
35 | addView(root);
36 | setBackgroundColor(Color.argb(255, 50, 50, 50));
37 |
38 | int startColor = Color.argb(255, 255, 64, 230);
39 | int endColor = Color.argb(255, 0, 174, 255);
40 | ArgbEvaluator evaluator = new ArgbEvaluator();
41 | for (int i = 0; i < ROW_COUNT; i++) {
42 | ExampleRowView exampleRowView = new ExampleRowView(context);
43 | Integer color = (Integer) evaluator.evaluate((float) i / (float) ROW_COUNT, startColor, endColor);
44 | exampleRowView.setText("Row " + i);
45 | exampleRowView.setBackgroundColor(color);
46 | content.addView(exampleRowView);
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/examples/scrollview/ExampleRowView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.examples.scrollview;
14 |
15 | import android.content.Context;
16 | import android.view.LayoutInflater;
17 | import android.view.ViewGroup;
18 | import android.widget.FrameLayout;
19 | import android.widget.TextView;
20 |
21 | import com.facebook.rebound.playground.R;
22 |
23 | public class ExampleRowView extends FrameLayout {
24 | private final TextView mTextView;
25 |
26 | public ExampleRowView(Context context) {
27 | super(context);
28 | LayoutInflater inflater = LayoutInflater.from(context);
29 | ViewGroup view = (ViewGroup) inflater.inflate(R.layout.example_row_view, this, false);
30 | mTextView = (TextView) view.findViewById(R.id.text_view);
31 | addView(view);
32 | }
33 |
34 | public void setText(String text) {
35 | mTextView.setText(text);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/java/com/facebook/rebound/playground/examples/scrollview/SpringOverScroller.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
3 | * Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package com.facebook.rebound.playground.examples.scrollview;
14 |
15 | import android.content.Context;
16 | import android.util.Log;
17 | import android.view.animation.Interpolator;
18 |
19 | import com.facebook.rebound.Spring;
20 | import com.facebook.rebound.SpringConfig;
21 | import com.facebook.rebound.SpringConfigRegistry;
22 | import com.facebook.rebound.SpringListener;
23 | import com.facebook.rebound.SpringSystem;
24 |
25 | public class SpringOverScroller implements SpringListener {
26 |
27 | private final SpringSystem mSpringSystem;
28 | private final Spring mSpringX;
29 | private final Spring mSpringY;
30 |
31 | private static final SpringConfig COASTING_CONFIG =
32 | SpringConfig.fromOrigamiTensionAndFriction(0, 0.5);
33 | private static final SpringConfig RUBBERBANDING_CONFIG =
34 | SpringConfig.fromOrigamiTensionAndFriction(20, 9);
35 |
36 | public SpringOverScroller(Context context) {
37 | this(context, null);
38 | }
39 |
40 | public SpringOverScroller(Context context, Interpolator interpolator) {
41 | this(context, interpolator, true);
42 | }
43 |
44 | public SpringOverScroller(Context context, Interpolator interpolator,
45 | float bounceCoefficientX, float bounceCoefficientY) {
46 | this(context, interpolator, true);
47 | }
48 |
49 | public SpringOverScroller(Context context, Interpolator interpolator,
50 | float bounceCoefficientX, float bounceCoefficientY, boolean flywheel) {
51 | this(context, interpolator, flywheel);
52 | }
53 |
54 | public SpringOverScroller(Context context, Interpolator interpolator, boolean flywheel) {
55 | mSpringSystem = SpringSystem.create();
56 | mSpringX = mSpringSystem
57 | .createSpring()
58 | .addListener(this);
59 | mSpringY = mSpringSystem
60 | .createSpring()
61 | .addListener(this);
62 | SpringConfigRegistry
63 | .getInstance()
64 | .addSpringConfig(RUBBERBANDING_CONFIG, "rubber-banding");
65 | SpringConfigRegistry
66 | .getInstance()
67 | .addSpringConfig(COASTING_CONFIG, "coasting");
68 | }
69 |
70 | public final void setFriction(float friction) {
71 | }
72 |
73 | public final boolean isFinished() {
74 | return mSpringX.isAtRest() && mSpringY.isAtRest();
75 | }
76 |
77 | public final int getCurrX() {
78 | return (int) Math.round(mSpringX.getCurrentValue());
79 | }
80 |
81 | public final int getCurrY() {
82 | return (int) Math.round(mSpringY.getCurrentValue());
83 | }
84 |
85 | public float getCurrVelocity() {
86 | double velX = mSpringX.getVelocity();
87 | double velY = mSpringX.getVelocity();
88 | return (int) Math.sqrt(velX * velX + velY * velY);
89 | }
90 |
91 | public final int getStartX() {
92 | return (int) Math.round(mSpringX.getStartValue());
93 | }
94 |
95 | public final int getStartY() {
96 | return (int) Math.round(mSpringY.getStartValue());
97 | }
98 |
99 | public final int getFinalX() {
100 | return (int) Math.round(mSpringX.getEndValue());
101 | }
102 |
103 | public final int getFinalY() {
104 | return (int) Math.round(mSpringY.getEndValue());
105 | }
106 |
107 | public boolean computeScrollOffset() {
108 | return !(mSpringX.isAtRest() && mSpringY.isAtRest());
109 | }
110 |
111 | public void startScroll(int startX, int startY, int dx, int dy) {
112 | mSpringX.setCurrentValue(startX).setEndValue(dx);
113 | mSpringY.setCurrentValue(startY).setEndValue(dy);
114 | }
115 |
116 | public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
117 | mSpringX.setCurrentValue(startX, false);
118 | mSpringY.setCurrentValue(startY, false);
119 | if (startX > maxX || startX < minX) {
120 | if (startX > maxX) {
121 | mSpringX.setEndValue(maxX);
122 | } else if (startX < minX) {
123 | mSpringX.setEndValue(minX);
124 | }
125 | mSpringX.setSpringConfig(RUBBERBANDING_CONFIG);
126 | return true;
127 | }
128 | if (startY > maxY || startY < minY) {
129 | if (startY > maxY) {
130 | mSpringY.setEndValue(maxY);
131 | } else if (startY < minY) {
132 | mSpringY.setEndValue(minY);
133 | }
134 | mSpringY.setSpringConfig(RUBBERBANDING_CONFIG);
135 | return true;
136 | }
137 | return true;
138 | }
139 |
140 | public void fling(int startX, int startY, int velocityX, int velocityY,
141 | int minX, int maxX, int minY, int maxY, int overX, int overY) {
142 | mSpringX
143 | .setSpringConfig(COASTING_CONFIG)
144 | .setCurrentValue(startX)
145 | .setVelocity(velocityX);
146 | mSpringY
147 | .setSpringConfig(COASTING_CONFIG)
148 | .setCurrentValue(startY)
149 | .setVelocity(velocityY);
150 | }
151 |
152 | public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
153 | }
154 |
155 | public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
156 | }
157 |
158 | public void abortAnimation() {
159 | mSpringX.setAtRest();
160 | mSpringY.setAtRest();
161 | }
162 |
163 | @Override
164 | public void onSpringUpdate(Spring spring) {
165 | Log.d("WSB", "cv:" + spring.getCurrentValue());
166 | }
167 |
168 | @Override
169 | public void onSpringAtRest(Spring spring) {
170 |
171 | }
172 |
173 | @Override
174 | public void onSpringActivate(Spring spring) {
175 |
176 | }
177 |
178 | @Override
179 | public void onSpringEndStateChange(Spring spring) {
180 |
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-hdpi/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-hdpi/landscape.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-hdpi/rebound_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-hdpi/rebound_icon.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-mdpi/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-mdpi/landscape.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-mdpi/rebound_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-mdpi/rebound_icon.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d1.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d10.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d11.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d12.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d2.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d3.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d4.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d5.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d6.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d7.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d8.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-nodpi/d9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-nodpi/d9.jpg
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-xhdpi/feedback.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-xhdpi/feedback.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-xhdpi/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-xhdpi/grid.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-xhdpi/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-xhdpi/landscape.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-xhdpi/rebound_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-xhdpi/rebound_icon.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-xhdpi/selected_photo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-xhdpi/selected_photo.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-xxhdpi/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-xxhdpi/landscape.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable-xxhdpi/rebound_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-android-playground/src/main/res/drawable-xxhdpi/rebound_icon.png
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable/rebound_tiles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/drawable/row_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/layout/activity_playground.xml:
--------------------------------------------------------------------------------
1 |
10 |
17 |
23 |
29 |
30 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/layout/cascade_effect.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/layout/example_row_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
14 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/layout/origami_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
16 |
23 |
30 |
37 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/layout/photo_scale_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
15 |
21 |
22 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/layout/row_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
19 |
27 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/layout/spring_scroll_view_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/menu/playground.xml:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #efefef
4 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Playground
5 | Settings
6 |
7 |
8 |
--------------------------------------------------------------------------------
/rebound-android-playground/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/rebound-android/BUCK:
--------------------------------------------------------------------------------
1 | android_library(
2 | name = 'src',
3 | srcs = glob(['src/main/java/**/*.java']),
4 | deps = ['//rebound-core:src'],
5 | visibility = ['PUBLIC'],
6 | )
7 |
8 | project_config(
9 | src_target = '//rebound-android:src',
10 | src_roots = ['src/main/java'],
11 | )
12 |
--------------------------------------------------------------------------------
/rebound-android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | dependencies {
4 | compile project(':rebound-core')
5 | }
6 |
7 | android {
8 | compileOptions {
9 | sourceCompatibility JavaVersion.VERSION_1_7
10 | targetCompatibility JavaVersion.VERSION_1_7
11 | }
12 | compileSdkVersion Integer.parseInt(project.ANDROID_COMPILE_SDK_VERSION)
13 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
14 |
15 | defaultConfig {
16 | minSdkVersion 11
17 | targetSdkVersion Integer.parseInt(project.ANDROID_TARGET_SDK_VERSION)
18 | }
19 |
20 | lintOptions {
21 | abortOnError false
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/rebound-android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/rebound-android/src/main/java/com/facebook/rebound/AndroidSpringLooperFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | import android.annotation.TargetApi;
14 | import android.os.Build;
15 | import android.os.Handler;
16 | import android.os.SystemClock;
17 | import android.view.Choreographer;
18 |
19 | /**
20 | * Android version of the spring looper that uses the most appropriate frame callback mechanism
21 | * available. It uses Android's {@link Choreographer} when available, otherwise it uses a
22 | * {@link Handler}.
23 | */
24 | abstract class AndroidSpringLooperFactory {
25 |
26 | /**
27 | * Create an Android {@link com.facebook.rebound.SpringLooper} for the detected Android platform.
28 | * @return a SpringLooper
29 | */
30 | public static SpringLooper createSpringLooper() {
31 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
32 | return ChoreographerAndroidSpringLooper.create();
33 | } else {
34 | return LegacyAndroidSpringLooper.create();
35 | }
36 | }
37 |
38 | /**
39 | * The base implementation of the Android spring looper, using a {@link Handler} for the
40 | * frame callbacks.
41 | */
42 | private static class LegacyAndroidSpringLooper extends SpringLooper {
43 |
44 | private final Handler mHandler;
45 | private final Runnable mLooperRunnable;
46 | private boolean mStarted;
47 | private long mLastTime;
48 |
49 | /**
50 | * @return an Android spring looper using a new {@link Handler} instance
51 | */
52 | public static SpringLooper create() {
53 | return new LegacyAndroidSpringLooper(new Handler());
54 | }
55 |
56 | public LegacyAndroidSpringLooper(Handler handler) {
57 | mHandler = handler;
58 | mLooperRunnable = new Runnable() {
59 | @Override
60 | public void run() {
61 | if (!mStarted || mSpringSystem == null) {
62 | return;
63 | }
64 | long currentTime = SystemClock.uptimeMillis();
65 | mSpringSystem.loop(currentTime - mLastTime);
66 | mLastTime = currentTime;
67 | mHandler.post(mLooperRunnable);
68 | }
69 | };
70 | }
71 |
72 | @Override
73 | public void start() {
74 | if (mStarted) {
75 | return;
76 | }
77 | mStarted = true;
78 | mLastTime = SystemClock.uptimeMillis();
79 | mHandler.removeCallbacks(mLooperRunnable);
80 | mHandler.post(mLooperRunnable);
81 | }
82 |
83 | @Override
84 | public void stop() {
85 | mStarted = false;
86 | mHandler.removeCallbacks(mLooperRunnable);
87 | }
88 | }
89 |
90 | /**
91 | * The Jelly Bean and up implementation of the spring looper that uses Android's
92 | * {@link Choreographer} instead of a {@link Handler}
93 | */
94 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
95 | private static class ChoreographerAndroidSpringLooper extends SpringLooper {
96 |
97 | private final Choreographer mChoreographer;
98 | private final Choreographer.FrameCallback mFrameCallback;
99 | private boolean mStarted;
100 | private long mLastTime;
101 |
102 | /**
103 | * @return an Android spring choreographer using the system {@link Choreographer}
104 | */
105 | public static ChoreographerAndroidSpringLooper create() {
106 | return new ChoreographerAndroidSpringLooper(Choreographer.getInstance());
107 | }
108 |
109 | public ChoreographerAndroidSpringLooper(Choreographer choreographer) {
110 | mChoreographer = choreographer;
111 | mFrameCallback = new Choreographer.FrameCallback() {
112 | @Override
113 | public void doFrame(long frameTimeNanos) {
114 | if (!mStarted || mSpringSystem == null) {
115 | return;
116 | }
117 | long currentTime = SystemClock.uptimeMillis();
118 | mSpringSystem.loop(currentTime - mLastTime);
119 | mLastTime = currentTime;
120 | mChoreographer.postFrameCallback(mFrameCallback);
121 | }
122 | };
123 | }
124 |
125 | @Override
126 | public void start() {
127 | if (mStarted) {
128 | return;
129 | }
130 | mStarted = true;
131 | mLastTime = SystemClock.uptimeMillis();
132 | mChoreographer.removeFrameCallback(mFrameCallback);
133 | mChoreographer.postFrameCallback(mFrameCallback);
134 | }
135 |
136 | @Override
137 | public void stop() {
138 | mStarted = false;
139 | mChoreographer.removeFrameCallback(mFrameCallback);
140 | }
141 | }
142 | }
--------------------------------------------------------------------------------
/rebound-android/src/main/java/com/facebook/rebound/AnimationQueue.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.rebound;
11 |
12 | import java.util.ArrayList;
13 | import java.util.Collection;
14 | import java.util.LinkedList;
15 | import java.util.List;
16 | import java.util.Queue;
17 |
18 | /**
19 | * AnimationQueue provides a way to trigger a delayed stream of animations off of a stream of
20 | * values. Each callback that is added the AnimationQueue will be process the stream delayed by
21 | * the number of animation frames equal to its position in the callback list. This makes it easy
22 | * to build cascading animations.
23 | *
24 | * TODO: Add options for changing the delay after which a callback receives a value from the
25 | * animation queue value stream.
26 | */
27 | public class AnimationQueue {
28 |
29 | /**
30 | * AnimationQueue.Callback receives the value from the stream that it should use in its onFrame
31 | * method.
32 | */
33 | public interface Callback {
34 | void onFrame(Double value);
35 | }
36 |
37 | private final ChoreographerCompat mChoreographer;
38 | private final Queue mPendingQueue = new LinkedList();
39 | private final Queue mAnimationQueue = new LinkedList();
40 | private final List mCallbacks = new ArrayList();
41 | private final ArrayList mTempValues = new ArrayList();
42 | private final ChoreographerCompat.FrameCallback mChoreographerCallback;
43 | private boolean mRunning;
44 |
45 | public AnimationQueue() {
46 | mChoreographer = ChoreographerCompat.getInstance();
47 | mChoreographerCallback = new ChoreographerCompat.FrameCallback() {
48 | @Override
49 | public void doFrame(long frameTimeNanos) {
50 | onFrame(frameTimeNanos);
51 | }
52 | };
53 | }
54 |
55 | /* Values */
56 |
57 | /**
58 | * Add a single value to the pending animation queue.
59 | * @param value the single value to add
60 | */
61 | public void addValue(Double value) {
62 | mPendingQueue.add(value);
63 | runIfIdle();
64 | }
65 |
66 | /**
67 | * Add a collection of values to the pending animation value queue
68 | * @param values the collection of values to add
69 | */
70 | public void addAllValues(Collection values) {
71 | mPendingQueue.addAll(values);
72 | runIfIdle();
73 | }
74 |
75 | /**
76 | * Clear all pending animation values.
77 | */
78 | public void clearValues() {
79 | mPendingQueue.clear();
80 | }
81 |
82 | /* Callbacks */
83 |
84 | /**
85 | * Add a callback to the AnimationQueue.
86 | * @param callback the callback to add
87 | */
88 | public void addCallback(Callback callback) {
89 | mCallbacks.add(callback);
90 | }
91 |
92 | /**
93 | * Remove the specified callback from the AnimationQueue.
94 | * @param callback the callback to remove
95 | */
96 | public void removeCallback(Callback callback) {
97 | mCallbacks.remove(callback);
98 | }
99 |
100 | /**
101 | * Remove any callbacks from the AnimationQueue.
102 | */
103 | public void clearCallbacks() {
104 | mCallbacks.clear();
105 | }
106 |
107 | /**
108 | * Start the animation loop if it is not currently running.
109 | */
110 | private void runIfIdle() {
111 | if (!mRunning) {
112 | mRunning = true;
113 | mChoreographer.postFrameCallback(mChoreographerCallback);
114 | }
115 | }
116 |
117 | /**
118 | * Called every time a new frame is ready to be rendered.
119 | *
120 | * Values are processed FIFO and each callback is given a chance to handle each value when its
121 | * turn comes before a value is poll'd off the AnimationQueue.
122 | *
123 | * @param frameTimeNanos The time in nanoseconds when the frame started being rendered, in the
124 | * nanoTime() timebase. Divide this value by 1000000 to convert it to the
125 | * uptimeMillis() time base.
126 | */
127 | private void onFrame(long frameTimeNanos) {
128 | Double nextPendingValue = mPendingQueue.poll();
129 |
130 | int drainingOffset;
131 | if (nextPendingValue != null) {
132 | mAnimationQueue.offer(nextPendingValue);
133 | drainingOffset = 0;
134 | } else {
135 | drainingOffset = Math.max(mCallbacks.size() - mAnimationQueue.size(), 0);
136 | }
137 |
138 | // Copy the values into a temporary ArrayList for processing.
139 | mTempValues.addAll(mAnimationQueue);
140 | for (int i = mTempValues.size() - 1; i > -1; i--) {
141 | Double val = mTempValues.get(i);
142 | int cbIdx = mTempValues.size() - 1 - i + drainingOffset;
143 | if (mCallbacks.size() > cbIdx) {
144 | mCallbacks.get(cbIdx).onFrame(val);
145 | }
146 | }
147 | mTempValues.clear();
148 |
149 | while (mAnimationQueue.size() + drainingOffset >= mCallbacks.size()) {
150 | mAnimationQueue.poll();
151 | }
152 |
153 | if (mAnimationQueue.isEmpty() && mPendingQueue.isEmpty()) {
154 | mRunning = false;
155 | } else {
156 | mChoreographer.postFrameCallback(mChoreographerCallback);
157 | }
158 | }
159 |
160 | }
161 |
162 |
--------------------------------------------------------------------------------
/rebound-android/src/main/java/com/facebook/rebound/ChoreographerCompat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | import android.annotation.TargetApi;
14 | import android.os.Build;
15 | import android.os.Handler;
16 | import android.os.Looper;
17 | import android.view.Choreographer;
18 |
19 | /**
20 | * Wrapper class for abstracting away availability of the JellyBean Choreographer. If Choreographer
21 | * is unavailable we fallback to using a normal Handler.
22 | */
23 | public class ChoreographerCompat {
24 |
25 | private static final long ONE_FRAME_MILLIS = 17;
26 | private static final boolean IS_JELLYBEAN_OR_HIGHER =
27 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
28 | private static final ChoreographerCompat INSTANCE = new ChoreographerCompat();
29 |
30 | private Handler mHandler;
31 | private Choreographer mChoreographer;
32 |
33 | public static ChoreographerCompat getInstance() {
34 | return INSTANCE;
35 | }
36 |
37 | private ChoreographerCompat() {
38 | if (IS_JELLYBEAN_OR_HIGHER) {
39 | mChoreographer = getChoreographer();
40 | } else {
41 | mHandler = new Handler(Looper.getMainLooper());
42 | }
43 | }
44 |
45 | public void postFrameCallback(FrameCallback callbackWrapper) {
46 | if (IS_JELLYBEAN_OR_HIGHER) {
47 | choreographerPostFrameCallback(callbackWrapper.getFrameCallback());
48 | } else {
49 | mHandler.postDelayed(callbackWrapper.getRunnable(), 0);
50 | }
51 | }
52 |
53 | public void postFrameCallbackDelayed(FrameCallback callbackWrapper, long delayMillis) {
54 | if (IS_JELLYBEAN_OR_HIGHER) {
55 | choreographerPostFrameCallbackDelayed(callbackWrapper.getFrameCallback(), delayMillis);
56 | } else {
57 | mHandler.postDelayed(callbackWrapper.getRunnable(), delayMillis + ONE_FRAME_MILLIS);
58 | }
59 | }
60 |
61 | public void removeFrameCallback(FrameCallback callbackWrapper) {
62 | if (IS_JELLYBEAN_OR_HIGHER) {
63 | choreographerRemoveFrameCallback(callbackWrapper.getFrameCallback());
64 | } else {
65 | mHandler.removeCallbacks(callbackWrapper.getRunnable());
66 | }
67 | }
68 |
69 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
70 | private Choreographer getChoreographer() {
71 | return Choreographer.getInstance();
72 | }
73 |
74 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
75 | private void choreographerPostFrameCallback(Choreographer.FrameCallback frameCallback) {
76 | mChoreographer.postFrameCallback(frameCallback);
77 | }
78 |
79 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
80 | private void choreographerPostFrameCallbackDelayed(
81 | Choreographer.FrameCallback frameCallback,
82 | long delayMillis) {
83 | mChoreographer.postFrameCallbackDelayed(frameCallback, delayMillis);
84 | }
85 |
86 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
87 | private void choreographerRemoveFrameCallback(Choreographer.FrameCallback frameCallback) {
88 | mChoreographer.removeFrameCallback(frameCallback);
89 | }
90 |
91 | /**
92 | * This class provides a compatibility wrapper around the JellyBean FrameCallback with methods
93 | * to access cached wrappers for submitting a real FrameCallback to a Choreographer or a Runnable
94 | * to a Handler.
95 | */
96 | public static abstract class FrameCallback {
97 |
98 | private Runnable mRunnable;
99 | private Choreographer.FrameCallback mFrameCallback;
100 |
101 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
102 | Choreographer.FrameCallback getFrameCallback() {
103 | if (mFrameCallback == null) {
104 | mFrameCallback = new Choreographer.FrameCallback() {
105 | @Override
106 | public void doFrame(long frameTimeNanos) {
107 | FrameCallback.this.doFrame(frameTimeNanos);
108 | }
109 | };
110 | }
111 | return mFrameCallback;
112 | }
113 |
114 | Runnable getRunnable() {
115 | if (mRunnable == null) {
116 | mRunnable = new Runnable() {
117 | @Override
118 | public void run() {
119 | doFrame(System.nanoTime());
120 | }
121 | };
122 | }
123 | return mRunnable;
124 | }
125 |
126 | public abstract void doFrame(long frameTimeNanos);
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/rebound-android/src/main/java/com/facebook/rebound/SpringChain.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.rebound;
11 |
12 | import java.util.List;
13 | import java.util.concurrent.CopyOnWriteArrayList;
14 |
15 | /**
16 | * SpringChain is a helper class for creating spring animations with multiple springs in a chain.
17 | * Chains of springs can be used to create cascading animations that maintain individual physics
18 | * state for each member of the chain. One spring in the chain is chosen to be the control spring.
19 | * Springs before and after the control spring in the chain are pulled along by their predecessor.
20 | * You can change which spring is the control spring at any point by calling
21 | * {@link SpringChain#setControlSpringIndex(int)}.
22 | */
23 | public class SpringChain implements SpringListener {
24 |
25 | /**
26 | * Add these spring configs to the registry to support live tuning through the
27 | * {@link com.facebook.rebound.ui.SpringConfiguratorView}
28 | */
29 | private static final SpringConfigRegistry registry = SpringConfigRegistry.getInstance();
30 | private static final int DEFAULT_MAIN_TENSION = 40;
31 | private static final int DEFAULT_MAIN_FRICTION = 6;
32 | private static final int DEFAULT_ATTACHMENT_TENSION = 70;
33 | private static final int DEFAULT_ATTACHMENT_FRICTION = 10;
34 | private static int id = 0;
35 |
36 |
37 | /**
38 | * Factory method for creating a new SpringChain with default SpringConfig.
39 | * @return the newly created SpringChain
40 | */
41 | public static SpringChain create() {
42 | return new SpringChain();
43 | }
44 |
45 | /**
46 | * Factory method for creating a new SpringChain with the provided SpringConfig.
47 | * @param mainTension tension for the main spring
48 | * @param mainFriction friction for the main spring
49 | * @param attachmentTension tension for the attachment spring
50 | * @param attachmentFriction friction for the attachment spring
51 | * @return the newly created SpringChain
52 | */
53 | public static SpringChain create(
54 | int mainTension,
55 | int mainFriction,
56 | int attachmentTension,
57 | int attachmentFriction) {
58 | return new SpringChain(mainTension, mainFriction, attachmentTension, attachmentFriction);
59 | }
60 |
61 | private final SpringSystem mSpringSystem = SpringSystem.create();
62 | private final CopyOnWriteArrayList mListeners =
63 | new CopyOnWriteArrayList();
64 | private final CopyOnWriteArrayList mSprings = new CopyOnWriteArrayList();
65 | private int mControlSpringIndex = -1;
66 |
67 | // The main spring config defines the tension and friction for the control spring. Keeping these
68 | // values separate allows the behavior of the trailing springs to be different than that of the
69 | // control point.
70 | private final SpringConfig mMainSpringConfig;
71 |
72 | // The attachment spring config defines the tension and friction for the rest of the springs in
73 | // the chain.
74 | private final SpringConfig mAttachmentSpringConfig;
75 |
76 | private SpringChain() {
77 | this(
78 | DEFAULT_MAIN_TENSION,
79 | DEFAULT_MAIN_FRICTION,
80 | DEFAULT_ATTACHMENT_TENSION,
81 | DEFAULT_ATTACHMENT_FRICTION);
82 | }
83 |
84 | private SpringChain(
85 | int mainTension,
86 | int mainFriction,
87 | int attachmentTension,
88 | int attachmentFriction) {
89 | mMainSpringConfig = SpringConfig.fromOrigamiTensionAndFriction(mainTension, mainFriction);
90 | mAttachmentSpringConfig =
91 | SpringConfig.fromOrigamiTensionAndFriction(attachmentTension, attachmentFriction);
92 | registry.addSpringConfig(mMainSpringConfig, "main spring " + id++);
93 | registry.addSpringConfig(mAttachmentSpringConfig, "attachment spring " + id++);
94 | }
95 |
96 | public SpringConfig getMainSpringConfig() {
97 | return mMainSpringConfig;
98 | }
99 |
100 | public SpringConfig getAttachmentSpringConfig() {
101 | return mAttachmentSpringConfig;
102 | }
103 |
104 | /**
105 | * Add a spring to the chain that will callback to the provided listener.
106 | * @param listener the listener to notify for this Spring in the chain
107 | * @return this SpringChain for chaining
108 | */
109 | public SpringChain addSpring(final SpringListener listener) {
110 | // We listen to each spring added to the SpringChain and dynamically chain the springs together
111 | // whenever the control spring state is modified.
112 | Spring spring = mSpringSystem
113 | .createSpring()
114 | .addListener(this)
115 | .setSpringConfig(mAttachmentSpringConfig);
116 | mSprings.add(spring);
117 | mListeners.add(listener);
118 | return this;
119 | }
120 |
121 | /**
122 | * Set the index of the control spring. This spring will drive the positions of all the springs
123 | * before and after it in the list when moved.
124 | * @param i the index to use for the control spring
125 | * @return this SpringChain
126 | */
127 | public SpringChain setControlSpringIndex(int i) {
128 | mControlSpringIndex = i;
129 | Spring controlSpring = mSprings.get(mControlSpringIndex);
130 | if (controlSpring == null) {
131 | return null;
132 | }
133 | for (Spring spring : mSpringSystem.getAllSprings()) {
134 | spring.setSpringConfig(mAttachmentSpringConfig);
135 | }
136 | getControlSpring().setSpringConfig(mMainSpringConfig);
137 | return this;
138 | }
139 |
140 | /**
141 | * Retrieve the control spring so you can manipulate it to drive the positions of the other
142 | * springs.
143 | * @return the control spring.
144 | */
145 | public Spring getControlSpring() {
146 | return mSprings.get(mControlSpringIndex);
147 | }
148 |
149 | /**
150 | * Retrieve the list of springs in the chain.
151 | * @return the list of springs
152 | */
153 | public List getAllSprings() {
154 | return mSprings;
155 | }
156 |
157 | @Override
158 | public void onSpringUpdate(Spring spring) {
159 | // Get the control spring index and update the endValue of each spring above and below it in the
160 | // spring collection triggering a cascading effect.
161 | int idx = mSprings.indexOf(spring);
162 | SpringListener listener = mListeners.get(idx);
163 | int above = -1;
164 | int below = -1;
165 | if (idx == mControlSpringIndex) {
166 | below = idx - 1;
167 | above = idx + 1;
168 | } else if (idx < mControlSpringIndex) {
169 | below = idx - 1;
170 | } else if (idx > mControlSpringIndex) {
171 | above = idx + 1;
172 | }
173 | if (above > -1 && above < mSprings.size()) {
174 | mSprings.get(above).setEndValue(spring.getCurrentValue());
175 | }
176 | if (below > -1 && below < mSprings.size()) {
177 | mSprings.get(below).setEndValue(spring.getCurrentValue());
178 | }
179 | listener.onSpringUpdate(spring);
180 | }
181 |
182 | @Override
183 | public void onSpringAtRest(Spring spring) {
184 | int idx = mSprings.indexOf(spring);
185 | mListeners.get(idx).onSpringAtRest(spring);
186 | }
187 |
188 | @Override
189 | public void onSpringActivate(Spring spring) {
190 | int idx = mSprings.indexOf(spring);
191 | mListeners.get(idx).onSpringActivate(spring);
192 | }
193 |
194 | @Override
195 | public void onSpringEndStateChange(Spring spring) {
196 | int idx = mSprings.indexOf(spring);
197 | mListeners.get(idx).onSpringEndStateChange(spring);
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/rebound-android/src/main/java/com/facebook/rebound/SpringSystem.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | /**
14 | * This is a wrapper for BaseSpringSystem that provides the convenience of automatically providing
15 | * the AndroidSpringLooper dependency in {@link SpringSystem#create}.
16 | */
17 | public class SpringSystem extends BaseSpringSystem {
18 |
19 | /**
20 | * Create a new SpringSystem providing the appropriate constructor parameters to work properly
21 | * in an Android environment.
22 | * @return the SpringSystem
23 | */
24 | public static SpringSystem create() {
25 | return new SpringSystem(AndroidSpringLooperFactory.createSpringLooper());
26 | }
27 |
28 | private SpringSystem(SpringLooper springLooper) {
29 | super(springLooper);
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/rebound-android/src/main/java/com/facebook/rebound/ui/SpringConfiguratorView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound.ui;
12 |
13 | import android.annotation.TargetApi;
14 | import android.content.Context;
15 | import android.content.res.Resources;
16 | import android.graphics.Color;
17 | import android.os.Build;
18 | import android.util.AttributeSet;
19 | import android.view.Gravity;
20 | import android.view.MotionEvent;
21 | import android.view.View;
22 | import android.view.ViewGroup;
23 | import android.widget.AbsListView;
24 | import android.widget.AdapterView;
25 | import android.widget.BaseAdapter;
26 | import android.widget.FrameLayout;
27 | import android.widget.LinearLayout;
28 | import android.widget.SeekBar;
29 | import android.widget.Spinner;
30 | import android.widget.TableLayout;
31 | import android.widget.TextView;
32 |
33 | import com.facebook.rebound.OrigamiValueConverter;
34 | import com.facebook.rebound.Spring;
35 | import com.facebook.rebound.SpringConfig;
36 | import com.facebook.rebound.SpringConfigRegistry;
37 | import com.facebook.rebound.SpringListener;
38 | import com.facebook.rebound.SpringSystem;
39 |
40 | import java.text.DecimalFormat;
41 | import java.util.ArrayList;
42 | import java.util.List;
43 | import java.util.Map;
44 |
45 | import static com.facebook.rebound.ui.Util.*;
46 |
47 | /**
48 | * The SpringConfiguratorView provides a reusable view for live-editing all registered springs
49 | * within an Application. Each registered Spring can be accessed by its id and its tension and
50 | * friction properties can be edited while the user tests the effected UI live.
51 | */
52 | public class SpringConfiguratorView extends FrameLayout {
53 |
54 | private static final int MAX_SEEKBAR_VAL = 100000;
55 | private static final float MIN_TENSION = 0;
56 | private static final float MAX_TENSION = 200;
57 | private static final float MIN_FRICTION = 0;
58 | private static final float MAX_FRICTION = 50;
59 | private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.#");
60 |
61 | private final SpinnerAdapter spinnerAdapter;
62 | private final List mSpringConfigs = new ArrayList();
63 | private final Spring mRevealerSpring;
64 | private final float mStashPx;
65 | private final float mRevealPx;
66 | private final SpringConfigRegistry springConfigRegistry;
67 | private final int mTextColor = Color.argb(255, 225, 225, 225);
68 | private SeekBar mTensionSeekBar;
69 | private SeekBar mFrictionSeekBar;
70 | private Spinner mSpringSelectorSpinner;
71 | private TextView mFrictionLabel;
72 | private TextView mTensionLabel;
73 | private SpringConfig mSelectedSpringConfig;
74 |
75 | public SpringConfiguratorView(Context context) {
76 | this(context, null);
77 | }
78 |
79 | public SpringConfiguratorView(Context context, AttributeSet attrs) {
80 | this(context, attrs, 0);
81 | }
82 |
83 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
84 | public SpringConfiguratorView(Context context, AttributeSet attrs, int defStyle) {
85 | super(context, attrs, defStyle);
86 |
87 | SpringSystem springSystem = SpringSystem.create();
88 | springConfigRegistry = SpringConfigRegistry.getInstance();
89 | spinnerAdapter = new SpinnerAdapter(context);
90 |
91 | Resources resources = getResources();
92 | mRevealPx = dpToPx(40, resources);
93 | mStashPx = dpToPx(280, resources);
94 |
95 | mRevealerSpring = springSystem.createSpring();
96 | SpringListener revealerSpringListener = new RevealerSpringListener();
97 | mRevealerSpring
98 | .setCurrentValue(1)
99 | .setEndValue(1)
100 | .addListener(revealerSpringListener);
101 |
102 | addView(generateHierarchy(context));
103 |
104 | SeekbarListener seekbarListener = new SeekbarListener();
105 | mTensionSeekBar.setMax(MAX_SEEKBAR_VAL);
106 | mTensionSeekBar.setOnSeekBarChangeListener(seekbarListener);
107 |
108 | mFrictionSeekBar.setMax(MAX_SEEKBAR_VAL);
109 | mFrictionSeekBar.setOnSeekBarChangeListener(seekbarListener);
110 |
111 | mSpringSelectorSpinner.setAdapter(spinnerAdapter);
112 | mSpringSelectorSpinner.setOnItemSelectedListener(new SpringSelectedListener());
113 | refreshSpringConfigurations();
114 |
115 | this.setTranslationY(mStashPx);
116 | }
117 |
118 | /**
119 | * Programmatically build up the view hierarchy to avoid the need for resources.
120 | * @return View hierarchy
121 | */
122 | private View generateHierarchy(Context context) {
123 | Resources resources = getResources();
124 |
125 | FrameLayout.LayoutParams params;
126 | int fivePx = dpToPx(5, resources);
127 | int tenPx = dpToPx(10, resources);
128 | int twentyPx = dpToPx(20, resources);
129 | TableLayout.LayoutParams tableLayoutParams = new TableLayout.LayoutParams(
130 | 0,
131 | ViewGroup.LayoutParams.WRAP_CONTENT,
132 | 1f);
133 | tableLayoutParams.setMargins(0, 0, fivePx, 0);
134 | LinearLayout seekWrapper;
135 |
136 | FrameLayout root = new FrameLayout(context);
137 | params = createLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(300, resources));
138 | root.setLayoutParams(params);
139 |
140 | FrameLayout container = new FrameLayout(context);
141 | params = createMatchParams();
142 | params.setMargins(0, twentyPx, 0, 0);
143 | container.setLayoutParams(params);
144 | container.setBackgroundColor(Color.argb(100, 0, 0, 0));
145 | root.addView(container);
146 |
147 | mSpringSelectorSpinner = new Spinner(context, Spinner.MODE_DIALOG);
148 | params = createMatchWrapParams();
149 | params.gravity = Gravity.TOP;
150 | params.setMargins(tenPx, tenPx, tenPx, 0);
151 | mSpringSelectorSpinner.setLayoutParams(params);
152 | container.addView(mSpringSelectorSpinner);
153 |
154 | LinearLayout linearLayout = new LinearLayout(context);
155 | params = createMatchWrapParams();
156 | params.setMargins(0, 0, 0, dpToPx(80, resources));
157 | params.gravity = Gravity.BOTTOM;
158 | linearLayout.setLayoutParams(params);
159 | linearLayout.setOrientation(LinearLayout.VERTICAL);
160 | container.addView(linearLayout);
161 |
162 | seekWrapper = new LinearLayout(context);
163 | params = createMatchWrapParams();
164 | params.setMargins(tenPx, tenPx, tenPx, twentyPx);
165 | seekWrapper.setPadding(tenPx, tenPx, tenPx, tenPx);
166 | seekWrapper.setLayoutParams(params);
167 | seekWrapper.setOrientation(LinearLayout.HORIZONTAL);
168 | linearLayout.addView(seekWrapper);
169 |
170 | mTensionSeekBar = new SeekBar(context);
171 | mTensionSeekBar.setLayoutParams(tableLayoutParams);
172 | seekWrapper.addView(mTensionSeekBar);
173 |
174 | mTensionLabel = new TextView(getContext());
175 | mTensionLabel.setTextColor(mTextColor);
176 | params = createLayoutParams(
177 | dpToPx(50, resources),
178 | ViewGroup.LayoutParams.MATCH_PARENT);
179 | mTensionLabel.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
180 | mTensionLabel.setLayoutParams(params);
181 | mTensionLabel.setMaxLines(1);
182 | seekWrapper.addView(mTensionLabel);
183 |
184 | seekWrapper = new LinearLayout(context);
185 | params = createMatchWrapParams();
186 | params.setMargins(tenPx, tenPx, tenPx, twentyPx);
187 | seekWrapper.setPadding(tenPx, tenPx, tenPx, tenPx);
188 | seekWrapper.setLayoutParams(params);
189 | seekWrapper.setOrientation(LinearLayout.HORIZONTAL);
190 | linearLayout.addView(seekWrapper);
191 |
192 | mFrictionSeekBar = new SeekBar(context);
193 | mFrictionSeekBar.setLayoutParams(tableLayoutParams);
194 | seekWrapper.addView(mFrictionSeekBar);
195 |
196 | mFrictionLabel = new TextView(getContext());
197 | mFrictionLabel.setTextColor(mTextColor);
198 | params = createLayoutParams(dpToPx(50, resources), ViewGroup.LayoutParams.MATCH_PARENT);
199 | mFrictionLabel.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
200 | mFrictionLabel.setLayoutParams(params);
201 | mFrictionLabel.setMaxLines(1);
202 | seekWrapper.addView(mFrictionLabel);
203 |
204 | View nub = new View(context);
205 | params = createLayoutParams(dpToPx(60, resources), dpToPx(40, resources));
206 | params.gravity = Gravity.TOP | Gravity.CENTER;
207 | nub.setLayoutParams(params);
208 | nub.setOnTouchListener(new OnNubTouchListener());
209 | nub.setBackgroundColor(Color.argb(255, 0, 164, 209));
210 | root.addView(nub);
211 |
212 | return root;
213 | }
214 |
215 | /**
216 | * remove the configurator from its parent and clean up springs and listeners
217 | */
218 | public void destroy() {
219 | ViewGroup parent = (ViewGroup) getParent();
220 | if (parent != null) {
221 | parent.removeView(this);
222 | }
223 | mRevealerSpring.destroy();
224 | }
225 |
226 | /**
227 | * reload the springs from the registry and update the UI
228 | */
229 | public void refreshSpringConfigurations() {
230 | Map springConfigMap = springConfigRegistry.getAllSpringConfig();
231 |
232 | spinnerAdapter.clear();
233 | mSpringConfigs.clear();
234 |
235 | for (Map.Entry entry : springConfigMap.entrySet()) {
236 | if (entry.getKey() == SpringConfig.defaultConfig) {
237 | continue;
238 | }
239 | mSpringConfigs.add(entry.getKey());
240 | spinnerAdapter.add(entry.getValue());
241 | }
242 | // Add the default config in last.
243 | mSpringConfigs.add(SpringConfig.defaultConfig);
244 | spinnerAdapter.add(springConfigMap.get(SpringConfig.defaultConfig));
245 | spinnerAdapter.notifyDataSetChanged();
246 | if (mSpringConfigs.size() > 0) {
247 | mSpringSelectorSpinner.setSelection(0);
248 | }
249 | }
250 |
251 | private class SpringSelectedListener implements AdapterView.OnItemSelectedListener {
252 |
253 | @Override
254 | public void onItemSelected(AdapterView> adapterView, View view, int i, long l) {
255 | mSelectedSpringConfig = mSpringConfigs.get(i);
256 | updateSeekBarsForSpringConfig(mSelectedSpringConfig);
257 | }
258 |
259 | @Override
260 | public void onNothingSelected(AdapterView> adapterView) {
261 | }
262 | }
263 |
264 | /**
265 | * listen to events on seekbars and update registered springs accordingly
266 | */
267 | private class SeekbarListener implements SeekBar.OnSeekBarChangeListener {
268 |
269 | @Override
270 | public void onProgressChanged(SeekBar seekBar, int val, boolean b) {
271 | float tensionRange = MAX_TENSION - MIN_TENSION;
272 | float frictionRange = MAX_FRICTION - MIN_FRICTION;
273 |
274 | if (seekBar == mTensionSeekBar) {
275 | float scaledTension = ((val) * tensionRange) / MAX_SEEKBAR_VAL + MIN_TENSION;
276 | mSelectedSpringConfig.tension =
277 | OrigamiValueConverter.tensionFromOrigamiValue(scaledTension);
278 | String roundedTensionLabel = DECIMAL_FORMAT.format(scaledTension);
279 | mTensionLabel.setText("T:" + roundedTensionLabel);
280 | }
281 |
282 | if (seekBar == mFrictionSeekBar) {
283 | float scaledFriction = ((val) * frictionRange) / MAX_SEEKBAR_VAL + MIN_FRICTION;
284 | mSelectedSpringConfig.friction =
285 | OrigamiValueConverter.frictionFromOrigamiValue(scaledFriction);
286 | String roundedFrictionLabel = DECIMAL_FORMAT.format(scaledFriction);
287 | mFrictionLabel.setText("F:" + roundedFrictionLabel);
288 | }
289 | }
290 |
291 | @Override
292 | public void onStartTrackingTouch(SeekBar seekBar) {
293 | }
294 |
295 | @Override
296 | public void onStopTrackingTouch(SeekBar seekBar) {
297 | }
298 | }
299 |
300 | /**
301 | * update the position of the seekbars based on the spring value;
302 | * @param springConfig current editing spring
303 | */
304 | private void updateSeekBarsForSpringConfig(SpringConfig springConfig) {
305 | float tension = (float) OrigamiValueConverter.origamiValueFromTension(springConfig.tension);
306 | float tensionRange = MAX_TENSION - MIN_TENSION;
307 | int scaledTension = Math.round(((tension - MIN_TENSION) * MAX_SEEKBAR_VAL) / tensionRange);
308 |
309 | float friction = (float) OrigamiValueConverter.origamiValueFromFriction(springConfig.friction);
310 | float frictionRange = MAX_FRICTION - MIN_FRICTION;
311 | int scaledFriction = Math.round(((friction - MIN_FRICTION) * MAX_SEEKBAR_VAL) / frictionRange);
312 |
313 | mTensionSeekBar.setProgress(scaledTension);
314 | mFrictionSeekBar.setProgress(scaledFriction);
315 | }
316 |
317 | /**
318 | * toggle visibility when the nub is tapped.
319 | */
320 | private class OnNubTouchListener implements View.OnTouchListener {
321 | @Override
322 | public boolean onTouch(View view, MotionEvent motionEvent) {
323 | if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
324 | togglePosition();
325 | }
326 | return true;
327 | }
328 | }
329 |
330 | private void togglePosition() {
331 | double currentValue = mRevealerSpring.getEndValue();
332 | mRevealerSpring
333 | .setEndValue(currentValue == 1 ? 0 : 1);
334 | }
335 |
336 | private class RevealerSpringListener implements SpringListener {
337 |
338 | @Override
339 | public void onSpringUpdate(Spring spring) {
340 | float val = (float) spring.getCurrentValue();
341 | float minTranslate = mRevealPx;
342 | float maxTranslate = mStashPx;
343 | float range = maxTranslate - minTranslate;
344 | float yTranslate = (val * range) + minTranslate;
345 | SpringConfiguratorView.this.setTranslationY(yTranslate);
346 | }
347 |
348 | @Override
349 | public void onSpringAtRest(Spring spring) {
350 | }
351 |
352 | @Override
353 | public void onSpringActivate(Spring spring) {
354 | }
355 |
356 | @Override
357 | public void onSpringEndStateChange(Spring spring) {
358 | }
359 | }
360 |
361 | private class SpinnerAdapter extends BaseAdapter {
362 |
363 | private final Context mContext;
364 | private final List mStrings;
365 |
366 | public SpinnerAdapter(Context context) {
367 | mContext = context;
368 | mStrings = new ArrayList();
369 | }
370 |
371 | @Override
372 | public int getCount() {
373 | return mStrings.size();
374 | }
375 |
376 | @Override
377 | public Object getItem(int position) {
378 | return mStrings.get(position);
379 | }
380 |
381 | @Override
382 | public long getItemId(int position) {
383 | return position;
384 | }
385 |
386 | public void add(String string) {
387 | mStrings.add(string);
388 | notifyDataSetChanged();
389 | }
390 |
391 | /**
392 | * Remove all elements from the list.
393 | */
394 | public void clear() {
395 | mStrings.clear();
396 | notifyDataSetChanged();
397 | }
398 |
399 | @Override
400 | public View getView(int position, View convertView, ViewGroup parent) {
401 | TextView textView;
402 | if (convertView == null) {
403 | textView = new TextView(mContext);
404 | AbsListView.LayoutParams params = new AbsListView.LayoutParams(
405 | ViewGroup.LayoutParams.MATCH_PARENT,
406 | ViewGroup.LayoutParams.MATCH_PARENT);
407 | textView.setLayoutParams(params);
408 | int twelvePx = dpToPx(12, getResources());
409 | textView.setPadding(twelvePx, twelvePx, twelvePx, twelvePx);
410 | textView.setTextColor(mTextColor);
411 | } else {
412 | textView = (TextView) convertView;
413 | }
414 | textView.setText(mStrings.get(position));
415 | return textView;
416 | }
417 | }
418 | }
419 |
420 |
--------------------------------------------------------------------------------
/rebound-android/src/main/java/com/facebook/rebound/ui/Util.java:
--------------------------------------------------------------------------------
1 | package com.facebook.rebound.ui;
2 |
3 | import android.content.res.Resources;
4 | import android.util.TypedValue;
5 | import android.view.ViewGroup;
6 | import android.widget.FrameLayout;
7 |
8 | /**
9 | * Utilities for generating view hierarchies without using resources.
10 | */
11 | public abstract class Util {
12 |
13 | public static int dpToPx(float dp, Resources res) {
14 | return (int) TypedValue.applyDimension(
15 | TypedValue.COMPLEX_UNIT_DIP,
16 | dp,
17 | res.getDisplayMetrics());
18 | }
19 |
20 | public static FrameLayout.LayoutParams createLayoutParams(int width, int height) {
21 | return new FrameLayout.LayoutParams(width, height);
22 | }
23 |
24 | public static FrameLayout.LayoutParams createMatchParams() {
25 | return createLayoutParams(
26 | ViewGroup.LayoutParams.MATCH_PARENT,
27 | ViewGroup.LayoutParams.MATCH_PARENT);
28 | }
29 |
30 | public static FrameLayout.LayoutParams createWrapParams() {
31 | return createLayoutParams(
32 | ViewGroup.LayoutParams.WRAP_CONTENT,
33 | ViewGroup.LayoutParams.WRAP_CONTENT);
34 | }
35 |
36 | public static FrameLayout.LayoutParams createWrapMatchParams() {
37 | return createLayoutParams(
38 | ViewGroup.LayoutParams.WRAP_CONTENT,
39 | ViewGroup.LayoutParams.MATCH_PARENT);
40 | }
41 |
42 | public static FrameLayout.LayoutParams createMatchWrapParams() {
43 | return createLayoutParams(
44 | ViewGroup.LayoutParams.MATCH_PARENT,
45 | ViewGroup.LayoutParams.WRAP_CONTENT);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/rebound-core/BUCK:
--------------------------------------------------------------------------------
1 | prebuilt_jar(
2 | name = 'mockito',
3 | binary_jar = 'libs/mockito-all-1.9.5.jar',
4 | )
5 |
6 | prebuilt_jar(
7 | name = 'hamcrest-core',
8 | binary_jar = 'libs/hamcrest-core-1.3.jar',
9 | )
10 |
11 | prebuilt_jar(
12 | name = 'junit',
13 | binary_jar = 'libs/junit-4.11.jar',
14 | )
15 |
16 | java_test(
17 | name = 'test',
18 | srcs = glob(['src/test/java/**/*Test.java']),
19 | deps = [
20 | '//rebound-core:mockito',
21 | '//rebound-core:hamcrest-core',
22 | '//rebound-core:junit',
23 | '//rebound-core:src'
24 | ],
25 | source_under_test = ['//rebound-core:src'],
26 | )
27 |
28 | android_library(
29 | name = 'src',
30 | srcs = glob(['src/main/java/**/*.java']),
31 | visibility = ['PUBLIC'],
32 | )
33 |
34 | project_config(
35 | src_target = '//rebound-core:src',
36 | test_target = '//rebound-core:test',
37 | src_roots = ['src/main/java'],
38 | test_roots = ['src/test/java'],
39 | )
40 |
--------------------------------------------------------------------------------
/rebound-core/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 |
4 | android {
5 | compileOptions {
6 | sourceCompatibility JavaVersion.VERSION_1_7
7 | targetCompatibility JavaVersion.VERSION_1_7
8 | }
9 | compileSdkVersion Integer.parseInt(project.ANDROID_COMPILE_SDK_VERSION)
10 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
11 |
12 | defaultConfig {
13 | versionCode Integer.parseInt(project.VERSION_CODE)
14 | versionName project.VERSION_NAME
15 | minSdkVersion 11
16 | targetSdkVersion Integer.parseInt(project.ANDROID_TARGET_SDK_VERSION)
17 | }
18 |
19 | }
20 |
21 | dependencies {
22 | testCompile files(
23 | 'libs/hamcrest-core-1.3.jar',
24 | 'libs/junit-4.11.jar',
25 | 'libs/mockito-all-1.9.5.jar')
26 | }
--------------------------------------------------------------------------------
/rebound-core/libs/hamcrest-core-1.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-core/libs/hamcrest-core-1.3.jar
--------------------------------------------------------------------------------
/rebound-core/libs/junit-4.11.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-core/libs/junit-4.11.jar
--------------------------------------------------------------------------------
/rebound-core/libs/mockito-all-1.9.5.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CarGuo/rebound/cf877b0d747473e8786fc73283b4f372aab1b99f/rebound-core/libs/mockito-all-1.9.5.jar
--------------------------------------------------------------------------------
/rebound-core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/BaseSpringSystem.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | import java.util.ArrayList;
14 | import java.util.Collection;
15 | import java.util.Collections;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.Map;
19 | import java.util.Set;
20 | import java.util.concurrent.CopyOnWriteArraySet;
21 |
22 | /**
23 | * BaseSpringSystem maintains the set of springs within an Application context. It is responsible for
24 | * Running the spring integration loop and maintains a registry of all the Springs it solves for.
25 | * In addition to listening to physics events on the individual Springs in the system, listeners
26 | * can be added to the BaseSpringSystem itself to provide pre and post integration setup.
27 | */
28 | public class BaseSpringSystem {
29 |
30 | private final Map mSpringRegistry = new HashMap();
31 | private final Set mActiveSprings = new CopyOnWriteArraySet();
32 | private final SpringLooper mSpringLooper;
33 | private final CopyOnWriteArraySet mListeners = new CopyOnWriteArraySet();
34 | private boolean mIdle = true;
35 |
36 | /**
37 | * create a new BaseSpringSystem
38 | * @param springLooper parameterized springLooper to allow testability of the
39 | * physics loop
40 | */
41 | public BaseSpringSystem(SpringLooper springLooper) {
42 | if (springLooper == null) {
43 | throw new IllegalArgumentException("springLooper is required");
44 | }
45 | mSpringLooper = springLooper;
46 | mSpringLooper.setSpringSystem(this);
47 | }
48 |
49 | /**
50 | * check if the system is idle
51 | * @return is the system idle
52 | */
53 | public boolean getIsIdle() {
54 | return mIdle;
55 | }
56 |
57 | /**
58 | * create a spring with a random uuid for its name.
59 | * @return the spring
60 | */
61 | public Spring createSpring() {
62 | Spring spring = new Spring(this);
63 | registerSpring(spring);
64 | return spring;
65 | }
66 |
67 | /**
68 | * get a spring by name
69 | * @param id id of the spring to retrieve
70 | * @return Spring with the specified key
71 | */
72 | public Spring getSpringById(String id) {
73 | if (id == null) {
74 | throw new IllegalArgumentException("id is required");
75 | }
76 | return mSpringRegistry.get(id);
77 | }
78 |
79 | /**
80 | * return all the springs in the simulator
81 | * @return all the springs
82 | */
83 | public List getAllSprings() {
84 | Collection collection = mSpringRegistry.values();
85 | List list;
86 | if (collection instanceof List) {
87 | list = (List)collection;
88 | } else {
89 | list = new ArrayList(collection);
90 | }
91 | return Collections.unmodifiableList(list);
92 | }
93 |
94 | /**
95 | * Registers a Spring to this BaseSpringSystem so it can be iterated if active.
96 | * @param spring the Spring to register
97 | */
98 | void registerSpring(Spring spring) {
99 | if (spring == null) {
100 | throw new IllegalArgumentException("spring is required");
101 | }
102 | if (mSpringRegistry.containsKey(spring.getId())) {
103 | throw new IllegalArgumentException("spring is already registered"); }
104 | mSpringRegistry.put(spring.getId(), spring);
105 | }
106 |
107 | /**
108 | * Deregisters a Spring from this BaseSpringSystem, so it won't be iterated anymore. The Spring should
109 | * not be used anymore after doing this.
110 | *
111 | * @param spring the Spring to deregister
112 | */
113 | void deregisterSpring(Spring spring) {
114 | if (spring == null) {
115 | throw new IllegalArgumentException("spring is required");
116 | }
117 | mActiveSprings.remove(spring);
118 | mSpringRegistry.remove(spring.getId());
119 | }
120 |
121 | /**
122 | * update the springs in the system
123 | * @param deltaTime delta since last update in millis
124 | */
125 | void advance(double deltaTime) {
126 | for (Spring spring : mActiveSprings) {
127 | // advance time in seconds
128 | if (spring.systemShouldAdvance()) {
129 | spring.advance(deltaTime / 1000.0);
130 | } else {
131 | mActiveSprings.remove(spring);
132 | }
133 | }
134 | }
135 |
136 | /**
137 | * loop the system until idle
138 | * @param elapsedMillis elapsed milliseconds
139 | */
140 | public void loop(double elapsedMillis) {
141 | for (SpringSystemListener listener : mListeners) {
142 | listener.onBeforeIntegrate(this);
143 | }
144 | advance(elapsedMillis);
145 | if (mActiveSprings.isEmpty()) {
146 | mIdle = true;
147 | }
148 | for (SpringSystemListener listener : mListeners) {
149 | listener.onAfterIntegrate(this);
150 | }
151 | if (mIdle) {
152 | mSpringLooper.stop();
153 | }
154 | }
155 |
156 | /**
157 | * This is used internally by the {@link Spring}s created by this {@link BaseSpringSystem} to notify
158 | * it has reached a state where it needs to be iterated. This will add the spring to the list of
159 | * active springs on this system and start the iteration if the system was idle before this call.
160 | * @param springId the id of the Spring to be activated
161 | */
162 | void activateSpring(String springId) {
163 | Spring spring = mSpringRegistry.get(springId);
164 | if (spring == null) {
165 | throw new IllegalArgumentException("springId " + springId + " does not reference a registered spring");
166 | }
167 | mActiveSprings.add(spring);
168 | if (getIsIdle()) {
169 | mIdle = false;
170 | mSpringLooper.start();
171 | }
172 | }
173 |
174 | /** listeners **/
175 |
176 | /**
177 | * Add new listener object.
178 | * @param newListener listener
179 | */
180 | public void addListener(SpringSystemListener newListener) {
181 | if (newListener == null) {
182 | throw new IllegalArgumentException("newListener is required");
183 | }
184 | mListeners.add(newListener);
185 | }
186 |
187 | /**
188 | * Remove listener object.
189 | * @param listenerToRemove listener
190 | */
191 | public void removeListener(SpringSystemListener listenerToRemove) {
192 | if (listenerToRemove == null) {
193 | throw new IllegalArgumentException("listenerToRemove is required");
194 | }
195 | mListeners.remove(listenerToRemove);
196 | }
197 |
198 | /**
199 | * Remove all listeners.
200 | */
201 | public void removeAllListeners() {
202 | mListeners.clear();
203 | }
204 | }
205 |
206 |
207 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/BouncyConversion.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | /**
14 | * This class converts values from the Quartz Composer Bouncy patch into Bouncy QC tension and
15 | * friction values.
16 | */
17 | public class BouncyConversion {
18 |
19 | private final double mBouncyTension;
20 | private final double mBouncyFriction;
21 | private final double mSpeed;
22 | private final double mBounciness;
23 |
24 | public BouncyConversion(double speed, double bounciness) {
25 | mSpeed = speed;
26 | mBounciness = bounciness;
27 | double b = normalize(bounciness / 1.7, 0, 20.);
28 | b = project_normal(b, 0.0, 0.8);
29 | double s = normalize(speed / 1.7, 0, 20.);
30 | mBouncyTension = project_normal(s, 0.5, 200);
31 | mBouncyFriction = quadratic_out_interpolation(b, b3_nobounce(mBouncyTension), 0.01);
32 | }
33 |
34 | public double getSpeed() {
35 | return mSpeed;
36 | }
37 |
38 | public double getBounciness() {
39 | return mBounciness;
40 | }
41 |
42 | public double getBouncyTension() {
43 | return mBouncyTension;
44 | }
45 |
46 | public double getBouncyFriction() {
47 | return mBouncyFriction;
48 | }
49 |
50 | private double normalize(double value, double startValue, double endValue) {
51 | return (value - startValue) / (endValue - startValue);
52 | }
53 |
54 | private double project_normal(double n, double start, double end) {
55 | return start + (n * (end - start));
56 | }
57 |
58 | private double linear_interpolation(double t, double start, double end) {
59 | return t * end + (1.f - t) * start;
60 | }
61 |
62 | private double quadratic_out_interpolation(double t, double start, double end) {
63 | return linear_interpolation(2*t - t*t, start, end);
64 | }
65 |
66 | private double b3_friction1(double x) {
67 | return (0.0007 * Math.pow(x, 3)) - (0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28;
68 | }
69 |
70 | private double b3_friction2(double x) {
71 | return (0.000044 * Math.pow(x, 3)) - (0.006 * Math.pow(x, 2)) + 0.36 * x + 2.;
72 | }
73 |
74 | private double b3_friction3(double x) {
75 | return (0.00000045 * Math.pow(x, 3)) - (0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84;
76 | }
77 |
78 | private double b3_nobounce(double tension) {
79 | double friction = 0;
80 | if (tension <= 18) {
81 | friction = b3_friction1(tension);
82 | } else if (tension > 18 && tension <= 44) {
83 | friction = b3_friction2(tension);
84 | } else if (tension > 44) {
85 | friction = b3_friction3(tension);
86 | } else {
87 | assert(false);
88 | }
89 | return friction;
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/OrigamiValueConverter.java:
--------------------------------------------------------------------------------
1 | package com.facebook.rebound;
2 |
3 | /**
4 | * Helper math util to convert tension & friction values from the Origami design tool to values
5 | * that the spring system needs.
6 | */
7 | public class OrigamiValueConverter {
8 |
9 | public static double tensionFromOrigamiValue(double oValue) {
10 | return oValue == 0 ? 0 : (oValue - 30.0) * 3.62 + 194.0;
11 | }
12 |
13 | public static double origamiValueFromTension(double tension) {
14 | return tension == 0 ? 0 : (tension - 194.0) / 3.62 + 30.0;
15 | }
16 |
17 | public static double frictionFromOrigamiValue(double oValue) {
18 | return oValue == 0 ? 0 : (oValue - 8.0) * 3.0 + 25.0;
19 | }
20 |
21 | public static double origamiValueFromFriction(double friction) {
22 | return friction == 0 ? 0 : (friction - 25.0) / 3.0 + 8.0;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/SimpleSpringListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | public class SimpleSpringListener implements SpringListener {
14 | @Override
15 | public void onSpringUpdate(Spring spring) {
16 | }
17 |
18 | @Override
19 | public void onSpringAtRest(Spring spring) {
20 | }
21 |
22 | @Override
23 | public void onSpringActivate(Spring spring) {
24 | }
25 |
26 | @Override
27 | public void onSpringEndStateChange(Spring spring) {
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/Spring.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | import java.util.concurrent.CopyOnWriteArraySet;
14 |
15 | /**
16 | * Classical spring implementing Hooke's law with configurable friction and tension.
17 | */
18 | public class Spring {
19 |
20 | // unique incrementer id for springs
21 | private static int ID = 0;
22 |
23 | // maximum amount of time to simulate per physics iteration in seconds (4 frames at 60 FPS)
24 | private static final double MAX_DELTA_TIME_SEC = 0.064;
25 | // fixed timestep to use in the physics solver in seconds
26 | private static final double SOLVER_TIMESTEP_SEC = 0.001;
27 | private SpringConfig mSpringConfig;
28 | private boolean mOvershootClampingEnabled;
29 |
30 | // storage for the current and prior physics state while integration is occurring
31 | private static class PhysicsState {
32 | double position;
33 | double velocity;
34 | }
35 |
36 | // unique id for the spring in the system
37 | private final String mId;
38 | // all physics simulation objects are final and reused in each processing pass
39 | private final PhysicsState mCurrentState = new PhysicsState();
40 | private final PhysicsState mPreviousState = new PhysicsState();
41 | private final PhysicsState mTempState = new PhysicsState();
42 | private double mStartValue;
43 | private double mEndValue;
44 | private boolean mWasAtRest = true;
45 | // thresholds for determining when the spring is at rest
46 | private double mRestSpeedThreshold = 0.005;
47 | private double mDisplacementFromRestThreshold = 0.005;
48 | private double mTimeAccumulator = 0;
49 | private final CopyOnWriteArraySet mListeners =
50 | new CopyOnWriteArraySet();
51 |
52 | private final BaseSpringSystem mSpringSystem;
53 |
54 | /**
55 | * create a new spring
56 | */
57 | Spring(BaseSpringSystem springSystem) {
58 | if (springSystem == null) {
59 | throw new IllegalArgumentException("Spring cannot be created outside of a BaseSpringSystem");
60 | }
61 | mSpringSystem = springSystem;
62 | mId = "spring:" + ID++;
63 | setSpringConfig(SpringConfig.defaultConfig);
64 | }
65 |
66 | /**
67 | * Destroys this Spring, meaning that it will be deregistered from its BaseSpringSystem so it won't be
68 | * iterated anymore and will clear its set of listeners. Do not use the Spring after calling this,
69 | * doing so may just cause an exception to be thrown.
70 | */
71 | public void destroy() {
72 | mListeners.clear();
73 | mSpringSystem.deregisterSpring(this);
74 | }
75 |
76 | /**
77 | * get the unique id for this spring
78 | * @return the unique id
79 | */
80 | public String getId() {
81 | return mId;
82 | }
83 |
84 | /**
85 | * set the config class
86 | * @param springConfig config class for the spring
87 | * @return this Spring instance for chaining
88 | */
89 | public Spring setSpringConfig(SpringConfig springConfig) {
90 | if (springConfig == null) {
91 | throw new IllegalArgumentException("springConfig is required");
92 | }
93 | mSpringConfig = springConfig;
94 | return this;
95 | }
96 |
97 | /**
98 | * retrieve the spring config for this spring
99 | * @return the SpringConfig applied to this spring
100 | */
101 | public SpringConfig getSpringConfig() {
102 | return mSpringConfig;
103 | }
104 |
105 | /**
106 | * Set the displaced value to determine the displacement for the spring from the rest value.
107 | * This value is retained and used to calculate the displacement ratio.
108 | * The default signature also sets the Spring at rest to facilitate the common behavior of moving
109 | * a spring to a new position.
110 | * @param currentValue the new start and current value for the spring
111 | * @return the spring for chaining
112 | */
113 | public Spring setCurrentValue(double currentValue) {
114 | return setCurrentValue(currentValue, true);
115 | }
116 |
117 | /**
118 | * The full signature for setCurrentValue includes the option of not setting the spring at rest
119 | * after updating its currentValue. Passing setAtRest false means that if the endValue of the
120 | * spring is not equal to the currentValue, the physics system will start iterating to resolve
121 | * the spring to the end value. This is almost never the behavior that you want, so the default
122 | * setCurrentValue signature passes true.
123 | * @param currentValue the new start and current value for the spring
124 | * @param setAtRest optionally set the spring at rest after updating its current value.
125 | * see {@link com.facebook.rebound.Spring#setAtRest()}
126 | * @return the spring for chaining
127 | */
128 | public Spring setCurrentValue(double currentValue, boolean setAtRest) {
129 | mStartValue = currentValue;
130 | mCurrentState.position = currentValue;
131 | mSpringSystem.activateSpring(this.getId());
132 | for (SpringListener listener : mListeners) {
133 | listener.onSpringUpdate(this);
134 | }
135 | if (setAtRest) {
136 | setAtRest();
137 | }
138 | return this;
139 | }
140 |
141 | /**
142 | * Get the displacement value from the last time setCurrentValue was called.
143 | * @return displacement value
144 | */
145 | public double getStartValue() {
146 | return mStartValue;
147 | }
148 |
149 | /**
150 | * Get the current
151 | * @return current value
152 | */
153 | public double getCurrentValue() {
154 | return mCurrentState.position;
155 | }
156 |
157 | /**
158 | * get the displacement of the springs current value from its rest value.
159 | * @return the distance displaced by
160 | */
161 | public double getCurrentDisplacementDistance() {
162 | return getDisplacementDistanceForState(mCurrentState);
163 | }
164 |
165 | /**
166 | * get the displacement from rest for a given physics state
167 | * @param state the state to measure from
168 | * @return the distance displaced by
169 | */
170 | private double getDisplacementDistanceForState(PhysicsState state) {
171 | return Math.abs(mEndValue - state.position);
172 | }
173 |
174 | /**
175 | * set the rest value to determine the displacement for the spring
176 | * @param endValue the endValue for the spring
177 | * @return the spring for chaining
178 | */
179 | public Spring setEndValue(double endValue) {
180 | if (mEndValue == endValue && isAtRest()) {
181 | return this;
182 | }
183 | mStartValue = getCurrentValue();
184 | mEndValue = endValue;
185 | mSpringSystem.activateSpring(this.getId());
186 | for (SpringListener listener : mListeners) {
187 | listener.onSpringEndStateChange(this);
188 | }
189 | return this;
190 | }
191 |
192 | /**
193 | * get the rest value used for determining the displacement of the spring
194 | * @return the rest value for the spring
195 | */
196 | public double getEndValue() {
197 | return mEndValue;
198 | }
199 |
200 | /**
201 | * set the velocity on the spring in pixels per second
202 | * @param velocity velocity value
203 | * @return the spring for chaining
204 | */
205 | public Spring setVelocity(double velocity) {
206 | if (velocity == mCurrentState.velocity) {
207 | return this;
208 | }
209 | mCurrentState.velocity = velocity;
210 | mSpringSystem.activateSpring(this.getId());
211 | return this;
212 | }
213 |
214 | /**
215 | * get the velocity of the spring
216 | * @return the current velocity
217 | */
218 | public double getVelocity() {
219 | return mCurrentState.velocity;
220 | }
221 |
222 | /**
223 | * Sets the speed at which the spring should be considered at rest.
224 | * @param restSpeedThreshold speed pixels per second
225 | * @return the spring for chaining
226 | */
227 | public Spring setRestSpeedThreshold(double restSpeedThreshold) {
228 | mRestSpeedThreshold = restSpeedThreshold;
229 | return this;
230 | }
231 |
232 | /**
233 | * Returns the speed at which the spring should be considered at rest in pixels per second
234 | * @return speed in pixels per second
235 | */
236 | public double getRestSpeedThreshold() {
237 | return mRestSpeedThreshold;
238 | }
239 |
240 | /**
241 | * set the threshold of displacement from rest below which the spring should be considered at rest
242 | * @param displacementFromRestThreshold displacement to consider resting below
243 | * @return the spring for chaining
244 | */
245 | public Spring setRestDisplacementThreshold(double displacementFromRestThreshold) {
246 | mDisplacementFromRestThreshold = displacementFromRestThreshold;
247 | return this;
248 | }
249 |
250 | /**
251 | * get the threshold of displacement from rest below which the spring should be considered at rest
252 | * @return displacement to consider resting below
253 | */
254 | public double getRestDisplacementThreshold() {
255 | return mDisplacementFromRestThreshold;
256 | }
257 |
258 | /**
259 | * Force the spring to clamp at its end value to avoid overshooting the target value.
260 | * @param overshootClampingEnabled whether or not to enable overshoot clamping
261 | * @return the spring for chaining
262 | */
263 | public Spring setOvershootClampingEnabled(boolean overshootClampingEnabled) {
264 | mOvershootClampingEnabled = overshootClampingEnabled;
265 | return this;
266 | }
267 |
268 | /**
269 | * Check if overshoot clamping is enabled.
270 | * @return is overshoot clamping enabled
271 | */
272 | public boolean isOvershootClampingEnabled() {
273 | return mOvershootClampingEnabled;
274 | }
275 |
276 | /**
277 | * Check if the spring is overshooting beyond its target.
278 | * @return true if the spring is overshooting its target
279 | */
280 | public boolean isOvershooting() {
281 | return mSpringConfig.tension > 0 &&
282 | ((mStartValue < mEndValue && getCurrentValue() > mEndValue) ||
283 | (mStartValue > mEndValue && getCurrentValue() < mEndValue));
284 | }
285 |
286 | /**
287 | * advance the physics simulation in SOLVER_TIMESTEP_SEC sized chunks to fulfill the required
288 | * realTimeDelta.
289 | * The math is inlined inside the loop since it made a huge performance impact when there are
290 | * several springs being advanced.
291 | * @param realDeltaTime clock drift
292 | */
293 | void advance(double realDeltaTime) {
294 |
295 | boolean isAtRest = isAtRest();
296 |
297 | if (isAtRest && mWasAtRest) {
298 | /* begin debug
299 | Log.d(TAG, "bailing out because we are at rest:" + getName());
300 | end debug */
301 | return;
302 | }
303 |
304 | // clamp the amount of realTime to simulate to avoid stuttering in the UI. We should be able
305 | // to catch up in a subsequent advance if necessary.
306 | double adjustedDeltaTime = realDeltaTime;
307 | if (realDeltaTime > MAX_DELTA_TIME_SEC) {
308 | adjustedDeltaTime = MAX_DELTA_TIME_SEC;
309 | }
310 |
311 | /* begin debug
312 | long startTime = System.currentTimeMillis();
313 | int iterations = 0;
314 | end debug */
315 |
316 | mTimeAccumulator += adjustedDeltaTime;
317 |
318 | double tension = mSpringConfig.tension;
319 | double friction = mSpringConfig.friction;
320 |
321 | double position = mCurrentState.position;
322 | double velocity = mCurrentState.velocity;
323 | double tempPosition = mTempState.position;
324 | double tempVelocity = mTempState.velocity;
325 |
326 | double aVelocity, aAcceleration;
327 | double bVelocity, bAcceleration;
328 | double cVelocity, cAcceleration;
329 | double dVelocity, dAcceleration;
330 |
331 | double dxdt, dvdt;
332 |
333 | // iterate over the true time
334 | while (mTimeAccumulator >= SOLVER_TIMESTEP_SEC) {
335 | /* begin debug
336 | iterations++;
337 | end debug */
338 | mTimeAccumulator -= SOLVER_TIMESTEP_SEC;
339 |
340 | if (mTimeAccumulator < SOLVER_TIMESTEP_SEC) {
341 | // This will be the last iteration. Remember the previous state in case we need to
342 | // interpolate
343 | mPreviousState.position = position;
344 | mPreviousState.velocity = velocity;
345 | }
346 |
347 | // Perform an RK4 integration to provide better detection of the acceleration curve via
348 | // sampling of Euler integrations at 4 intervals feeding each derivative into the calculation
349 | // of the next and taking a weighted sum of the 4 derivatives as the final output.
350 |
351 | // This math was inlined since it made for big performance improvements when advancing several
352 | // springs in one pass of the BaseSpringSystem.
353 |
354 | // The initial derivative is based on the current velocity and the calculated acceleration
355 | aVelocity = velocity;
356 | aAcceleration = (tension * (mEndValue - tempPosition)) - friction * velocity;
357 |
358 | // Calculate the next derivatives starting with the last derivative and integrating over the
359 | // timestep
360 | tempPosition = position + aVelocity * SOLVER_TIMESTEP_SEC * 0.5;
361 | tempVelocity = velocity + aAcceleration * SOLVER_TIMESTEP_SEC * 0.5;
362 | bVelocity = tempVelocity;
363 | bAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;
364 |
365 | tempPosition = position + bVelocity * SOLVER_TIMESTEP_SEC * 0.5;
366 | tempVelocity = velocity + bAcceleration * SOLVER_TIMESTEP_SEC * 0.5;
367 | cVelocity = tempVelocity;
368 | cAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;
369 |
370 | tempPosition = position + cVelocity * SOLVER_TIMESTEP_SEC;
371 | tempVelocity = velocity + cAcceleration * SOLVER_TIMESTEP_SEC;
372 | dVelocity = tempVelocity;
373 | dAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;
374 |
375 | // Take the weighted sum of the 4 derivatives as the final output.
376 | dxdt = 1.0/6.0 * (aVelocity + 2.0 * (bVelocity + cVelocity) + dVelocity);
377 | dvdt = 1.0/6.0 * (aAcceleration + 2.0 * (bAcceleration + cAcceleration) + dAcceleration);
378 |
379 | position += dxdt * SOLVER_TIMESTEP_SEC;
380 | velocity += dvdt * SOLVER_TIMESTEP_SEC;
381 | }
382 |
383 | mTempState.position = tempPosition;
384 | mTempState.velocity = tempVelocity;
385 |
386 | mCurrentState.position = position;
387 | mCurrentState.velocity = velocity;
388 |
389 | if (mTimeAccumulator > 0) {
390 | interpolate(mTimeAccumulator / SOLVER_TIMESTEP_SEC);
391 | }
392 |
393 | // End the spring immediately if it is overshooting and overshoot clamping is enabled.
394 | // Also make sure that if the spring was considered within a resting threshold that it's now
395 | // snapped to its end value.
396 | if (isAtRest() || (mOvershootClampingEnabled && isOvershooting())) {
397 | // Don't call setCurrentValue because that forces a call to onSpringUpdate
398 | if (tension > 0) {
399 | mStartValue = mEndValue;
400 | mCurrentState.position = mEndValue;
401 | } else {
402 | mEndValue = mCurrentState.position;
403 | mStartValue = mEndValue;
404 | }
405 | setVelocity(0);
406 | isAtRest = true;
407 | }
408 |
409 | /* begin debug
410 | long endTime = System.currentTimeMillis();
411 | long elapsedMillis = endTime - startTime;
412 | Log.d(TAG,
413 | "iterations:" + iterations +
414 | " iterationTime:" + elapsedMillis +
415 | " position:" + mCurrentState.position +
416 | " velocity:" + mCurrentState.velocity +
417 | " realDeltaTime:" + realDeltaTime +
418 | " adjustedDeltaTime:" + adjustedDeltaTime +
419 | " isAtRest:" + isAtRest +
420 | " wasAtRest:" + mWasAtRest);
421 | end debug */
422 |
423 | // NB: do these checks outside the loop so all listeners are properly notified of the state
424 | // transition
425 | boolean notifyActivate = false;
426 | if (mWasAtRest) {
427 | mWasAtRest = false;
428 | notifyActivate = true;
429 | }
430 | boolean notifyAtRest = false;
431 | if (isAtRest) {
432 | mWasAtRest = true;
433 | notifyAtRest = true;
434 | }
435 | for (SpringListener listener : mListeners) {
436 | // starting to move
437 | if (notifyActivate) {
438 | listener.onSpringActivate(this);
439 | }
440 |
441 | // updated
442 | listener.onSpringUpdate(this);
443 |
444 | // coming to rest
445 | if (notifyAtRest) {
446 | listener.onSpringAtRest(this);
447 | }
448 | }
449 | }
450 |
451 | /**
452 | * Check if this spring should be advanced by the system. * The rule is if the spring is
453 | * currently at rest and it was at rest in the previous advance, the system can skip this spring
454 | * @return should the system process this spring
455 | */
456 | public boolean systemShouldAdvance() {
457 | return !isAtRest() || !wasAtRest();
458 | }
459 |
460 | /**
461 | * Check if the spring was at rest in the prior iteration. This is used for ensuring the ending
462 | * callbacks are fired as the spring comes to a rest.
463 | * @return true if the spring was at rest in the prior iteration
464 | */
465 | public boolean wasAtRest() {
466 | return mWasAtRest;
467 | }
468 |
469 | /**
470 | * check if the current state is at rest
471 | * @return is the spring at rest
472 | */
473 | public boolean isAtRest() {
474 | return Math.abs(mCurrentState.velocity) <= mRestSpeedThreshold &&
475 | (getDisplacementDistanceForState(mCurrentState) <= mDisplacementFromRestThreshold ||
476 | mSpringConfig.tension == 0);
477 | }
478 |
479 | /**
480 | * Set the spring to be at rest by making its end value equal to its current value and setting
481 | * velocity to 0.
482 | * @return this object
483 | */
484 | public Spring setAtRest() {
485 | mEndValue = mCurrentState.position;
486 | mTempState.position = mCurrentState.position;
487 | mCurrentState.velocity = 0;
488 | return this;
489 | }
490 |
491 | /**
492 | * linear interpolation between the previous and current physics state based on the amount of
493 | * timestep remaining after processing the rendering delta time in timestep sized chunks.
494 | * @param alpha from 0 to 1, where 0 is the previous state, 1 is the current state
495 | */
496 | private void interpolate(double alpha) {
497 | mCurrentState.position = mCurrentState.position * alpha + mPreviousState.position *(1-alpha);
498 | mCurrentState.velocity = mCurrentState.velocity * alpha + mPreviousState.velocity *(1-alpha);
499 | }
500 |
501 | /** listeners **/
502 |
503 | /**
504 | * add a listener
505 | * @param newListener to add
506 | * @return the spring for chaining
507 | */
508 | public Spring addListener(SpringListener newListener) {
509 | if (newListener == null) {
510 | throw new IllegalArgumentException("newListener is required");
511 | }
512 | mListeners.add(newListener);
513 | return this;
514 | }
515 |
516 | /**
517 | * remove a listener
518 | * @param listenerToRemove to remove
519 | * @return the spring for chaining
520 | */
521 | public Spring removeListener(SpringListener listenerToRemove) {
522 | if (listenerToRemove == null) {
523 | throw new IllegalArgumentException("listenerToRemove is required");
524 | }
525 | mListeners.remove(listenerToRemove);
526 | return this;
527 | }
528 |
529 | /**
530 | * remove all of the listeners
531 | * @return the spring for chaining
532 | */
533 | public Spring removeAllListeners() {
534 | mListeners.clear();
535 | return this;
536 | }
537 |
538 | /**
539 | * This method checks to see that the current spring displacement value is equal to the input,
540 | * accounting for the spring's rest displacement threshold.
541 | * @param value The value to compare the spring value to
542 | * @return Whether the displacement value from the spring is within the bounds of the compare
543 | * value, accounting for threshold
544 | */
545 | public boolean currentValueIsApproximately(double value) {
546 | return Math.abs(getCurrentValue() - value) <= getRestDisplacementThreshold();
547 | }
548 |
549 | }
550 |
551 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/SpringConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | /**
14 | * Data structure for storing spring configuration.
15 | */
16 | public class SpringConfig {
17 | public double friction;
18 | public double tension;
19 |
20 | public static SpringConfig defaultConfig = SpringConfig.fromOrigamiTensionAndFriction(40, 7);
21 |
22 | /**
23 | * constructor for the SpringConfig
24 | * @param tension tension value for the SpringConfig
25 | * @param friction friction value for the SpringConfig
26 | */
27 | public SpringConfig(double tension, double friction) {
28 | this.tension = tension;
29 | this.friction = friction;
30 | }
31 |
32 | /**
33 | * A helper to make creating a SpringConfig easier with values mapping to the Origami values.
34 | * @param qcTension tension as defined in the Quartz Composition
35 | * @param qcFriction friction as defined in the Quartz Composition
36 | * @return a SpringConfig that maps to these values
37 | */
38 | public static SpringConfig fromOrigamiTensionAndFriction(double qcTension, double qcFriction) {
39 | return new SpringConfig(
40 | OrigamiValueConverter.tensionFromOrigamiValue(qcTension),
41 | OrigamiValueConverter.frictionFromOrigamiValue(qcFriction)
42 | );
43 | }
44 |
45 | /**
46 | * Map values from the Origami POP Animation patch, which are based on a bounciness and speed
47 | * value.
48 | * @param bounciness bounciness of the POP Animation
49 | * @param speed speed of the POP Animation
50 | * @return a SpringConfig mapping to the specified POP Animation values.
51 | */
52 | public static SpringConfig fromBouncinessAndSpeed(double bounciness, double speed) {
53 | BouncyConversion bouncyConversion = new BouncyConversion(speed, bounciness);
54 | return fromOrigamiTensionAndFriction(
55 | bouncyConversion.getBouncyTension(),
56 | bouncyConversion.getBouncyFriction());
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/SpringConfigRegistry.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | import java.util.Collections;
14 | import java.util.HashMap;
15 | import java.util.Map;
16 |
17 | /**
18 | * class for maintaining a registry of all spring configs
19 | */
20 | public class SpringConfigRegistry {
21 |
22 | private static final SpringConfigRegistry INSTANCE = new SpringConfigRegistry(true);
23 |
24 | public static SpringConfigRegistry getInstance() {
25 | return INSTANCE;
26 | }
27 |
28 | private final Map mSpringConfigMap;
29 |
30 | /**
31 | * constructor for the SpringConfigRegistry
32 | */
33 | SpringConfigRegistry(boolean includeDefaultEntry) {
34 | mSpringConfigMap = new HashMap();
35 | if (includeDefaultEntry) {
36 | addSpringConfig(SpringConfig.defaultConfig, "default config");
37 | }
38 | }
39 |
40 | /**
41 | * add a SpringConfig to the registry
42 | *
43 | * @param springConfig SpringConfig to add to the registry
44 | * @param configName name to give the SpringConfig in the registry
45 | * @return true if the SpringConfig was added, false if a config with that name is already
46 | * present.
47 | */
48 | public boolean addSpringConfig(SpringConfig springConfig, String configName) {
49 | if (springConfig == null) {
50 | throw new IllegalArgumentException("springConfig is required");
51 | }
52 | if (configName == null) {
53 | throw new IllegalArgumentException("configName is required");
54 | }
55 | if (mSpringConfigMap.containsKey(springConfig)) {
56 | return false;
57 | }
58 | mSpringConfigMap.put(springConfig, configName);
59 | return true;
60 | }
61 |
62 | /**
63 | * remove a specific SpringConfig from the registry
64 | * @param springConfig the of the SpringConfig to remove
65 | * @return true if the SpringConfig was removed, false if it was not present.
66 | */
67 | public boolean removeSpringConfig(SpringConfig springConfig) {
68 | if (springConfig == null) {
69 | throw new IllegalArgumentException("springConfig is required");
70 | }
71 | return mSpringConfigMap.remove(springConfig) != null;
72 | }
73 |
74 | /**
75 | * retrieve all SpringConfig in the registry
76 | * @return a list of all SpringConfig
77 | */
78 | public Map getAllSpringConfig() {
79 | return Collections.unmodifiableMap(mSpringConfigMap);
80 | }
81 |
82 | /**
83 | * clear all SpringConfig in the registry
84 | */
85 | public void removeAllSpringConfig() {
86 | mSpringConfigMap.clear();
87 | }
88 | }
89 |
90 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/SpringListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | public interface SpringListener {
14 |
15 | /**
16 | * called whenever the spring is updated
17 | * @param spring the Spring sending the update
18 | */
19 | void onSpringUpdate(Spring spring);
20 |
21 | /**
22 | * called whenever the spring achieves a resting state
23 | * @param spring the spring that's now resting
24 | */
25 | void onSpringAtRest(Spring spring);
26 |
27 | /**
28 | * called whenever the spring leaves its resting state
29 | * @param spring the spring that has left its resting state
30 | */
31 | void onSpringActivate(Spring spring);
32 |
33 | /**
34 | * called whenever the spring notifies of displacement state changes
35 | * @param spring the spring whose end state has changed
36 | */
37 | void onSpringEndStateChange(Spring spring);
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/SpringLooper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | /**
14 | * The spring looper is an interface for implementing platform-dependent run loops.
15 | */
16 | public abstract class SpringLooper {
17 |
18 | protected BaseSpringSystem mSpringSystem;
19 |
20 | /**
21 | * Set the BaseSpringSystem that the SpringLooper will call back to.
22 | * @param springSystem the spring system to call loop on.
23 | */
24 | public void setSpringSystem(BaseSpringSystem springSystem) {
25 | mSpringSystem = springSystem;
26 | }
27 |
28 | /**
29 | * The BaseSpringSystem has requested that the looper begins running this {@link Runnable}
30 | * on every frame. The {@link Runnable} will continue running on every frame until
31 | * {@link #stop()} is called.
32 | * If an existing {@link Runnable} had been started on this looper, it will be cancelled.
33 | */
34 | public abstract void start();
35 |
36 | /**
37 | * The looper will no longer run the {@link Runnable}.
38 | */
39 | public abstract void stop();
40 | }
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/SpringSystemListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | /**
14 | * SpringSystemListener provides an interface for listening to events before and after each Physics
15 | * solving loop the BaseSpringSystem runs.
16 | */
17 | public interface SpringSystemListener {
18 |
19 | /**
20 | * Runs before each pass through the physics integration loop providing an opportunity to do any
21 | * setup or alterations to the Physics state before integrating.
22 | * @param springSystem the BaseSpringSystem listened to
23 | */
24 | void onBeforeIntegrate(BaseSpringSystem springSystem);
25 |
26 | /**
27 | * Runs after each pass through the physics integration loop providing an opportunity to do any
28 | * setup or alterations to the Physics state after integrating.
29 | * @param springSystem the BaseSpringSystem listened to
30 | */
31 | void onAfterIntegrate(BaseSpringSystem springSystem);
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/SpringUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | public class SpringUtil {
14 |
15 | /**
16 | * Map a value within a given range to another range.
17 | * @param value the value to map
18 | * @param fromLow the low end of the range the value is within
19 | * @param fromHigh the high end of the range the value is within
20 | * @param toLow the low end of the range to map to
21 | * @param toHigh the high end of the range to map to
22 | * @return the mapped value
23 | */
24 | public static double mapValueFromRangeToRange(
25 | double value,
26 | double fromLow,
27 | double fromHigh,
28 | double toLow,
29 | double toHigh) {
30 | double fromRangeSize = fromHigh - fromLow;
31 | double toRangeSize = toHigh - toLow;
32 | double valueScale = (value - fromLow) / fromRangeSize;
33 | return toLow + (valueScale * toRangeSize);
34 | }
35 |
36 | /**
37 | * Clamp a value to be within the provided range.
38 | * @param value the value to clamp
39 | * @param low the low end of the range
40 | * @param high the high end of the range
41 | * @return the clamped value
42 | */
43 | public static double clamp(double value, double low, double high) {
44 | return Math.min(Math.max(value, low), high);
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/SteppingLooper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.rebound;
11 |
12 | public class SteppingLooper extends SpringLooper {
13 |
14 | private boolean mStarted;
15 | private long mLastTime;
16 |
17 | @Override
18 | public void start() {
19 | mStarted = true;
20 | mLastTime = 0;
21 | }
22 |
23 | public boolean step(long interval) {
24 | if (mSpringSystem == null || !mStarted) {
25 | return false;
26 | }
27 | long currentTime = mLastTime + interval;
28 | mSpringSystem.loop(currentTime);
29 | mLastTime = currentTime;
30 | return mSpringSystem.getIsIdle();
31 | }
32 |
33 | @Override
34 | public void stop() {
35 | mStarted = false;
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/rebound-core/src/main/java/com/facebook/rebound/SynchronousLooper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.rebound;
11 |
12 | public class SynchronousLooper extends SpringLooper {
13 |
14 | public static final double SIXTY_FPS = 16.6667;
15 | private double mTimeStep;
16 | private boolean mRunning;
17 |
18 | public SynchronousLooper() {
19 | mTimeStep = SIXTY_FPS;
20 | }
21 |
22 | public double getTimeStep() {
23 | return mTimeStep;
24 | }
25 |
26 | public void setTimeStep(double timeStep) {
27 | mTimeStep = timeStep;
28 | }
29 |
30 | @Override
31 | public void start() {
32 | mRunning = true;
33 | while (!mSpringSystem.getIsIdle()) {
34 | if (mRunning == false) {
35 | break;
36 | }
37 | mSpringSystem.loop(mTimeStep);
38 | }
39 | }
40 |
41 | @Override
42 | public void stop() {
43 | mRunning = false;
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/rebound-core/src/test/java/com/facebook/rebound/SpringConfigRegistryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | import org.junit.Before;
14 | import org.junit.Test;
15 |
16 | import java.util.Map;
17 |
18 | import static org.junit.Assert.assertEquals;
19 | import static org.junit.Assert.assertTrue;
20 | import static org.mockito.Mockito.spy;
21 |
22 | public class SpringConfigRegistryTest {
23 |
24 | private static final String CONFIG_NAME = "test config";
25 | private static final double TENSION = SpringConfig.defaultConfig.tension;
26 | private static final double FRICTION = SpringConfig.defaultConfig.friction;
27 |
28 | private SpringConfigRegistry mSpringConfigRegistrySpy;
29 |
30 | @Before
31 | public void beforeEach() {
32 | mSpringConfigRegistrySpy = spy(new SpringConfigRegistry(false));
33 | }
34 |
35 | @Test
36 | public void testAddSpringConfig() {
37 | SpringConfig config = new SpringConfig(TENSION, FRICTION);
38 | mSpringConfigRegistrySpy.addSpringConfig(config, CONFIG_NAME);
39 | Map configs = mSpringConfigRegistrySpy.getAllSpringConfig();
40 |
41 | assertEquals(1, configs.size());
42 | assertTrue(configs.containsKey(config));
43 | String configName = configs.get(config);
44 | assertEquals(configName, CONFIG_NAME);
45 | }
46 |
47 | @Test
48 | public void testRemoveSpringConfig() {
49 | SpringConfig config = new SpringConfig(TENSION, FRICTION);
50 | mSpringConfigRegistrySpy.addSpringConfig(config, CONFIG_NAME);
51 |
52 | Map configs = mSpringConfigRegistrySpy.getAllSpringConfig();
53 | assertEquals(1, configs.size());
54 | mSpringConfigRegistrySpy.removeSpringConfig(config);
55 |
56 | configs = mSpringConfigRegistrySpy.getAllSpringConfig();
57 | assertEquals(0, configs.size());
58 | }
59 |
60 | @Test
61 | public void testRemoveAllSpringConfig() {
62 | SpringConfig configA = new SpringConfig(0, 0);
63 | SpringConfig configB = new SpringConfig(0, 0);
64 | SpringConfig configC = new SpringConfig(0, 0);
65 |
66 | mSpringConfigRegistrySpy.addSpringConfig(configA, "a");
67 | mSpringConfigRegistrySpy.addSpringConfig(configB, "b");
68 | mSpringConfigRegistrySpy.addSpringConfig(configC, "c");
69 |
70 | Map configs = mSpringConfigRegistrySpy.getAllSpringConfig();
71 | assertEquals(3, configs.size());
72 |
73 | mSpringConfigRegistrySpy.removeAllSpringConfig();
74 | configs = mSpringConfigRegistrySpy.getAllSpringConfig();
75 | assertEquals(0, configs.size());
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/rebound-core/src/test/java/com/facebook/rebound/SpringSystemTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | package com.facebook.rebound;
12 |
13 | import org.junit.Before;
14 | import org.junit.Test;
15 | import org.mockito.InOrder;
16 |
17 | import static org.junit.Assert.*;
18 | import static org.mockito.Mockito.*;
19 |
20 | public class SpringSystemTest {
21 |
22 | private BaseSpringSystem mSpringSystemSpy;
23 | private SynchronousLooper mSynchronousLooper;
24 | private Spring mMockSpring;
25 |
26 | @Before
27 | public void beforeEach() {
28 | mSynchronousLooper = new SynchronousLooper();
29 | mSpringSystemSpy = spy(new BaseSpringSystem(mSynchronousLooper));
30 | mSynchronousLooper.setSpringSystem(mSpringSystemSpy);
31 | // NB: make sure the runnable calls the spy
32 | mMockSpring = mock(Spring.class);
33 | when(mMockSpring.getId()).thenReturn("spring_id");
34 | }
35 |
36 | @Test
37 | public void testRegisterSpringOnCreation() {
38 | Spring spring = mSpringSystemSpy.createSpring();
39 | verify(mSpringSystemSpy).registerSpring(spring);
40 | }
41 |
42 | @Test
43 | public void testCreateSpringWithoutName() {
44 | Spring spring = mSpringSystemSpy.createSpring();
45 | assertNotNull(spring.getId());
46 | assertEquals(spring, mSpringSystemSpy.getSpringById(spring.getId()));
47 | assertEquals(1, mSpringSystemSpy.getAllSprings().size());
48 | }
49 |
50 | @Test
51 | public void testLoop() {
52 | mSpringSystemSpy.registerSpring(mMockSpring);
53 | when(mMockSpring.systemShouldAdvance()).thenReturn(true, false);
54 | mSpringSystemSpy.activateSpring(mMockSpring.getId());
55 | verify(mSpringSystemSpy, times(2)).advance(mSynchronousLooper.getTimeStep());
56 | verify(mMockSpring, times(1)).advance(mSynchronousLooper.getTimeStep() / 1000);
57 | assertTrue(mSpringSystemSpy.getIsIdle());
58 | }
59 |
60 | @Test
61 | public void testLoopWithMultiplePassesRunsAndTerminates() {
62 | InOrder inOrder = inOrder(mMockSpring, mSpringSystemSpy);
63 | when(mMockSpring.systemShouldAdvance()).thenReturn(true, true, true, false);
64 |
65 | mSpringSystemSpy.registerSpring(mMockSpring);
66 | mSpringSystemSpy.activateSpring(mMockSpring.getId());
67 |
68 | double stepMillis = mSynchronousLooper.getTimeStep();
69 | double stepSeconds = mSynchronousLooper.getTimeStep() / 1000;
70 |
71 | inOrder.verify(mSpringSystemSpy, times(1)).advance(stepMillis);
72 | inOrder.verify(mMockSpring, times(1)).advance(stepSeconds);
73 | inOrder.verify(mSpringSystemSpy, times(1)).advance(stepMillis);
74 | inOrder.verify(mMockSpring, times(1)).advance(stepSeconds);
75 | inOrder.verify(mSpringSystemSpy, times(1)).advance(stepMillis);
76 | inOrder.verify(mMockSpring, times(1)).advance(stepSeconds);
77 | inOrder.verify(mSpringSystemSpy, times(1)).advance(stepMillis); // one extra pass through the system
78 |
79 | assertTrue(mSpringSystemSpy.getIsIdle());
80 | }
81 |
82 | @Test
83 | public void testSpringSystemListener() {
84 | SpringSystemListener listener = spy(new SpringSystemListener() {
85 | @Override
86 | public void onBeforeIntegrate(BaseSpringSystem springSystem) {
87 | }
88 |
89 | @Override
90 | public void onAfterIntegrate(BaseSpringSystem springSystem) {
91 | }
92 | });
93 |
94 | InOrder inOrder = inOrder(listener);
95 | mSpringSystemSpy.registerSpring(mMockSpring);
96 | when(mMockSpring.systemShouldAdvance()).thenReturn(true, false);
97 |
98 | mSpringSystemSpy.addListener(listener);
99 | mSpringSystemSpy.activateSpring(mMockSpring.getId());
100 |
101 | inOrder.verify(listener).onBeforeIntegrate(mSpringSystemSpy);
102 | inOrder.verify(listener).onAfterIntegrate(mSpringSystemSpy);
103 | inOrder.verify(listener).onBeforeIntegrate(mSpringSystemSpy);
104 | inOrder.verify(listener).onAfterIntegrate(mSpringSystemSpy);
105 |
106 | mSpringSystemSpy.removeListener(listener);
107 |
108 | mSpringSystemSpy.activateSpring(mMockSpring.getId());
109 | inOrder.verify(listener, never()).onBeforeIntegrate(mSpringSystemSpy);
110 | inOrder.verify(listener, never()).onAfterIntegrate(mSpringSystemSpy);
111 | }
112 |
113 | @Test
114 | public void testActivatingAndDeactivatingSpring() {
115 | when(mMockSpring.systemShouldAdvance()).thenReturn(true, false, false);
116 | InOrder inOrder = inOrder(mMockSpring, mSpringSystemSpy);
117 | mSpringSystemSpy.registerSpring(mMockSpring);
118 | assertTrue(mSpringSystemSpy.getIsIdle());
119 |
120 | double stepMillis = mSynchronousLooper.getTimeStep();
121 | double stepSeconds = mSynchronousLooper.getTimeStep() / 1000;
122 |
123 | mSpringSystemSpy.activateSpring(mMockSpring.getId());
124 | inOrder.verify(mSpringSystemSpy).advance(stepMillis);
125 | inOrder.verify(mMockSpring).systemShouldAdvance();
126 | inOrder.verify(mMockSpring).advance(stepSeconds);
127 |
128 | inOrder.verify(mSpringSystemSpy).advance(stepMillis);
129 | inOrder.verify(mMockSpring).systemShouldAdvance();
130 | inOrder.verify(mMockSpring, never()).advance(stepSeconds);
131 | assertTrue(mSpringSystemSpy.getIsIdle());
132 |
133 | mSpringSystemSpy.loop(stepMillis);
134 | inOrder.verify(mSpringSystemSpy).advance(stepMillis);
135 | inOrder.verify(mMockSpring, never()).systemShouldAdvance();
136 | inOrder.verify(mMockSpring, never()).advance(stepSeconds);
137 | assertTrue(mSpringSystemSpy.getIsIdle());
138 |
139 | mSpringSystemSpy.activateSpring(mMockSpring.getId());
140 | inOrder.verify(mSpringSystemSpy).advance(stepMillis);
141 | inOrder.verify(mMockSpring).systemShouldAdvance();
142 | inOrder.verify(mMockSpring, never()).advance(stepSeconds);
143 | assertTrue(mSpringSystemSpy.getIsIdle());
144 |
145 | mSpringSystemSpy.loop(stepMillis);
146 | inOrder.verify(mSpringSystemSpy).advance(stepMillis);
147 | inOrder.verify(mMockSpring, never()).systemShouldAdvance();
148 | inOrder.verify(mMockSpring, never()).advance(stepSeconds);
149 | assertTrue(mSpringSystemSpy.getIsIdle());
150 | }
151 |
152 | @Test
153 | public void testCanAddListenersWhileIterating() {
154 | when(mMockSpring.systemShouldAdvance()).thenReturn(true, false);
155 | mSpringSystemSpy.addListener(new SimpleSpringSystemListener() {
156 | @Override
157 | public void onAfterIntegrate(BaseSpringSystem springSystem) {
158 | springSystem.addListener(new SimpleSpringSystemListener());
159 | }
160 | });
161 | mSpringSystemSpy.addListener(new SimpleSpringSystemListener());
162 | mSpringSystemSpy.loop(1);
163 | }
164 |
165 | @Test
166 | public void testCanRemoveListenersWhileIterating() {
167 | when(mMockSpring.systemShouldAdvance()).thenReturn(true, true, false);
168 | final SimpleSpringSystemListener nextListener = new SimpleSpringSystemListener();
169 | mSpringSystemSpy
170 | .addListener(new SimpleSpringSystemListener() {
171 | @Override
172 | public void onAfterIntegrate(BaseSpringSystem springSystem) {
173 | springSystem.removeListener(nextListener);
174 | }
175 | });
176 | mSpringSystemSpy
177 | .addListener(nextListener);
178 | mSpringSystemSpy.loop(1);
179 | mSpringSystemSpy.loop(1);
180 | mSpringSystemSpy.loop(1);
181 | }
182 |
183 | private class SimpleSpringSystemListener implements SpringSystemListener {
184 | @Override
185 | public void onBeforeIntegrate(BaseSpringSystem springSystem) {
186 |
187 | }
188 |
189 | @Override
190 | public void onAfterIntegrate(BaseSpringSystem springSystem) {
191 |
192 | }
193 | }
194 | }
195 |
196 |
--------------------------------------------------------------------------------
/release_process.txt:
--------------------------------------------------------------------------------
1 | How to Release a new version:
2 | -----------------------------
3 | - Review all commits that will be in the new version.
4 | - Run all tests and build/install sample apps to verify that everything looks sane
5 | - Update the version number in build.gradle to the new version number
6 | - Create a new git tag for the version in the format v0.0.0
7 | - build the distribution jar
8 | ./gradlew reboundDistJar
9 | - Draft a new release on github include release notes of all the changes and the
10 | jar generated at build/libs/rebound-0.0.0.jar
11 | - publish to maven central
12 | - ./gradlew uploadArchives
13 | - login to https://oss.sonatype.org
14 | - Go to staging repositories and look at the bottom of the list for comfacebook-0000 this will be your
15 | staging repo. Verify that the new release is in the content.
16 | - press Close at the top of the first horizontal pane while this staging repo is selected
17 | - hit refresh and see that the status is now Closed
18 | - hit release to submit the artifacts for publication to central. The sync may take some time to complete
19 | as well as the update of the maven search index.
20 | - once the sync is complete update the gh-pages to reflect that a new version is available on central
21 | - update the published javadocs with
22 | ./gradlew javadoc
23 | upload the docs generated at rebound-core/build to gh-pages
24 | - tweet that there is an update
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':rebound-core'
2 | include ':rebound-android'
3 | include ':rebound-android-example'
4 | include ':rebound-android-playground'
--------------------------------------------------------------------------------