Skip to content
This repository has been archived by the owner on Aug 13, 2021. It is now read-only.

Commit

Permalink
Introduce new builder pattern and create DynamicSpringSource for Mate…
Browse files Browse the repository at this point in the history
…rialSpring.

Summary:
This is similar to the coreanimation pattern in material-motion-swift.
This is a useful pattern for animation systems that require the target object and property to be provided up-front.
For example, off-thread animation systems require this.

A new `build` channel is added to MotionObservable.
An interaction that uses an off-thread animation system would use the `build` channel rather than the `next` channel.

This `build` channel passes along:
 1. a MotionBuilder<T> object
 2. a T[] of config values

The MotionBuilder<T> can take a T[] of config values to create some type of motion, and is created by the interaction.
The T[] is transformed by constraint operators as it is passed down the `build` channel.
Only specific T->T operators can transform this T[].

If a stream given to `MotionRuntime.write(stream, property)` has an active `build` channel,
the MotionBuilder<T> will be given the transformed T[] and the property.

The new DynamicSpringSource and DynamicSpringBuilder uses this builder pattern.
DynamicSpringBuilder supports writing T values to Property<T> by using a TypeVectorizer to break a T value into multiple Float values.
Each float value is animated by a separate SpringAnimation.

Part of #64

Reviewers: O2 Material Motion, O6 Material Android platform reviewers, #material_motion, featherless

Reviewed By: O2 Material Motion, #material_motion, featherless

Subscribers: featherless

Tags: #material_motion

Differential Revision: http://codereview.cc/D3150
  • Loading branch information
