package com.kaichunlin.transition;
import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.animation.Interpolator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Provides common implementations for all transitions.
*/
public abstract class AbstractTransition<T extends AbstractTransition, S extends AbstractTransition.Setup> implements Transition<S> {
List<S> mSetupList = new ArrayList<>();
String mId;
boolean mReverse;
Interpolator mInterpolator;
View mTarget;
boolean mUpdateStateAfterUpdateProgress;
public AbstractTransition(@Nullable String id) {
this.mId = id;
}
@Override
public T setId(@Nullable String id) {
mId = id;
return self();
}
@Override
public String getId() {
return mId == null ? (mId = toString()) : mId;
}
@Override
public boolean startTransition(float progress) {
if (startTransition()) {
updateProgress(progress);
return true;
}
return false;
}
@Override
public void setProgress(float progress) {
//TODO optimize
startTransition();
updateProgress(progress);
stopTransition();
}
@Override
public T reverse() {
String id = getId();
String REVERSE = "_REVERSE";
if (id.endsWith(REVERSE)) {
setId(id.substring(0, id.length() - REVERSE.length()));
} else {
setId(id + REVERSE);
}
mReverse = !mReverse;
return self();
}
@Override
public T setSetup(@NonNull S setup) {
if (setup != null) {
mSetupList.add(setup);
}
return self();
}
/**
* Only true if it has merged another Transition
*
* @return
*/
boolean hasMultipleSetup() {
return mSetupList.size() > 1;
}
@Override
public void setTarget(@Nullable View target) {
mTarget = target;
invalidate();
}
@Override
public View getTarget() {
return mTarget;
}
@Override
public T setUpdateStateAfterUpdateProgress(boolean updateStateAfterUpdateProgress) {
mUpdateStateAfterUpdateProgress = updateStateAfterUpdateProgress;
return self();
}
/**
* Invalidates the current transition, which may mean the currently running transition is stopped.
*/
protected abstract void invalidate();
// Returns the correct type.
protected abstract T self();
@Override
public T setInterpolator(@Nullable Interpolator interpolator) {
mInterpolator = interpolator;
return self();
}
@CheckResult
@Override
public AbstractTransition clone() {
AbstractTransition newClone = null;
try {
newClone = (AbstractTransition) super.clone();
newClone.setId(newClone.getId() + "_CLONE");
newClone.mSetupList = new ArrayList<>(mSetupList.size());
newClone.mSetupList.addAll(mSetupList);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return newClone;
}
/**
* Checks to see if another AbstractTransition's states is isCompatible for merging.
*
* @param another
* @return
*/
@CheckResult
public boolean isCompatible(AbstractTransition another) {
if (getClass().equals(another.getClass()) && mTarget == another.mTarget && mReverse == another.mReverse && ((mInterpolator == null && another.mInterpolator == null) ||
mInterpolator.getClass().equals(another.mInterpolator.getClass()))) {
return true;
}
return false;
}
/**
* Merge another AbstractTransition's states into this object, such that the other AbstractTransition
* can be discarded.
*
* @param another
* @return true if the merge is successful.
*/
public boolean merge(AbstractTransition another) {
if (!isCompatible(another)) {
return false;
}
if (another.mId != null) {
if (mId == null) {
mId = another.mId;
} else {
StringBuilder sb = new StringBuilder(mId.length() + another.mId.length());
sb.append(mId);
sb.append("_MERGED_");
sb.append(another.mId);
mId = sb.toString();
}
}
mUpdateStateAfterUpdateProgress |= another.mUpdateStateAfterUpdateProgress;
mSetupList.addAll(another.mSetupList);
Collections.sort(mSetupList, new Comparator<S>() {
@Override
public int compare(S lhs, S rhs) {
if (lhs instanceof AbstractTransitionBuilder && rhs instanceof AbstractTransitionBuilder) {
AbstractTransitionBuilder left = (AbstractTransitionBuilder) lhs;
AbstractTransitionBuilder right = (AbstractTransitionBuilder) rhs;
float startLeft = left.mReverse ? left.mEnd : left.mStart;
float startRight = right.mReverse ? right.mEnd : right.mStart;
return (int) ((startRight - startLeft) * 1000);
}
return 0;
}
});
return true;
}
/**
* Represents an object that configures how a Transition for a specific effect.
*/
public interface Setup {
}
}