diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/utils/StopLogic.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/utils/StopLogic.java
index b3877f045..207d1cea9 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/utils/StopLogic.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/utils/StopLogic.java
@@ -16,6 +16,8 @@
package androidx.constraintlayout.motion.utils;
+import androidx.constraintlayout.core.motion.utils.SpringStopEngine;
+import androidx.constraintlayout.core.motion.utils.StopEngine;
import androidx.constraintlayout.core.motion.utils.StopLogicEngine;
import androidx.constraintlayout.motion.widget.MotionInterpolator;
@@ -28,7 +30,9 @@
* @hide
*/
public class StopLogic extends MotionInterpolator {
- private StopLogicEngine engine = new StopLogicEngine();
+ private StopLogicEngine mStopLogicEngine = new StopLogicEngine();
+ private SpringStopEngine mSpringStopEngine;
+ private StopEngine mEngine = mStopLogicEngine;
/**
* Debugging logic to log the state.
@@ -39,24 +43,56 @@ public class StopLogic extends MotionInterpolator {
*/
public String debug(String desc, float time) {
- return engine.debug(desc, time);
+ return mEngine.debug(desc, time);
}
public float getVelocity(float x) {
- return engine.getVelocity(x);
+ return mEngine.getVelocity(x);
}
public void config(float currentPos, float destination, float currentVelocity, float maxTime, float maxAcceleration, float maxVelocity) {
- engine.config(currentPos, destination, currentVelocity, maxTime, maxAcceleration, maxVelocity);
+ mEngine = mStopLogicEngine;
+ mStopLogicEngine.config(currentPos, destination, currentVelocity, maxTime, maxAcceleration, maxVelocity);
+ }
+
+ /**
+ * This configure the stop logic to be a spring.
+ * Moving from currentPosition(P0) to destination with an initial velocity of currentVelocity (V0)
+ * moving as if it has a mass (m) with spring constant stiffness(k), and friction(c)
+ * It moves with the equation acceleration a = (-k.x-c.v)/m.
+ * x = current position - destination
+ * v is velocity
+ *
+ * @param currentPos The current position
+ * @param destination The destination position
+ * @param currentVelocity the initial velocity
+ * @param mass the mass
+ * @param stiffness the stiffness or spring constant (the force by which the spring pulls)
+ * @param damping the stiffness or spring constant. (the resistance to the motion)
+ * @param stopThreshold (When the max velocity of the movement is below this it stops)
+ * @param boundaryMode This will allow you to control if it overshoots or bounces when it hits 0 and 1
+ */
+ public void springConfig(float currentPos, float destination, float currentVelocity,
+ float mass, float stiffness, float damping, float stopThreshold,
+ int boundaryMode) {
+ if (mSpringStopEngine == null) {
+ mSpringStopEngine = new SpringStopEngine();
+ }
+ mEngine = mSpringStopEngine;
+ mSpringStopEngine.springConfig(currentPos, destination, currentVelocity, mass, stiffness, damping, stopThreshold, boundaryMode);
}
@Override
public float getInterpolation(float v) {
- return engine.getInterpolation(v);
+ return mEngine.getInterpolation(v);
}
@Override
public float getVelocity() {
- return engine.getVelocity();
+ return mEngine.getVelocity();
+ }
+
+ public boolean isStopped() {
+ return mEngine.isStopped();
}
}
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java
index 927279468..b58321977 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java
@@ -998,6 +998,8 @@ public class MotionLayout extends ConstraintLayout implements
public static final int TOUCH_UP_STOP = 3;
public static final int TOUCH_UP_DECELERATE = 4;
public static final int TOUCH_UP_DECELERATE_AND_COMPLETE = 5;
+ public static final int TOUCH_UP_NEVER_TO_START = 6;
+ public static final int TOUCH_UP_NEVER_TO_END = 7;
static final String TAG = "MotionLayout";
private final static boolean DEBUG = false;
@@ -1888,15 +1890,24 @@ public void touchAnimateTo(int touchUpMode, float position, float currentVelocit
switch (touchUpMode) {
case TOUCH_UP_COMPLETE:
+ case TOUCH_UP_NEVER_TO_START:
+ case TOUCH_UP_NEVER_TO_END:
case TOUCH_UP_COMPLETE_TO_START:
case TOUCH_UP_COMPLETE_TO_END: {
- if (touchUpMode == TOUCH_UP_COMPLETE_TO_START) {
+ if (touchUpMode == TOUCH_UP_COMPLETE_TO_START || touchUpMode == TOUCH_UP_NEVER_TO_END) {
position = 0;
- } else if (touchUpMode == TOUCH_UP_COMPLETE_TO_END) {
+ } else if (touchUpMode == TOUCH_UP_COMPLETE_TO_END || touchUpMode == TOUCH_UP_NEVER_TO_START) {
position = 1;
}
- mStopLogic.config(mTransitionLastPosition, position, currentVelocity,
- mTransitionDuration, mScene.getMaxAcceleration(), mScene.getMaxVelocity());
+ float stiff = mScene.getSpringStiffiness();
+ if(Float.isNaN(stiff)) {
+ mStopLogic.config(mTransitionLastPosition, position, currentVelocity,
+ mTransitionDuration, mScene.getMaxAcceleration(), mScene.getMaxVelocity());
+ } else {
+ mStopLogic.springConfig(mTransitionLastPosition, position, currentVelocity,
+ mScene.getSpringMass(), mScene.getSpringStiffiness(), mScene.getSpringDamping(),
+ mScene.getSpringStopThreshold(), mScene.getSpringBoundary());
+ }
int currentState = mCurrentState; // TODO: we really should remove setProgress(), this is a temporary fix
mTransitionGoalPosition = position;
@@ -1935,6 +1946,46 @@ public void touchAnimateTo(int touchUpMode, float position, float currentVelocit
invalidate();
}
+ /**
+ * Allows you to use trigger spring motion touch behaviour.
+ * You must have configured all the spring parameters in the Transition's OnSwipe
+ *
+ * @param position the position 0 - 1
+ * @param currentVelocity the current velocity rate of change in position per second
+ */
+ public void touchSpringTo(float position, float currentVelocity) {
+ if (DEBUG) {
+ Log.v(TAG, " " + Debug.getLocation() + " touchAnimateTo " + position + " " + currentVelocity);
+ }
+ if (mScene == null) {
+ return;
+ }
+ if (mTransitionLastPosition == position) {
+ return;
+ }
+
+ mTemporalInterpolator = true;
+ mAnimationStartTime = getNanoTime();
+ mTransitionDuration = mScene.getDuration() / 1000f;
+
+ mTransitionGoalPosition = position;
+ mInTransition = true;
+
+ mStopLogic.springConfig(mTransitionLastPosition, position, currentVelocity,
+ mScene.getSpringMass(), mScene.getSpringStiffiness(), mScene.getSpringDamping(),
+ mScene.getSpringStopThreshold(), mScene.getSpringBoundary());
+
+ int currentState = mCurrentState; // TODO: we really should remove setProgress(), this is a temporary fix
+ mTransitionGoalPosition = position;
+ mCurrentState = currentState;
+ mInterpolator = mStopLogic;
+
+
+ mTransitionInstantly = false;
+ mAnimationStartTime = getNanoTime();
+ invalidate();
+ }
+
private static boolean willJump(float velocity, float position, float maxAcceleration) {
if (velocity > 0) {
float time = velocity / maxAcceleration;
@@ -2158,6 +2209,25 @@ public boolean isInRotation() {
return mInRotation;
}
+ /**
+ * This jumps to a state
+ * It will be at that state after one repaint cycle
+ * If the current transition contains that state.
+ * It setsProgress 0 or 1 to that state.
+ * If not in the current transition itsl
+ *
+ * @param id state to set
+ */
+ public void jumpToState(int id) {
+ if (mBeginState == id) {
+ setProgress(0);
+ } else if (mEndState == id) {
+ setProgress(1);
+ } else {
+ setTransition(id, id);
+ }
+ }
+
/**
* Animate to the state defined by the id.
* Width and height may be used in the picking of the id using this StateSet.
@@ -2195,10 +2265,16 @@ public void transitionToState(int id, int screenWidth, int screenHeight, int dur
}
if (mBeginState == id) {
animateTo(0.0f);
+ if (duration > 0) {
+ mTransitionDuration = duration / 1000f;
+ }
return;
}
if (mEndState == id) {
animateTo(1.0f);
+ if (duration > 0) {
+ mTransitionDuration = duration / 1000f;
+ }
return;
}
mEndState = id;
@@ -3500,11 +3576,19 @@ void evaluate(boolean force) {
mTransitionLastPosition = position;
mTransitionPosition = position;
mTransitionLastTime = currentTime;
-
+ int NOT_STOP_LOGIC = 0;
+ int STOP_LOGIC_CONTINUE = 1;
+ int STOP_LOGIC_STOP = 2;
+ int stopLogicDone = NOT_STOP_LOGIC;
if (mInterpolator != null && !done) {
if (mTemporalInterpolator) {
float time = (currentTime - mAnimationStartTime) * 1E-9f;
position = mInterpolator.getInterpolation(time);
+ if (mInterpolator == mStopLogic) {
+ boolean dp = mStopLogic.isStopped();
+ stopLogicDone = (dp)?STOP_LOGIC_STOP:STOP_LOGIC_CONTINUE;
+ }
+
if (DEBUG) {
Log.v(TAG, Debug.getLocation() + " mTransitionLastPosition = " + mTransitionLastPosition + " position = " + position);
}
@@ -3514,7 +3598,7 @@ void evaluate(boolean force) {
if (mInterpolator instanceof MotionInterpolator) {
float lastVelocity = ((MotionInterpolator) mInterpolator).getVelocity();
mLastVelocity = lastVelocity;
- if (Math.abs(lastVelocity) * mTransitionDuration <= EPSILON) {
+ if (Math.abs(lastVelocity) * mTransitionDuration <= EPSILON && stopLogicDone==STOP_LOGIC_STOP) {
mInTransition = false;
}
if (lastVelocity > 0 && position >= 1.0f) {
@@ -3544,15 +3628,17 @@ void evaluate(boolean force) {
setState(TransitionState.MOVING);
}
- if ((dir > 0 && position >= mTransitionGoalPosition)
- || (dir <= 0 && position <= mTransitionGoalPosition)) {
- position = mTransitionGoalPosition;
- mInTransition = false;
- }
+ if ( stopLogicDone != STOP_LOGIC_CONTINUE) {
+ if ((dir > 0 && position >= mTransitionGoalPosition)
+ || (dir <= 0 && position <= mTransitionGoalPosition)) {
+ position = mTransitionGoalPosition;
+ mInTransition = false;
+ }
- if (position >= 1.0f || position <= 0.0f) {
- mInTransition = false;
- setState(TransitionState.FINISHED);
+ if (position >= 1.0f || position <= 0.0f) {
+ mInTransition = false;
+ setState(TransitionState.FINISHED);
+ }
}
int n = getChildCount();
@@ -3994,7 +4080,10 @@ public boolean onTouchEvent(MotionEvent event) {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- mPreviouseRotation = getDisplay().getRotation();
+ Display display = getDisplay();
+ if (display != null) {
+ mPreviouseRotation = display.getRotation();
+ }
}
if (mScene != null && mCurrentState != UNSET) {
ConstraintSet cSet = mScene.getConstraintSet(mCurrentState);
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionScene.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionScene.java
index dfe3dbb33..b7782ffa7 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionScene.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionScene.java
@@ -455,6 +455,7 @@ public boolean isViewTransitionEnabled(int id) {
public boolean applyViewTransition(int viewTransitionId, MotionController motionController) {
return mViewTransitionController.applyViewTransition(viewTransitionId, motionController);
}
+
///////////////////////////////////////////////////////////////////////////////
// ====================== Transition ==========================================
@@ -758,7 +759,8 @@ public TransitionOnClick(Context context, Transition transition, XmlPullParser p
}
a.recycle();
}
- public TransitionOnClick( Transition transition, int id, int action) {
+
+ public TransitionOnClick(Transition transition, int id, int action) {
mTransition = transition;
mTargetId = id;
mMode = action;
@@ -1664,6 +1666,40 @@ float getMaxVelocity() {
return 0;
}
+ float getSpringStiffiness() {
+ if (mCurrentTransition != null && mCurrentTransition.mTouchResponse != null) {
+ return mCurrentTransition.mTouchResponse.getSpringStiffness();
+ }
+ return 0;
+ }
+
+ float getSpringMass() {
+ if (mCurrentTransition != null && mCurrentTransition.mTouchResponse != null) {
+ return mCurrentTransition.mTouchResponse.getSpringMass();
+ }
+ return 0;
+ }
+
+ float getSpringDamping() {
+ if (mCurrentTransition != null && mCurrentTransition.mTouchResponse != null) {
+ return mCurrentTransition.mTouchResponse.getSpringDamping();
+ }
+ return 0;
+ }
+
+ float getSpringStopThreshold() {
+ if (mCurrentTransition != null && mCurrentTransition.mTouchResponse != null) {
+ return mCurrentTransition.mTouchResponse.getSpringStopThreshold();
+ }
+ return 0;
+ }
+ int getSpringBoundary() {
+ if (mCurrentTransition != null && mCurrentTransition.mTouchResponse != null) {
+ return mCurrentTransition.mTouchResponse.getSpringBoundary();
+ }
+ return 0;
+ }
+
void setupTouch() {
if (mCurrentTransition != null && mCurrentTransition.mTouchResponse != null) {
mCurrentTransition.mTouchResponse.setupTouch();
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/OnSwipe.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/OnSwipe.java
index fca7457ec..8e13d9b50 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/OnSwipe.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/OnSwipe.java
@@ -33,7 +33,15 @@ public class OnSwipe {
private float mDragScale = 1f;
private int mFlags = 0;
private float mDragThreshold = 10;
-
+ private float mSpringDamping = Float.NaN;
+ private float mSpringMass = 1;
+ private float mSpringStiffness = Float.NaN;
+ private float mSpringStopThreshold = Float.NaN;
+ private int mSpringBoundary = 0;
+ public static final int SPRING_BOUNDARY_OVERSHOOT = 0;
+ public static final int SPRING_BOUNDARY_BOUNCESTART = 1;
+ public static final int SPRING_BOUNDARY_BOUNCEEND = 2;
+ public static final int SPRING_BOUNDARY_BOUNCEBOTH = 3;
public static final int DRAG_UP = 0;
public static final int DRAG_DOWN = 1;
public static final int DRAG_LEFT = 2;
@@ -60,6 +68,8 @@ public class OnSwipe {
public static final int ON_UP_STOP = 3;
public static final int ON_UP_DECELERATE = 4;
public static final int ON_UP_DECELERATE_AND_COMPLETE = 5;
+ public static final int ON_UP_NEVER_TO_START = 6;
+ public static final int ON_UP_NEVER_TO_END = 7;
/**
* The id of the view who's movement is matched to your drag
@@ -255,7 +265,7 @@ public int getLimitBoundsTo() {
* The view to center the rotation about
*
* @param rotationCenterId
- * @return
+ * @return this
*/
public OnSwipe setRotateCenter(int rotationCenterId) {
mRotationCenterId = rotationCenterId;
@@ -265,4 +275,112 @@ public OnSwipe setRotateCenter(int rotationCenterId) {
public int getRotationCenterId() {
return mRotationCenterId;
}
+
+
+ public float getSpringDamping() {
+ return mSpringDamping;
+ }
+
+ /**
+ * Set the damping of the spring if using spring.
+ * c in "a = (-k*x-c*v)/m" equation for the acceleration of a spring
+ *
+ * @param springDamping
+ * @return this
+ */
+ public OnSwipe setSpringDamping(float springDamping) {
+ mSpringDamping = springDamping;
+ return this;
+ }
+
+ /**
+ * Get the mass of the spring.
+ * the m in "a = (-k*x-c*v)/m" equation for the acceleration of a spring
+ *
+ * @return
+ */
+ public float getSpringMass() {
+ return mSpringMass;
+ }
+
+ /**
+ * Set the Mass of the spring if using spring.
+ * m in "a = (-k*x-c*v)/m" equation for the acceleration of a spring
+ *
+ * @param springMass
+ * @return this
+ */
+ public OnSwipe setSpringMass(float springMass) {
+ mSpringMass = springMass;
+ return this;
+ }
+
+ /**
+ * get the stiffness of the spring
+ *
+ * @return NaN if not set
+ */
+ public float getSpringStiffness() {
+ return mSpringStiffness;
+ }
+
+ /**
+ * set the stiffness of the spring if using spring.
+ * If this is set the swipe will use a spring return system.
+ * If set to NaN it will revert to the norm system.
+ * K in "a = (-k*x-c*v)/m" equation for the acceleration of a spring
+ *
+ * @param springStiffness
+ * @return
+ */
+ public OnSwipe setSpringStiffness(float springStiffness) {
+ mSpringStiffness = springStiffness;
+ return this;
+ }
+
+ /**
+ * The threshold for spring motion to stop.
+ *
+ * @return
+ */
+ public float getSpringStopThreshold() {
+ return mSpringStopThreshold;
+ }
+
+ /**
+ * set the threshold for spring motion to stop.
+ * This is in change in progress / second
+ * If the spring will never go above that threshold again it will stop.
+ *
+ * @param springStopThreshold
+ * @return
+ */
+ public OnSwipe setSpringStopThreshold(float springStopThreshold) {
+ mSpringStopThreshold = springStopThreshold;
+ return this;
+ }
+
+ /**
+ * The behaviour at the boundaries 0 and 1
+ *
+ * @return
+ */
+ public int getSpringBoundary() {
+ return mSpringBoundary;
+ }
+
+ /**
+ * The behaviour at the boundaries 0 and 1.
+ * SPRING_BOUNDARY_OVERSHOOT = 0;
+ * SPRING_BOUNDARY_BOUNCE_START = 1;
+ * SPRING_BOUNDARY_BOUNCE_END = 2;
+ * SPRING_BOUNDARY_BOUNCE_BOTH = 3;
+ *
+ * @param springBoundary
+ * @return
+ */
+ public OnSwipe setSpringBoundary(int springBoundary) {
+ mSpringBoundary = springBoundary;
+ return this;
+ }
}
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/TouchResponse.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/TouchResponse.java
index 57f388344..c68b6dfb5 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/TouchResponse.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/TouchResponse.java
@@ -104,6 +104,11 @@ class TouchResponse {
static final int FLAG_DISABLE_POST_SCROLL = 1;
static final int FLAG_DISABLE_SCROLL = 2;
private float mDragThreshold = 10;
+ private float mSpringDamping = Float.NaN;
+ private float mSpringMass = 1;
+ private float mSpringStiffness = Float.NaN;
+ private float mSpringStopThreshold = Float.NaN;
+ private int mSpringBoundary = 0;
TouchResponse(Context context, MotionLayout layout, XmlPullParser parser) {
mMotionLayout = layout;
@@ -136,6 +141,11 @@ public TouchResponse(MotionLayout layout, OnSwipe onSwipe) {
mFlags = onSwipe.getNestedScrollFlags();
mLimitBoundsTo = onSwipe.getLimitBoundsTo();
mRotationCenterId = onSwipe.getRotationCenterId();
+ mSpringBoundary= onSwipe.getSpringBoundary();
+ mSpringDamping= onSwipe.getSpringDamping();
+ mSpringMass= onSwipe.getSpringMass();
+ mSpringStiffness= onSwipe.getSpringStiffness();
+ mSpringStopThreshold= onSwipe.getSpringStopThreshold();
}
public void setRTL(boolean rtl) {
@@ -205,6 +215,17 @@ private void fill(TypedArray a) {
mLimitBoundsTo = a.getResourceId(attr, 0);
} else if (attr == R.styleable.OnSwipe_rotationCenterId) {
mRotationCenterId = a.getResourceId(attr, mRotationCenterId);
+ } else if (attr == R.styleable.OnSwipe_springDamping) {
+ mSpringDamping = a.getFloat(attr, mSpringDamping);
+ } else if (attr == R.styleable.OnSwipe_springMass) {
+ mSpringMass = a.getFloat(attr, mSpringMass);
+ } else if (attr == R.styleable.OnSwipe_springStiffness) {
+ mSpringStiffness = a.getFloat(attr, mSpringStiffness);
+ } else if (attr == R.styleable.OnSwipe_springStopThreshold) {
+ mSpringStopThreshold = a.getFloat(attr, mSpringStopThreshold);
+ } else if (attr == R.styleable.OnSwipe_springBoundary) {
+ mSpringBoundary = a.getInt(attr, mSpringBoundary);
+
}
}
@@ -344,7 +365,21 @@ void processTouchRotateEvent(MotionEvent event, MotionLayout.MotionTracker veloc
}
if (pos != 0.0f && pos != 1.0f && mOnTouchUp != MotionLayout.TOUCH_UP_STOP) {
angularVelocity = (float) angularVelocity * mDragScale / mAnchorDpDt[1];
- mMotionLayout.touchAnimateTo(mOnTouchUp, (pos < 0.5) ? 0.0f : 1.0f, 3 * angularVelocity);
+ float target = (pos < 0.5) ? 0.0f : 1.0f;
+
+ if (mOnTouchUp == MotionLayout.TOUCH_UP_NEVER_TO_START) {
+ if (currentPos + angularVelocity < 0) {
+ angularVelocity = Math.abs(angularVelocity);
+ }
+ target = 1;
+ }
+ if (mOnTouchUp == MotionLayout.TOUCH_UP_NEVER_TO_END) {
+ if (currentPos + angularVelocity > 1) {
+ angularVelocity = -Math.abs(angularVelocity);
+ }
+ target = 0;
+ }
+ mMotionLayout.touchAnimateTo(mOnTouchUp, target , 3 * angularVelocity);
if (0.0f >= currentPos || 1.0f <= currentPos) {
mMotionLayout.setState(MotionLayout.TransitionState.FINISHED);
}
@@ -437,8 +472,16 @@ void processTouchEvent(MotionEvent event, MotionLayout.MotionTracker velocityTra
if (DEBUG) {
Log.v(TAG, "# ACTION_MOVE CHANGE = " + change);
}
+
pos = Math.max(Math.min(pos + change, 1), 0);
+ if (mOnTouchUp == MotionLayout.TOUCH_UP_NEVER_TO_START) {
+ pos = Math.max( pos , 0.01f);
+ }
+ if (mOnTouchUp == MotionLayout.TOUCH_UP_NEVER_TO_END) {
+ pos = Math.min(pos , 0.99f);
+ }
+
float current = mMotionLayout.getProgress();
if (pos != current) {
if (current == 0.0f || current == 1.0f) {
@@ -495,7 +538,22 @@ void processTouchEvent(MotionEvent event, MotionLayout.MotionTracker velocityTra
pos += velocity / 3; // TODO calibration & animation speed based on velocity
}
if (pos != 0.0f && pos != 1.0f && mOnTouchUp != MotionLayout.TOUCH_UP_STOP) {
- mMotionLayout.touchAnimateTo(mOnTouchUp, (pos < 0.5) ? 0.0f : 1.0f, velocity);
+ float target = (pos < 0.5) ? 0.0f : 1.0f;
+
+ if (mOnTouchUp == MotionLayout.TOUCH_UP_NEVER_TO_START) {
+ if (currentPos + velocity < 0) {
+ velocity = Math.abs(velocity);
+ }
+ target = 1;
+ }
+ if (mOnTouchUp == MotionLayout.TOUCH_UP_NEVER_TO_END) {
+ if (currentPos + velocity > 1) {
+ velocity = -Math.abs(velocity);
+ }
+ target = 0;
+ }
+
+ mMotionLayout.touchAnimateTo(mOnTouchUp, target, velocity);
if (0.0f >= currentPos || 1.0f <= currentPos) {
mMotionLayout.setState(MotionLayout.TransitionState.FINISHED);
}
@@ -754,4 +812,53 @@ public int getFlags() {
public void setTouchUpMode(int touchUpMode) {
mOnTouchUp = touchUpMode;
}
+
+ /**
+ * the stiffness of the spring if using spring
+ * K in "a = (-k*x-c*v)/m" equation for the acceleration of a spring
+ * @return NaN if not set
+ */
+ public float getSpringStiffness() {
+ return mSpringStiffness;
+ }
+
+ /**
+ * the Mass of the spring if using spring
+ * m in "a = (-k*x-c*v)/m" equation for the acceleration of a spring
+ * @return default is 1
+ */
+ public float getSpringMass() {
+ return mSpringMass;
+ }
+
+ /**
+ * the damping of the spring if using spring
+ * c in "a = (-k*x-c*v)/m" equation for the acceleration of a spring
+ * @return NaN if not set
+ */
+ public float getSpringDamping() {
+ return mSpringDamping;
+ }
+
+ /**
+ * The threshold below
+ * @return NaN if not set
+ */
+ public float getSpringStopThreshold() {
+ return mSpringStopThreshold;
+ }
+
+ /**
+ * The spring's behaviour when it hits 0 or 1. It can be made ot overshoot or bounce
+ * overshoot = 0
+ * bounceStart = 1
+ * bounceEnd = 2
+ * bounceBoth = 3
+ * @return Bounce mode
+ */
+ public int getSpringBoundary() {
+ return mSpringBoundary;
+ }
+
+
}
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java
index f5fb5aefd..d02a38f60 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java
@@ -26,11 +26,13 @@
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.ViewOutlineProvider;
import androidx.annotation.RequiresApi;
import androidx.appcompat.content.res.AppCompatResources;
+import androidx.constraintlayout.motion.widget.Debug;
import androidx.constraintlayout.widget.R;
/**
diff --git a/constraintlayout/constraintlayout/src/main/res/values/attrs.xml b/constraintlayout/constraintlayout/src/main/res/values/attrs.xml
index 9bc2e1e1d..1b7e5a274 100644
--- a/constraintlayout/constraintlayout/src/main/res/values/attrs.xml
+++ b/constraintlayout/constraintlayout/src/main/res/values/attrs.xml
@@ -1285,6 +1285,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -1304,6 +1314,8 @@
+
+
diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/SpringStopEngine.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/SpringStopEngine.java
new file mode 100644
index 000000000..8364bf7fd
--- /dev/null
+++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/SpringStopEngine.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.core.motion.utils;
+
+/**
+ * This contains the class to provide the logic for an animation to come to a stop using a spring
+ * model.
+ *
+ * @hide
+ */
+public class SpringStopEngine implements StopEngine {
+ double mDamping = 0.5f;
+ private static final double UNSET = Double.MAX_VALUE;
+ private boolean mInitialized = false;
+ private double mStiffness;
+ private double mTargetPos;
+ private double mLastVelocity;
+ private float mLastTime;
+ private float mPos;
+ private float mV;
+ private float mMass;
+ private float mStopThreshold;
+ private int mBoundaryMode = 0;
+
+ @Override
+ public String debug(String desc, float time) {
+ return null;
+ }
+
+ void log(String str) {
+ StackTraceElement s = new Throwable().getStackTrace()[1];
+ String line = ".(" + s.getFileName() + ":" + s.getLineNumber() + ") " + s.getMethodName() + "() ";
+ System.out.println(line + str);
+ }
+
+ public void springConfig(float currentPos, float target, float currentVelocity, float mass,
+ float stiffness, float damping, float stopThreshold, int boundaryMode) {
+ mTargetPos = target;
+ mDamping = damping;
+ mInitialized = false;
+ mPos = currentPos;
+ mLastVelocity = currentVelocity;
+ mStiffness = stiffness;
+ mMass = mass;
+ mStopThreshold = stopThreshold;
+ mBoundaryMode = boundaryMode;
+ mLastTime = 0;
+ }
+
+ @Override
+ public float getVelocity(float t) {
+ return (float) mV;
+ }
+
+ @Override
+ public float getInterpolation(float time) {
+ compute(time - mLastTime);
+ mLastTime = time;
+ return (float) (mPos);
+ }
+
+ public float getAcceleration() {
+ double k = mStiffness;
+ double c = mDamping;
+ double x = (mPos - mTargetPos);
+ return (float) (-k * x - c * mV) / mMass;
+ }
+
+ @Override
+ public float getVelocity() {
+ return 0;
+ }
+
+ @Override
+ public boolean isStopped() {
+ double x = (mPos - mTargetPos);
+ double k = mStiffness;
+ double v = mV;
+ double m = mMass;
+ double energy = v * v * m + k * x * x;
+ double max_def = Math.sqrt(energy / k);
+ return max_def <= mStopThreshold;
+ }
+
+ private void compute(double dt) {
+ double x = (mPos - mTargetPos);
+ double a = getAcceleration();
+ double dv = a * dt;
+ double avgV = mV + dv / 2;
+ mV += dv / 2;
+ mPos += avgV * dt;
+ if (mBoundaryMode > 0) {
+ if (mPos < 0 && ((mBoundaryMode & 1) == 1)) {
+ mPos = -mPos;
+ mV = -mV;
+ }
+ if (mPos > 1 && ((mBoundaryMode & 2) == 2)) {
+ mPos = 2 - mPos;
+ mV = -mV;
+ }
+ }
+ }
+}
diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/StopEngine.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/StopEngine.java
new file mode 100644
index 000000000..8c3847123
--- /dev/null
+++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/StopEngine.java
@@ -0,0 +1,13 @@
+package androidx.constraintlayout.core.motion.utils;
+
+public interface StopEngine {
+ String debug(String desc, float time);
+
+ float getVelocity(float x);
+
+ float getInterpolation(float v);
+
+ float getVelocity();
+
+ boolean isStopped();
+}
diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java
index 4611eadd1..cfd3cc3d7 100644
--- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java
+++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java
@@ -24,7 +24,7 @@
*
* @hide
*/
-public class StopLogicEngine {
+public class StopLogicEngine implements StopEngine {
private float mStage1Velocity, mStage2Velocity, mStage3Velocity; // the velocity at the start of each period
private float mStage1Duration, mStage2Duration, mStage3Duration; // the time for each period
private float mStage1EndPosition, mStage2EndPosition, mStage3EndPosition; // ending position
@@ -33,6 +33,8 @@ public class StopLogicEngine {
private boolean mBackwards = false;
private float mStartPosition;
private float mLastPosition;
+ private boolean mDone = false;
+ private static final float EPSILON = 0.00001f;
/**
* Debugging logic to log the state.
@@ -106,6 +108,7 @@ public float getVelocity(float x) {
}
private float calcY(float time) {
+ mDone = false;
if (time <= mStage1Duration) {
return mStage1Velocity * time + (mStage2Velocity - mStage1Velocity) * time * time / (2 * mStage1Duration);
}
@@ -121,16 +124,17 @@ private float calcY(float time) {
return mStage2EndPosition;
}
time -= mStage2Duration;
- if (time < mStage3Duration) {
+ if (time <= mStage3Duration) {
return mStage2EndPosition + mStage3Velocity * time - mStage3Velocity * time * time / (2 * mStage3Duration);
}
+ mDone = true;
return mStage3EndPosition;
}
public void config(float currentPos, float destination, float currentVelocity,
float maxTime, float maxAcceleration, float maxVelocity) {
-
+ mDone = false;
mStartPosition = currentPos;
mBackwards = (currentPos > destination);
if (mBackwards) {
@@ -150,8 +154,14 @@ public float getVelocity() {
return (mBackwards) ? -getVelocity(mLastPosition) : getVelocity(mLastPosition);
}
+ @Override
+ public boolean isStopped() {
+ return getVelocity() < EPSILON && Math.abs(mStage3EndPosition-mLastPosition) < EPSILON;
+ }
+
private void setup(float velocity, float distance, float maxAcceleration, float maxVelocity,
float maxTime) {
+ mDone = false;
if (velocity == 0) {
velocity = 0.0001f;
}
diff --git a/projects/MotionLayoutExperiments/app/build.gradle b/projects/MotionLayoutExperiments/app/build.gradle
index 9b1f895e0..15579662c 100644
--- a/projects/MotionLayoutExperiments/app/build.gradle
+++ b/projects/MotionLayoutExperiments/app/build.gradle
@@ -40,6 +40,7 @@ dependencies {
implementation project(path: ':constraintlayout')
implementation 'org.jetbrains:annotations:15.0'
testImplementation 'junit:junit:4.13'
+ testImplementation project(path: ':core')
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
\ No newline at end of file
diff --git a/projects/MotionLayoutExperiments/app/src/androidTest/java/androidx/constraintlayout/validation/SampleTest.kt b/projects/MotionLayoutExperiments/app/src/androidTest/java/androidx/constraintlayout/validation/SampleTest.kt
new file mode 100644
index 000000000..ef05042fd
--- /dev/null
+++ b/projects/MotionLayoutExperiments/app/src/androidTest/java/androidx/constraintlayout/validation/SampleTest.kt
@@ -0,0 +1,27 @@
+package com.example.developertemplate
+
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class SampleTest {
+
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ Log.v("FOO", appContext.packageName);
+ assertEquals("androidx.constraintlayout.experiments.motionlayout", appContext.packageName)
+ }
+}
diff --git a/projects/MotionLayoutExperiments/app/src/main/res/layout/demo_110_menu.xml b/projects/MotionLayoutExperiments/app/src/main/res/layout/demo_110_menu.xml
index 424b58005..73ca531b1 100644
--- a/projects/MotionLayoutExperiments/app/src/main/res/layout/demo_110_menu.xml
+++ b/projects/MotionLayoutExperiments/app/src/main/res/layout/demo_110_menu.xml
@@ -103,6 +103,7 @@
android:id="@+id/fade"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ app:motionEffect_viewTransition="@+id/foo"
app:motionEffect_translationY="-40dp"
app:constraint_referenced_ids="t1,tf1,t2,tf2,t3,tf3,t4,tf4,t5,tf5"
/>
diff --git a/projects/MotionLayoutExperiments/app/src/main/res/xml/demo_110_menu_scene.xml b/projects/MotionLayoutExperiments/app/src/main/res/xml/demo_110_menu_scene.xml
index 6129d7cd6..b35d61f2a 100644
--- a/projects/MotionLayoutExperiments/app/src/main/res/xml/demo_110_menu_scene.xml
+++ b/projects/MotionLayoutExperiments/app/src/main/res/xml/demo_110_menu_scene.xml
@@ -40,15 +40,22 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/MotionLayoutExperiments/app/src/test/java/android/support/constraint/core/test/MotionLayoutUtilsUnitTest.java b/projects/MotionLayoutExperiments/app/src/test/java/android/support/constraint/core/test/MotionLayoutUtilsUnitTest.java
new file mode 100644
index 000000000..2ac6861ef
--- /dev/null
+++ b/projects/MotionLayoutExperiments/app/src/test/java/android/support/constraint/core/test/MotionLayoutUtilsUnitTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.constraint.core.test;
+
+
+import androidx.constraintlayout.core.motion.utils.CurveFit;
+import androidx.constraintlayout.core.motion.utils.Easing;
+import androidx.constraintlayout.core.motion.utils.HyperSpline;
+import androidx.constraintlayout.core.motion.utils.LinearCurveFit;
+import androidx.constraintlayout.core.motion.utils.Oscillator;
+import androidx.constraintlayout.motion.utils.StopLogic;
+
+import org.junit.Test;
+
+import java.text.DecimalFormat;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class MotionLayoutUtilsUnitTest {
+ @Test
+ public void unit_test_framework_working() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+
+ @Test
+ public void testHyperSpline01() throws Exception {
+ double[][] points = {
+ {0, 0}, {1, 1}, {2, 2}
+ };
+ HyperSpline spline = new HyperSpline(points);
+ double value = spline.getPos(0.5, 1);
+ assertEquals(1, value, 0.001);
+ }
+
+ @Test
+ public void testCurveFit01() throws Exception {
+ double[][] points = {
+ {0, 0}, {1, 1}, {2, 0}
+ };
+ double[] time = {
+ 0, 5, 10
+ };
+ CurveFit spline = CurveFit.get(CurveFit.SPLINE, time, points);
+ double value = spline.getPos(5, 0);
+ assertEquals(1, value, 0.001);
+ value = spline.getPos(7, 0);
+ assertEquals(1.4, value, 0.001);
+ value = spline.getPos(7, 1);
+ assertEquals(0.744, value, 0.001);
+ }
+
+ @Test
+ public void testCurveFit02() throws Exception {
+ double[][] points = {
+ {0, 0}, {1, 1}, {2, 0}
+ };
+ double[] time = {
+ 0, 5, 10
+ };
+ CurveFit spline = CurveFit.get(CurveFit.LINEAR, time, points);
+ double value = spline.getPos(5, 0);
+ assertEquals(1, value, 0.001);
+ value = spline.getPos(7, 0);
+ assertEquals(1.4, value, 0.001);
+ value = spline.getPos(7, 1);
+ assertEquals(0.6, value, 0.001);
+ }
+
+ @Test
+ public void testEasing01() throws Exception {
+ double value, diffValue;
+ Easing easing;
+ easing = Easing.getInterpolator("cubic=(1,1,0,0)");
+ value = easing.get(0.5);
+ assertEquals(0.5, value, 0.001);
+ diffValue = easing.getDiff(0.5);
+ assertEquals(1, diffValue, 0.001);
+ diffValue = easing.getDiff(0.1);
+ assertEquals(1, diffValue, 0.001);
+ diffValue = easing.getDiff(0.9);
+ assertEquals(1, diffValue, 0.001);
+
+ easing = Easing.getInterpolator("cubic=(1,0,0,1)");
+ value = easing.get(0.5);
+ assertEquals(0.5, value, 0.001);
+
+ diffValue = easing.getDiff(0.001);
+ assertEquals(0, diffValue, 0.001);
+ diffValue = easing.getDiff(0.9999);
+ assertEquals(0, diffValue, 0.001);
+
+ easing = Easing.getInterpolator("cubic=(0.5,1,0.5,0)");
+ value = easing.get(0.5);
+ assertEquals(0.5, value, 0.001);
+ diffValue = easing.getDiff(0.5);
+ assertEquals(0, diffValue, 0.001);
+ diffValue = easing.getDiff(0.00001);
+ assertEquals(2, diffValue, 0.001);
+ diffValue = easing.getDiff(0.99999);
+ assertEquals(2, diffValue, 0.001);
+
+ }
+
+ @Test
+ public void testLinearCurveFit01() throws Exception {
+ double value, diffValue;
+ double[][] points = {
+ {0, 0}, {1, 1}, {2, 0}
+ };
+ double[] time = {
+ 0, 5, 10
+ };
+ LinearCurveFit lcurve = new LinearCurveFit(time, points);
+ value = lcurve.getPos(5, 0);
+ assertEquals(1, value, 0.001);
+ value = lcurve.getPos(7, 0);
+ assertEquals(1.4, value, 0.001);
+ value = lcurve.getPos(7, 1);
+ assertEquals(0.6, value, 0.001);
+ }
+
+ @Test
+ public void testOscillator01() throws Exception {
+ Oscillator o = new Oscillator();
+ o.setType(Oscillator.SQUARE_WAVE, null);
+ o.addPoint(0, 0);
+ o.addPoint(0.5, 10);
+ o.addPoint(1, 0);
+ o.normalize();
+ assertEquals(19, countZeroCrossings(o, Oscillator.SIN_WAVE));
+ assertEquals(19, countZeroCrossings(o, Oscillator.SQUARE_WAVE));
+ assertEquals(19, countZeroCrossings(o, Oscillator.TRIANGLE_WAVE));
+ assertEquals(19, countZeroCrossings(o, Oscillator.SAW_WAVE));
+ assertEquals(19, countZeroCrossings(o, Oscillator.REVERSE_SAW_WAVE));
+ assertEquals(20, countZeroCrossings(o, Oscillator.COS_WAVE));
+ }
+
+ @Test
+ public void testStopLogic01() throws Exception {
+ String []results = {
+ "[0.4, 0.36, 0.42, 0.578, 0.778, 0.938, 0.999, 1, 1, 1]",
+ "[0.4, 0.383, 0.464, 0.64, 0.838, 0.967, 1, 1, 1, 1]",
+ "[0.4, 0.405, 0.509, 0.697, 0.885, 0.986, 1, 1, 1, 1]",
+ "[0.4, 0.427, 0.553, 0.75, 0.921, 0.997, 1, 1, 1, 1]",
+ "[0.4, 0.449, 0.598, 0.798, 0.948, 1, 1, 1, 1, 1]",
+ "[0.4, 0.472, 0.64, 0.838, 0.967, 1, 1, 1, 1, 1]",
+ "[0.4, 0.494, 0.678, 0.87, 0.981, 1, 1, 1, 1, 1]",
+ "[0.4, 0.516, 0.71, 0.894, 0.989, 1, 1, 1, 1, 1]",
+ "[0.4, 0.538, 0.737, 0.913, 0.995, 1, 1, 1, 1, 1]",
+ "[0.4, 0.56, 0.76, 0.927, 0.998, 1, 1, 1, 1, 1]"
+
+ };
+
+ for (int i = 0; i < 10; i++) {
+ float[] f = stopGraph((i - 4) * .1f );
+ assertEquals(" test "+i,results[i],arrayToString(f));
+ }
+ }
+
+ private int countZeroCrossings(Oscillator o, int type) {
+ int n = 1000;
+ double last = o.getValue(0,0);
+ int count = 0;
+ o.setType(type, null);
+ for (int i = 0; i < n; i++) {
+
+ double v = o.getValue(0.0001 + i / (double) n,0);
+ if (v * last < 0) {
+ count++;
+ }
+ last = v;
+ }
+ return count;
+ }
+ String arrayToString(float []f) {
+ String ret = "[";
+ DecimalFormat df = new DecimalFormat("###.###");
+ for (int i = 0; i < f.length; i++) {
+ Float aFloat = f[i];
+ if (i>0) {
+ ret += ", ";
+ }
+ ret += df.format(f[i]);
+ }
+ return ret+"]";
+ }
+
+ private static float[] stopGraph(float vel ) {
+ StopLogic breakLogic = new StopLogic();
+ breakLogic.config(.4f, 1f, vel, 1, 2f, 0.9f);
+ float[] ret = new float[10];
+
+ for (int i = 0; i < ret.length; i++) {
+ float time = 2*i / (float) (ret.length - 1);
+ float pos = breakLogic.getInterpolation(time);
+ ret[i] = pos;
+ }
+ return ret;
+ }
+
+}
\ No newline at end of file
diff --git a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckSetStates.java b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckSetStates.java
index 6de2f959f..417bc9ebc 100644
--- a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckSetStates.java
+++ b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckSetStates.java
@@ -32,10 +32,10 @@ public class CheckSetStates extends AppCompatActivity {
private static final String TAG = "CustomSwipeClick";
String layout_name;
MotionLayout mMotionLayout;
- int []states = {R.id.s1,R.id.s2,R.id.s3,R.id.s4,R.id.s5,R.id.s6,R.id.s7,R.id.s8,R.id.s9};
+ int[] states = {R.id.s1, R.id.s2, R.id.s3, R.id.s4, R.id.s5, R.id.s6, R.id.s7, R.id.s8, R.id.s9};
@Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extra = getIntent().getExtras();
String prelayout = extra.getString(Utils.KEY);
@@ -48,25 +48,40 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
TextView text = findViewById(R.id.text);
}
-
- public void jump1(View view) {
+ int getNextState() {
+ int state = 0;
+ int cstate = mMotionLayout.getCurrentState();
+ Log.v(TAG, Debug.getLoc() + " current " + Debug.getName(getApplicationContext(), cstate));
for (int i = 0; i < states.length; i++) {
- if (mMotionLayout.getCurrentState() == states[i]) {
-// mMotionLayout.setState(states[(i+1)%states.length],-1,-1);
- mMotionLayout.transitionToState(states[(i+1)%states.length]);
- Log.v(TAG, Debug.getLoc()+" "+i );
- return;
+ if (cstate == states[i]) {
+ state = i + 1;
+ return state % states.length;
+
}
}
+ return state;
+ }
+
+ public void jump1(View view) {
+ int state = getNextState();
+ mMotionLayout.transitionToState(states[state]);
+ Log.v(TAG, Debug.getLoc() + " " + state);
+
}
+
public void jump2(View view) {
- for (int i = 0; i < states.length; i++) {
- if (mMotionLayout.getCurrentState() == states[i]) {
- mMotionLayout.setState(states[(i+1)%states.length],-1,-1);
- mMotionLayout.setProgress(0);
- Log.v(TAG, Debug.getLoc()+" "+i );
- return;
- }
- }
+ int state = getNextState();
+ int cstate = mMotionLayout.getCurrentState();
+ int end = mMotionLayout.getEndState();
+ int start = mMotionLayout.getStartState();
+ Log.v(TAG, Debug.getLoc() + "set " + Debug.getName(getApplicationContext(), start) +
+ " -> " + Debug.getName(getApplicationContext(), end) +
+ " now " + Debug.getName(getApplicationContext(), cstate));
+ //mMotionLayout.transitionToState(states[state], 1);
+ mMotionLayout.jumpToState(states[state]);
+ cstate = mMotionLayout.getCurrentState();
+ Log.v(TAG, Debug.getLoc() + "set " + Debug.getName(getApplicationContext(), states[state]) +
+ " = " + Debug.getName(getApplicationContext(), cstate));
+
}
}
diff --git a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java
index d1b194baa..6e5d3c23c 100644
--- a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java
+++ b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java
@@ -97,8 +97,8 @@ public class VerificationActivity extends AppCompatActivity implements View.OnCl
private static boolean REVERSE = false;
- private final String RUN_FIRST = "verification_450";
- private final String LAYOUTS_MATCHES = "verification_\\d+";
+ private final String RUN_FIRST = "verification_501";
+ private final String LAYOUTS_MATCHES = "v.*_.*";
private static String SHOW_FIRST = "";
MotionLayout mMotionLayout;
diff --git a/projects/MotionLayoutVerification/app/src/main/res/drawable/ic_baseline_redo_24.xml b/projects/MotionLayoutVerification/app/src/main/res/drawable/ic_baseline_redo_24.xml
new file mode 100644
index 000000000..37e9b2a72
--- /dev/null
+++ b/projects/MotionLayoutVerification/app/src/main/res/drawable/ic_baseline_redo_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/projects/MotionLayoutVerification/app/src/main/res/drawable/ic_baseline_undo_24.xml b/projects/MotionLayoutVerification/app/src/main/res/drawable/ic_baseline_undo_24.xml
new file mode 100644
index 000000000..bb1b6293c
--- /dev/null
+++ b/projects/MotionLayoutVerification/app/src/main/res/drawable/ic_baseline_undo_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/projects/MotionLayoutVerification/app/src/main/res/layout/verification_041.xml b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_041.xml
index 1b6288f1f..26933ddd3 100644
--- a/projects/MotionLayoutVerification/app/src/main/res/layout/verification_041.xml
+++ b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_041.xml
@@ -61,6 +61,7 @@
android:layout_marginTop="64dp"
android:background="#A66"
android:onClick="click"
+ app:overlay="false"
android:src="@drawable/hoford"
app:altSrc="@drawable/avatar_5_raster"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/projects/MotionLayoutVerification/app/src/main/res/layout/verification_057.xml b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_057.xml
index 37fb6286b..ae79d7cf4 100644
--- a/projects/MotionLayoutVerification/app/src/main/res/layout/verification_057.xml
+++ b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_057.xml
@@ -14,58 +14,58 @@
+
\ No newline at end of file
diff --git a/projects/MotionLayoutVerification/app/src/main/res/layout/verification_500.xml b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_500.xml
new file mode 100644
index 000000000..4f9e1f233
--- /dev/null
+++ b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_500.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/MotionLayoutVerification/app/src/main/res/layout/verification_501.xml b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_501.xml
new file mode 100644
index 000000000..9b70a04cb
--- /dev/null
+++ b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_501.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_041.xml b/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_041.xml
index b429815ef..ab51d61dc 100644
--- a/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_041.xml
+++ b/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_041.xml
@@ -157,7 +157,7 @@
motion:motionTarget="@+id/button">
+ motion:customReference="@drawable/ic_airplane" />
@@ -265,8 +265,8 @@
-
+
diff --git a/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_500.xml b/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_500.xml
new file mode 100644
index 000000000..1e4f92532
--- /dev/null
+++ b/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_500.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_501.xml b/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_501.xml
new file mode 100644
index 000000000..af6e915c9
--- /dev/null
+++ b/projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_501.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/MotionLayoutVerification/app/src/test/java/android/support/constraint/core/test/MotionLayoutUtilsUnitTest.java b/projects/MotionLayoutVerification/app/src/test/java/android/support/constraint/core/test/MotionLayoutUtilsUnitTest.java
index 1451726a5..0c061ede6 100644
--- a/projects/MotionLayoutVerification/app/src/test/java/android/support/constraint/core/test/MotionLayoutUtilsUnitTest.java
+++ b/projects/MotionLayoutVerification/app/src/test/java/android/support/constraint/core/test/MotionLayoutUtilsUnitTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.