Mark Wei committed May 3, 2017
1 parent 8eb6b3a commit 0bd3880
Show file tree
Hide file tree
Showing 42 changed files with 474 additions and 107 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ allprojects {
ext {
compileSdkVersion = 25
buildToolsVersion = '25.0.2'
minSdkVersion = 15
minSdkVersion = 16
targetSdkVersion = compileSdkVersion

supportLibVersion = '25.3.1'
Expand Down
1 change: 1 addition & 0 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ dependencies {
compile 'com.github.material-motion:gestures-android:develop-SNAPSHOT'
compile 'com.github.material-motion:physics-android:develop-SNAPSHOT'
compile "com.android.support:support-core-utils:$supportLibVersion"
compile "com.android.support:support-dynamic-animation:$supportLibVersion"

testCompile 'com.google.truth:truth:0.28'
testCompile 'junit:junit:4.12'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public abstract class FilterOperation<T> extends Operation<T, T> {
public abstract boolean filter(T value);

@Override
public void next(MotionObserver<T> observer, T value) {
public final void next(MotionObserver<T> observer, T value) {
if (filter(value)) {
observer.next(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ public abstract class Interaction<O, T> {
public final ReactiveProperty<Boolean> enabled = ReactiveProperty.of(true);
public final ReactiveProperty<Integer> state = ReactiveProperty.of(MotionState.AT_REST);

public abstract void apply(MotionRuntime runtime, O target, ConstraintApplicator<T> constraints);
protected abstract void apply(MotionRuntime runtime, O target, ConstraintApplicator<T> constraints);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.google.android.material.motion;

public abstract class MotionBuilder<T> {

public abstract void start(ReactiveProperty<T> property, T[] values);

public abstract void stop();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import com.google.android.indefinite.observable.IndefiniteObservable;
import com.google.android.indefinite.observable.Observer;
import com.google.android.material.motion.MotionObserver.SimpleMotionObserver;

/**
* A MotionObservable is a type of <a href="http://reactivex.io/documentation/observable.html">Observable</a>
Expand All @@ -40,7 +41,7 @@ public MotionObservable(Connector<MotionObserver<T>> connector) {
* @see #subscribe(Observer)
*/
public Subscription subscribe() {
return super.subscribe(new MotionObserver<T>() {
return super.subscribe(new SimpleMotionObserver<T>() {
@Override
public void next(T value) {
}
Expand Down Expand Up @@ -75,6 +76,15 @@ public Disconnector connect(final MotionObserver<U> observer) {
public void next(T value) {
operation.next(observer, value);
}

@Override
public void build(MotionBuilder<T> builder, T[] values) {
if (operation instanceof SameTypedMapOperation) {
//noinspection unchecked
((SameTypedMapOperation<T>) operation).build(
(MotionObserver<T>) observer, builder, values);
}
}
});
operation.postConnect(observer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ public abstract class MotionObserver<T> extends Observer<T> {
@Override
public abstract void next(T value);

public abstract void build(MotionBuilder<T> builder, T... values);

/**
* A simple observer for when you only want to implement {@link #next(Object)}.
*/
public static abstract class SimpleMotionObserver<T> extends MotionObserver<T> {
@Override
public void build(MotionBuilder<T> builder, T[] values) {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package com.google.android.material.motion;

import android.support.v4.util.SimpleArrayMap;
import android.util.Property;
import android.view.View;

Expand Down Expand Up @@ -53,6 +52,11 @@ public <T> void write(MotionObservable<T> stream, final ReactiveProperty<T> prop
public void next(T value) {
property.write(value);
}

@Override
public void build(MotionBuilder<T> builder, T[] values) {
builder.start(property, values);
}
}));
}

Expand All @@ -64,14 +68,14 @@ public final <O, T> void addInteraction(

public final <O, T> void addInteraction(
Interaction<O, T> interaction, O target, ConstraintApplicator<T> constraints) {
interaction.apply(this, target, constraints);
List<Interaction<?, ?>> interactions = cachedInteractions.get(target);
if (interactions == null) {
interactions = new ArrayList<>();
cachedInteractions.put(target, interactions);
}

interactions.add(interaction);

interaction.apply(this, target, constraints);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.google.android.material.motion;

import com.google.android.indefinite.observable.Observer;

/**
* An operation is able to transform incoming values before choosing whether or not to pass them
* downstream.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ protected final void onWrite(T value) {
/**
* A reactive property backed by a {@link Property}.
*/
private static final class PropertyReactiveProperty<O, T> extends ReactiveProperty<T> {
public static final class PropertyReactiveProperty<O, T> extends ReactiveProperty<T> {

private final O target;
private final Property<O, T> property;
public final O target;
public final Property<O, T> property;

public PropertyReactiveProperty(O target, Property<O, T> property) {
this.target = target;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.google.android.material.motion;

public abstract class SameTypedMapOperation<T> extends MapOperation<T, T> {

public final void build(MotionObserver<T> observer, MotionBuilder<T> builder, T[] values) {
for (int i = 0; i < values.length; i++) {
values[i] = transform(values[i]);
}
observer.build(builder, values);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.android.material.motion.Interaction;
import com.google.android.material.motion.MotionRuntime;
import com.google.android.material.motion.ReactiveProperty;
import com.google.android.material.motion.sources.DynamicSpringSource;
import com.google.android.material.motion.sources.PhysicsSpringSource;
import com.google.android.material.motion.springs.PointFTypeVectorizer;

Expand Down Expand Up @@ -60,7 +61,7 @@ public void apply(MotionRuntime runtime, View target, ConstraintApplicator<Point
ReactiveProperty.of(1f),
ReactiveProperty.of(1f),
ReactiveProperty.of(4f),
PhysicsSpringSource.SYSTEM);
DynamicSpringSource.SYSTEM);

runtime.addInteraction(draggable, target, constraints);
// TODO: Cannot apply constraints to spring because while draggable acts on translation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import android.support.annotation.VisibleForTesting;
import android.view.View;

import com.google.android.material.motion.MotionObserver;
import com.google.android.material.motion.MapOperation;
import com.google.android.material.motion.Operation;
import com.google.android.material.motion.gestures.GestureRecognizer;

Expand All @@ -22,10 +22,9 @@ public final class Anchored {
}

public static <T extends GestureRecognizer> Operation<T, PointF> anchored(final View view) {
return new Operation<T, PointF>() {

return new MapOperation<T, PointF>() {
@Override
public void next(MotionObserver<PointF> observer, T gestureRecognizer) {
public PointF transform(T gestureRecognizer) {
array[0] = view.getPivotX();
array[1] = view.getPivotY();
GestureRecognizer.getTransformationMatrix(view, matrix, inverse);
Expand All @@ -35,7 +34,7 @@ public void next(MotionObserver<PointF> observer, T gestureRecognizer) {
gestureRecognizer.getUntransformedCentroidX() - array[0],
gestureRecognizer.getUntransformedCentroidY() - array[1]
);
observer.next(adjustment);
return adjustment;
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import android.support.annotation.VisibleForTesting;

import com.google.android.material.motion.MapOperation;
import com.google.android.material.motion.Operation;
import com.google.android.material.motion.SameTypedMapOperation;

public final class Inverted {

Expand All @@ -13,7 +13,7 @@ public final class Inverted {
}

public static Operation<Boolean, Boolean> inverted() {
return new MapOperation<Boolean, Boolean>() {
return new SameTypedMapOperation<Boolean>() {
@Override
public Boolean transform(Boolean value) {
return !value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import android.graphics.PointF;
import android.support.annotation.VisibleForTesting;

import com.google.android.material.motion.MapOperation;
import com.google.android.material.motion.Operation;
import com.google.android.material.motion.SameTypedMapOperation;

public final class LockToXAxis {

Expand All @@ -17,7 +17,7 @@ public final class LockToXAxis {
* For an incoming translational PointF stream, overwrites the y value with the given yValue.
*/
public static Operation<PointF, PointF> lockToXAxis(final float yValue) {
return new MapOperation<PointF, PointF>() {
return new SameTypedMapOperation<PointF>() {
@Override
public PointF transform(PointF value) {
return new PointF(value.x, yValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

import android.graphics.PointF;

import com.google.android.material.motion.MapOperation;
import com.google.android.material.motion.Operation;
import com.google.android.material.motion.SameTypedMapOperation;

public final class LockToYAxis {
/**
* For an incoming translational PointF stream, overwrites the x value with the given xValue.
*/
public static Operation<PointF, PointF> lockToYAxis(final float xValue) {
return new MapOperation<PointF, PointF>() {
return new SameTypedMapOperation<PointF>() {
@Override
public PointF transform(PointF value) {
return new PointF(xValue, value.y);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import android.support.annotation.VisibleForTesting;
import android.util.Log;

import com.google.android.material.motion.MotionObserver;
import com.google.android.material.motion.MapOperation;
import com.google.android.material.motion.Operation;

public final class LogOp {
Expand All @@ -22,11 +22,11 @@ public static <T> Operation<T, T> log(final String tag, final String prefix) {
}

public static <T> Operation<T, T> log(final int priority, final String tag, final String prefix) {
return new Operation<T, T>() {
return new MapOperation<T, T>() {
@Override
public void next(MotionObserver<T> observer, T value) {
public T transform(T value) {
Log.println(priority, tag, prefix + value);
observer.next(value);
return value;
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import android.support.annotation.VisibleForTesting;

import com.google.android.material.motion.MapOperation;
import com.google.android.material.motion.Operation;
import com.google.android.material.motion.SameTypedMapOperation;

public final class LowerBound {

Expand All @@ -13,7 +13,7 @@ public final class LowerBound {
}

public static <T extends Comparable<T>> Operation<T, T> lowerBound(final T lowerBound) {
return new MapOperation<T, T>() {
return new SameTypedMapOperation<T>() {
@Override
public T transform(T value) {
if (value.compareTo(lowerBound) < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import android.graphics.PointF;
import android.support.annotation.VisibleForTesting;

import com.google.android.material.motion.MapOperation;
import com.google.android.material.motion.Operation;
import com.google.android.material.motion.SameTypedMapOperation;

public final class NormalizedBy {

Expand All @@ -18,7 +18,7 @@ public static Operation<Float, Float> normalizedBy(final float normal) {
}

public static Operation<PointF, PointF> normalizedAllBy(final float normal) {
return new MapOperation<PointF, PointF>() {
return new SameTypedMapOperation<PointF>() {
@Override
public PointF transform(PointF value) {
return new PointF(value.x / normal, value.y / normal);
Expand All @@ -27,7 +27,7 @@ public PointF transform(PointF value) {
}

public static Operation<PointF, PointF> normalizedAllBy(final PointF normal) {
return new MapOperation<PointF, PointF>() {
return new SameTypedMapOperation<PointF>() {
@Override
public PointF transform(PointF value) {
return new PointF(value.x / normal.x, value.y / normal.y);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import android.graphics.PointF;
import android.support.annotation.VisibleForTesting;

import com.google.android.material.motion.MapOperation;
import com.google.android.material.motion.Operation;
import com.google.android.material.motion.SameTypedMapOperation;

public final class OffsetBy {

Expand All @@ -14,7 +14,7 @@ public final class OffsetBy {
}

public static Operation<Float, Float> offsetBy(final float offset) {
return new MapOperation<Float, Float>() {
return new SameTypedMapOperation<Float>() {
@Override
public Float transform(Float value) {
return value + offset;
Expand All @@ -23,7 +23,7 @@ public Float transform(Float value) {
}

public static Operation<PointF, PointF> offsetAllBy(final float offset) {
return new MapOperation<PointF, PointF>() {
return new SameTypedMapOperation<PointF>() {
@Override
public PointF transform(PointF value) {
return new PointF(value.x + offset, value.y + offset);
Expand All @@ -32,7 +32,7 @@ public PointF transform(PointF value) {
}

public static Operation<PointF, PointF> offsetAllBy(final PointF offset) {
return new MapOperation<PointF, PointF>() {
return new SameTypedMapOperation<PointF>() {
@Override
public PointF transform(PointF value) {
return new PointF(value.x + offset.x, value.y + offset.y);
Expand Down
Loading

0 comments on commit 0bd3880

Please sign in to comment.