* Finds the next focusable component that fits in the specified bounds.
*
*
* @param topFocus look for a candidate is the one at the top of the bounds
* if topFocus is true, or at the bottom of the bounds if topFocus is
* false
* @param top the top offset of the bounds in which a focusable must be
* found
* @param bottom the bottom offset of the bounds in which a focusable must
* be found
* @return the next focusable component in the bounds or null if none can
* be found
*/
private View findFocusableViewInBounds(boolean topFocus, int top, int bottom) {
List focusables = getFocusables(View.FOCUS_FORWARD);
View focusCandidate = null;
/*
* A fully contained focusable is one where its top is below the bound's
* top, and its bottom is above the bound's bottom. A partially
* contained focusable is one where some part of it is within the
* bounds, but it also has some part that is not within bounds. A fully contained
* focusable is preferred to a partially contained focusable.
*/
boolean foundFullyContainedFocusable = false;
int count = focusables.size();
if(isVertScroll()){
for (int i = 0; i < count; i++) {
View view = focusables.get(i);
int viewTop = view.getTop();
int viewBottom = view.getBottom();
if (top < viewBottom && viewTop < bottom) {
/*
* the focusable is in the target area, it is a candidate for
* focusing
*/
final boolean viewIsFullyContained = (top < viewTop) &&
(viewBottom < bottom);
if (focusCandidate == null) {
/* No candidate, take this one */
focusCandidate = view;
foundFullyContainedFocusable = viewIsFullyContained;
} else {
final boolean viewIsCloserToBoundary =
(topFocus && viewTop < focusCandidate.getTop()) ||
(!topFocus && viewBottom > focusCandidate
.getBottom());
if (foundFullyContainedFocusable) {
if (viewIsFullyContained && viewIsCloserToBoundary) {
/*
* We're dealing with only fully contained views, so
* it has to be closer to the boundary to beat our
* candidate
*/
focusCandidate = view;
}
} else {
if (viewIsFullyContained) {
/* Any fully contained view beats a partially contained view */
focusCandidate = view;
foundFullyContainedFocusable = true;
} else if (viewIsCloserToBoundary) {
/*
* Partially contained view beats another partially
* contained view if it's closer
*/
focusCandidate = view;
}
}
}
}
}
}
else {
for (int i = 0; i < count; i++) {
View view = focusables.get(i);
int viewLeft = view.getLeft();
int viewRight = view.getRight();
if (top < viewRight && viewLeft < bottom) {
/*
* the focusable is in the target area, it is a candidate for
* focusing
*/
final boolean viewIsFullyContained = (top < viewLeft) &&
(viewRight < bottom);
if (focusCandidate == null) {
/* No candidate, take this one */
focusCandidate = view;
foundFullyContainedFocusable = viewIsFullyContained;
} else {
final boolean viewIsCloserToBoundary =
(topFocus && viewLeft < focusCandidate.getLeft()) ||
(!topFocus && viewRight > focusCandidate.getRight());
if (foundFullyContainedFocusable) {
if (viewIsFullyContained && viewIsCloserToBoundary) {
/*
* We're dealing with only fully contained views, so
* it has to be closer to the boundary to beat our
* candidate
*/
focusCandidate = view;
}
} else {
if (viewIsFullyContained) {
/* Any fully contained view beats a partially contained view */
focusCandidate = view;
foundFullyContainedFocusable = true;
} else if (viewIsCloserToBoundary) {
/*
* Partially contained view beats another partially
* contained view if it's closer
*/
focusCandidate = view;
}
}
}
}
}
}
return focusCandidate;
}
/**
* Handles scrolling in response to a "page up/down" shortcut press. This
* method will scroll the view by one page up or down and give the focus
* to the topmost/bottommost component in the new visible area. If no
* component is a good candidate for focus, this scrollview reclaims the
* focus.
*
* @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
* to go one page up or
* {@link android.view.View#FOCUS_DOWN} to go one page down
* @return true if the key event is consumed by this method, false otherwise
*/
public boolean pageScroll(int direction) {
boolean down = direction == View.FOCUS_DOWN;
if(isVertScroll()) {
int height = getHeight();
if (down) {
mTempRect.top = getScrollY() + height;
int count = getChildCount();
if (count > 0) {
View view = getChildAt(count - 1);
if (mTempRect.top + height > view.getBottom()) {
mTempRect.top = view.getBottom() - height;
}
}
} else {
mTempRect.top = getScrollY() - height;
if (mTempRect.top < 0) {
mTempRect.top = 0;
}
}
mTempRect.bottom = mTempRect.top + height;
return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
}
else{
int width = getWidth();
if (down) {
mTempRect.left = getScrollX() + width;
int count = getChildCount();
if (count > 0) {
View view = getChildAt(count - 1);
if (mTempRect.left + width > view.getRight()) {
mTempRect.left = view.getRight() - width;
}
}
} else {
mTempRect.left = getScrollX() - width;
if (mTempRect.left < 0) {
mTempRect.left = 0;
}
}
mTempRect.right = mTempRect.left + width;
return scrollAndFocus(direction, mTempRect.left,mTempRect.right);
}
}
/**
* Handles scrolling in response to a "home/end" shortcut press. This
* method will scroll the view to the top or bottom and give the focus
* to the topmost/bottommost component in the new visible area. If no
* component is a good candidate for focus, this scrollview reclaims the
* focus.
*
* @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
* to go the top of the view or
* {@link android.view.View#FOCUS_DOWN} to go the bottom
* @return true if the key event is consumed by this method, false otherwise
*/
public boolean fullScroll(int direction) {
boolean down = direction == View.FOCUS_DOWN;
int height = getHeight();
mTempRect.top = 0;
mTempRect.bottom = height;
if (down) {
int count = getChildCount();
if (count > 0) {
View view = getChildAt(count - 1);
mTempRect.bottom = view.getBottom() + getPaddingBottom();
mTempRect.top = mTempRect.bottom - height;
}
}
return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
}
/**
* Scrolls the view to make the area defined by top and
* bottom visible. This method attempts to give the focus
* to a component visible in this area. If no component can be focused in
* the new visible area, the focus is reclaimed by this ScrollView.
*
* @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
* to go upward, {@link android.view.View#FOCUS_DOWN} to downward
* @param top the top offset of the new area to be made visible
* @param bottom the bottom offset of the new area to be made visible
* @return true if the key event is consumed by this method, false otherwise
*/
private boolean scrollAndFocus(int direction, int top, int bottom) {
boolean handled = true;
if(isVertScroll()){
int height = getHeight();
int containerTop = getScrollY();
int containerBottom = containerTop + height;
boolean up = direction == View.FOCUS_UP;
View newFocused = findFocusableViewInBounds(up, top, bottom);
if (newFocused == null) {
newFocused = this;
}
if (top >= containerTop && bottom <= containerBottom) {
handled = false;
} else {
int delta = up ? (top - containerTop) : (bottom - containerBottom);
doScrollY(delta);
}
if (newFocused != findFocus()) newFocused.requestFocus(direction);
}
else {
int width = getWidth();
int containerLeft = getScrollX();
int containerRight = containerLeft + width;
boolean up = direction == View.FOCUS_UP;
View newFocused = findFocusableViewInBounds(up, top, bottom);
if (newFocused == null) {
newFocused = this;
}
if (top >= containerLeft && bottom <= containerRight) {
handled = false;
} else {
int delta = up ? (top - containerLeft) : (bottom - containerRight);
doScrollX(delta);
}
if (newFocused != findFocus()) newFocused.requestFocus(direction);
}
return handled;
}
/**
* Handle scrolling in response to an up or down arrow click.
*
* @param direction The direction corresponding to the arrow key that was
* pressed
* @return True if we consumed the event, false otherwise
*/
public boolean arrowScroll(int direction) {
View currentFocused = findFocus();
if (currentFocused == this) currentFocused = null;
View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
final int maxJump = getMaxScrollAmount();
if(isVertScroll()){
if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) {
nextFocused.getDrawingRect(mTempRect);
offsetDescendantRectToMyCoords(nextFocused, mTempRect);
int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
doScrollY(scrollDelta);
nextFocused.requestFocus(direction);
} else {
// no new focus
int scrollDelta = maxJump;
if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) {
scrollDelta = getScrollY();
} else if (direction == View.FOCUS_DOWN) {
if (getChildCount() > 0) {
int daBottom = getChildAt(0).getBottom();
int screenBottom = getScrollY() + getHeight() - getPaddingBottom();
if (daBottom - screenBottom < maxJump) {
scrollDelta = daBottom - screenBottom;
}
}
}
if (scrollDelta == 0) {
return false;
}
doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta);
}
}
else {
if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getWidth())) {
nextFocused.getDrawingRect(mTempRect);
offsetDescendantRectToMyCoords(nextFocused, mTempRect);
int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
doScrollX(scrollDelta);
nextFocused.requestFocus(direction);
} else {
// no new focus
int scrollDelta = maxJump;
if (direction == View.FOCUS_UP && getScrollX() < scrollDelta) {
scrollDelta = getScrollX();
} else if (direction == View.FOCUS_DOWN) {
if (getChildCount() > 0) {
int daRight = getChildAt(0).getRight();
int screenRight = getScrollX() + getWidth() - getPaddingRight();
if (daRight - screenRight < maxJump) {
scrollDelta = daRight - screenRight;
}
}
}
if (scrollDelta == 0) {
return false;
}
doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta);
}
}
if (currentFocused != null && currentFocused.isFocused()
&& isOffScreen(currentFocused)) {
// previously focused item still has focus and is off screen, give
// it up (take it back to ourselves)
// (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are
// sure to
// get it)
final int descendantFocusability = getDescendantFocusability(); // save
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
requestFocus();
setDescendantFocusability(descendantFocusability); // restore
}
return true;
}
/**
* @return whether the descendant of this scroll view is scrolled off
* screen.
*/
private boolean isOffScreen(View descendant) {
if(isVertScroll()){
return !isWithinDeltaOfScreen(descendant, 0, getHeight());
}
else {
return !isWithinDeltaOfScreen(descendant, 0, getWidth());
}
}
/**
* @return whether the descendant of this scroll view is within delta
* pixels of being on the screen.
*/
private boolean isWithinDeltaOfScreen(View descendant, int delta, int value) {
descendant.getDrawingRect(mTempRect);
offsetDescendantRectToMyCoords(descendant, mTempRect);
if(isVertScroll()){
return (mTempRect.bottom + delta) >= getScrollY() && (mTempRect.top - delta) <= (getScrollY() + value);
}
else {
return (mTempRect.right + delta) >= getScrollX() && (mTempRect.left - delta) <= (getScrollX() + value);
}
}
/**
* Smooth scroll by a Y delta
*
* @param delta the number of pixels to scroll by on the Y axis
*/
private void doScrollY(int delta) {
if (delta != 0) {
if (mSmoothScrollingEnabled) {
smoothScrollBy(0, delta);
} else {
scrollBy(0, delta);
}
}
}
private void doScrollX(int delta) {
if (delta != 0) {
if (mSmoothScrollingEnabled) {
smoothScrollBy(delta,0);
} else {
scrollBy(delta, 0);
}
}
}
/**
* Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
*
* @param dx the number of pixels to scroll by on the X axis
* @param dy the number of pixels to scroll by on the Y axis
*/
public final void smoothScrollBy(int dx, int dy) {
if (getChildCount() == 0) {
// Nothing to do.
return;
}
long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
if (duration > ANIMATED_SCROLL_GAP) {
if(isVertScroll()){
final int height = getHeight() - getPaddingBottom() - getPaddingTop();
final int bottom = getChildAt(0).getHeight();
final int maxY = Math.max(0, bottom - height);
final int scrollY = getScrollY();
dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
mScroller.startScroll(getScrollX(), scrollY, 0, dy);
}
else {
final int width = getWidth() - getPaddingRight() - getPaddingLeft();
final int right = getChildAt(0).getRight();
final int maxX = Math.max(0, right - width);
final int scrollX = getScrollX();
dx = Math.max(0, Math.min(scrollX + dx, maxX)) - scrollX;
mScroller.startScroll(scrollX, getScrollY(), dx, 0);
}
postInvalidateOnAnimation();
} else {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
scrollBy(dx, dy);
}
mLastScroll = AnimationUtils.currentAnimationTimeMillis();
}
/**
* Like {@link #scrollTo}, but scroll smoothly instead of immediately.
*
* @param x the position where to scroll on the X axis
* @param y the position where to scroll on the Y axis
*/
public final void smoothScrollTo(int x, int y) {
smoothScrollBy(x - getScrollX(), y - getScrollY());
}
/**
* The scroll range of a scroll view is the overall height of all of its
* children.
*/
@Override
protected int computeVerticalScrollRange() {
final int count = getChildCount();
final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop();
final int contentWidth = getWidth() - getPaddingRight() - getPaddingLeft();
if(isVertScroll()){
if (count == 0) {
return contentHeight;
}
}
else {
if (count == 0) {
return contentWidth;
}
}
if(isVertScroll()){
int scrollRange = getChildAt(0).getBottom();
final int scrollY = getScrollY();
final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
if (scrollY < 0) {
scrollRange -= scrollY;
} else if (scrollY > overscrollBottom) {
scrollRange += scrollY - overscrollBottom;
}
return scrollRange;
}
else {
int scrollRange = getChildAt(0).getRight();
final int scrollX = getScrollX();
final int overscrollRight = Math.max(0, scrollRange - contentWidth);
if (scrollX < 0) {
scrollRange -= scrollX;
} else if (scrollX > overscrollRight) {
scrollRange += scrollX - overscrollRight;
}
return scrollRange;
}
}
@Override
protected int computeVerticalScrollOffset() {
return Math.max(0, super.computeVerticalScrollOffset());
}
@Override
protected int computeHorizontalScrollOffset() {
return Math.max(0, super.computeHorizontalScrollOffset());
}
@Override
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
ViewGroup.LayoutParams lp = child.getLayoutParams();
int childWidthMeasureSpec;
int childHeightMeasureSpec;
if(isVertScroll()){
childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft()
+ getPaddingRight(), lp.width);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
else {
childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, getPaddingTop()
+ getPaddingBottom(), lp.height);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidthMeasureSpec;
int childHeightMeasureSpec;
if (isVertScroll()) {
childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);
}
else {
childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
lp.leftMargin + lp.rightMargin, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@Override
public void computeScroll() {
//滚动尚未完成
if (mScroller.computeScrollOffset()) {
// This is called at drawing time by ViewGroup. We don't want to
// re-show the scrollbars at this point, which scrollTo will do,
// so we replicate most of scrollTo here.
//
// It's a little odd to call onScrollChanged from inside the drawing.
//
// It is, except when you remember that computeScroll() is used to
// animate scrolling. So unless we want to defer the onScrollChanged()
// until the end of the animated scrolling, we don't really have a
// choice here.
//
// I agree. The alternative, which I think would be worse, is to post
// something and tell the subclasses later. This is bad because there
// will be a window where getScrollX()/Y is different from what the app
// thinks it is.
//
int oldX = getScrollX();
int oldY = getScrollY();
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
final int range = getScrollRange();
final int overscrollMode = getOverScrollMode();
final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
if(isVertScroll()){
overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
0, mOverflingDistance, false);
}else {
overScrollBy(x - oldX, y - oldY, oldX, oldY, range, 0,
mOverflingDistance,0, false);
}
onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);
}
if (!awakenScrollBars()) {
// Keep on drawing until the animation has finished.
postInvalidateOnAnimation();
}
}
}
/**
* Scrolls the view to the given child.
*
* @param child the View to scroll to
*/
private void scrollToChild(View child) {
child.getDrawingRect(mTempRect);
/* Offset from child's local coordinates to ScrollView coordinates */
offsetDescendantRectToMyCoords(child, mTempRect);
int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
if (scrollDelta != 0) {
if(isVertScroll()){
scrollBy(0, scrollDelta);
}
else {
scrollBy(scrollDelta, 0);
}
}
}
/**
* If rect is off screen, scroll just enough to get it (or at least the
* first screen size chunk of it) on screen.
*
* @param rect The rectangle.
* @param immediate True to scroll immediately without animation
* @return true if scrolling was performed
*/
private boolean scrollToChildRect(Rect rect, boolean immediate) {
final int delta = computeScrollDeltaToGetChildRectOnScreen(rect);
final boolean scroll = delta != 0;
if (scroll) {
if (immediate) {
if(isVertScroll()){
scrollBy(0, delta);
}
else {
scrollBy(delta, 0);
}
} else {
if(isVertScroll()){
smoothScrollBy(0, delta);
}
else {
smoothScrollBy(delta, 0);
}
}
}
return scroll;
}
/**
* Compute the amount to scroll in the Y direction in order to get
* a rectangle completely on the screen (or, if taller than the screen,
* at least the first screen size chunk of it).
*
* @param rect The rect.
* @return The scroll delta.
*/
protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
if (getChildCount() == 0) return 0;
int height = getHeight();
int screenTop = getScrollY();
int screenBottom = screenTop + height;
int width = getWidth();
int screenLeft = getScrollX();
int screenRight = screenLeft + width;
int fadingEdgeVertical = getVerticalFadingEdgeLength();
int fadingEdgeHorizontal = getHorizontalFadingEdgeLength();
if(isVertScroll()){
// leave room for top fading edge as long as rect isn't at very top
if (rect.top > 0) {
screenTop += fadingEdgeVertical;
}
// leave room for bottom fading edge as long as rect isn't at very bottom
if (rect.bottom < getChildAt(0).getHeight()) {
screenBottom -= fadingEdgeVertical;
}
int scrollYDelta = 0;
if (rect.bottom > screenBottom && rect.top > screenTop) {
// need to move down to get it in view: move down just enough so
// that the entire rectangle is in view (or at least the first
// screen size chunk).
if (rect.height() > height) {
// just enough to get screen size chunk on
scrollYDelta += (rect.top - screenTop);
} else {
// get entire rect at bottom of screen
scrollYDelta += (rect.bottom - screenBottom);
}
// make sure we aren't scrolling beyond the end of our content
int bottom = getChildAt(0).getBottom();
int distanceToBottom = bottom - screenBottom;
scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
} else if (rect.top < screenTop && rect.bottom < screenBottom) {
// need to move up to get it in view: move up just enough so that
// entire rectangle is in view (or at least the first screen
// size chunk of it).
if (rect.height() > height) {
// screen size chunk
scrollYDelta -= (screenBottom - rect.bottom);
} else {
// entire rect at top
scrollYDelta -= (screenTop - rect.top);
}
// make sure we aren't scrolling any further than the top our content
scrollYDelta = Math.max(scrollYDelta, -getScrollY());
}
return scrollYDelta;
}else {
// leave room for top fading edge as long as rect isn't at very top
if (rect.left > 0) {
screenLeft += fadingEdgeHorizontal;
}
// leave room for bottom fading edge as long as rect isn't at very bottom
if (rect.right < getChildAt(0).getWidth()) {
screenRight -= fadingEdgeHorizontal;
}
int scrollXDelta = 0;
if (rect.right > screenRight && rect.left > screenLeft) {
// need to move down to get it in view: move down just enough so
// that the entire rectangle is in view (or at least the first
// screen size chunk).
if (rect.width() > width) {
// just enough to get screen size chunk on
scrollXDelta += (rect.left - screenLeft);
} else {
// get entire rect at bottom of screen
scrollXDelta += (rect.right - screenRight);
}
// make sure we aren't scrolling beyond the end of our content
int right = getChildAt(0).getRight();
int distanceToRight = right - screenRight;
scrollXDelta = Math.min(scrollXDelta, distanceToRight);
} else if (rect.left < screenLeft && rect.right < screenRight) {
// need to move up to get it in view: move up just enough so that
// entire rectangle is in view (or at least the first screen
// size chunk of it).
if (rect.width() > width) {
// screen size chunk
scrollXDelta -= (screenRight - rect.right);
} else {
// entire rect at top
scrollXDelta -= (screenLeft - rect.left);
}
// make sure we aren't scrolling any further than the top our content
scrollXDelta = Math.max(scrollXDelta, -getScrollX());
}
return scrollXDelta;
}
}
@Override
public void requestChildFocus(View child, View focused) {
if (!mIsLayoutDirty) {
scrollToChild(focused);
} else {
// The child may not be laid out yet, we can't compute the scroll yet
mChildToScrollTo = focused;
}
super.requestChildFocus(child, focused);
}
/**
* When looking for focus in children of a scroll view, need to be a little
* more careful not to give focus to something that is scrolled off screen.
*
* This is more expensive than the default {@link android.view.ViewGroup}
* implementation, otherwise this behavior might have been made the default.
*/
@Override
protected boolean onRequestFocusInDescendants(int direction,
Rect previouslyFocusedRect) {
// convert from forward / backward notation to up / down / left / right
// (ugh).
if (direction == View.FOCUS_FORWARD) {
direction = View.FOCUS_DOWN;
} else if (direction == View.FOCUS_BACKWARD) {
direction = View.FOCUS_UP;
}
final View nextFocus = previouslyFocusedRect == null ?
FocusFinder.getInstance().findNextFocus(this, null, direction) :
FocusFinder.getInstance().findNextFocusFromRect(this,
previouslyFocusedRect, direction);
if (nextFocus == null) {
return false;
}
if (isOffScreen(nextFocus)) {
return false;
}
return nextFocus.requestFocus(direction, previouslyFocusedRect);
}
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
boolean immediate) {
// offset into coordinate space of this scroll view
rectangle.offset(child.getLeft() - child.getScrollX(),child.getTop() - child.getScrollY());
return scrollToChildRect(rectangle, immediate);
}
@Override
public void requestLayout() {
mIsLayoutDirty = true;
super.requestLayout();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mIsLayoutDirty = false;
// Give a child focus if it needs it
if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
scrollToChild(mChildToScrollTo);
}
mChildToScrollTo = null;
if (!isLaidOut()) {
if (mSavedState != null) {
if(isVertScroll()){
setScrollY(mSavedState.scrollPosition);
}
else {
setScrollX(mSavedState.scrollPosition);
}
mSavedState = null;
} // getScrollY() default value is "0"
final int childHeight = (getChildCount() > 0) ? getChildAt(0).getMeasuredHeight() : 0;
final int scrollRangeY = Math.max(0, childHeight - (b - t - getPaddingBottom() - getPaddingTop()));
final int childWidth = (getChildCount() > 0) ? getChildAt(0).getMeasuredWidth() : 0;
final int scrollRangeX = Math.max(0, childWidth - (r - l - getPaddingRight() - getPaddingLeft()));
if(isVertScroll()){
// Don't forget to clamp
if (getScrollY() > scrollRangeY) {
setScrollY(scrollRangeY);
} else if (getScrollY() < 0) {
//Original is setScaleY
setScrollY(0);
}
}
else {
// Don't forget to clamp
if (getScrollX() > scrollRangeX) {
setScrollX(scrollRangeX);
} else if (getScrollX() < 0) {
setScrollX(0);
}
}
}
// Calling this with the present values causes it to re-claim them
scrollTo(getScrollX(), getScrollY());
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
View currentFocused = findFocus();
if (null == currentFocused || this == currentFocused)
return;
// If the currently-focused view was visible on the screen when the
// screen was at the old height, then scroll the screen to make that
// view visible with the new screen height.
if(isVertScroll()){
if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) {
currentFocused.getDrawingRect(mTempRect);
offsetDescendantRectToMyCoords(currentFocused, mTempRect);
int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
doScrollY(scrollDelta);
}
}
else {
if (isWithinDeltaOfScreen(currentFocused, 0, oldw)) {
currentFocused.getDrawingRect(mTempRect);
offsetDescendantRectToMyCoords(currentFocused, mTempRect);
int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
doScrollX(scrollDelta);
}
}
}
/**
* Return true if child is a descendant of parent, (or equal to the parent).
*/
private static boolean isViewDescendantOf(View child, View parent) {
if (child == parent) {
return true;
}
final ViewParent theParent = child.getParent();
return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
}
/**
* Fling the scroll view
*
* @param velocity The initial velocity in the Y direction. Positive
* numbers mean that the finger/cursor is moving down the screen,
* which means we want to scroll towards the top.
*/
public void fling(int velocity) {
if (getChildCount() > 0) {
int height = getHeight() - getPaddingBottom() - getPaddingTop();
int bottom = getChildAt(0).getHeight();
int width = getWidth() - getPaddingRight() - getPaddingLeft();
int right = getChildAt(0).getRight();
if(isVertScroll()) {
mScroller.fling(getScrollX(), getScrollY(), 0, velocity, 0, 0, 0,
Math.max(0, bottom - height), 0, height / 2);
}
else {
mScroller.fling(getScrollX(), getScrollY(), velocity, 0, 0, Math.max(0, right - width), 0,
0, width/2, 0);
}
postInvalidateOnAnimation();
}
}
private void endDrag() {
mIsBeingDragged = false;
recycleVelocityTracker();
}
/**
* {@inheritDoc}
*
* This version also clamps the scrolling to the bounds of our child.
*/
@Override
public void scrollTo(int x, int y) {
// we rely on the fact the View.scrollBy calls scrollTo.
if (getChildCount() > 0) {
if (x != getScrollX() || y != getScrollY()) {
super.scrollTo(x, y);
}
}
}
@Override
public void setOverScrollMode(int mode) {
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (getContext().getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
// Some old apps reused IDs in ways they shouldn't have.
// Don't break them, but they don't get scroll state restoration.
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
mSavedState = ss;
requestLayout();
}
@Override
protected Parcelable onSaveInstanceState() {
if (getContext().getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
// Some old apps reused IDs in ways they shouldn't have.
// Don't break them, but they don't get scroll state restoration.
return super.onSaveInstanceState();
}
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.scrollPosition = getScrollY();
return ss;
}
static class SavedState extends BaseSavedState {
public int scrollPosition;
SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
scrollPosition = source.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(scrollPosition);
}
@Override
public String toString() {
return "HorizontalScrollView.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " scrollPosition=" + scrollPosition + "}";
}
public static final Parcelable.Creator CREATOR
= new Parcelable.Creator() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
protected boolean overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
final int overScrollMode = OVER_SCROLL_ALWAYS;
final boolean canScrollHorizontal =
computeHorizontalScrollRange() > computeHorizontalScrollExtent();
final boolean canScrollVertical =
computeVerticalScrollRange() > computeVerticalScrollExtent();
final boolean overScrollHorizontal = overScrollMode == OVER_SCROLL_ALWAYS ||
(overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
final boolean overScrollVertical = overScrollMode == OVER_SCROLL_ALWAYS ||
(overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
// 不涉及 Drag + Overscroll 的 Fling
int newScrollX = scrollX + deltaX;
if (!overScrollHorizontal) {
maxOverScrollX = 0;
}
int newScrollY = scrollY + deltaY;
if (!overScrollVertical) {
maxOverScrollY = 0;
}
// Clamp values if at the limits and record
int padding = 0;
final int left = -maxOverScrollX - padding;
final int right = maxOverScrollX + scrollRangeX + padding;
final int top = -maxOverScrollY - padding;
final int bottom = maxOverScrollY + scrollRangeY + padding;
boolean clampedX = false;
if (newScrollX > right) {
//newScrollX = right;
clampedX = true;
} else if (newScrollX < left) {
//newScrollX = left;
clampedX = true;
}
boolean clampedY = false;
if (newScrollY > bottom) {
//newScrollY = bottom;
clampedY = true;
} else if (newScrollY < top) {
//newScrollY = top;
clampedY = true;
}
if (mIsBeingDragged && clampedX) {
newScrollX = (int)(scrollX + deltaX * FOLLOW_HAND_FACTOR);
}
if (mIsBeingDragged && clampedY) {
newScrollY = (int)(scrollY + deltaY * FOLLOW_HAND_FACTOR);
}
onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
return clampedX || clampedY;
}
private boolean isVertScroll(){
return mScroller.isVertScroll();
}
public AnOverScroller getScroller(){ return mScroller; }
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AnInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator;
import android.animation.TimeInterpolator;
/**
* An interpolator defines the rate of change of an animation. This allows
* the basic animation effects (alpha, scale, translate, rotate) to be
* accelerated, decelerated, repeated, etc.
*/
public abstract class AnInterpolator implements TimeInterpolator {
// A new interface, TimeInterpolator, was introduced for the new android.animation
// package. This older Interpolator interface extends TimeInterpolator so that users of
// the new Animator-based animations can use either the old Interpolator implementations or
// new classes that implement TimeInterpolator directly.
//TODO Use reflection
public float arg1,arg2,arg3,arg4 = -1f;
public String string1,string2,string3,string4 = "NULL";
public float min1,min2,min3,min4,max1,max2,max3,max4 = -1f;
public int argNum = 0;
public void initArgData(int i, float val, String name, float min, float max){
if(i == 0){
//arg1 = val;
string1 = name;
min1 = min;
max1 = max;
}
else if(i == 1){
//arg2 = val;
string2 = name;
min2 = min;
max2 = max;
}
else if(i == 2){
//arg3 = val;
string3 = name;
min3 = min;
max3 = max;
}
else if(i == 3){
//arg4 = val;
string4 = name;
min4 = min;
max4 = max;
}
setArgValue(i,val);
argNum++;
}
public void resetArgValue(int i, float value){
}
public void setArgValue(int i,float val){
if(i == 0){
arg1 = val;
}
else if(i == 1){
arg2 = val;
}
else if(i == 2){
arg3 = val;
}
else if(i == 3){
arg4 = val;
}
}
public int getArgNum(){
return argNum;
}
public float getArgValue(int i) {
if(i == 0){
if(arg1 != -1)
return (float) arg1;
else
return -1;
}
else if(i == 1){
if(arg2 != -1)
return (float) arg2;
else
return -1;
}
else if(i == 2){
if(arg3 != -1)
return (float) arg3;
else
return -1;
}
else if(i == 3){
if(arg4 != -1)
return (float) arg4;
else
return -1;
}
return -1;
}
public String getArgString(int i) {
if(i == 0){
if(string1 != "NULL")
return string1;
else
return "NULL";
}
else if(i == 1){
if(string2 != "NULL")
return string2;
else
return "NULL";
}
else if(i == 2){
if(string3 != "NULL")
return string3;
else
return "NULL";
}
else if(i == 3){
if(string4 != "NULL")
return string4;
else
return "NULL";
}
return "NULL";
}
public float getArgMin(int i) {
if(i == 0){
if(min1 != -1)
return min1;
else
return -1;
}
else if(i == 1){
if(min2 != -1)
return min2;
else
return -1;
}
else if(i == 2){
if(min3 != -1)
return min3;
else
return -1;
}
else if(i == 3){
if(min4 != -1)
return min4;
else
return -1;
}
return -1;
}
public float getArgMax(int i) {
if(i == 0){
if(max1 != -1)
return max1;
else
return -1;
}
else if(i == 1){
if(max2 != -1)
return max2;
else
return -1;
}
else if(i == 2){
if(max3 != -1)
return max3;
else
return -1;
}
else if(i == 3){
if(max4 != -1)
return max4;
else
return -1;
}
return -1;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/AccelerateDecelerateInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class AccelerateDecelerateInterpolator extends AnInterpolator {
public AccelerateDecelerateInterpolator() {
}
@SuppressWarnings({"UnusedDeclaration"})
public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/AccelerateInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class AccelerateInterpolator extends AnInterpolator {
private float mFactor;
private double mDoubleFactor;
public AccelerateInterpolator() {
mFactor = 1.0f;
mDoubleFactor = 2.0;
initArgData(0,1,"factor",0,10);
}
public AccelerateInterpolator(float factor) {
mFactor = factor;
mDoubleFactor = 2 * mFactor;
initArgData(0,factor,"factor",0,10);
}
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
mFactor = value;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/AnticipateInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class AnticipateInterpolator extends AnInterpolator {
private float mTension;
public AnticipateInterpolator() {
mTension = 2.0f;
initArgData(0,2,"factor",0,10);
}
/**
* @param tension Amount of anticipation. When tension equals 0.0f, there is
* no anticipation and the interpolator becomes a simple
* acceleration interpolator.
*/
public AnticipateInterpolator(float tension) {
mTension = tension;
initArgData(0,tension,"factor",0,10);
}
public float getInterpolation(float t) {
// a(t) = t * t * ((tension + 1) * t - tension)
return t * t * ((mTension + 1) * t - mTension);
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
mTension = value;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/AnticipateOvershootInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class AnticipateOvershootInterpolator extends AnInterpolator {
private float mTension;
public AnticipateOvershootInterpolator() {
mTension = 2.0f * 1.5f;
initArgData(0,2,"factor",0,10);
}
public AnticipateOvershootInterpolator(float tension) {
mTension = tension * 1.5f;
initArgData(0,tension,"factor",0,10);
}
public AnticipateOvershootInterpolator(float tension, float extraTension) {
mTension = tension * extraTension;
initArgData(0,tension,"factor",0,10);
}
private static float a(float t, float s) {
return t * t * ((s + 1) * t - s);
}
private static float o(float t, float s) {
return t * t * ((s + 1) * t + s);
}
public float getInterpolation(float t) {
// a(t, s) = t * t * ((s + 1) * t - s)
// o(t, s) = t * t * ((s + 1) * t + s)
// f(t) = 0.5 * a(t * 2, tension * extraTension), when t < 0.5
// f(t) = 0.5 * (o(t * 2 - 2, tension * extraTension) + 2), when t <= 1.0
if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension);
else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f);
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
mTension = value;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/BounceInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class BounceInterpolator extends AnInterpolator {
public BounceInterpolator() {
}
private static float bounce(float t) {
return t * t * 8.0f;
}
public float getInterpolation(float t) {
// _b(t) = t * t * 8
// bs(t) = _b(t) for t < 0.3535
// bs(t) = _b(t - 0.54719) + 0.7 for t < 0.7408
// bs(t) = _b(t - 0.8526) + 0.9 for t < 0.9644
// bs(t) = _b(t - 1.0435) + 0.95 for t <= 1.0
// b(t) = bs(t * 1.1226)
t *= 1.1226f;
if (t < 0.3535f) return bounce(t);
else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f;
else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
else return bounce(t - 1.0435f) + 0.95f;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/CycleInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class CycleInterpolator extends AnInterpolator {
public CycleInterpolator(float cycles) {
mCycles = cycles;
initArgData(0,cycles,"factor",0,10);
}
public float getInterpolation(float input) {
return (float)(Math.sin(2 * mCycles * Math.PI * input));
}
private float mCycles;
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
mCycles = value;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/DecelerateInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class DecelerateInterpolator extends AnInterpolator {
private float mFactor = 1.0f;
public DecelerateInterpolator() {
initArgData(0,1,"factor",0,10);
}
public DecelerateInterpolator(float factor) {
mFactor = factor;
initArgData(0,factor,"factor",0,10);
}
public float getInterpolation(float input) {
float result;
if (mFactor == 1.0f) {
result = (float)(1.0f - (1.0f - input) * (1.0f - input));
} else {
result = (float)(1.0f - Math.pow((1.0f - input), 2 * mFactor));
}
return result;
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
mFactor = value;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/FastOutLinearInInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class FastOutLinearInInterpolator extends LookupTableInterpolator {
private static final float[] VALUES = new float[] {
0.0000f, 0.0001f, 0.0002f, 0.0005f, 0.0008f, 0.0013f, 0.0018f,
0.0024f, 0.0032f, 0.0040f, 0.0049f, 0.0059f, 0.0069f, 0.0081f,
0.0093f, 0.0106f, 0.0120f, 0.0135f, 0.0151f, 0.0167f, 0.0184f,
0.0201f, 0.0220f, 0.0239f, 0.0259f, 0.0279f, 0.0300f, 0.0322f,
0.0345f, 0.0368f, 0.0391f, 0.0416f, 0.0441f, 0.0466f, 0.0492f,
0.0519f, 0.0547f, 0.0574f, 0.0603f, 0.0632f, 0.0662f, 0.0692f,
0.0722f, 0.0754f, 0.0785f, 0.0817f, 0.0850f, 0.0884f, 0.0917f,
0.0952f, 0.0986f, 0.1021f, 0.1057f, 0.1093f, 0.1130f, 0.1167f,
0.1205f, 0.1243f, 0.1281f, 0.1320f, 0.1359f, 0.1399f, 0.1439f,
0.1480f, 0.1521f, 0.1562f, 0.1604f, 0.1647f, 0.1689f, 0.1732f,
0.1776f, 0.1820f, 0.1864f, 0.1909f, 0.1954f, 0.1999f, 0.2045f,
0.2091f, 0.2138f, 0.2184f, 0.2232f, 0.2279f, 0.2327f, 0.2376f,
0.2424f, 0.2473f, 0.2523f, 0.2572f, 0.2622f, 0.2673f, 0.2723f,
0.2774f, 0.2826f, 0.2877f, 0.2929f, 0.2982f, 0.3034f, 0.3087f,
0.3141f, 0.3194f, 0.3248f, 0.3302f, 0.3357f, 0.3412f, 0.3467f,
0.3522f, 0.3578f, 0.3634f, 0.3690f, 0.3747f, 0.3804f, 0.3861f,
0.3918f, 0.3976f, 0.4034f, 0.4092f, 0.4151f, 0.4210f, 0.4269f,
0.4329f, 0.4388f, 0.4448f, 0.4508f, 0.4569f, 0.4630f, 0.4691f,
0.4752f, 0.4814f, 0.4876f, 0.4938f, 0.5000f, 0.5063f, 0.5126f,
0.5189f, 0.5252f, 0.5316f, 0.5380f, 0.5444f, 0.5508f, 0.5573f,
0.5638f, 0.5703f, 0.5768f, 0.5834f, 0.5900f, 0.5966f, 0.6033f,
0.6099f, 0.6166f, 0.6233f, 0.6301f, 0.6369f, 0.6436f, 0.6505f,
0.6573f, 0.6642f, 0.6710f, 0.6780f, 0.6849f, 0.6919f, 0.6988f,
0.7059f, 0.7129f, 0.7199f, 0.7270f, 0.7341f, 0.7413f, 0.7484f,
0.7556f, 0.7628f, 0.7700f, 0.7773f, 0.7846f, 0.7919f, 0.7992f,
0.8066f, 0.8140f, 0.8214f, 0.8288f, 0.8363f, 0.8437f, 0.8513f,
0.8588f, 0.8664f, 0.8740f, 0.8816f, 0.8892f, 0.8969f, 0.9046f,
0.9124f, 0.9201f, 0.9280f, 0.9358f, 0.9437f, 0.9516f, 0.9595f,
0.9675f, 0.9755f, 0.9836f, 0.9918f, 1.0000f
};
public FastOutLinearInInterpolator() {
super(VALUES);
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/FastOutSlowInInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class FastOutSlowInInterpolator extends LookupTableInterpolator {
private static final float[] VALUES = new float[] {
0.0000f, 0.0001f, 0.0002f, 0.0005f, 0.0009f, 0.0014f, 0.0020f,
0.0027f, 0.0036f, 0.0046f, 0.0058f, 0.0071f, 0.0085f, 0.0101f,
0.0118f, 0.0137f, 0.0158f, 0.0180f, 0.0205f, 0.0231f, 0.0259f,
0.0289f, 0.0321f, 0.0355f, 0.0391f, 0.0430f, 0.0471f, 0.0514f,
0.0560f, 0.0608f, 0.0660f, 0.0714f, 0.0771f, 0.0830f, 0.0893f,
0.0959f, 0.1029f, 0.1101f, 0.1177f, 0.1257f, 0.1339f, 0.1426f,
0.1516f, 0.1610f, 0.1707f, 0.1808f, 0.1913f, 0.2021f, 0.2133f,
0.2248f, 0.2366f, 0.2487f, 0.2611f, 0.2738f, 0.2867f, 0.2998f,
0.3131f, 0.3265f, 0.3400f, 0.3536f, 0.3673f, 0.3810f, 0.3946f,
0.4082f, 0.4217f, 0.4352f, 0.4485f, 0.4616f, 0.4746f, 0.4874f,
0.5000f, 0.5124f, 0.5246f, 0.5365f, 0.5482f, 0.5597f, 0.5710f,
0.5820f, 0.5928f, 0.6033f, 0.6136f, 0.6237f, 0.6335f, 0.6431f,
0.6525f, 0.6616f, 0.6706f, 0.6793f, 0.6878f, 0.6961f, 0.7043f,
0.7122f, 0.7199f, 0.7275f, 0.7349f, 0.7421f, 0.7491f, 0.7559f,
0.7626f, 0.7692f, 0.7756f, 0.7818f, 0.7879f, 0.7938f, 0.7996f,
0.8053f, 0.8108f, 0.8162f, 0.8215f, 0.8266f, 0.8317f, 0.8366f,
0.8414f, 0.8461f, 0.8507f, 0.8551f, 0.8595f, 0.8638f, 0.8679f,
0.8720f, 0.8760f, 0.8798f, 0.8836f, 0.8873f, 0.8909f, 0.8945f,
0.8979f, 0.9013f, 0.9046f, 0.9078f, 0.9109f, 0.9139f, 0.9169f,
0.9198f, 0.9227f, 0.9254f, 0.9281f, 0.9307f, 0.9333f, 0.9358f,
0.9382f, 0.9406f, 0.9429f, 0.9452f, 0.9474f, 0.9495f, 0.9516f,
0.9536f, 0.9556f, 0.9575f, 0.9594f, 0.9612f, 0.9629f, 0.9646f,
0.9663f, 0.9679f, 0.9695f, 0.9710f, 0.9725f, 0.9739f, 0.9753f,
0.9766f, 0.9779f, 0.9791f, 0.9803f, 0.9815f, 0.9826f, 0.9837f,
0.9848f, 0.9858f, 0.9867f, 0.9877f, 0.9885f, 0.9894f, 0.9902f,
0.9910f, 0.9917f, 0.9924f, 0.9931f, 0.9937f, 0.9944f, 0.9949f,
0.9955f, 0.9960f, 0.9964f, 0.9969f, 0.9973f, 0.9977f, 0.9980f,
0.9984f, 0.9986f, 0.9989f, 0.9991f, 0.9993f, 0.9995f, 0.9997f,
0.9998f, 0.9999f, 0.9999f, 1.0000f, 1.0000f
};
public FastOutSlowInInterpolator() {
super(VALUES);
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/LinearInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class LinearInterpolator extends AnInterpolator {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/LinearOutSlowInInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class LinearOutSlowInInterpolator extends LookupTableInterpolator {
private static final float[] VALUES = new float[] {
0.0000f, 0.0222f, 0.0424f, 0.0613f, 0.0793f, 0.0966f, 0.1132f,
0.1293f, 0.1449f, 0.1600f, 0.1747f, 0.1890f, 0.2029f, 0.2165f,
0.2298f, 0.2428f, 0.2555f, 0.2680f, 0.2802f, 0.2921f, 0.3038f,
0.3153f, 0.3266f, 0.3377f, 0.3486f, 0.3592f, 0.3697f, 0.3801f,
0.3902f, 0.4002f, 0.4100f, 0.4196f, 0.4291f, 0.4385f, 0.4477f,
0.4567f, 0.4656f, 0.4744f, 0.4831f, 0.4916f, 0.5000f, 0.5083f,
0.5164f, 0.5245f, 0.5324f, 0.5402f, 0.5479f, 0.5555f, 0.5629f,
0.5703f, 0.5776f, 0.5847f, 0.5918f, 0.5988f, 0.6057f, 0.6124f,
0.6191f, 0.6257f, 0.6322f, 0.6387f, 0.6450f, 0.6512f, 0.6574f,
0.6635f, 0.6695f, 0.6754f, 0.6812f, 0.6870f, 0.6927f, 0.6983f,
0.7038f, 0.7093f, 0.7147f, 0.7200f, 0.7252f, 0.7304f, 0.7355f,
0.7406f, 0.7455f, 0.7504f, 0.7553f, 0.7600f, 0.7647f, 0.7694f,
0.7740f, 0.7785f, 0.7829f, 0.7873f, 0.7917f, 0.7959f, 0.8002f,
0.8043f, 0.8084f, 0.8125f, 0.8165f, 0.8204f, 0.8243f, 0.8281f,
0.8319f, 0.8356f, 0.8392f, 0.8429f, 0.8464f, 0.8499f, 0.8534f,
0.8568f, 0.8601f, 0.8634f, 0.8667f, 0.8699f, 0.8731f, 0.8762f,
0.8792f, 0.8823f, 0.8852f, 0.8882f, 0.8910f, 0.8939f, 0.8967f,
0.8994f, 0.9021f, 0.9048f, 0.9074f, 0.9100f, 0.9125f, 0.9150f,
0.9174f, 0.9198f, 0.9222f, 0.9245f, 0.9268f, 0.9290f, 0.9312f,
0.9334f, 0.9355f, 0.9376f, 0.9396f, 0.9416f, 0.9436f, 0.9455f,
0.9474f, 0.9492f, 0.9510f, 0.9528f, 0.9545f, 0.9562f, 0.9579f,
0.9595f, 0.9611f, 0.9627f, 0.9642f, 0.9657f, 0.9672f, 0.9686f,
0.9700f, 0.9713f, 0.9726f, 0.9739f, 0.9752f, 0.9764f, 0.9776f,
0.9787f, 0.9798f, 0.9809f, 0.9820f, 0.9830f, 0.9840f, 0.9849f,
0.9859f, 0.9868f, 0.9876f, 0.9885f, 0.9893f, 0.9900f, 0.9908f,
0.9915f, 0.9922f, 0.9928f, 0.9934f, 0.9940f, 0.9946f, 0.9951f,
0.9956f, 0.9961f, 0.9966f, 0.9970f, 0.9974f, 0.9977f, 0.9981f,
0.9984f, 0.9987f, 0.9989f, 0.9992f, 0.9994f, 0.9995f, 0.9997f,
0.9998f, 0.9999f, 0.9999f, 1.0000f, 1.0000f
};
public LinearOutSlowInInterpolator() {
super(VALUES);
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/LookupTableInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class LookupTableInterpolator extends AnInterpolator {
private final float[] mValues;
private final float mStepSize;
protected LookupTableInterpolator(float[] values) {
mValues = values;
mStepSize = 1f / (mValues.length - 1);
}
@Override
public float getInterpolation(float input) {
if (input >= 1.0f) {
return 1.0f;
}
if (input <= 0f) {
return 0f;
}
// Calculate index - We use min with length - 2 to avoid IndexOutOfBoundsException when
// we lerp (linearly interpolate) in the return statement
int position = Math.min((int) (input * (mValues.length - 1)), mValues.length - 2);
// Calculate values to account for small offsets as the lookup table has discrete values
float quantized = position * mStepSize;
float diff = input - quantized;
float weight = diff / mStepSize;
// Linearly interpolate between the table values
return mValues[position] + weight * (mValues[position + 1] - mValues[position]);
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/OvershootInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class OvershootInterpolator extends AnInterpolator {
private float mTension;
public OvershootInterpolator() {
mTension = 2.0f;
initArgData(0,2,"factor",0,10);
}
public OvershootInterpolator(float tension) {
mTension = tension;
initArgData(0,tension,"factor",0,10);
}
public float getInterpolation(float t) {
// _o(t) = t * t * ((tension + 1) * t + tension)
// o(t) = _o(t - 1) + 1
t -= 1.0f;
return t * t * ((mTension + 1) * t + mTension) + 1.0f;
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
mTension = value;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidNative/PathInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator.AndroidNative;
import android.graphics.Path;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
public class PathInterpolator extends AnInterpolator {
private static final float PRECISION = 0.002f;
private float[] mX; // x coordinates in the line
private float[] mY; // y coordinates in the line
private float x1,y1,x2,y2;
public PathInterpolator(float controlX1, float controlY1, float controlX2, float controlY2) {
x1= controlX1;
y1 = controlY1;
x2 = controlX2;
y2 = controlY2;
initCubic(x1, y1, x2, y2);
initArgData(0,x1,"x1",0.0f,1.0f);
initArgData(1,y1,"y1",0.0f,1.0f);
initArgData(2,x2,"x2",0.0f,1.0f);
initArgData(3,y2,"y2",0.0f,1.0f);
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
x1 = value;
}
if(i == 1){
y1 = value;
}
if(i == 2){
x2 = value;
}
if(i == 3){
y2 = value;
}
initCubic(x1, y1, x2, y2);
}
private void initCubic(float x1, float y1, float x2, float y2) {
Path path = new Path();
path.moveTo(0, 0);
path.cubicTo(x1, y1, x2, y2, 1f, 1f);
initPath(path);
}
private void initPath(Path path) {
float[] pointComponents = path.approximate(PRECISION);
int numPoints = pointComponents.length / 3;
if (pointComponents[1] != 0 || pointComponents[2] != 0
|| pointComponents[pointComponents.length - 2] != 1
|| pointComponents[pointComponents.length - 1] != 1) {
throw new IllegalArgumentException("The Path must start at (0,0) and end at (1,1)");
}
mX = new float[numPoints];
mY = new float[numPoints];
float prevX = 0;
float prevFraction = 0;
int componentIndex = 0;
for (int i = 0; i < numPoints; i++) {
float fraction = pointComponents[componentIndex++];
float x = pointComponents[componentIndex++];
float y = pointComponents[componentIndex++];
if (fraction == prevFraction && x != prevX) {
throw new IllegalArgumentException(
"The Path cannot have discontinuity in the X axis.");
}
if (x < prevX) {
throw new IllegalArgumentException("The Path cannot loop back on itself.");
}
mX[i] = x;
mY[i] = y;
prevX = x;
prevFraction = fraction;
}
}
@Override
public float getInterpolation(float t) {
if (t <= 0) {
return 0;
} else if (t >= 1) {
return 1;
}
// Do a binary search for the correct x to interpolate between.
int startIndex = 0;
int endIndex = mX.length - 1;
while (endIndex - startIndex > 1) {
int midIndex = (startIndex + endIndex) / 2;
if (t < mX[midIndex]) {
endIndex = midIndex;
} else {
startIndex = midIndex;
}
}
float xRange = mX[endIndex] - mX[startIndex];
if (xRange == 0) {
return mY[startIndex];
}
float tInRange = t - mX[startIndex];
float fraction = tInRange / xRange;
float startY = mY[startIndex];
float endY = mY[endIndex];
return startY + (fraction * (endY - startY));
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidSpringInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator;
// Interpolator Version of Android's SpringAnimation
import android.util.Log;
import com.martinrgb.animer.core.math.calculator.SpringInterpolatorCalculator;
public class AndroidSpringInterpolator extends AnInterpolator{
//Parameters
private float mStiffness = 1500.f;
private float mDampingRatio = 0.5f;
private float mVelocity = 0.f;
private float mDuration = 1.f;
private float mLastPlacement = 0.f;
public AndroidSpringInterpolator(float stiffness, float dampingratio,float velocity,float duration) {
this.mStiffness = stiffness;
this.mDampingRatio = dampingratio;
this.mVelocity = velocity;
this.mDuration = duration/1000.f;
//this.mDuration = new SpringInterpolatorCalculator(stiffness,dampingratio).getDuration();
initArgData(0,(float) stiffness,"stiffness",0.01f,3000);
initArgData(1,(float) dampingratio,"dampingratio",0.01f,3000);
initArgData(2,(float) velocity,"velocity",-5000,5000);
initArgData(3,(float) duration,"duration",0,5000);
}
public AndroidSpringInterpolator(float stiffness, float dampingratio,float duration) {
this.mStiffness = stiffness;
this.mDampingRatio = dampingratio;
this.mVelocity = 0.f;
this.mDuration = duration/1000.f;
//this.mDuration = new SpringInterpolatorCalculator(stiffness,dampingratio).getDuration();
initArgData(0,(float) stiffness,"stiffness",0.01f,3000);
initArgData(1,(float) dampingratio,"dampingratio",0.01f,1);
initArgData(2,(float) 0,"velocity",-5000,5000);
initArgData(3,(float) duration,"duration",0,5000);
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
mStiffness = value;
}
if(i == 1){
mDampingRatio = value;
}
if(i == 2){
mVelocity = value;
}
if(i == 3){
mDuration = value/1000;
}
}
@Override
public float getInterpolation(float ratio) {
if (ratio == 0.0f || ratio == 1.0f)
return ratio;
else {
float deltaT = ratio * mDuration;
float starVal = 0;
float endVal = 1;
float mNaturalFreq = (float) Math.sqrt(mStiffness);
float mDampedFreq = (float)(mNaturalFreq*Math.sqrt(1.0 - mDampingRatio* mDampingRatio));
float lastVelocity = mVelocity;
//float lastDisplacement = ratio - endVal* deltaT/60 - endVal;
float lastDisplacement = ratio - endVal;
float coeffB = (float) (1.0 / mDampedFreq * (mDampingRatio * mNaturalFreq * lastDisplacement + lastVelocity));
float displacement = (float) (Math.pow(Math.E,-mDampingRatio * mNaturalFreq * deltaT) * (lastDisplacement * Math.cos(mDampedFreq * deltaT) + coeffB * Math.sin(mDampedFreq * deltaT)));
float mValue = displacement + endVal;
Log.e("ratio",String.valueOf(displacement));
if(mDuration == 0){
return starVal;
}
else{
return mValue;
}
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/AndroidSpringInterpolator2.java
================================================
package com.martinrgb.animer.core.interpolator;
import android.animation.TimeInterpolator;
import android.util.Log;
public class AndroidSpringInterpolator2 implements TimeInterpolator {
//Parameters
private float mStiffness;
private float mDampingRatio;
private float mVelocity;
private float mDuration;
private boolean canGetDuration = true;
public AndroidSpringInterpolator2(float stiffness, float dampingratio,float velocity) {
this.mStiffness = stiffness;
this.mDampingRatio = dampingratio;
this.mVelocity = velocity/1000.f;
}
@Override
public float getInterpolation(float ratio) {
if(canGetDuration){
getDuration(ratio);
}
float starVal = 0;
float endVal = 1;
float mDeltaT = ratio * mDuration;
float lastDisplacement = ratio - endVal;
float mNaturalFreq = (float) Math.sqrt(mStiffness);
float mDampedFreq = (float)(mNaturalFreq*Math.sqrt(1.0 - mDampingRatio* mDampingRatio));
float cosCoeff = lastDisplacement;
float sinCoeff = (float) (1.0 / mDampedFreq * (mDampingRatio * mNaturalFreq * lastDisplacement + mVelocity));
float displacement = (float) (Math.pow(Math.E,-mDampingRatio * mNaturalFreq * mDeltaT) * (cosCoeff * Math.cos(mDampedFreq * mDeltaT) + sinCoeff * Math.sin(mDampedFreq * mDeltaT)));
mVelocity = (float) (displacement * (-mNaturalFreq) * mDampingRatio
+ Math.pow(Math.E, -mDampingRatio * mNaturalFreq * mDeltaT)
* (-mDampedFreq * cosCoeff * Math.sin(mDampedFreq * mDeltaT)
+ mDampedFreq * sinCoeff * Math.cos(mDampedFreq * mDeltaT)));
float mValue = displacement + endVal;
Log.e("mValue",String.valueOf(mValue));
return mValue;
}
public void setVelocityInSeconds(float velocity){
mVelocity = velocity/1000.f;
}
public void setDampingRatio(float dampingRatio){
mDampingRatio = dampingRatio;
}
private void getDuration(float ratio){
if(ratio !=0){
float oneFrameRatio = ratio - 0;
float timeInMs = (1.f/oneFrameRatio)*(1000.f/60.f);
mDuration = timeInMs/1000.f;
canGetDuration = false;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/CustomBounceInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator;
public class CustomBounceInterpolator extends AnInterpolator{
//Parameters
private static final float maxStifness = 50.f;
private static final float maxFrictionMultipler = 1.f;
private float mTension = 0.f;
private float mFriction = 0.f;
//Curve Position parameters(No Adjust)
private static final float amplitude = 1.f;
private static final float phase = 0.f;
//Original Scale parameters(Better No Adjust)
private static final float originalStiffness = 12.f;
private static final float originalFrictionMultipler = 0.3f;
private static final float mass = 0.058f;
//Internal parameters
private float pulsation;
private float friction;
private void computePulsation() {
this.pulsation = (float) Math.sqrt((originalStiffness + mTension) / mass);
}
private void computeFriction() {
this.friction = (originalFrictionMultipler + mFriction) * pulsation;
}
private void computeInternalParameters() {
// never call computeFriction() without
// updating the pulsation
computePulsation();
computeFriction();
}
public CustomBounceInterpolator( float tension, float friction) {
this.mTension = Math.min(Math.max(tension,0.f),100.f) * (maxStifness- originalStiffness)/100.f;
this.mFriction = Math.min(Math.max(friction,0.f),100.f) * (maxFrictionMultipler - originalFrictionMultipler)/100.f;
computeInternalParameters();
initArgData(0,tension,"tension",0,100);
initArgData(1,friction,"friction",0,100);
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
this.mTension = Math.min(Math.max(value,0.f),100.f) * (maxStifness- originalStiffness)/100.f;
}
if(i == 1){
this.mFriction = Math.min(Math.max(friction,0.f),100.f) * (maxFrictionMultipler - originalFrictionMultipler)/100.f;
}
computeInternalParameters();
}
public CustomBounceInterpolator() {
computeInternalParameters();
}
@Override
public float getInterpolation(float ratio) {
if (ratio == 0.0f || ratio == 1.0f)
return ratio;
else {
float value = amplitude * (float) Math.exp(-friction * ratio) *
(float) Math.cos(pulsation * ratio + phase) ;
return -Math.abs(value)+1.f;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/CustomDampingInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator;
public class CustomDampingInterpolator extends AnInterpolator{
//Parameters
private static final float maxStifness = 50.f;
private static final float maxFrictionMultipler = 1.f;
private float mTension = 0.f;
private float mFriction = 0.f;
//Curve Position parameters(No Adjust)
private static final float amplitude = 1.f;
private static final float phase = 0.f;
//Original Scale parameters(Better No Adjust)
private static final float originalStiffness = 12.f;
private static final float originalFrictionMultipler = 0.3f;
private static final float mass = 0.058f;
//Internal parameters
private float pulsation;
private float friction;
private void computePulsation() {
this.pulsation = (float) Math.sqrt((originalStiffness + mTension) / mass);
}
private void computeFriction() {
this.friction = (originalFrictionMultipler + mFriction) * pulsation;
}
private void computeInternalParameters() {
// never call computeFriction() without
// updating the pulsation
computePulsation();
computeFriction();
}
public CustomDampingInterpolator( float tension, float friction) {
this.mTension = Math.min(Math.max(tension,0.f),100.f) * (maxStifness- originalStiffness)/100.f;
this.mFriction = Math.min(Math.max(friction,0.f),100.f) * (maxFrictionMultipler - originalFrictionMultipler)/100.f;
// this.mFriction = friction;
// this.mTension = tension;
computeInternalParameters();
initArgData(0,tension,"tension",0,100);
initArgData(1,friction,"friction",0,100);
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
this.mTension = Math.min(Math.max(value,0.f),100.f) * (maxStifness- originalStiffness)/100.f;
}
if(i == 1){
this.mFriction = Math.min(Math.max(friction,0.f),100.f) * (maxFrictionMultipler - originalFrictionMultipler)/100.f;
}
computeInternalParameters();
}
public CustomDampingInterpolator() {
computeInternalParameters();
}
@Override
public float getInterpolation(float ratio) {
if (ratio == 0.0f || ratio == 1.0f)
return ratio;
else {
float value = amplitude * (float) Math.exp(-friction * ratio) *
(float) Math.cos(pulsation * ratio + phase) ;
return -value+1;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/CustomMocosSpringInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator;
public class CustomMocosSpringInterpolator extends AnInterpolator{
private double mGamma, mVDiv2;
private boolean mOscilative;
private double mEps;
private double mA, mB;
private double mDuration;
private double tension,damping,velocity;
public CustomMocosSpringInterpolator(double tension, double damping) {
this(tension, damping, 0.001);
this.setInitialVelocity(20.);
initArgData(0,(float) tension,"tension",0,200);
initArgData(1,(float) damping,"damping",0,100);
initArgData(2,(float) 20,"velocity",0,1000);
}
public CustomMocosSpringInterpolator(double tension, double damping, double velocity) {
//mEps = eps;
this.tension = tension;
this.damping = damping;
this.velocity = velocity;
init();
initArgData(0,(float) tension,"tension",0,200);
initArgData(1,(float) damping,"damping",0,100);
initArgData(2,(float) velocity,"velocity",0,1000);
}
private void init(){
mEps = 0.001;
mOscilative = (4 * this.tension - this.damping * this.damping > 0);
if (mOscilative) {
mGamma = Math.sqrt(4 * this.tension - this.damping * this.damping) / 2;
mVDiv2 = this.damping / 2;
} else {
mGamma = Math.sqrt(this.damping * this.damping - 4 * this.tension) / 2;
mVDiv2 = this.damping / 2;
}
this.setInitialVelocity(velocity);
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
this.tension = (double) value;
init();
}
if(i == 1){
this.damping = (double) value;
init();
}
if(i == 2){
this.velocity = (double) value;
init();
}
}
public void setInitialVelocity(double v0) {
if (mOscilative) {
mB = Math.atan(-mGamma / (v0 - mVDiv2));
mA = -1 / Math.sin(mB);
mDuration = Math.log(Math.abs(mA) / mEps) / mVDiv2;
} else {
mA = (v0 - (mGamma + mVDiv2)) / (2 * mGamma);
mB = -1 - mA;
mDuration = Math.log(Math.abs(mA) / mEps) / (mVDiv2 - mGamma);
}
}
public double getDesiredDuration() {
return mDuration;
}
@Override
public float getInterpolation(float input) {
if (input >= 1) {
return 1;
}
double t = input * mDuration;
return (float) (mOscilative ?
(mA * Math.exp(-mVDiv2 * t) * Math.sin(mGamma * t + mB) + 1) :
(mA * Math.exp((mGamma - mVDiv2) * t) + mB * Math.exp(-(mGamma + mVDiv2) * t) + 1));
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/CustomSpringInterpolator.java
================================================
package com.martinrgb.animer.core.interpolator;
public class CustomSpringInterpolator extends AnInterpolator{
private float factor = 0.5f;
public CustomSpringInterpolator(float factor) {
this.factor = factor;
initArgData(0,(float) factor,"factor",0,10);
}
public CustomSpringInterpolator() {
initArgData(0,(float) 0.5,"factor",0,10);
}
@Override
public float getInterpolation(float ratio) {
if (ratio == 0.0f || ratio == 1.0f)
return ratio;
else {
float value = (float) (Math.pow(2, -10 * ratio) * Math.sin((ratio - factor / 4.0d) * (2.0d * Math.PI) / factor) + 1);
return value;
}
}
@Override
public void resetArgValue(int i, float value){
setArgValue(i,value);
if(i == 0){
factor = value;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/interpolator/FlingSpringAnim.java
================================================
/*
* Copyright (C) 2019 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 com.android.launcher3.anim;
import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
import androidx.dynamicanimation.animation.FlingAnimation;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
/**
* Given a property to animate and a target value and starting velocity, first apply friction to
* the fling until we pass the target, then apply a spring force to pull towards the target.
*/
public class FlingSpringAnim {
private static final float FLING_FRICTION = 1.5f;
private static final float SPRING_STIFFNESS = 400f;
private static final float SPRING_DAMPING = 0.8f;
private final FlingAnimation mFlingAnim;
private SpringAnimation mSpringAnim;
private float mTargetPosition;
public FlingSpringAnim(K object, FloatPropertyCompat property, float startPosition,
float targetPosition, float startVelocity, float minVisChange, float minValue,
float maxValue, float springVelocityFactor, OnAnimationEndListener onEndListener) {
mFlingAnim = new FlingAnimation(object, property)
.setFriction(FLING_FRICTION)
// Have the spring pull towards the target if we've slowed down too much before
// reaching it.
.setMinimumVisibleChange(minVisChange)
.setStartVelocity(startVelocity)
.setMinValue(minValue)
.setMaxValue(maxValue);
mTargetPosition = targetPosition;
mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
mSpringAnim = new SpringAnimation(object, property)
.setStartValue(value)
.setStartVelocity(velocity * springVelocityFactor)
.setSpring(new SpringForce(mTargetPosition)
.setStiffness(SPRING_STIFFNESS)
.setDampingRatio(SPRING_DAMPING));
mSpringAnim.addEndListener(onEndListener);
mSpringAnim.animateToFinalPosition(mTargetPosition);
}));
}
public float getTargetPosition() {
return mTargetPosition;
}
public void updatePosition(float startPosition, float targetPosition) {
mFlingAnim.setMinValue(Math.min(startPosition, targetPosition))
.setMaxValue(Math.max(startPosition, targetPosition));
mTargetPosition = targetPosition;
if (mSpringAnim != null) {
mSpringAnim.animateToFinalPosition(mTargetPosition);
}
}
public void start() {
mFlingAnim.start();
}
public void end() {
mFlingAnim.cancel();
if (mSpringAnim.canSkipToEnd()) {
mSpringAnim.skipToEnd();
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/math/calculator/FlingCalculator.java
================================================
package com.martinrgb.animer.core.math.calculator;
public class FlingCalculator {
private float mFriction;
private float mVelocity;
private float mDuration;
private float mTransiton;
public FlingCalculator(float velocity,float friction) {
mFriction = friction*-4.2f;
mVelocity = velocity;
mDuration = calculate()[0];
mTransiton = calculate()[1];
}
private float[] calculate() {
float sampleScale = 1.5f;
float maxItertation = 0;
float maxValue = 0;
float sampeScale = 1.5f;
for (float i = 1 / (60 * sampleScale); i < 20.; i += 1 / (60 * sampleScale)) {
float currentVelocity = mVelocity * (float) Math.exp(i * mFriction);
float currentTransition = (mVelocity / mFriction) * (float) (Math.exp(mFriction * i) - 1);
float speedThereshold = 2.3f;
if (Math.abs(currentVelocity) <= speedThereshold) {
maxItertation = i;
maxValue = (currentTransition);
}
else{
}
}
return new float[]{maxItertation, maxValue};
}
public float getDuration() {
return mDuration;
}
public float getTransiton() {
return mTransiton;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/math/calculator/SpringInterpolatorCalculator.java
================================================
package com.martinrgb.animer.core.math.calculator;
public class SpringInterpolatorCalculator{
private double mStiffness,mMass,mDamping,mFactor,mTime;
private float mEpsilon,mDuration,mVelocity;
public SpringInterpolatorCalculator(double stiffness,double dampingratio) {
mStiffness = stiffness;
mMass = 1;
mDamping = computeDamping(stiffness,dampingratio,mMass);
mVelocity = 0;
mEpsilon = 1/1000f;
}
public double computeDamping(double stiffness,double dampingRatio,double mass){
//double mass = mMass;
return dampingRatio * (2 * Math.sqrt(mass * stiffness));
}
private double computeMaxValue() {
float time = 0;
float value = 0;
float velocity = mVelocity;
float maxValue = 0;
while (!(time > 0 && Math.abs(velocity) < mEpsilon)) {
time += mEpsilon;
float k = 0 - (float) mStiffness;
float b = 0 - (float) mDamping;
float F_spring = k * ((value) - 1);
float F_damper = b * (velocity);
velocity += ((F_spring + F_damper) / mMass) * mEpsilon;
value += velocity * mEpsilon;
if (maxValue < value) {
maxValue = value;
}
}
mDuration = time;
return maxValue;
}
private double computeSpringMax(double factor) {
double maxValue = 0;
double epsilon = mEpsilon;
double count = 1 / epsilon;
for (int i = 0; i < count; i++) {
double x = i * mEpsilon;
double result = Math.pow(2, -10 * x) * Math.sin((x - factor / 4) * (2 * Math.PI) / factor) + 1;
if (maxValue < result) {
maxValue = result;
}
}
return maxValue;
}
private double findCloseNum(double num) {
int arraySize= (int) (1/mEpsilon);
double[] arr = new double[arraySize];
for (int i = 0; i < 1/mEpsilon - 1; i++) {
arr[i] = this.computeSpringMax(i * mEpsilon);
}
int index = 0;
double d_value = 10;
for (int i = 0; i < arr.length; i++) {
double new_d_value = Math.abs(arr[i] - num);
if (new_d_value <= d_value) {
if (new_d_value == d_value && arr[i] < arr[index]) {
continue;
}
index = i;
d_value = new_d_value;
}
}
return index / (1/mEpsilon);
}
public float getFactor(){
return (float) findCloseNum(computeMaxValue());
}
public float getDuration(){
return mDuration;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/math/converter/AnSpringConverter.java
================================================
package com.martinrgb.animer.core.math.converter;
abstract class AnSpringConverter {
public double mMass = 1;
public double mStiffness;
public double mDamping;
public double mDampingRatio;
public double mTension;
public double mFriction;
public double mBouncyTension;
public double mBouncyFriction;
public double mDuration;
public double mS;
public double mB;
public double mBounciness;
public double mSpeed;
public double mVelocity;
public AnSpringConverter() {
}
public double computeDamping(double stiffness,double dampingRatio,double mass){
//double mass = mMass;
return dampingRatio * (2 * Math.sqrt(mass * stiffness));
}
public double computeDampingRatio(double tension,double friction,double mass) {
//double mass = mMass;
return friction / (2 * Math.sqrt(mass * tension));
}
public double computeDuration(double tension, double friction,double mass) {
double epsilon = 0.001;
double velocity = 0.0;
//double mass = mMass;
double dampingRatio = this.computeDampingRatio(tension, friction,mass);
double undampedFrequency = Math.sqrt(tension / mass);
if (dampingRatio < 1) {
double a = Math.sqrt(1 - Math.pow(dampingRatio, 2));
double b = velocity / (a * undampedFrequency);
double c = dampingRatio / a;
double d = -((b - c) / epsilon);
if (d <= 0) {
return 0.0;
}
return Math.log(d) / (dampingRatio * undampedFrequency);
} else {
return 0.0;
}
}
public double computeTension(double dampingratio,double duration,double mass) {
//let mass = this.mass;
double a = Math.sqrt(1 - Math.pow(dampingratio, 2));
double d = (dampingratio/a)*1000.;
double tension = Math.pow(Math.log(d)/(dampingratio * duration),2)*mass;
return tension;
}
public double computeFriction(double dampingratio,double tension,double mass){
//let mass = this.mass;
double a = (2 * Math.sqrt(mass * tension));
double friction = dampingratio * a;
return friction;
}
public double tensionConversion(double oValue) {
return (oValue - 30.0) * 3.62 + 194.0;
}
public double frictionConversion(double oValue) {
return (oValue - 8.0) * 3.0 + 25.0;
}
public double bouncyTesnionConversion(double tension){
return (tension - 194.0)/3.62 + 30.;
}
public double bouncyFrictionConversion(double friction){
return (friction - 25.)/3. + 8.;
}
public double getParaS(double n,double start,double end){
return (n - start)/(end - start);
}
public double getParaB(double finalVal, double start,double end) {
double a = 1;
double b = -2;
double c = (finalVal - start)/(end-start);
double root_part = Math.sqrt(b * b - 4 * a * c);
double denom = 2 * a;
double root1 = ( -b + root_part ) / denom;
double root2 = ( -b - root_part ) / denom;
if(root2 <0) return root1;
if(root1 <0) return root2;
return Math.min(root1,root2);
}
public double computeSpeed(double value,double startValue,double endValue){
return (value * (endValue - startValue) + startValue)*1.7 ;
}
public double normalize(double value, double startValue, double endValue) {
return (value - startValue) / (endValue - startValue);
}
public double projectNormal(double n, double start, double end) {
return start + (n * (end - start));
}
public double linearInterpolation(double t, double start, double end) {
return t * end + (1.0 - t) * start;
}
public double quadraticOutInterpolation(double t, double start, double end) {
return linearInterpolation(2 * t - t * t, start, end);
}
public double b3Friction1(double x) {
return (0.0007 * Math.pow(x, 3)) -
(0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28;
}
public double b3Friction2(double x) {
return (0.000044 * Math.pow(x, 3)) -
(0.006 * Math.pow(x, 2)) + 0.36 * x + 2.;
}
public double b3Friction3(double x) {
return (0.00000045 * Math.pow(x, 3)) -
(0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84;
}
public double b3Nobounce(double tension) {
double friction = 0;
if (tension <= 18) {
friction = this.b3Friction1(tension);
} else if (tension > 18 && tension <= 44) {
friction = this.b3Friction2(tension);
} else {
friction = this.b3Friction3(tension);
}
return friction;
}
public double computeDuration(double stiffness, double dampingratio) {
double epsilon = 0.001;
double velocity = 0.0;
double mass = 1;
double dampingRatio = dampingratio;
double undampedFrequency = Math.sqrt(stiffness / mass);
if (dampingRatio < 1) {
double a = Math.sqrt(1 - Math.pow(dampingRatio, 2));
double b = velocity / (a * undampedFrequency);
double c = dampingRatio / a;
double d = -((b - c) / epsilon);
if (d <= 0) {
return 0.0;
}
return Math.log(d) / (dampingRatio * undampedFrequency);
} else {
return 0.0;
}
}
public float getStiffness() {
return (float) mStiffness;
}
public float getDampingRatio() {
return (float) mDampingRatio;
}
public float getDuration(){
return (float) computeDuration(mStiffness,mDampingRatio);
}
public float getArg(int i) {
if(i == 0){
return (float) mStiffness;
}
else if(i == 1){
return (float) mDampingRatio;
}
return -1;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/math/converter/AndroidSpringConverter.java
================================================
package com.martinrgb.animer.core.math.converter;
public class AndroidSpringConverter extends AnSpringConverter {
private boolean otherParaCalculation = false;
public AndroidSpringConverter(double stiffness,double dampingratio) {
super();
calculate(stiffness,dampingratio,1,0);
}
public AndroidSpringConverter(double stiffness,double dampingratio,double mass,double velocity) {
super();
calculate(stiffness,dampingratio,mass,velocity);
}
private void calculate(double s,double d,double m,double v){
mStiffness = s;
mDampingRatio = d;
mMass = m;
mVelocity = v;
mDamping = this.computeDamping(mStiffness, mDampingRatio,mMass);
mTension = mStiffness;
mFriction = mDamping;
if(otherParaCalculation){
mBouncyTension = this.bouncyTesnionConversion(mTension);
mBouncyFriction = this.bouncyFrictionConversion(mFriction);
mDuration = this.computeDuration(mTension, mFriction,mMass);
mS = this.getParaS(mBouncyTension,0.5,200);
mSpeed = this.computeSpeed(this.getParaS(mBouncyTension,0.5,200),0.,20.);
mB = this.getParaB(mBouncyFriction,this.b3Nobounce(mBouncyTension), 0.01);
mBounciness = 20*1.7*mB/0.8;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/math/converter/DHOConverter.java
================================================
package com.martinrgb.animer.core.math.converter;
public class DHOConverter extends AnSpringConverter {
private boolean otherParaCalculation = false;
public DHOConverter(double stiffness,double damping) {
super();
calculate(stiffness,damping,1,0);
}
public DHOConverter(double stiffness,double damping,double mass,double velocity) {
super();
calculate(stiffness,damping,mass,velocity);
}
private void calculate(double s,double d,double m,double v){
mStiffness = s;
mDamping = d;
mMass = m;
mVelocity = v;
mTension = mStiffness;
mFriction = mDamping;
mDampingRatio = this.computeDampingRatio(mStiffness, mDamping,mMass);
if(otherParaCalculation){
mBouncyTension = this.bouncyTesnionConversion(mTension);
mBouncyFriction = this.bouncyFrictionConversion(mFriction);
mDuration = this.computeDuration(mTension, mFriction,mMass);
mS = this.getParaS(mBouncyTension,0.5,200);
mSpeed = this.computeSpeed(this.getParaS(mBouncyTension,0.5,200),0.,20.);
mB = this.getParaB(mBouncyFriction,this.b3Nobounce(mBouncyTension), 0.01);
mBounciness = 20*1.7*mB/0.8;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/math/converter/OrigamiPOPConverter.java
================================================
package com.martinrgb.animer.core.math.converter;
public class OrigamiPOPConverter extends AnSpringConverter {
private boolean otherParaCalculation = false;
public OrigamiPOPConverter(double bounciness,double speed) {
super();
calculate(bounciness,speed,1,0);
}
public OrigamiPOPConverter(double bounciness,double speed,double mass,double velocity) {
super();
calculate(bounciness,speed,mass,velocity);
}
private void calculate(double b,double s,double m,double v){
mBounciness = b;
mSpeed = s;
mMass = m;
mVelocity = v;
mB = this.normalize(mBounciness / 1.7, 0, 20.0);
mB = this.projectNormal(mB, 0.0, 0.8);
mS = this.normalize(mSpeed / 1.7, 0, 20.0);
mBouncyTension = this.projectNormal(mS, 0.5, 200);
mBouncyFriction = this.quadraticOutInterpolation(mB, this.b3Nobounce(this.mBouncyTension), 0.01);
mTension = this.tensionConversion(mBouncyTension);
mFriction = this.frictionConversion(mBouncyFriction);
mStiffness = mTension;
mDamping = mFriction;
mDampingRatio = this.computeDampingRatio(mTension, mFriction,mMass);
if(otherParaCalculation){
mDuration = this.computeDuration(mTension, mFriction,mMass);
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/math/converter/RK4Converter.java
================================================
package com.martinrgb.animer.core.math.converter;
public class RK4Converter extends AnSpringConverter {
private boolean otherParaCalculation = false;
public RK4Converter(double tension,double friction) {
super();
calculate(tension,friction,1,0);
}
public RK4Converter(double tension,double friction,double mass,double velocity) {
super();
calculate(tension,friction,mass,velocity);
}
private void calculate(double t,double f,double m,double v){
mStiffness = t;
mDamping = f;
mMass = m;
mVelocity = v;
mTension = mStiffness;
mFriction = mDamping;
mDampingRatio = this.computeDampingRatio(mStiffness, mDamping,mMass);
if(otherParaCalculation){
mBouncyTension = this.bouncyTesnionConversion(mTension);
mBouncyFriction = this.bouncyFrictionConversion(mFriction);
mDuration = this.computeDuration(mTension, mFriction,mMass);
mS = this.getParaS(mBouncyTension,0.5,200);
mSpeed = this.computeSpeed(this.getParaS(mBouncyTension,0.5,200),0.,20.);
mB = this.getParaB(mBouncyFriction,this.b3Nobounce(mBouncyTension), 0.01);
mBounciness = 20*1.7*mB/0.8;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/math/converter/UIViewSpringConverter.java
================================================
package com.martinrgb.animer.core.math.converter;
public class UIViewSpringConverter extends AnSpringConverter {
private boolean otherParaCalculation = false;
public UIViewSpringConverter(double dampingratio,double duration) {
super();
calculate(dampingratio,duration,1,0);
}
public UIViewSpringConverter(double dampingratio,double duration,double mass,double velocity) {
super();
calculate(dampingratio,duration,mass,velocity);
}
private void calculate(double dampingRatio,double duration,double m,double v){
mDampingRatio = dampingRatio;
mDuration = duration;
mMass = m;
mVelocity = v;
mTension = this.computeTension(mDampingRatio,mDuration,mMass);
mStiffness = mTension;
if(otherParaCalculation){
mFriction = this.computeFriction(mDampingRatio,mTension,mMass);
mDamping = mFriction;
mBouncyTension = this.bouncyTesnionConversion(mTension);
mBouncyFriction = this.bouncyFrictionConversion(mFriction);
mS = this.getParaS(mBouncyTension,0.5,200);
mSpeed = this.computeSpeed(this.getParaS(mBouncyTension,0.5,200),0.,20.);
mB = this.getParaB(mBouncyFriction,this.b3Nobounce(mBouncyTension), 0.01);
mBounciness = 20*1.7*mB/0.8;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/property/AnProperty.java
================================================
package com.martinrgb.animer.core.property;
public abstract class AnProperty {
final String mPropertyName;
public AnProperty(String name) {
mPropertyName = name;
}
public abstract float getValue(T object);
public abstract void setValue(T object, float value);
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/solver/AnSolver.java
================================================
package com.martinrgb.animer.core.solver;
import android.animation.TimeInterpolator;
import android.util.Log;
import android.view.animation.LinearInterpolator;
public class AnSolver extends Object{
// ############################################
// Construct
// ############################################
private Object arg1,arg2;
private SolverListener mListener = null;
private int SOLVER_MODE = -1;
public AnSolver(Object val1,Object val2,int mode){
unBindSolverListener();
setSolverMode(mode);
setArg1(val1);
setArg2(val2);
}
public interface SolverListener {
void onSolverUpdate(Object arg1, Object arg2);
}
public void bindSolverListener(SolverListener listener) {
mListener = listener;
}
public void unBindSolverListener(){
if(mListener !=null){
mListener = null;
}
}
public void setArg1(Object val){
arg1 = val;
if(mListener !=null){
mListener.onSolverUpdate(arg1,arg2);
}
}
public Object getArg1(){
return arg1;
}
public void setArg2(Object val){
arg2 = val;
if(mListener !=null){
mListener.onSolverUpdate(arg1,arg2);
}
}
public Object getArg2(){
return arg2;
}
public int getSolverMode() {
return SOLVER_MODE;
}
public void setSolverMode(int solverMode) {
if(getSolverMode() != solverMode){
unBindSolverListener();
SOLVER_MODE = solverMode;
}
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/state/PhysicsState.java
================================================
package com.martinrgb.animer.core.state;
import android.util.Log;
import java.util.HashMap;
import java.util.Map;
public class PhysicsState {
private float value;
private float velocity;
Map kvMap = new HashMap<>();
// ############################################
// Constructor
// ############################################
public PhysicsState() {
updatePhysics(0,0);
setStateValue("Start",0);
setStateValue("End",1);
}
public PhysicsState(float start) {
updatePhysics(start,0);
setStateValue("Start",start);
setStateValue("End",0);
}
public PhysicsState(float start,float end) {
updatePhysics(start,0);
setStateValue("Start",start);
setStateValue("End",end);
}
// ############################################
// PhysicsState Value's Getter & Setter
// ############################################
public void updatePhysics(float val,float vel){
updatePhysicsValue(val);
updatePhysicsVelocity(vel);
}
public void updatePhysicsVelocity(float vel){
velocity = vel;
}
public void updatePhysicsValue(float val){
value = val;
}
public float getPhysicsValue() {
return value;
}
public float getPhysicsVelocity() {
return velocity;
}
// ############################################
// PhysicsState State's Getter & Setter
// ############################################
public void setStateValue(String key,float value){
kvMap.put(key,value);
}
public float getStateValue(String key){
try {
return kvMap.get(key);
} catch (Exception e) {
e.printStackTrace();
Log.e("setStateValue first", Log.getStackTraceString(e));
}
return -1;
}
//TODO: Prev State
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/util/AnSpringOscillateHelper.java
================================================
package com.martinrgb.animer.core.util;
import android.os.SystemClock;
import androidx.dynamicanimation.animation.SpringAnimation;
//Examples
//
//final AnSpringOscillateHelper anSpringOscillateHelper = new AnSpringOscillateHelper(AnSpringOscillateHelper.TIME,500);
//final AnSpringOscillateHelper anSpringOscillateHelper = new AnSpringOscillateHelper(AnSpringOscillateHelper.COUNT,4);
//
//springAnimation.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
// @Override
// public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
// anSpringOscillateHelper.observe(springAnimation_2,velocity,value,startValue,endValue);
// }
//});
//
//springAnimation.addEndListener(new DynamicAnimation.OnAnimationEndListener() {
// @Override
// public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, float velocity) {
// anSpringOscillateHelper.reset();
// }
//});
// for rapid stop spring animation
public class AnSpringOscillateHelper {
public abstract static class OscillateLimitedMode{ }
public static final OscillateLimitedMode TIME = new OscillateLimitedMode() {};
public static final OscillateLimitedMode COUNT = new OscillateLimitedMode() {};
private OscillateLimitedMode mLimitedMode;
private int mOscillateCounter = 0;
private int mLimitedCounts ;
private long mOscillateTimer = 0;
private long mLimitedTime ;
private boolean mShouldTriggerOnce = true;
private float prevStiffness,prevDampingRatio;
private static final float ACCELERATION_STIFFNESS = 3000f;
private static final float ACCELERATION_DAMPINGRATIO = 0.99f;
public AnSpringOscillateHelper(OscillateLimitedMode oscillateLimitedMode,int value){
if(oscillateLimitedMode == TIME){
setLimitedMode(TIME);
setLimitedTime(value);
}
else if(oscillateLimitedMode == COUNT){
setLimitedMode(COUNT);
setLimitedCounts(value);
}
}
public void observe(SpringAnimation springAnimation,float currentVelocity,float currentValue,float startValue,float endValue){
if(mShouldTriggerOnce){
mOscillateTimer = SystemClock.elapsedRealtime();
resetAccelerationAnimation(springAnimation);
mShouldTriggerOnce = false;
}
if(getLimitedMode() == TIME){
float animationElapsedTime = SystemClock.elapsedRealtime() - mOscillateTimer;
if(animationElapsedTime > getLimitedTime()){
springAnimation.getSpring().setStiffness(ACCELERATION_STIFFNESS);
springAnimation.getSpring().setDampingRatio(ACCELERATION_DAMPINGRATIO);
}
}
if(getLimitedMode() == COUNT){
float finalPos = springAnimation.getSpring().getFinalPosition();
// from -> to
if(finalPos == endValue){
if(mOscillateCounter %2==0 && currentValue < finalPos){
mOscillateCounter++;
}
if(mOscillateCounter %2!=0 && currentValue > finalPos){
mOscillateCounter++;
}
if((mOscillateCounter +1)/2 == getLimitedCounts() && currentValue > finalPos){
springAnimation.getSpring().setStiffness(ACCELERATION_STIFFNESS);
springAnimation.getSpring().setDampingRatio(ACCELERATION_DAMPINGRATIO);
}
}
// to -> from
if(finalPos == startValue){
if(mOscillateCounter %2==0 && currentValue > finalPos){
mOscillateCounter++;
}
if(mOscillateCounter %2!=0 && currentValue < finalPos){
mOscillateCounter++;
}
if((mOscillateCounter +1)/2 == getLimitedCounts() && currentValue < finalPos){
springAnimation.getSpring().setStiffness(ACCELERATION_STIFFNESS);
springAnimation.getSpring().setDampingRatio(ACCELERATION_DAMPINGRATIO);
}
}
}
}
public void reset(){
mOscillateCounter = 0;
mShouldTriggerOnce = true;
}
private void resetAccelerationAnimation(SpringAnimation springAnimation){
if(springAnimation.getSpring().getStiffness() == ACCELERATION_STIFFNESS){
springAnimation.getSpring().setStiffness(prevStiffness);
}
else{
prevStiffness = springAnimation.getSpring().getStiffness();
}
if(springAnimation.getSpring().getDampingRatio() == ACCELERATION_DAMPINGRATIO){
springAnimation.getSpring().setDampingRatio(prevDampingRatio);
}
else{
prevDampingRatio = springAnimation.getSpring().getDampingRatio();
}
}
// getter & setter
public void setLimitedCounts(int counts) {
this.mLimitedCounts = counts;
}
public float getLimitedCounts(){return this.mLimitedCounts; }
public void setLimitedTime(long time) {
this.mLimitedTime = time;
}
public long getLimitedTime(){return this.mLimitedTime; }
private void setLimitedMode(OscillateLimitedMode oscillateLimitMode) {
this.mLimitedMode = oscillateLimitMode;
}
private OscillateLimitedMode getLimitedMode() {
return this.mLimitedMode;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/core/util/AnUtil.java
================================================
package com.martinrgb.animer.core.util;
// From Facebook Rebound
public class AnUtil {
/**
* Map a value within a given range to another range.
* @param value the value to map
* @param fromLow the low end of the range the value is within
* @param fromHigh the high end of the range the value is within
* @param toLow the low end of the range to map to
* @param toHigh the high end of the range to map to
* @return the mapped value
*/
public static float mapValueFromRangeToRange(
float value,
float fromLow,
float fromHigh,
float toLow,
float toHigh) {
float fromRangeSize = fromHigh - fromLow;
float toRangeSize = toHigh - toLow;
float valueScale = (value - fromLow) / fromRangeSize;
return toLow + (valueScale * toRangeSize);
}
public static float mapClampedValueFromRangeToRange(
float value,
float fromLow,
float fromHigh,
float toLow,
float toHigh) {
float fromRangeSize = fromHigh - fromLow;
float toRangeSize = toHigh - toLow;
float valueScale = (value - fromLow) / fromRangeSize;
return toLow + (Math.max(0,Math.min(1,valueScale)) * toRangeSize);
}
/**
* Clamp a value to be within the provided range.
* @param value the value to clamp
* @param low the low end of the range
* @param high the high end of the range
* @return the clamped value
*/
public static double clamp(double value, double low, double high) {
return Math.min(Math.max(value, low), high);
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/AnConfigData.java
================================================
package com.martinrgb.animer.monitor;
import android.util.Log;
import java.util.LinkedHashMap;
public class AnConfigData {
private int DATA_MODE = 0;
LinkedHashMap configMap = new LinkedHashMap();
public AnConfigData(Object o1,Object o2,int mode) {
setMode(mode);
addConfig("arg1",o1);
addConfig("arg2",o2);
}
public AnConfigData(Object o1,Object o2) {
addConfig("arg1",o1);
addConfig("arg2",o2);
}
public void setArguments(String type,String arg1,Object arg1_min,Object arg1_max,String arg2,Object arg2_min,Object arg2_max){
addConfig("converter_type",type);
addConfig("arg1_name",arg1);
addConfig("arg1_min",arg1_min);
addConfig("arg1_max",arg1_max);
addConfig("arg2_name",arg2);
addConfig("arg2_min",arg2_min);
addConfig("arg2_max",arg2_max);
}
// ############################################
// PhysicsState State's Getter & Setter
// ############################################
public void addConfig(String key, Object value){
configMap.put(key,value);
}
public LinkedHashMap getConfigs(){
return configMap;
}
public void clearConfigs(){
configMap.clear();
}
public void cloneConfigFrom(LinkedHashMap targetMap){
if(targetMap instanceof LinkedHashMap){
configMap.clear();
configMap = (LinkedHashMap) targetMap.clone();
}
else{
}
}
public Object getKeyByString(String key){
try {
return configMap.get(key);
} catch (Exception e) {
e.printStackTrace();
Log.e("setStateValue first", Log.getStackTraceString(e));
}
return -1;
}
// TODO:Cannot use this,only String works
public Object[] getConfigByIndex(int index){
Object key = configMap.keySet().toArray()[index];
Object value = configMap.get(key);
Log.e("index: ",String.valueOf(index));
Log.e("key: ",String.valueOf(key));
Log.e("value: ",String.valueOf(value));
//return valueForFirstKey;
return new Object[]{key,value};
}
public int getMode() {
return DATA_MODE;
}
public void setMode(int mode) {
DATA_MODE = mode;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/AnConfigMap.java
================================================
package com.martinrgb.animer.monitor;
import java.util.LinkedHashMap;
public class AnConfigMap extends LinkedHashMap {
private AnConfigMap mLinkedHashMap;
public AnConfigMap() {
mLinkedHashMap = this;
}
public Object getValue(int index){
Object key = mLinkedHashMap.keySet().toArray()[index];
Object value = mLinkedHashMap.get(key);
return value;
}
public Object getKey(int index){
Object key = mLinkedHashMap.keySet().toArray()[index];
return key;
}
public void resetIndex(int index,Object value){
Object key = mLinkedHashMap.keySet().toArray()[index];
mLinkedHashMap.replace(key,value);
}
public int getIndexByString(String string){
for(int i = 0;i< mLinkedHashMap.size();i++){
Object key = mLinkedHashMap.keySet().toArray()[i];
Object value = mLinkedHashMap.get(key);
if(string.equals(key.toString())){
return i;
}
}
return -1;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/AnConfigRegistry.java
================================================
package com.martinrgb.animer.monitor;
import com.martinrgb.animer.Animer;
import com.martinrgb.animer.core.interpolator.AndroidNative.AccelerateDecelerateInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.AccelerateInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.AnticipateInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.AnticipateOvershootInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.BounceInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.FastOutLinearInInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.FastOutSlowInInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.LinearInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.LinearOutSlowInInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.PathInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidSpringInterpolator;
import com.martinrgb.animer.core.interpolator.CustomBounceInterpolator;
import com.martinrgb.animer.core.interpolator.CustomDampingInterpolator;
import com.martinrgb.animer.core.interpolator.CustomMocosSpringInterpolator;
import com.martinrgb.animer.core.interpolator.CustomSpringInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.CycleInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.DecelerateInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.OvershootInterpolator;
public class AnConfigRegistry {
private static final AnConfigRegistry INSTANCE = new AnConfigRegistry();
public static AnConfigRegistry getInstance() {
return INSTANCE;
}
private final AnConfigMap mAnimerMap;
private AnConfigMap mSolverMap;
AnConfigRegistry() {
mAnimerMap = new AnConfigMap();
initSolverConfig();
}
private void initSolverConfig(){
mSolverMap= new AnConfigMap();
mSolverMap.put("AndroidSpring",Animer.springDroid(1500,0.5f));
mSolverMap.put("AndroidFling",Animer.flingDroid(4000,0.8f));
mSolverMap.put("iOSUIViewSpring",Animer.springiOSUIView(0.5f,0.5f));
mSolverMap.put("iOSCoreAnimationSpring",Animer.springiOSCoreAnimation(100,10));
mSolverMap.put("OrigamiPOPSpring",Animer.springOrigamiPOP(5,10));
mSolverMap.put("RK4Spring",Animer.springRK4(200,25));
mSolverMap.put("DHOSpring",Animer.springDHO(50,2f));
mSolverMap.put("ProtopieSpring",Animer.springProtopie(300,15f));
mSolverMap.put("PrincipleSpring",Animer.springPrinciple(380,20f));
mSolverMap.put("CubicBezier",Animer.interpolatorDroid(new PathInterpolator(0.5f,0.5f,0.5f,0.5f),500));
mSolverMap.put("LinearInterpolator",Animer.interpolatorDroid(new LinearInterpolator(),500));
mSolverMap.put("AccelerateDecelerateInterpolator",Animer.interpolatorDroid(new AccelerateDecelerateInterpolator(),500));
mSolverMap.put("AccelerateInterpolator",Animer.interpolatorDroid(new AccelerateInterpolator(2),500));
mSolverMap.put("DecelerateInterpolator",Animer.interpolatorDroid(new DecelerateInterpolator(2),500));
mSolverMap.put("AnticipateInterpolator",Animer.interpolatorDroid(new AnticipateInterpolator(2),500));
mSolverMap.put("OvershootInterpolator",Animer.interpolatorDroid(new OvershootInterpolator(2),500));
mSolverMap.put("AnticipateOvershootInterpolator",Animer.interpolatorDroid(new AnticipateOvershootInterpolator(2),500));
mSolverMap.put("BounceInterpolator",Animer.interpolatorDroid(new BounceInterpolator(),500));
mSolverMap.put("CycleInterpolator",Animer.interpolatorDroid(new CycleInterpolator(2),500));
mSolverMap.put("FastOutSlowInInterpolator",Animer.interpolatorDroid(new FastOutSlowInInterpolator(),500));
mSolverMap.put("LinearOutSlowInInterpolator",Animer.interpolatorDroid(new LinearOutSlowInInterpolator(),500));
mSolverMap.put("FastOutLinearInInterpolator",Animer.interpolatorDroid(new FastOutLinearInInterpolator(),500));
mSolverMap.put("CustomMocosSpringInterpolator",Animer.interpolatorDroid(new CustomMocosSpringInterpolator(100,15,0),500));
mSolverMap.put("CustomSpringInterpolator",Animer.interpolatorDroid(new CustomSpringInterpolator(0.5f),500));
mSolverMap.put("CustomBounceInterpolator",Animer.interpolatorDroid(new CustomBounceInterpolator(0,0),500));
mSolverMap.put("CustomDampingInterpolator",Animer.interpolatorDroid(new CustomDampingInterpolator(0,0),500));
mSolverMap.put("AndroidSpringInterpolator",Animer.interpolatorDroid(new AndroidSpringInterpolator(1500,0.5f,500),500));
}
public void removeAllSolverConfig(){
mSolverMap.clear();
}
public boolean addSolver(String configName, Animer.AnimerSolver animerSolver) {
if (animerSolver == null) {
throw new IllegalArgumentException("animerSolver is required");
}
if (configName == null) {
throw new IllegalArgumentException("configName is required");
}
if (mSolverMap.containsKey(animerSolver)) {
return false;
}
mSolverMap.put(configName,animerSolver);
return true;
}
public boolean addAnimer(String configName,Animer animer) {
if (animer == null) {
throw new IllegalArgumentException("animer is required");
}
if (configName == null) {
throw new IllegalArgumentException("configName is required");
}
if (mAnimerMap.containsKey(animer)) {
return false;
}
mAnimerMap.put(configName,animer);
return true;
}
public boolean removeAnimerConfig(Animer animer) {
if (animer == null) {
throw new IllegalArgumentException("animer is required");
}
return mAnimerMap.remove(animer) != null;
}
public void removeAllAnimerConfig() {
mAnimerMap.clear();
}
public AnConfigMap getAllAnimer() {
return mAnimerMap;
}
public AnConfigMap getAllSolverTypes(){
// AnConfigMap map = new AnConfigMap();
// map.put("AndroidSpring",Animer.springDroid(1500,0.5f));
// map.put("AndroidFling",Animer.flingDroid(4000,0.8f));
// map.put("iOSUIViewSpring",Animer.springiOSUIView(0.5f,0.5f));
// map.put("iOSCoreAnimationSpring",Animer.springiOSCoreAnimation(100,10));
// map.put("OrigamiPOPSpring",Animer.springOrigamiPOP(5,10));
// map.put("RK4Spring",Animer.springRK4(200,25));
// map.put("DHOSpring",Animer.springDHO(50,2f));
// map.put("ProtopieSpring",Animer.springProtopie(300,15f));
// map.put("PrincipleSpring",Animer.springPrinciple(380,20f));
// map.put("CubicBezier",Animer.interpolatorDroid(new PathInterpolator(0.5f,0.5f,0.5f,0.5f),500));
// map.put("LinearInterpolator",Animer.interpolatorDroid(new LinearInterpolator(),500));
// map.put("AccelerateDecelerateInterpolator",Animer.interpolatorDroid(new AccelerateDecelerateInterpolator(),500));
// map.put("AccelerateInterpolator",Animer.interpolatorDroid(new AccelerateInterpolator(2),500));
// map.put("DecelerateInterpolator",Animer.interpolatorDroid(new DecelerateInterpolator(2),500));
// map.put("AnticipateInterpolator",Animer.interpolatorDroid(new AnticipateInterpolator(2),500));
// map.put("OvershootInterpolator",Animer.interpolatorDroid(new OvershootInterpolator(2),500));
// map.put("AnticipateOvershootInterpolator",Animer.interpolatorDroid(new AnticipateOvershootInterpolator(2),500));
// map.put("BounceInterpolator",Animer.interpolatorDroid(new BounceInterpolator(),500));
// map.put("CycleInterpolator",Animer.interpolatorDroid(new CycleInterpolator(2),500));
// map.put("FastOutSlowInInterpolator",Animer.interpolatorDroid(new FastOutSlowInInterpolator(),500));
// map.put("LinearOutSlowInInterpolator",Animer.interpolatorDroid(new LinearOutSlowInInterpolator(),500));
// map.put("FastOutLinearInInterpolator",Animer.interpolatorDroid(new FastOutLinearInInterpolator(),500));
// map.put("CustomMocosSpringInterpolator",Animer.interpolatorDroid(new CustomMocosSpringInterpolator(100,15,0),500));
// map.put("CustomSpringInterpolator",Animer.interpolatorDroid(new CustomSpringInterpolator(0.5f),500));
// map.put("CustomBounceInterpolator",Animer.interpolatorDroid(new CustomBounceInterpolator(0,0),500));
// map.put("CustomDampingInterpolator",Animer.interpolatorDroid(new CustomDampingInterpolator(0,0),500));
// map.put("AndroidSpringInterpolator",Animer.interpolatorDroid(new AndroidSpringInterpolator(1500,0.5f,500),500));
// return map;
return mSolverMap;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/AnConfigView.java
================================================
package com.martinrgb.animer.monitor;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.opengl.GLSurfaceView;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TableLayout;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import com.martinrgb.animer.Animer;
import com.martinrgb.animer.R;
import com.martinrgb.animer.core.interpolator.AnInterpolator;
import com.martinrgb.animer.core.math.converter.DHOConverter;
import com.martinrgb.animer.core.math.converter.OrigamiPOPConverter;
import com.martinrgb.animer.core.math.converter.RK4Converter;
import com.martinrgb.animer.core.math.converter.UIViewSpringConverter;
import com.martinrgb.animer.monitor.fps.FPSDetector;
import com.martinrgb.animer.monitor.fps.FrameDataCallback;
import com.martinrgb.animer.monitor.shader.ShaderSurfaceView;
import java.text.DecimalFormat;
public class AnConfigView extends FrameLayout {
private Spinner mSolverObjectSelectorSpinner,mSolverTypeSelectorSpinner;
private AnSpinnerAdapter solverObjectSpinnerAdapter,solverTypeSpinnerAdapter;
private Animer currentAnimer,mRevealAnimer,mFPSAnimer;
private AnConfigRegistry anConfigRegistry;
private LinearLayout listLayout;
private SeekbarListener seekbarListener;
private SolverSelectedListener solverSelectedListener;
private ShaderSurfaceView shaderSurfaceView;
//private final int mTextColor = Color.argb(255, 255, 255, 255);
private int mainColor;
private int secondaryColor;
private int backgroundColor;
private int fontSize;
private Typeface typeface;
private String currentObjectType = "NULL";
private int listSize = 2;
private static int SEEKBAR_START_ID = 15000;
private static int SEEKLABEL_START_ID_START_ID = 20000;
private static int EDITTEXT_START_ID_START_ID = 25000;
private static final int MAX_SEEKBAR_VAL = 100000;
private static final int MIN_SEEKBAR_VAL = 1;
//TODO
private static final DecimalFormat DECIMAL_FORMAT_2 = new DecimalFormat("#.##");
private static final DecimalFormat DECIMAL_FORMAT_1 = new DecimalFormat("#.#");
private static final DecimalFormat DECIMAL_FORMAT_3 = new DecimalFormat("#.###");
private float MAX_VAL1,MAX_VAL2,MAX_VAL3,MAX_VAL4,MAX_VAL5;
private float[] MAX_VALUES = new float[]{MAX_VAL1,MAX_VAL2,MAX_VAL3,MAX_VAL4,MAX_VAL5};
private float MIN_VAL1,MIN_VAL2,MIN_VAL3,MIN_VAL4,MIN_VAL5;
private float[] MIN_VALUES = new float[]{MIN_VAL1,MIN_VAL2,MIN_VAL3,MIN_VAL4,MIN_VAL5};
private float RANGE_VAL1,RANGE_VAL2,RANGE_VAL3,RANGE_VAL4,RANGE_VAL5;
private float[] RANGE_VALUES = new float[]{RANGE_VAL1,RANGE_VAL2,RANGE_VAL3,RANGE_VAL4,RANGE_VAL5};
private float seekBarValue1,seekBarValue2,seekBarValue3,seekBarValue4,seekBarValue5;
private Object[] SEEKBAR_VALUES = new Object[]{seekBarValue1,seekBarValue2,seekBarValue3,seekBarValue4,seekBarValue5};
private TextView mArgument1SeekLabel,mArgument2SeekLabel,mArgument3SeekLabel,mArgument4SeekLabel,mArgument5SeekLabel;
private TextView[] SEEKBAR_LABElS = new TextView[]{mArgument1SeekLabel,mArgument2SeekLabel,mArgument3SeekLabel,mArgument4SeekLabel,mArgument5SeekLabel};
private EditText mArgument1EditText,mArgument2EditText,mArgument3EditText,mArgument4EditText,mArgument5EditText;
private EditText[] EDITTEXTS = new EditText[]{mArgument1EditText,mArgument2EditText,mArgument3EditText,mArgument4EditText,mArgument5EditText};
private SeekBar mArgument1SeekBar,mArgument2SeekBar,mArgument3SeekBar,mArgument4SeekBar,mArgument5SeekBar;
private SeekBar[] SEEKBARS = new SeekBar[]{mArgument1SeekBar,mArgument2SeekBar,mArgument3SeekBar,mArgument4SeekBar,mArgument5SeekBar};
private final int MARGIN_SIZE = (int) getResources().getDimension(R.dimen.margin_size);
private final int PADDING_SIZE = (int) getResources().getDimension(R.dimen.padding_size);
private final int PX_120 = dpToPx(120, getResources());
private TextView fpsView;
private AnConfigMap mSolverTypesMap;
private AnConfigMap mAnimerObjectsMap;
private Animer.TriggeredListener triggeredListener;
private Context mContext;
public AnConfigView(Context context) {
this(context, null);
}
public AnConfigView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AnConfigView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
private boolean hadInited = false;
private void initView(Context context) {
typeface = Typeface.createFromAsset(context.getAssets(), "Montserrat-SemiBold.ttf");
secondaryColor = ContextCompat.getColor(context, R.color.secondaryColor);
mainColor = ContextCompat.getColor(context,R.color.mainColor);
backgroundColor = ContextCompat.getColor(context,R.color.backgroundColor);
fontSize = getResources().getDimensionPixelSize(R.dimen.font_size);
View view = inflate(getContext(), R.layout.config_view, null);
addView(view);
//Log.e("r: ",String.valueOf() + "g:" + String.valueOf(Color.green(mainColor)) + "b:" + String.valueOf(String.valueOf(Color.blue(mainColor))) );
fpsView = findViewById(R.id.fps_view);
fpsView.setTypeface(typeface);
fpsView.setTextSize(fontSize);
fpsView.setOnTouchListener(new OnFPSTouchListener());
shaderSurfaceView = findViewById(R.id.shader_surfaceview);
shaderSurfaceView.setFactorInput(1500,0);
shaderSurfaceView.setFactorInput(0.5f,1);
shaderSurfaceView.setMainColor((float)Color.red(mainColor)/255.f,(float) Color.green(mainColor)/255.f,(float) Color.blue(mainColor)/255.f );
Log.e("rgb-r:", String.valueOf((float)Color.red(mainColor)/255.f));
Log.e("rgb-g:", String.valueOf((float)Color.green(mainColor)/255.f));
Log.e("rgb-b:", String.valueOf((float)Color.blue(mainColor)/255.f));
shaderSurfaceView.setSecondaryColor((float)Color.red(secondaryColor)/255.f,(float) Color.green(secondaryColor)/255.f,(float) Color.blue(secondaryColor)/255.f );
mContext = context;
// ## Spinner
anConfigRegistry = AnConfigRegistry.getInstance();
triggeredListener = new Animer.TriggeredListener() {
@Override
public void onTrigger(boolean triggered) {
shaderSurfaceView.resetTime();
//TODO ResetWhen Request
if(triggered){
shaderSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
else{
shaderSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}
};
solverObjectSpinnerAdapter = new AnSpinnerAdapter(context,getResources());
solverTypeSpinnerAdapter = new AnSpinnerAdapter(context,getResources());
mSolverObjectSelectorSpinner = findViewById(R.id.object_spinner);
mSolverTypeSelectorSpinner = findViewById(R.id.type_spinner);
solverSelectedListener = new SolverSelectedListener();
seekbarListener = new SeekbarListener();
mSolverObjectSelectorSpinner.setAdapter(solverObjectSpinnerAdapter);
mSolverObjectSelectorSpinner.setOnItemSelectedListener(solverSelectedListener);
mSolverTypeSelectorSpinner.setAdapter(solverTypeSpinnerAdapter);
mSolverTypeSelectorSpinner.setOnItemSelectedListener(solverSelectedListener);
refreshAnimerConfigs();
// ## List
listLayout = findViewById(R.id.list_layout);
// ## Nub
View nub = findViewById(R.id.nub);
nub.setOnTouchListener(new OnNubTouchListener());
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// Put your code here.
if(!hadInited) {
mRevealAnimer = new Animer();
mRevealAnimer.setSolver(Animer.springDroid(500, 0.95f));
mRevealAnimer.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
AnConfigView.this.setTranslationY(value);
}
});
mFPSAnimer = new Animer();
mFPSAnimer.setSolver(Animer.springDroid(600, 0.7f));
mFPSAnimer.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
fpsView.setScaleX(value);
fpsView.setScaleY(value);
}
});
mRevealAnimer.setCurrentValue(-(AnConfigView.this.getMeasuredHeight() - getResources().getDimension(R.dimen.nub_height)));
mFPSAnimer.setCurrentValue(1);
hadInited = true;
float maxValue = 0;
float minValue = -(AnConfigView.this.getMeasuredHeight() - getResources().getDimension(R.dimen.nub_height));
if(setRevealed){
mRevealAnimer.setCurrentValue(maxValue);
nub.setVisibility(INVISIBLE);
}
else{
mRevealAnimer.setCurrentValue(minValue);
nub.setVisibility(VISIBLE);
}
}
}
});
this.setElevation(1000);
}
public void refreshAnimerConfigs() {
mAnimerObjectsMap = anConfigRegistry.getAllAnimer();
solverObjectSpinnerAdapter.clear();
for(int i = 0; i< mAnimerObjectsMap.size(); i++){
solverObjectSpinnerAdapter.add(String.valueOf(mAnimerObjectsMap.getKey(i)));
}
solverObjectSpinnerAdapter.notifyDataSetChanged();
if (solverObjectSpinnerAdapter.getCount() > 0) {
// object first time selection
mSolverObjectSelectorSpinner.setSelection(0);
initTypeConfigs();
}
mSolverTypesMap = anConfigRegistry.getAllSolverTypes();
solverTypeSpinnerAdapter.clear();
for(int i = 0; i< mSolverTypesMap.size(); i++){
solverTypeSpinnerAdapter.add(String.valueOf(mSolverTypesMap.getKey(i)));
}
solverTypeSpinnerAdapter.notifyDataSetChanged();
if (solverObjectSpinnerAdapter.getCount() > 0) {
// solver first time selection
if(currentAnimer !=null && currentAnimer.getTriggerListener() !=null){
currentAnimer.removeTriggerListener();
}
currentAnimer = (Animer) mAnimerObjectsMap.getValue(0);
currentAnimer.setTriggerListener(triggeredListener);
//shaderSurfaceView.requestRender();
recreateList();
int typeIndex = 0;
// select the right interpolator
if(String.valueOf(currentAnimer.getCurrentSolver().getConfigSet().getKeyByString("converter_type")) == "AndroidInterpolator"){
typeIndex = mSolverTypesMap.getIndexByString(String.valueOf(currentAnimer.getCurrentSolver().getArg1().getClass().getSimpleName()));
}
// select the right animator
else{
typeIndex = mSolverTypesMap.getIndexByString(String.valueOf(currentAnimer.getCurrentSolver().getConfigSet().getKeyByString("converter_type")));
}
mSolverTypeSelectorSpinner.setSelection(typeIndex,false);
}
}
private void initTypeConfigs() {
mSolverTypesMap = anConfigRegistry.getAllSolverTypes();
solverTypeSpinnerAdapter.clear();
for(int i = 0; i< mSolverTypesMap.size(); i++){
solverTypeSpinnerAdapter.add(String.valueOf(mSolverTypesMap.getKey(i)));
}
solverTypeSpinnerAdapter.notifyDataSetChanged();
if (solverObjectSpinnerAdapter.getCount() > 0) {
// solver first time selection
if(currentAnimer !=null && currentAnimer.getTriggerListener() !=null){
currentAnimer.removeTriggerListener();
}
currentAnimer = (Animer) mAnimerObjectsMap.getValue(0);
currentAnimer.setTriggerListener(triggeredListener);
//shaderSurfaceView.requestRender();
recreateList();
int typeIndex = 0;
// select the right interpolator
if(String.valueOf(currentAnimer.getCurrentSolver().getConfigSet().getKeyByString("converter_type")) == "AndroidInterpolator"){
typeIndex = mSolverTypesMap.getIndexByString(String.valueOf(currentAnimer.getCurrentSolver().getArg1().getClass().getSimpleName()));
}
// select the right animator
else{
typeIndex = mSolverTypesMap.getIndexByString(String.valueOf(currentAnimer.getCurrentSolver().getConfigSet().getKeyByString("converter_type")));
}
mSolverTypeSelectorSpinner.setSelection(typeIndex,false);
}
}
private void recreateList(){
FrameLayout.LayoutParams params;
LinearLayout seekWrapper;
TableLayout.LayoutParams tableLayoutParams = new TableLayout.LayoutParams(0,ViewGroup.LayoutParams.WRAP_CONTENT,1f);
tableLayoutParams.setMargins(MARGIN_SIZE, MARGIN_SIZE, MARGIN_SIZE, MARGIN_SIZE);
listLayout.removeAllViews();
if(currentAnimer.getCurrentSolverData().getKeyByString("converter_type").toString() != "AndroidInterpolator") {
listSize =2;
}
else{
AnInterpolator mInterpolator = (AnInterpolator) currentAnimer.getCurrentSolver().getArg1();
listSize = 1 + (mInterpolator.getArgNum()) ;
}
for (int i = 0;i adapterView, View view, int i, long l) {
if(adapterView == mSolverObjectSelectorSpinner){
// get animer from Map
solverObjectSpinnerAdapter.setSelectedItemIndex(i);
if(currentAnimer !=null && currentAnimer.getTriggerListener() !=null){
currentAnimer.removeTriggerListener();
}
if(typeIndex !=-1){
prevTypeIndex = typeIndex;
}
currentAnimer = (Animer) mAnimerObjectsMap.getValue(i);
currentAnimer.setTriggerListener(triggeredListener);
//shaderSurfaceView.requestRender();
recreateList();
redefineMinMax(currentAnimer.getCurrentSolver());
updateSeekBars(currentAnimer.getCurrentSolver());
// will not excute in init
if(objectChecker > 0){
typeSpinnerIsFixedSelection = true;
// select the right interpolator
if(String.valueOf(currentAnimer.getCurrentSolver().getConfigSet().getKeyByString("converter_type")).toString().contains("AndroidInterpolator")){
typeIndex = mSolverTypesMap.getIndexByString(String.valueOf(currentAnimer.getCurrentSolver().getArg1().getClass().getSimpleName()));
}
// select the right animator
else{
typeIndex = mSolverTypesMap.getIndexByString(currentAnimer.getCurrentSolver().getConfigSet().getKeyByString("converter_type").toString());
}
// when aniamtor type is equal,fix bugs
if(mSolverTypeSelectorSpinner.getSelectedItemPosition() == typeIndex){
typeSpinnerIsFixedSelection = false;
}
mSolverTypeSelectorSpinner.setSelection(typeIndex,false);
}
objectChecker++;
}
else if (adapterView == mSolverTypeSelectorSpinner){
// will not excute in init
solverTypeSpinnerAdapter.setSelectedItemIndex(i);
if(typeChecker > 0) {
if(typeSpinnerIsFixedSelection){
typeSpinnerIsFixedSelection = false;
}
//TODO Remeber Parameters Before
else{
// reset animer from Map
Animer.AnimerSolver seltectedSolver = (Animer.AnimerSolver) mSolverTypesMap.getValue(i);
currentAnimer.setSolver(seltectedSolver);
recreateList();
redefineMinMax(currentAnimer.getCurrentSolver());
updateSeekBars(currentAnimer.getCurrentSolver());
}
}
typeChecker++;
}
}
@Override
public void onNothingSelected(AdapterView> adapterView) {
}
}
private void redefineMinMax(Animer.AnimerSolver animerSolver){
currentObjectType = animerSolver.getConfigSet().getKeyByString("converter_type").toString();
if(currentObjectType != "AndroidInterpolator"){
for (int index = 0;index (float) mMax){
convertedValue = mMax;
EDITTEXTS[mIndex].setText(String.valueOf(mMax));
}
else if(convertedValue < (float) mMin) {
convertedValue = mMin;
EDITTEXTS[mIndex].setText(String.valueOf(mMin));
}
float calculatedProgress = (convertedValue - MIN_VALUES[mIndex])/ RANGE_VALUES[mIndex]* (MAX_SEEKBAR_VAL - MIN_SEEKBAR_VAL) + MIN_SEEKBAR_VAL;
canSetEditText = false;
SEEKBARS[mIndex].setProgress((int) calculatedProgress);
canSetEditText = true;
}
}
}
}
public static boolean isNumeric(String strNum) {
if (strNum == null) {
return false;
}
try {
double d = Double.parseDouble(strNum);
} catch (NumberFormatException nfe) {
return false;
}
return true;
}
private class SeekbarListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int val, boolean b) {
//TODO Request Renderer
if(currentObjectType != "AndroidInterpolator") {
for (int i = 0; i < listSize; i++) {
if (seekBar == SEEKBARS[i]) {
SEEKBAR_VALUES[i] = ((float) (val - MIN_SEEKBAR_VAL) / (MAX_SEEKBAR_VAL - MIN_SEEKBAR_VAL)) * RANGE_VALUES[i] + MIN_VALUES[i];
if (i == 0) {
String roundedValue1Label = DECIMAL_FORMAT_2.format(SEEKBAR_VALUES[i]);
SEEKBAR_LABElS[i].setText((String) currentAnimer.getCurrentSolver().getConfigSet().getKeyByString("arg" + String.valueOf(i + 1) + "_name") + ": ");
if(canSetEditText){
EDITTEXTS[i].setText(roundedValue1Label);
}
currentAnimer.getCurrentSolver().getConfigSet().addConfig("arg" + String.valueOf(i + 1) + "", Float.valueOf(roundedValue1Label));
} else if (i == 1) {
String roundedValue1Label = DECIMAL_FORMAT_3.format(SEEKBAR_VALUES[i]);
SEEKBAR_LABElS[i].setText((String) currentAnimer.getCurrentSolver().getConfigSet().getKeyByString("arg" + String.valueOf(i + 1) + "_name") + ": ");
if(canSetEditText){
EDITTEXTS[i].setText(roundedValue1Label);
}
currentAnimer.getCurrentSolver().getConfigSet().addConfig("arg" + String.valueOf(i + 1) + "", Float.valueOf(roundedValue1Label));
}
}
}
// Seekbar in Fling not works at all
if (currentObjectType != "AndroidFling") {
Object val1 = getConvertValueByIndexAndType(0, currentObjectType);
Object val2 = getConvertValueByIndexAndType(1, currentObjectType);
currentAnimer.getCurrentSolver().setArg1(val1);
currentAnimer.getCurrentSolver().setArg2(val2);
float convertVal1 = Float.valueOf(String.valueOf(val1));
float convertVal2 = Float.valueOf(String.valueOf(val2));
shaderSurfaceView.setCurveMode(1);
shaderSurfaceView.setFactorInput(convertVal1,0);
shaderSurfaceView.setFactorInput(convertVal2,1);
}
else{
Object val1 = getConvertValueByIndexAndType(0, currentObjectType);
Object val2 = getConvertValueByIndexAndType(1, currentObjectType);
float convertVal1 = Float.valueOf(String.valueOf(val1));
float convertVal2 = Float.valueOf(String.valueOf(val2));
currentAnimer.getCurrentSolver().setArg2(Math.max(0.01f,(float)val2));
shaderSurfaceView.setCurveMode(0);
shaderSurfaceView.setFactorInput(convertVal1,0);
shaderSurfaceView.setFactorInput(convertVal2,1);
}
}
else{
getCurveModeByString();
// Interpolator Factor
for (int i = 0; i < listSize - 1; i++) {
if (seekBar == SEEKBARS[i]) {
SEEKBAR_VALUES[i] = ((float) (val - MIN_SEEKBAR_VAL) / (MAX_SEEKBAR_VAL - MIN_SEEKBAR_VAL)) * RANGE_VALUES[i] + MIN_VALUES[i];
String roundedValue1Label = DECIMAL_FORMAT_3.format(SEEKBAR_VALUES[i]);
SEEKBAR_LABElS[i].setText(((AnInterpolator) currentAnimer.getCurrentSolver().getArg1()).getArgString(i) + ": ");
if(canSetEditText){
EDITTEXTS[i].setText(roundedValue1Label);
}
((AnInterpolator) currentAnimer.getCurrentSolver().getArg1()).resetArgValue(i,Float.valueOf(roundedValue1Label));
shaderSurfaceView.setFactorInput(Float.valueOf(roundedValue1Label),i);
if(currentAnimer.getCurrentSolver().getArg1().getClass().getSimpleName().contains("PathInterpolator")){
}
}
}
// Interpolator Duration
if (seekBar == SEEKBARS[listSize - 1]) {
SEEKBAR_VALUES[listSize - 1] = ((float) (val - MIN_SEEKBAR_VAL) / (MAX_SEEKBAR_VAL - MIN_SEEKBAR_VAL)) * RANGE_VALUES[listSize - 1] + MIN_VALUES[listSize - 1];
String roundedValue1Label = DECIMAL_FORMAT_1.format(SEEKBAR_VALUES[listSize - 1]);
SEEKBAR_LABElS[listSize - 1].setText((String) currentAnimer.getCurrentSolver().getConfigSet().getKeyByString("arg" + String.valueOf(2) + "_name") + ": ");
if(canSetEditText){
EDITTEXTS[listSize - 1].setText(roundedValue1Label);
}
currentAnimer.getCurrentSolver().getConfigSet().addConfig("arg" + String.valueOf(2) + "", Float.valueOf(roundedValue1Label));
float floatVal = Float.valueOf(roundedValue1Label);
currentAnimer.getCurrentSolver().setArg2( (long) floatVal);
shaderSurfaceView.setDuration(floatVal/1000);
}
}
shaderSurfaceView.requestRender();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
isEditListenerWork = false;
canSetEditText = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
isEditListenerWork = true;
canSetEditText = false;
}
}
private String[] interpolatorArray = new String[] {
"PathInterpolator","LinearInterpolator","AccelerateDecelerateInterpolator","AccelerateInterpolator",
"DecelerateInterpolator","AnticipateInterpolator","OvershootInterpolator","AnticipateOvershootInterpolator",
"BounceInterpolator","CycleInterpolator","FastOutSlowInInterpolator","LinearOutSlowInInterpolator",
"FastOutLinearInInterpolator","CustomMocosSpringInterpolator","CustomSpringInterpolator","CustomBounceInterpolator",
"CustomDampingInterpolator","AndroidSpringInterpolator"
};
private void getCurveModeByString(){
for(int i=0;i minValue && nubDragMoveY + currViewTransY< maxValue){
mRevealAnimer.setCurrentValue(nubDragMoveY + currViewTransY);
}
break;
case MotionEvent.ACTION_UP:
if( Math.abs(nubDragMoveY) > (maxValue - minValue)/3){
mRevealAnimer.setEndValue((currViewTransY == minValue)?maxValue:minValue);
}
else{
mRevealAnimer.setEndValue((currViewTransY == minValue)?minValue:maxValue);
}
break;
case MotionEvent.ACTION_CANCEL:
if( Math.abs(nubDragMoveY) > (maxValue - minValue)/3){
mRevealAnimer.setEndValue((currViewTransY == minValue)?maxValue:minValue);
}
else{
mRevealAnimer.setEndValue((currViewTransY == minValue)?minValue:maxValue);
}
break;
}
return true;
}
}
private class OnFPSTouchListener implements View.OnTouchListener {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
mFPSAnimer.setEndValue(0.8f);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
mFPSAnimer.setEndValue(1f);
if (String.valueOf(fpsView.getText()).contains("FPS")) {
FPSDetector.create().addFrameDataCallback(new FrameDataCallback() {
@Override
public void doFrame(long previousFrameNS, long currentFrameNS, int droppedFrames, float currentFPS) {
if(currentFPS <50 && currentFPS > 30){
fpsView.setTextColor(Color.YELLOW);
}
else if(currentFPS>=50){
fpsView.setTextColor(Color.GREEN);
}
else if(currentFPS < 30){
fpsView.setTextColor(Color.RED);
}
fpsView.setText(String.valueOf(currentFPS));
}
}).show(mContext);
} else {
FPSDetector.hide(mContext);
fpsView.setTextColor(backgroundColor);
fpsView.setText("FPS");
}
break;
case MotionEvent.ACTION_CANCEL:
mFPSAnimer.setEndValue(1);
if (String.valueOf(fpsView.getText()).contains("FPS")) {
FPSDetector.create().addFrameDataCallback(new FrameDataCallback() {
@Override
public void doFrame(long previousFrameNS, long currentFrameNS, int droppedFrames, float currentFPS) {
if(currentFPS <50 && currentFPS > 30){
fpsView.setTextColor(Color.YELLOW);
}
else if(currentFPS>=50){
fpsView.setTextColor(Color.GREEN);
}
else if(currentFPS < 30){
fpsView.setTextColor(Color.RED);
}
fpsView.setText(String.valueOf(currentFPS));
}
}).show(mContext);
} else {
FPSDetector.hide(mContext);
fpsView.setTextColor(backgroundColor);
fpsView.setText("FPS");
}
break;
}
return true;
}
}
public static int dpToPx(float dp, Resources res) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,res.getDisplayMetrics());
}
public static FrameLayout.LayoutParams createLayoutParams(int width, int height) {
return new FrameLayout.LayoutParams(width, height);
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/AnSpinnerAdapter.java
================================================
package com.martinrgb.animer.monitor;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import com.martinrgb.animer.R;
import java.util.ArrayList;
import java.util.List;
public class AnSpinnerAdapter extends BaseAdapter {
private final Context mContext;
private final List mStrings;
private final Resources mResources;
private int mFontSize;
//private final int mTextColor = Color.argb(255, 255, 255, 255);
private int mTextColor;
public AnSpinnerAdapter(Context context,Resources resources) {
mTextColor = ContextCompat.getColor(context, R.color.secondaryColor);
mContext = context;
mResources = resources;
mStrings = new ArrayList();
mFontSize = resources.getDimensionPixelSize(R.dimen.font_size);
}
@Override
public int getCount() {
return mStrings.size();
}
@Override
public Object getItem(int position) {
return mStrings.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
public void add(String string) {
mStrings.add(string);
notifyDataSetChanged();
}
public void clear() {
mStrings.clear();
notifyDataSetChanged();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView;
if (convertView == null) {
textView = new TextView(mContext);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
textView.setLayoutParams(params);
int twelvePx = dpToPx(12, mResources);
int ninePx = dpToPx(9, mResources);
textView.setPadding(dpToPx(32,mResources), ninePx, dpToPx(32,mResources),ninePx);
textView.setTextColor(mTextColor);
textView.setTextSize(mFontSize);
textView.setTypeface( Typeface.createFromAsset(mContext.getAssets(), "Montserrat-SemiBold.ttf"));
} else {
textView = (TextView) convertView;
}
textView.setText(mStrings.get(position));
return textView;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent)
{
View v = null;
v = super.getDropDownView(position, null, parent);
// If this is the selected item position
if (position == seletecedIndex) {
v.setAlpha(1.f);
}
else {
v.setAlpha(0.5f);
}
return v;
}
private int seletecedIndex = -1;
public void setSelectedItemIndex(int i){
seletecedIndex = i;
}
public static int dpToPx(float dp, Resources res) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
res.getDisplayMetrics());
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/fps/Calculation.java
================================================
package com.martinrgb.animer.monitor.fps;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class Calculation
{
public enum Metric {GOOD, BAD, MEDIUM};
public static List getDroppedSet(FPSConfig fpsConfig, List dataSet)
{
List droppedSet = new ArrayList<>();
long start = -1;
for (Long value : dataSet) {
if (start == -1) {
start = value;
continue;
}
int droppedCount = droppedCount(start, value, fpsConfig.deviceRefreshRateInMs);
if (droppedCount > 0)
{
droppedSet.add(droppedCount);
}
start = value;
}
return droppedSet;
}
public static int droppedCount(long start, long end, float devRefreshRate){
int count = 0;
long diffNs = end - start;
long diffMs = TimeUnit.MILLISECONDS.convert(diffNs, TimeUnit.NANOSECONDS);
long dev = Math.round(devRefreshRate);
if (diffMs > dev) {
long droppedCount = (diffMs / dev);
count = (int) droppedCount;
}
return count;
}
public static AbstractMap.SimpleEntry calculateMetric(FPSConfig fpsConfig,
List dataSet,
List droppedSet)
{
long timeInNS = dataSet.get(dataSet.size() - 1) - dataSet.get(0);
long size = getNumberOfFramesInSet(timeInNS, fpsConfig);
//metric
int runningOver = 0;
// total dropped
int dropped = 0;
for(Integer k : droppedSet){
dropped+=k;
if (k >=2) {
runningOver+=k;
}
}
float multiplier = fpsConfig.refreshRate / size;
float answer = multiplier * (size - dropped);
long realAnswer = Math.round(answer);
// calculate metric
float percentOver = (float)runningOver/(float)size;
Metric metric = Metric.GOOD;
if (percentOver >= fpsConfig.redFlagPercentage) {
metric = Metric.BAD;
} else if (percentOver >= fpsConfig.yellowFlagPercentage) {
metric = Metric.MEDIUM;
}
return new AbstractMap.SimpleEntry(metric, realAnswer);
}
protected static long getNumberOfFramesInSet(long realSampleLengthNs, FPSConfig fpsConfig)
{
float realSampleLengthMs = TimeUnit.MILLISECONDS.convert(realSampleLengthNs, TimeUnit.NANOSECONDS);
float size = realSampleLengthMs/fpsConfig.deviceRefreshRateInMs;
return Math.round(size);
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/fps/FPSBuilder.java
================================================
package com.martinrgb.animer.monitor.fps;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.view.Choreographer;
import android.view.Display;
import android.view.WindowManager;
/**
* Created by brianplummer on 8/29/15.
*/
public class FPSBuilder
{
private static FPSConfig fpsConfig;
private static FPSFrameCallback fpsFrameCallback;
//private static TinyCoach tinyCoach;
private static Foreground.Listener foregroundListener = new Foreground.Listener() {
@Override
public void onBecameForeground() {
//tinyCoach.show();
}
@Override
public void onBecameBackground() {
//tinyCoach.hide(false);
}
};
protected FPSBuilder(){
fpsConfig = new FPSConfig();
}
/**
* configures the fpsConfig to the device's hardware
* refreshRate ex. 60fps and deviceRefreshRateInMs ex. 16.6ms
* @param context
*/
private void setFrameRate(Context context){
Display display = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
fpsConfig.deviceRefreshRateInMs = 1000f/display.getRefreshRate();
fpsConfig.refreshRate = display.getRefreshRate();
}
/**
* stops the frame callback and foreground listener
* nulls out static variables
* called from FPSLibrary in a static context
* @param context
*/
protected static void hide(Context context) {
// tell callback to stop registering itself
fpsFrameCallback.setEnabled(false);
Foreground.get(context).removeListener(foregroundListener);
// remove the view from the window
//tinyCoach.destroy();
// null it all out
//tinyCoach = null;
fpsFrameCallback = null;
fpsConfig = null;
}
// PUBLIC BUILDER METHODS
/**
* show fps meter, this regisers the frame callback that
* collects the fps info and pushes it to the ui
* @param context
*/
public void show(Context context) {
// if (overlayPermRequest(context)) {
// //once permission is granted then you must call show() again
// return;
// }
//are we running? if so, call tinyCoach.show() and return
// if (tinyCoach != null) {
// tinyCoach.show();
// return;
// }
// set device's frame rate info into the config
setFrameRate(context);
// create the presenter that updates the view
// tinyCoach = new TinyCoach((Application) context.getApplicationContext(), fpsConfig);
// create our choreographer callback and register it
fpsFrameCallback = new FPSFrameCallback(fpsConfig);
Choreographer.getInstance().postFrameCallback(fpsFrameCallback);
//set activity background/foreground listener
Foreground.init((Application) context.getApplicationContext()).addListener(foregroundListener);
}
/**
* this adds a frame callback that the library will invoke on the
* each time the choreographer calls us, we will send you the frame times
* and number of dropped frames.
* @param callback
* @return
*/
public FPSBuilder addFrameDataCallback(FrameDataCallback callback) {
fpsConfig.frameDataCallback = callback;
return this;
}
/**
* set red flag percent, default is 20%
*
* @param percentage
* @return
*/
public FPSBuilder redFlagPercentage(float percentage) {
fpsConfig.redFlagPercentage = percentage;
return this;
}
/**
* set red flag percent, default is 5%
* @param percentage
* @return
*/
public FPSBuilder yellowFlagPercentage(float percentage) {
fpsConfig.yellowFlagPercentage = percentage;
return this;
}
/**
* starting x position of fps meter default is 200px
* @param xPosition
* @return
*/
public FPSBuilder startingXPosition(int xPosition) {
fpsConfig.startingXPosition = xPosition;
fpsConfig.xOrYSpecified = true;
return this;
}
/**
* starting y positon of fps meter default is 600px
* @param yPosition
* @return
*/
public FPSBuilder startingYPosition(int yPosition) {
fpsConfig.startingYPosition = yPosition;
fpsConfig.xOrYSpecified = true;
return this;
}
/**
* starting gravity of fps meter default is Gravity.TOP | Gravity.START;
* @param gravity
* @return
*/
public FPSBuilder startingGravity(int gravity) {
fpsConfig.startingGravity = gravity;
fpsConfig.gravitySpecified = true;
return this;
}
/**
* request overlay permission when api >= 23
* @param context
* @return
*/
private boolean overlayPermRequest(Context context) {
boolean permNeeded = false;
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
if (!Settings.canDrawOverlays(context))
{
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + context.getPackageName()));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
permNeeded = true;
}
}
return permNeeded;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/fps/FPSConfig.java
================================================
package com.martinrgb.animer.monitor.fps;
import android.view.Gravity;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
/**
* Created by brianplummer on 8/29/15.
*/
public class FPSConfig implements Serializable
{
public static int DEFAULT_GRAVITY = Gravity.TOP | Gravity.START;
public float redFlagPercentage = 0.2f; //
public float yellowFlagPercentage = 0.05f; //
public float refreshRate = 60; //60fps
public float deviceRefreshRateInMs = 16.6f; //value from device ex 16.6 ms
// starting coordinates
public int startingXPosition = 200;
public int startingYPosition = 600;
public int startingGravity = DEFAULT_GRAVITY;
public boolean xOrYSpecified = false;
public boolean gravitySpecified = false;
// client facing callback that provides frame info
public FrameDataCallback frameDataCallback = null;
// making final for now.....want to be solid on the math before we allow an
// arbitrary value
public final long sampleTimeInMs = 736;//928;//736; // default sample time
protected FPSConfig()
{}
public long getSampleTimeInNs()
{
return TimeUnit.NANOSECONDS.convert(sampleTimeInMs, TimeUnit.MILLISECONDS);
}
public long getDeviceRefreshRateInNs()
{
float value = deviceRefreshRateInMs * 1000000f;
return (long) value;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/fps/FPSDetector.java
================================================
package com.martinrgb.animer.monitor.fps;
import android.content.Context;
/**
* Created by brianplummer on 8/29/15.
*/
public class FPSDetector
{
public static FPSBuilder create(){
return new FPSBuilder();
}
public static void hide(Context context) {
FPSBuilder.hide(context.getApplicationContext());
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/fps/FPSFrameCallback.java
================================================
package com.martinrgb.animer.monitor.fps;
import android.util.Log;
import android.view.Choreographer;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
/**
* Created by brianplummer on 8/29/15.
*/
public class FPSFrameCallback implements Choreographer.FrameCallback
{
private FPSConfig fpsConfig;
//private TinyCoach tinyCoach;
private List dataSet; //holds the frame times of the sample set
private boolean enabled = true;
private long startSampleTimeInNs = 0;
public FPSFrameCallback(FPSConfig fpsConfig) {
this.fpsConfig = fpsConfig;
//this.tinyCoach = tinyCoach;
dataSet = new ArrayList<>();
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public void doFrame(long frameTimeNanos)
{
//if not enabled then we bail out now and don't register the callback
if (!enabled){
destroy();
return;
}
//initial case
if (startSampleTimeInNs == 0){
startSampleTimeInNs = frameTimeNanos;
}
// only invoked for callbacks....
else if (fpsConfig.frameDataCallback != null)
{
long start = dataSet.get(dataSet.size()-1);
int droppedCount = Calculation.droppedCount(start, frameTimeNanos, fpsConfig.deviceRefreshRateInMs);
fpsConfig.frameDataCallback.doFrame(start, frameTimeNanos, droppedCount,currentFPS);
}
//we have exceeded the sample length ~700ms worth of data...we should push results and save current
//frame time in new list
if (isFinishedWithSample(frameTimeNanos))
{
collectSampleAndSend(frameTimeNanos);
}
// add current frame time to our list
dataSet.add(frameTimeNanos);
//we need to register for the next frame callback
Choreographer.getInstance().postFrameCallback(this);
}
private float currentFPS = 0.0f;
private void collectSampleAndSend(long frameTimeNanos)
{
//this occurs only when we have gathered over the sample time ~700ms
List dataSetCopy = new ArrayList<>();
dataSetCopy.addAll(dataSet);
//push data to the presenter
//tinyCoach.showData(fpsConfig, dataSetCopy);
List droppedSet = Calculation.getDroppedSet(fpsConfig, dataSet);
AbstractMap.SimpleEntry answer = Calculation.calculateMetric(fpsConfig, dataSet, droppedSet);
currentFPS = answer.getValue();
//Log.e("FPS",String.valueOf(answer.getValue()));
// clear data
dataSet.clear();
//reset sample timer to last frame
startSampleTimeInNs = frameTimeNanos;
}
/**
* returns true when sample length is exceed
* @param frameTimeNanos current frame time in NS
* @return
*/
private boolean isFinishedWithSample(long frameTimeNanos)
{
return frameTimeNanos-startSampleTimeInNs > fpsConfig.getSampleTimeInNs();
}
private void destroy()
{
dataSet.clear();
fpsConfig = null;
//tinyCoach = null;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/fps/Foreground.java
================================================
package com.martinrgb.animer.monitor.fps;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
// COPIED FROM: https://gist.github.com/steveliles/11116937
/**
* Usage:
*
* 1. Get the Foreground Singleton, passing a Context or Application object unless you
* are sure that the Singleton has definitely already been initialised elsewhere.
*
* 2.a) Perform a direct, synchronous check: Foreground.isForeground() / .isBackground()
*
* or
*
* 2.b) Register to be notified (useful in Service or other non-UI components):
*
* Foreground.Listener myListener = new Foreground.Listener(){
* public void onBecameForeground(){
* // ... whatever you want to do
* }
* public void onBecameBackground(){
* // ... whatever you want to do
* }
* }
*
* public void onCreate(){
* super.onCreate();
* Foreground.get(this).addListener(listener);
* }
*
* public void onDestroy(){
* super.onCreate();
* Foreground.get(this).removeListener(listener);
* }
*/
public class Foreground implements Application.ActivityLifecycleCallbacks {
public static final long CHECK_DELAY = 600;
public static final String TAG = Foreground.class.getName();
public interface Listener {
public void onBecameForeground();
public void onBecameBackground();
}
private static Foreground instance;
private boolean foreground = true, paused = true;
private Handler handler = new Handler();
private List listeners = new CopyOnWriteArrayList();
private Runnable check;
/**
* Its not strictly necessary to use this method - _usually_ invoking
* get with a Context gives us a path to retrieve the Application and
* initialise, but sometimes (e.g. in test harness) the ApplicationContext
* is != the Application, and the docs make no guarantees.
*
* @param application
* @return an initialised Foreground instance
*/
public static Foreground init(Application application){
if (instance == null) {
instance = new Foreground();
application.registerActivityLifecycleCallbacks(instance);
}
return instance;
}
public static Foreground get(Application application){
if (instance == null) {
init(application);
}
return instance;
}
public static Foreground get(Context ctx){
if (instance == null) {
Context appCtx = ctx.getApplicationContext();
if (appCtx instanceof Application) {
init((Application)appCtx);
}
throw new IllegalStateException(
"Foreground is not initialised and " +
"cannot obtain the Application object");
}
return instance;
}
public static Foreground get(){
if (instance == null) {
throw new IllegalStateException(
"Foreground is not initialised - invoke " +
"at least once with parameterised init/get");
}
return instance;
}
public boolean isForeground(){
return foreground;
}
public boolean isBackground(){
return !foreground;
}
public void addListener(Listener listener){
listeners.add(listener);
}
public void removeListener(Listener listener){
listeners.remove(listener);
}
@Override
public void onActivityResumed(Activity activity) {
paused = false;
boolean wasBackground = !foreground;
foreground = true;
if (check != null)
handler.removeCallbacks(check);
if (wasBackground){
Log.i(TAG, "went foreground");
for (Listener l : listeners) {
try {
l.onBecameForeground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
@Override
public void onActivityPaused(Activity activity) {
paused = true;
if (check != null)
handler.removeCallbacks(check);
handler.postDelayed(check = new Runnable(){
@Override
public void run() {
if (foreground && paused) {
foreground = false;
Log.i(TAG, "went background");
for (Listener l : listeners) {
try {
l.onBecameBackground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
}, CHECK_DELAY);
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
@Override
public void onActivityStarted(Activity activity) {}
@Override
public void onActivityStopped(Activity activity) {}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
@Override
public void onActivityDestroyed(Activity activity) {}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/fps/FrameDataCallback.java
================================================
package com.martinrgb.animer.monitor.fps;
/**
* Created by brianplummer on 11/12/15.
*/
public interface FrameDataCallback
{
/**
* this is called for every doFrame() on the choreographer callback
* use this very judiciously. Logging synchronously from here is a bad
* idea as doFrame will be called every 16-32ms.
* @param previousFrameNS previous vsync frame time in NS
* @param currentFrameNS current vsync frame time in NS
* @param droppedFrames number of dropped frames between current and previous times
*/
void doFrame(long previousFrameNS, long currentFrameNS, int droppedFrames,float currentFPS);
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/shader/ShaderProgram.java
================================================
package com.martinrgb.animer.monitor.shader;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;
import com.martinrgb.animer.R;
import com.martinrgb.animer.monitor.shader.util.ShaderHelper;
import com.martinrgb.animer.monitor.shader.util.TextReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
public class ShaderProgram {
private float[] mModelMatrix = new float[16];
private float[] mViewMatrix = new float[16];
private float[] mProjectionMatrix = new float[16];
private float[] mMVPMatrix = new float[16];
private final int mPositionDataSize = 4;
private final int mBytesPerFloat = 4;
private FloatBuffer vertexBuffer;
public static final String ATTRIBUTE_POSITION = "a_position";
public static final String UNIFORM_RESOLUTION = "u_resolution";
public static final String UNIFORM_TIME = "u_time";
public static final String UNIFORM_FACTOR = "u_factor";
public static final String UNIFORM_MVP = "u_MVPMatrix";
public static final String UNIFORM_MODE = "u_mode";
public static final String UNIFORM_DURATION = "u_duration";
public static final String UNIFORM_MAIN_COLOR = "u_mainColor";
public static final String UNIFORM_SECONDARY_COLOR = "u_secondaryColor";
private int program = 0;
private int positionLoc = -1;
private int resolutionLoc = -1;
private int modeLoc = -1;
private int timeLoc = -1;
private int durationLoc = -1;
private int mvpLoc = -1;
private int secondaryColorLoc = -1;
private int mainColorLoc = -1;
private static int factorLength = 5;
private final int factorLocs[] = new int[32];
public ShaderProgram(Context context) {
if (program != 0) { program = 0; }
program = ShaderHelper.buildProgram(
TextReader.readTextFileFromResource(context,R.raw.simplevert),
TextReader.readTextFileFromResource(context,R.raw.simplefrag));
}
public void setOnCreate(){
positionLoc = GLES20.glGetAttribLocation(program, ATTRIBUTE_POSITION);
resolutionLoc = GLES20.glGetUniformLocation(program, UNIFORM_RESOLUTION);
timeLoc = GLES20.glGetUniformLocation(program, UNIFORM_TIME);
mvpLoc = GLES20.glGetUniformLocation(program, UNIFORM_MVP);
modeLoc = GLES20.glGetUniformLocation(program, UNIFORM_MODE);
durationLoc = GLES20.glGetUniformLocation(program,UNIFORM_DURATION);
mainColorLoc = GLES20.glGetUniformLocation(program,UNIFORM_MAIN_COLOR);
secondaryColorLoc = GLES20.glGetUniformLocation(program,UNIFORM_SECONDARY_COLOR);
// for (int i = 0;i < factorLength;i++ ) {
// factorLocs[i] = GLES20.glGetUniformLocation(program,UNIFORM_FACTOR + (i+1));
// }
final float[] verticesData = {
1.0f,-1.0f,0.0f,1.0f,
-1.0f,-1.0f,0.0f,1.0f,
1.0f,1.0f,0.0f,1.0f,
-1.0f,1.0f,0.0f,1.0f};
vertexBuffer = ByteBuffer.allocateDirect(verticesData.length * mBytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
vertexBuffer.put(verticesData).position(0);
GLES20.glEnableVertexAttribArray(positionLoc);
final float eyeX = 0.0f;
final float eyeY = 0.0f;
final float eyeZ = 1.0f;
final float lookX = 0.0f;
final float lookY = 0.0f;
final float lookZ = -5.0f;
final float upX = 0.0f;
final float upY = 1.0f;
final float upZ = 0.0f;
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
}
public void setOnChange(int width, int height){
final float ratio = (float) width / height;
final float left = -ratio;
final float right = ratio;
final float bottom = -1.0f;
final float top = 1.0f;
final float near = 1.0f;
final float far = 10.0f;
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
}
public void setOnDrawFrame(float[] resolution,float time,float[] factors,float mode,float duration,float[] mainColor,float[] secondaryColor){
// ######################### clear the canvas #########################
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// ######################### first program #########################
GLES20.glUseProgram(program);
if (positionLoc > -1){ GLES20.glVertexAttribPointer(positionLoc, mPositionDataSize, GLES20.GL_FLOAT, false,0, vertexBuffer); }
if (resolutionLoc > -1) { GLES20.glUniform2fv(resolutionLoc,1,resolution,0); }
if (timeLoc > -1) { GLES20.glUniform1f(timeLoc,time); }
if (modeLoc > -1) { GLES20.glUniform1f(modeLoc,mode); }
if (durationLoc > -1) { GLES20.glUniform1f(durationLoc,duration); }
if (mainColorLoc > -1) { GLES20.glUniform3f(mainColorLoc,mainColor[0],mainColor[1],mainColor[2]); }
if (secondaryColorLoc > -1) { GLES20.glUniform3f(secondaryColorLoc,secondaryColor[0],secondaryColor[1],secondaryColor[2]); }
//5 Factors
for (int i = 0; i < factorLength; ++i) { //factorLocs.length
factorLocs[i] = GLES20.glGetUniformLocation(program,UNIFORM_FACTOR + (i+1));
GLES20.glUniform1f(factorLocs[i],factors[i]);
}
//Draw the triangle facing straight on.
if(mvpLoc > -1){
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(mvpLoc, 1, false, mMVPMatrix, 0);
}
GLES20.glViewport(0,0,(int) resolution[0],(int) resolution[1]);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glDrawArrays(GL_TRIANGLE_STRIP,0,4);
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/shader/ShaderRenderer.java
================================================
package com.martinrgb.animer.monitor.shader;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import com.martinrgb.animer.monitor.shader.util.FPSCounter;
import com.martinrgb.animer.monitor.shader.util.LoggerConfig;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class ShaderRenderer implements GLSurfaceView.Renderer {
private float resolution[] = new float[]{0,0};
private float secondaryColor[] = new float[]{0,0,0};
private float mainColor[] = new float[]{0,0,0};
private long startTime;
private static final float NS_PER_SECOND = 1000000000f;
private final Context context;
private ShaderProgram shaderProgram;
public ShaderRenderer(Context context) {
this.context = context;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glClearColor(0f, 0f, 0f, 0f);
shaderProgram = new ShaderProgram(context);
shaderProgram.setOnCreate();
// setFactorInput(mFactor1,0);
// setFactorInput(mFactor2,1);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
startTime = System.nanoTime();
resolution[0] = width;
resolution[1] = height;
shaderProgram.setOnChange(width,height);
}
@Override
public void onDrawFrame(GL10 gl) {
//float time = (System.nanoTime() - startTime) / NS_PER_SECOND;
float time = (isInteraction)? (System.nanoTime() - startTime) / NS_PER_SECOND:0;
shaderProgram.setOnDrawFrame(resolution,time,factors,mode,duration,mainColor,secondaryColor);
if(LoggerConfig.ON == true){
FPSCounter.logFrameRate();
}
}
private float factors[] = new float[32];
private float mode;
private float duration;
// private float mFactor1 = 1500;
// private float mFactor2 = 0.5f;
public void setFactorInput(float factor,int i){
factors[i] = factor;
}
public void setMainColor(float r,float g,float b){
mainColor[0] = r;
mainColor[1] = g;
mainColor[2] = b;
}
public void setSecondaryColor(float r,float g,float b){
secondaryColor[0] = r;
secondaryColor[1] = g;
secondaryColor[2] = b;
}
public void setCurveMode(float i){
mode = i ;
}
public void setDuration(float i){
duration = i ;
}
private boolean isInteraction = false;
public void resetTime(){
isInteraction = true;
startTime = System.nanoTime();
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/shader/ShaderSurfaceView.java
================================================
package com.martinrgb.animer.monitor.shader;
import android.content.Context;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class ShaderSurfaceView extends GLSurfaceView {
private ShaderRenderer renderer;
public ShaderSurfaceView(Context context) {
super(context);
setRenderer(context);
}
public ShaderSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
setRenderer(context);
}
private void setRenderer(Context context) {
renderer = new ShaderRenderer(context);
setEGLContextClientVersion(2);
setZOrderOnTop(true);
setEGLConfigChooser(8, 8, 8, 8, 16, 0);
getHolder().setFormat(PixelFormat.RGBA_8888);
setRenderer(renderer);
//TODO Request Renderer
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
public ShaderRenderer getRenderer() {
return renderer;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
public void setFactorInput(float factor,int i){
renderer.setFactorInput(factor,i);
}
public void setMainColor(float r,float g,float b){
renderer.setMainColor(r,g,b);
}
public void setSecondaryColor(float r,float g,float b){
renderer.setSecondaryColor(r,g,b);
}
public void setCurveMode(float i){
renderer.setCurveMode(i);
}
public void setDuration(float i){
renderer.setDuration(i);
}
public void resetTime(){renderer.resetTime();}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/shader/util/FPSCounter.java
================================================
package com.martinrgb.animer.monitor.shader.util;
import android.os.SystemClock;
import android.util.Log;
public class FPSCounter {
private static long startTimeMs;
private static int frameCount;
public static void logFrameRate(){
final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
final double elapsedSeconds = (elapsedRealtimeMs - startTimeMs)/1000.0;
if(elapsedSeconds >= 1.0){
Log.v("Current FPS is ",frameCount/elapsedSeconds + "fps");
startTimeMs = SystemClock.elapsedRealtime();
frameCount = 0;
}
frameCount ++;
}
public static int getFPS() {
return frameCount;
}
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/shader/util/LoggerConfig.java
================================================
package com.martinrgb.animer.monitor.shader.util;
public class LoggerConfig {
public static final boolean ON = true;
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/shader/util/ShaderHelper.java
================================================
package com.martinrgb.animer.monitor.shader.util;
import android.opengl.GLES20;
import android.util.Log;
import static android.opengl.GLES20.GL_COMPILE_STATUS;
import static android.opengl.GLES20.GL_LINK_STATUS;
import static android.opengl.GLES20.GL_VALIDATE_STATUS;
import static android.opengl.GLES20.glAttachShader;
import static android.opengl.GLES20.glCreateProgram;
import static android.opengl.GLES20.glDeleteProgram;
import static android.opengl.GLES20.glGetProgramInfoLog;
import static android.opengl.GLES20.glGetProgramiv;
import static android.opengl.GLES20.glGetShaderiv;
import static android.opengl.GLES20.glLinkProgram;
import static android.opengl.GLES20.glValidateProgram;
public class ShaderHelper {
private static final String TAG = "ShaderHelper";
//构建顶点着色器对象
public static int complieVertexShader(String shaderCode){
return compileShader(GLES20.GL_VERTEX_SHADER,shaderCode);
}
//构建片段着色器对象
public static int complieFragmentShader(String shaderCode){
return compileShader(GLES20.GL_FRAGMENT_SHADER,shaderCode);
}
//构建单个着色器对象
private static int compileShader(int type,String shaderCode){
//glCreateShader 构建了着色器对象,并把 ID 存入shaderObjectID
final int shaderObjectId = GLES20.glCreateShader(type);
if(shaderObjectId == 0){
if(LoggerConfig.ON){
Log.w(TAG,"Could not create new shader");
}
return 0;
}
//把着色器源代码传入着色器对象里
GLES20.glShaderSource(shaderObjectId, shaderCode);
//编译着色器
GLES20.glCompileShader(shaderObjectId);
//检测编译是否成功
final int[] compileStatus = new int[1];
glGetShaderiv(shaderObjectId,GL_COMPILE_STATUS,compileStatus,0);
//检测连接成功失败的日志
//如果编译好了,给出ID
if (LoggerConfig.ON) {
Log.v(TAG, "Results of compiling source:" + "\n" + shaderCode + "\n:" + GLES20.glGetShaderInfoLog(
shaderObjectId));
}
//如果编译失败
if (compileStatus[0] == 0) {
GLES20.glDeleteShader(shaderObjectId);
if (LoggerConfig.ON) {
Log.w(TAG, "Compilation of shader failed");
}
}
return shaderObjectId;
};
//将顶点着色器和片段着色器连接,并构建对象
public static int linkProgram(int vertexShaderId,int fragmentShaderId){
//创建新的程序对象
final int programObjectId = glCreateProgram();
//如果对象没有创建
if(programObjectId == 0){
if(LoggerConfig.ON){
Log.w(TAG,"Could not create new program");
}
return 0;
}
//程序对象附着上着色器
glAttachShader(programObjectId,vertexShaderId);
glAttachShader(programObjectId,fragmentShaderId);
//链接程序
glLinkProgram(programObjectId);
//检测连接成功失败的日志
final int[] linkStatus = new int[1];
glGetProgramiv(programObjectId,GL_LINK_STATUS,linkStatus,0);
//如果创建好了,给出ID
if (LoggerConfig.ON) {
// Print the program info log to the Android log output.
Log.v(TAG, "Results of linking program:\n"
+ glGetProgramInfoLog(programObjectId));
}
//如果创建失败
if(linkStatus[0] == 0){
glDeleteProgram(programObjectId);
if(LoggerConfig.ON){
Log.w(TAG,"Linking of program failed");
}
return 0;
}
return programObjectId;
};
public static boolean validateProgram(int programObjectId){
glValidateProgram(programObjectId);
final int[] validateStatus = new int[1];
glGetProgramiv(programObjectId,GL_VALIDATE_STATUS,validateStatus,0);
Log.v(TAG, "Results of validating program: " + validateStatus[0]
+ "\nLog:" + glGetProgramInfoLog(programObjectId));
return validateStatus[0] != 0;
};
public static int buildProgram(String vertexShaderSource,String fragmentShaderSource){
int program;
int vertexShader = complieVertexShader(vertexShaderSource);
int fragmentShader = complieFragmentShader(fragmentShaderSource);
program = linkProgram(vertexShader,fragmentShader);
if(LoggerConfig.ON){
validateProgram(program);
}
return program;
};
}
================================================
FILE: animer/src/main/java/com/martinrgb/animer/monitor/shader/util/TextReader.java
================================================
package com.martinrgb.animer.monitor.shader.util;
import android.content.Context;
import android.content.res.Resources;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class TextReader {
public static String readTextFileFromResource(Context context, int resourceID){
StringBuilder body = new StringBuilder();
try{
InputStream inputStream = context.getResources().openRawResource(resourceID);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String nextLine;
while((nextLine = bufferedReader.readLine()) != null){
body.append(nextLine);
body.append('\n');
}
} catch (IOException e){
throw new RuntimeException("Could not open resource:" + resourceID,e);
} catch (Resources.NotFoundException nfe){
throw new RuntimeException("Resource not found:" + resourceID,nfe);
}
return body.toString();
};
}
================================================
FILE: animer/src/main/res/drawable/background_spinner.xml
================================================
-
================================================
FILE: animer/src/main/res/drawable/gradient.xml
================================================
-
================================================
FILE: animer/src/main/res/drawable/ic_button_background.xml
================================================
-
================================================
FILE: animer/src/main/res/drawable/ic_edit_border.xml
================================================
================================================
FILE: animer/src/main/res/drawable/ic_grid.xml
================================================
================================================
FILE: animer/src/main/res/drawable/ic_nub.xml
================================================
================================================
FILE: animer/src/main/res/drawable/ic_nub2.xml
================================================
================================================
FILE: animer/src/main/res/drawable/ic_spinner.xml
================================================
================================================
FILE: animer/src/main/res/drawable/ic_thumb.xml
================================================
================================================
FILE: animer/src/main/res/drawable/popbackground_spinner.xml
================================================
-
================================================
FILE: animer/src/main/res/drawable/text_cursor.xml
================================================
================================================
FILE: animer/src/main/res/layout/config_view.xml
================================================
================================================
FILE: animer/src/main/res/raw/simplefrag.glsl
================================================
#ifdef GL_ES
precision highp float;
#endif
//#extension GL_OES_standard_derivatives : enable
#define PI 3.14159265359
#define E 2.718281828459045
uniform vec2 u_resolution;
uniform float u_time;
varying vec2 vFlingCalc;
varying float vSpringCalc;
varying float vDuration;
varying float vMode;
varying vec2 vUv;
varying float v_factor1;
varying float v_factor2;
varying float v_factor3;
varying float v_factor4;
varying float v_factor5;
varying vec3 v_secondaryColor;
varying vec3 v_mainColor;
const float lineJitter = 0.5;
const float lineWidth = 7.0;
const float gridWidth = 1.7;
const float scale = 0.0013;
const float Samples = 3.;
float timeProgress;
vec2 circlePos = vec2(0.);
float circleRadius = 0.0008;
bool reset = false;
int duration_mode = 0;
float FlingSimulator(in float time){
float startVal = 0.;
float deltaT = time * vDuration;
float mRealFriction = v_factor2*(-4.2);
float valTransition = (0. - v_factor1/mRealFriction) + ( v_factor1/ mRealFriction) * (exp(mRealFriction * deltaT ) );
float mLastVal = valTransition/vFlingCalc[1];
return mLastVal/2.;
}
float SpringSimulator(in float time){
float deltaT = time*1. * vDuration;
float starVal = 0.;
float endVal = 1.;
float mNaturalFreq = sqrt(v_factor1);
float mDampedFreq = mNaturalFreq*sqrt(1.0 - v_factor2* v_factor2);
//TODO vSpringVelocity
float lastVelocity = 0.;
float lastDisplacement = time*1. - endVal;
float coeffB = 1.0 / mDampedFreq * (v_factor2 * mNaturalFreq * lastDisplacement + lastVelocity);
float displacement = pow(E,-v_factor2 * mNaturalFreq * deltaT) * (lastDisplacement * cos(mDampedFreq * deltaT) + coeffB * sin(mDampedFreq * deltaT));
float mValue = displacement + endVal;
return mValue/2.+0.;
}
float CubicBezierSimulator(in float time,vec4 bezierPoint) {
if (time > 1.0) {
return 1.;
} else if(time < 0.){
return 0.;
}
float x = time;
float z;
vec2 c,b,a;
for (int i = 1; i < 20; i++) {
c.x = 3. * bezierPoint[0];
b.x = 3. * (bezierPoint[2] - bezierPoint[0]) - c.x;
a.x = 1. - c.x - b.x;
z = x * (c.x + x * (b.x + x * a.x)) - time;
if (abs(z) < 1e-3) {
break;
}
x -= z / (c.x + x * (2. * b.x + 3. * a.x * x));
}
c.y = 3. * bezierPoint[1];
b.y = 3. * (bezierPoint[3] - bezierPoint[1]) - c.y;
a.y = 1. - c.y - b.y;
float mValue = x * (c.y + x * (b.y + x * a.y));
return mValue/1.;
}
float AccelerateInterpolator(in float time){
if (time <= 0.0) {
return 0.;
}
if (v_factor1 == 1.0) {
return time * time;
} else {
return pow(time, 2.*v_factor1);
}
}
float DecelerateInterpolator(in float time) {
if(time >=1.0){
return 1.;
}
if (v_factor1 == 1.0) {
return 1.0 - (1.0 - time) * (1.0 - time);
} else {
return (1.0 - pow((1.0 - time), 2.0 * v_factor1));
}
}
float LinearInterpolator(in float time){
return time;
}
float AccelerateDecelerateInterpolator(in float time){
return (cos((time + 1.) * PI) / 2.0) + 0.5;
}
float AnticipateInterpolator(in float time){
return time * time * ((v_factor1 + 1.) * time - v_factor1);
}
float OvershootInterpolator(float time) {
time -= 1.0;
return time * time * ((v_factor1 + 1.) * time + v_factor1) + 1.0;
}
float AOSIA(float t, float s) {
return t * t * ((s + 1.) * t - s);
}
float AOSIO(float t, float s) {
return t * t * ((s + 1.) * t + s);
}
float AnticipateOvershootInterpolator(float time) {
float t = time;
if (t < 0.5) return 0.5 * AOSIA(t * 2.0, v_factor1*1.5);
else return 0.5 * (AOSIO(t * 2.0 - 2.0, v_factor1*1.5) + 2.0);
}
float BounceInterpolator(in float time){
float t= time;
t *= 1.1226;
if (t < 0.3535) return t * t * 8.0;
else if (t < 0.7408) return (t - 0.54719)*(t - 0.54719)*8. + 0.7;
else if (t < 0.9644) return (t - 0.8526)*(t - 0.8526)*8. + 0.9;
else return (t - 1.0435)*(t - 1.0435)*8. + 0.95;
}
float CycleInterpolator(in float time) {
float mValue = sin(2. * v_factor1 * PI * time);
return mValue/2. + 0.5;
}
float FastOutLinearInInterpolator(in float time){
return CubicBezierSimulator(time,vec4(0.40,0.00,1.00,1.00));
}
float FastOutSlowInInterpolator(in float time){
return CubicBezierSimulator(time,vec4(0.40,0.00,0.20,1.00));
}
float LinearOutSlowInInterpolator(in float time){
return CubicBezierSimulator(time,vec4(0.00,0.00,0.20,1.00));
}
float CustomSpringInterpolator(in float ratio) {
if (ratio == 0.0 || ratio == 1.0)
return ratio/2.;
else {
float value = (pow(2., -10. * ratio) * sin((ratio - v_factor1 / 4.0) * (2.0 * PI) / v_factor1) + 1.);
return value/2.;
}
}
float CustomBounceInterpolator(in float ratio){
float amplitude = 1.;
float phase = 0.;
float originalStiffness = 12.;
float originalFrictionMultipler = 0.3;
float mass = 0.058;
float maxStifness = 50.;
float maxFrictionMultipler = 1.;
float aTension = min(max(v_factor1,0.),100.) * (maxStifness- originalStiffness)/100.;
float aFriction = min(max(v_factor2,0.),100.) * (maxFrictionMultipler - originalFrictionMultipler)/100.;
float pulsation = sqrt((originalStiffness + aTension) / mass);
float friction = (originalFrictionMultipler + aFriction) * pulsation;
if (ratio == 0.0 || ratio == 1.0)
return ratio/2.;
else {
float value = amplitude * exp(-friction * ratio) * cos(pulsation * ratio + phase) ;
return (-abs(value)+1.)/2.;
}
}
float CustomDampingInterpolator(in float ratio){
float amplitude = 1.;
float phase = 0.;
float originalStiffness = 12.;
float originalFrictionMultipler = 0.3;
float mass = 0.058;
float maxStifness = 50.;
float maxFrictionMultipler = 1.;
float aTension = min(max(v_factor1,0.),100.) * (maxStifness- originalStiffness)/100.;
float aFriction = min(max(v_factor2,0.),100.) * (maxFrictionMultipler - originalFrictionMultipler)/100.;
float pulsation = sqrt((originalStiffness + aTension) / mass);
float friction = (originalFrictionMultipler + aFriction) * pulsation;
if (ratio == 0.0 || ratio == 1.0)
return ratio/2.;
else {
float value = amplitude * exp(-friction * ratio) * cos(pulsation * ratio + phase) ;
return (-(value)+1.)/2.;
}
}
float AndroidSpringInterpolator(float ratio) {
if (ratio == 0.0 || ratio == 1.0)
return ratio/2.;
else {
float deltaT = ratio * vDuration;
float starVal = 0.;
float endVal = 1.;
float mNaturalFreq = sqrt(v_factor1);
float mDampedFreq = (mNaturalFreq*sqrt(1.0 - v_factor2* v_factor2));
//TODO vSpringVelocity
float lastVelocity = 0.;
//float lastDisplacement = ratio - endVal* deltaT/60 - endVal;
float lastDisplacement = ratio - endVal;
float coeffB = (1.0 / mDampedFreq * (v_factor2 * mNaturalFreq * lastDisplacement + lastVelocity));
float displacement = (pow(E,-v_factor2 * mNaturalFreq * deltaT) * (lastDisplacement * cos(mDampedFreq * deltaT) + coeffB * sin(mDampedFreq * deltaT)));
float mValue = displacement + endVal;
if(vDuration == 0.){
return starVal/2.;
}
else{
return mValue/2.;
}
}
}
float CustomMocosSpringInterpolator(in float ratio) {
if (ratio >= 1.) {
return 1./2.;
}
float tension = v_factor1;
float damping = v_factor2;
float velocity = v_factor3;
float mEps = 0.001;
float mGamma,mVDiv2,mB,mA,mMocosDuration;
bool mOscilative = (4. * tension - damping * damping > 0.);
if (mOscilative) {
mGamma = sqrt(4. * tension - damping * damping) / 2.;
mVDiv2 = damping / 2.;
mB = atan(-mGamma / (velocity - mVDiv2));
mA = -1. / sin(mB);
mMocosDuration = log(abs(mA) / mEps) / mVDiv2;
} else {
mGamma = sqrt(damping * damping - 4. * tension) / 2.;
mVDiv2 = damping / 2.;
mA = (velocity - (mGamma + mVDiv2)) / (2. * mGamma);
mB = -1. - mA;
mMocosDuration = log(abs(mA) / mEps) / (mVDiv2 - mGamma);
}
float t = ratio * mMocosDuration;
if(mOscilative){
return (mA * exp(-mVDiv2 * t) * sin(mGamma * t + mB) + 1.)/2.;
}
else{
return (mA * exp((mGamma - mVDiv2) * t) + mB * exp(-(mGamma + mVDiv2) * t) + 1.)/2.;
}
}
float plot(vec2 st, float progress){
float pct;
if(vMode == 0.0){
pct = FlingSimulator(progress);
}
else if(vMode == 1.0){
pct = SpringSimulator(progress);
}
else if(vMode == 2.0){
pct = CubicBezierSimulator(progress,vec4(v_factor1,v_factor2,v_factor3,v_factor4));
}
else if(vMode == 2.01){
pct = LinearInterpolator(progress);
}
else if(vMode == 2.02){
pct = AccelerateDecelerateInterpolator(progress);
}
else if(vMode == 2.03){
pct = AccelerateInterpolator(progress);
}
else if(vMode == 2.04){
pct = DecelerateInterpolator(progress);
}
else if(vMode == 2.05){
pct = AnticipateInterpolator(progress);
}
else if(vMode == 2.06){
pct = OvershootInterpolator(progress);
}
else if(vMode == 2.07){
pct = AnticipateOvershootInterpolator(progress);
}
else if(vMode == 2.08){
pct = BounceInterpolator(progress);
}
else if(vMode == 2.09){
pct = CycleInterpolator(progress);
}
else if(vMode == 2.10){
pct = FastOutSlowInInterpolator(progress);
}
else if(vMode == 2.11){
pct = LinearOutSlowInInterpolator(progress);
}
else if(vMode == 2.12){
pct = FastOutLinearInInterpolator(progress);
}
else if(vMode == 2.13){
pct = CustomMocosSpringInterpolator(progress);
}
else if(vMode == 2.14){
pct = CustomSpringInterpolator(progress);
}
else if(vMode == 2.15){
pct = CustomBounceInterpolator(progress);
}
else if(vMode == 2.16){
pct = CustomDampingInterpolator(progress);
}
else if(vMode == 2.17){
pct = BounceInterpolator(progress);
}
return smoothstep( pct - 0.025, pct, st.y) -
smoothstep( pct, pct + 0.025, st.y);
}
vec4 plot2D(in vec2 _st, in float _width ,vec3 initColor) {
const float samples = float(Samples);
vec2 steping = _width*vec2(scale)/samples;
float count = 0.0;
float mySamples = 0.0;
for (float i = 0.0; i < samples; i++) {
for (float j = 0.0;j < samples; j++) {
if (i*i+j*j>samples*samples)
continue;
mySamples++;
float ii = i;
float jj = j;
float f = 0.;
if(vMode == 0.0){
f = FlingSimulator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 1.0){
f = SpringSimulator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.0){
f = CubicBezierSimulator((_st.x+ ii*steping.x),vec4(v_factor1,v_factor2,v_factor3,v_factor4))-(_st.y+ jj*steping.y);
}
else if(vMode == 2.01){
f = LinearInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.02){
f = AccelerateDecelerateInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.03){
f = AccelerateInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.04){
f = DecelerateInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.05){
f = AnticipateInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.06){
f = OvershootInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.07){
f = AnticipateOvershootInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.08){
f = BounceInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.09){
f = CycleInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.10){
f = FastOutSlowInInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.11){
f = LinearOutSlowInInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.12){
f = FastOutLinearInInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.13){
f = CustomMocosSpringInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.14){
f = CustomSpringInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.15){
f = CustomBounceInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.16){
f = CustomDampingInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
else if(vMode == 2.17){
f = AndroidSpringInterpolator((_st.x+ ii*steping.x) )-(_st.y+ jj*steping.y);
}
count += (f>0.0) ? 1. : -1.0 ;
}
}
if (abs(count)!=mySamples)
return vec4( vec3(abs(float(count*50.))/float(mySamples))*initColor ,1.); // count*10. make stroke soilder
return vec4(0.);
}
float circle(in vec2 _st, in float _radius){
vec2 dist = _st-vec2(0.5);
return 1.-smoothstep(_radius-(_radius*0.01),_radius+(_radius*0.01),dot(dist,dist)*4.0);
}
vec2 circle2D(in float progress){
if(vMode == 0.0){
return vec2(progress,FlingSimulator(progress));
}
else if(vMode == 1.0){
return vec2(progress,SpringSimulator(progress));
}
else if(vMode == 2.0){
return vec2(progress,CubicBezierSimulator(progress,vec4(v_factor1,v_factor2,v_factor3,v_factor4)));
}
else if(vMode == 2.01){
return vec2(progress,LinearInterpolator(progress));
}
else if(vMode == 2.02){
return vec2(progress,AccelerateDecelerateInterpolator(progress));
}
else if(vMode == 2.03){
return vec2(progress,AccelerateInterpolator(progress));
}
else if(vMode == 2.04){
return vec2(progress,DecelerateInterpolator(progress));
}
else if(vMode == 2.05){
return vec2(progress,AnticipateInterpolator(progress));
}
else if(vMode == 2.06){
return vec2(progress,OvershootInterpolator(progress));
}
else if(vMode == 2.07){
return vec2(progress,AnticipateOvershootInterpolator(progress));
}
else if(vMode == 2.08){
return vec2(progress,BounceInterpolator(progress));
}
else if(vMode == 2.09){
return vec2(progress,CycleInterpolator(progress));
}
else if(vMode == 2.10){
return vec2(progress,FastOutSlowInInterpolator(progress));
}
else if(vMode == 2.11){
return vec2(progress,LinearOutSlowInInterpolator(progress));
}
else if(vMode == 2.12){
return vec2(progress,FastOutLinearInInterpolator(progress));
}
else if(vMode == 2.13){
return vec2(progress,CustomMocosSpringInterpolator(progress));
}
else if(vMode == 2.14){
return vec2(progress,CustomSpringInterpolator(progress));
}
else if(vMode == 2.15){
return vec2(progress,CustomBounceInterpolator(progress));
}
else if(vMode == 2.16){
return vec2(progress,CustomDampingInterpolator(progress));
}
else if(vMode == 2.17){
return vec2(progress,AndroidSpringInterpolator(progress));
}
}
float when_gt(float x, float y) {
return max(sign(x - y), 0.0);
}
float when_lt(float x, float y) {
return max(sign(y - x), 0.0);
}
void main(){
vec2 st = vUv;
//#Scale
float mScale = 0.8;
st *= 1./mScale;
st -= (1./mScale - 1.)/2.;
timeProgress = min(u_time/vDuration,1.);
vec4 color = vec4(0.,0.,0.,0.);
if(st.x>-0.01 &&st.y>-0.5 && st.y<1.5 && st.x<1.01){
// ### Plot2D
color = plot2D(st,lineWidth,v_secondaryColor);
// ### Plot
//float v2 = plot(st, st.x);
//color = mix(color, vec4(v_secondaryColor,1.), v2);
if(st.x
#09CDFF
#FFFFFF
#4DFFFFFF
#000000
#00000000
#4C4C4C
#B0B0B0
#19181D
#111112
#19181D
#1A191E
================================================
FILE: animer/src/main/res/values/dimension.xml
================================================
4dp
4dp
46dp
13px
================================================
FILE: animer/src/main/res/values/styles.xml
================================================
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.martinrgb.animerexample_1"
minSdkVersion 27
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation project(':animer')
//implementation 'com.martinrgb:animer:0.1.6.0'
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: app/src/androidTest/java/com/martinrgb/animerexample/ExampleInstrumentedTest.java
================================================
package com.martinrgb.animerexample;
import android.content.Context;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.martinrgb.swipeexample", appContext.getPackageName());
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/martinrgb/animerexample/MainActivity.java
================================================
package com.martinrgb.animerexample;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import com.martinrgb.animer.Animer;
import com.martinrgb.animer.core.interpolator.AndroidNative.AccelerateDecelerateInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.DecelerateInterpolator;
import com.martinrgb.animer.monitor.AnConfigRegistry;
import com.martinrgb.animer.monitor.AnConfigView;
public class MainActivity extends AppCompatActivity {
private ImageView iv1,iv2,iv3,iv4;
private Animer animer1,animer2,animer3,animer4,animer5,animer6,animer7,animerNew;
private boolean isOpen,isOpen2,isOpen3,isOpen4 = false;
private AnConfigView mAnimerConfiguratorView;
private ImageView ivNew;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
deleteBars();
setContentView(R.layout.activity_main);
iv1 = findViewById(R.id.iv);
iv2 = findViewById(R.id.iv2);;
iv3 = findViewById(R.id.iv3);
iv4 = findViewById(R.id.iv4);
ivNew = findViewById(R.id.iv5);
tv = findViewById(R.id.tv);
Animer.AnimerSolver solverB = Animer.springDroid(1000,0.5f);
animer1 = new Animer(iv1,solverB,Animer.TRANSLATION_X,0,500);
animer2 = new Animer(iv2,Animer.interpolatorDroid(new AccelerateDecelerateInterpolator(),1500),Animer.TRANSLATION_X,0,500);
animer3 = new Animer(iv3,Animer.interpolatorDroid(new DecelerateInterpolator(2),1200),Animer.TRANSLATION_X,0,720);
animer4 = new Animer(iv1,Animer.springRK4(100,10),Animer.ROTATION,1,1.2f);
animer5 = new Animer(iv2,Animer.springDHO(200,20),Animer.ROTATION,0,720);
animer6 = new Animer(iv3,Animer.springOrigamiPOP(30,10),Animer.ROTATION,200,800);
animer7 = new Animer(iv4,Animer.springRK4(230,15),Animer.SCALE,1,0.5f);
ivNew.getLayoutParams().width = 44 * 3;
animerNew = new Animer();
animerNew.setSolver(Animer.springDroid(600,0.99f));
animerNew.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
ivNew.getLayoutParams().height = (int) (44*3 + progress*(200-44)*3);
ivNew.requestLayout();
}
});
animerNew.setCurrentValue(0);
animer1.setCurrentValue(200);
animer2.setCurrentValue(200);
animer3.setCurrentValue(200);
mAnimerConfiguratorView = (AnConfigView) findViewById(R.id.an_configurator);
AnConfigRegistry.getInstance().addAnimer("Image Scale Animation",animer7);
AnConfigRegistry.getInstance().addAnimer("Red TranslationX",animer1);
AnConfigRegistry.getInstance().addAnimer("Blue TranslationX",animer2);
AnConfigRegistry.getInstance().addAnimer("Green TranslationX",animer3);
AnConfigRegistry.getInstance().addAnimer("Red Rotation",animer4);
AnConfigRegistry.getInstance().addAnimer("Blue Rotation",animer5);
AnConfigRegistry.getInstance().addAnimer("Green Rotation",animer6);
AnConfigRegistry.getInstance().addAnimer("Volume Simulation",animerNew);
mAnimerConfiguratorView.refreshAnimerConfigs();
iv1.setOnClickListener(view -> {
if(!isOpen){
animer1.setEndValue(800);
//animer1.animateToState("stateA");
animer4.setEndValue(720);
}
else{
animer1.setEndValue(200);
//animer1.animateToState("stateB");
animer4.setEndValue(0);
}
isOpen = !isOpen;
});
iv2.setOnClickListener(view -> {
if(!isOpen2){
animer2.setEndValue(800);
animer5.setEndValue(720);
}
else{
animer2.setEndValue(200);
animer5.setEndValue(0);
}
isOpen2 = !isOpen2;
});
iv3.setOnClickListener(view -> {
if(!isOpen3){
animer3.setEndValue(800);
animer6.setEndValue(720);
}
else{
animer3.setEndValue(200);
animer6.setEndValue(0);
}
isOpen3 = !isOpen3;
});
iv4.setOnClickListener(view -> {
if(!isOpen4){
animer7.setEndValue(0.5f);
}
else{
animer7.setEndValue(1f);
}
isOpen4 = !isOpen4;
});
ivNew.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
animerNew.setEndValue(1);
tv.setText("手势状态:Down");
Log.i("TAG", "touched down");
break;
case MotionEvent.ACTION_MOVE:
Log.i("TAG", "touched move");
break;
case MotionEvent.ACTION_UP:
animerNew.setEndValue(0);
tv.setText("手势状态:Up");
Log.i("TAG", "touched up");
break;
case MotionEvent.ACTION_CANCEL:
Log.i("TAG", "touched cancel");
break;
}
return true;
}
});
}
private void deleteBars() {
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
}
}
================================================
FILE: app/src/main/java/com/martinrgb/animerexample/PrototypeActivity.java
================================================
package com.martinrgb.animerexample;
import android.graphics.Point;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import androidx.appcompat.app.AppCompatActivity;
import com.martinrgb.animer.Animer;
import com.martinrgb.animer.core.interpolator.AndroidNative.AccelerateDecelerateInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.FastOutSlowInInterpolator;
import com.martinrgb.animer.core.interpolator.AndroidNative.LinearInterpolator;
import com.martinrgb.animer.core.util.AnUtil;
import com.martinrgb.animer.monitor.AnConfigRegistry;
import com.martinrgb.animer.monitor.AnConfigView;
public class PrototypeActivity extends AppCompatActivity {
private SmoothCornersImage smoothCornersImage;
private AnConfigView mAnimerConfiguratorView;
private static final Animer.AnimerSolver solverRect = Animer.springRK4(650,45);
private static final Animer.AnimerSolver solverScale = Animer.springRK4(400,30f);
private static final Animer.AnimerSolver solverButtonScale = Animer.springRK4(500,30f);
private static final Animer.AnimerSolver solverRadius = Animer.interpolatorDroid(new FastOutSlowInInterpolator(),600);
private static final Animer.AnimerSolver solverTrans = Animer.springDroid(800,0.95f);
private static final Animer.AnimerSolver solverAlpha = Animer.interpolatorDroid(new LinearInterpolator(),100);
private static final Animer.AnimerSolver solverNav = Animer.interpolatorDroid(new AccelerateDecelerateInterpolator(),150);
private Animer mRectAnimer,mTransAnimer,mRadiusAnimer,mAlphaAnimer, mNavAnimer,mScaleAnimer,mScaleButtonAnimer;
private boolean isShowDetail = false;
private float initW, initH, initR, initTranslationX;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
deleteBars();
setContentView(R.layout.activity_prototype);
smoothCornersImage = findViewById(R.id.smooth_iv);
ViewTreeObserver vto = smoothCornersImage.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
smoothCornersImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
initTranslationX = smoothCornersImage.getTranslationX();
initW = smoothCornersImage.getMeasuredWidth();
initH = smoothCornersImage.getMeasuredHeight();
initR = smoothCornersImage.getRoundRadius();
}
});
getDisplayPoint();
setAnimerSystem();
findViewById(R.id.smooth_iv).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
// if (!isShowDetail) {
// mScaleAnimer.setEndValue(0.95f);
// }
mScaleAnimer.setEndValue(0.95f);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
mRectAnimer.setEndValue(1);
mTransAnimer.setEndValue(1);
mRadiusAnimer.setEndValue(0);
mAlphaAnimer.setEndValue(1);
mNavAnimer.setEndValue(1);
mScaleAnimer.setEndValue(1f);
// if (!isShowDetail) {
// mRectAnimer.setEndValue(1);
// mTransAnimer.setEndValue(1);
// mRadiusAnimer.setEndValue(0);
// mAlphaAnimer.setEndValue(1);
// mNavAnimer.setEndValue(1);
// mScaleAnimer.setEndValue(1f);
// } else {
// mRectAnimer.setEndValue(0);
// mTransAnimer.setEndValue(0);
// mRadiusAnimer.setEndValue(initR);
// mAlphaAnimer.setEndValue(0);
// mNavAnimer.setEndValue(0);
// }
isShowDetail = !isShowDetail;
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
});
findViewById(R.id.arrow_iv).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
// if (isShowDetail) {
// mScaleAnimer.setEndValue(0.95f);
// }
mScaleButtonAnimer.setEndValue(0.95f);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
mRectAnimer.setEndValue(0);
mTransAnimer.setEndValue(0);
mRadiusAnimer.setEndValue(initR);
mAlphaAnimer.setEndValue(0);
mNavAnimer.setEndValue(0);
mScaleButtonAnimer.setEndValue(1);
//isShowDetail = !isShowDetail;
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
});
}
private void setAnimerSystem() {
mRectAnimer = new Animer();
mRectAnimer.setSolver(solverRect);
mRectAnimer.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
float XVal = (float) AnUtil.mapValueFromRangeToRange(value, 0, 1, initTranslationX, 0);
float YVal = (float) AnUtil.mapValueFromRangeToRange(value, 0, 1, 0, -918);
float widthVal = (float) AnUtil.mapValueFromRangeToRange(value, 0, 1, initW, sWidth);
float heightVal = (float) AnUtil.mapValueFromRangeToRange(value, 0, 1, initH, sWidth);
float reverseHeightVal = (float) AnUtil.mapValueFromRangeToRange(value, 0, 1, (sWidth - 420) / 2, 0);
smoothCornersImage.setRectSize(widthVal, heightVal);
smoothCornersImage.setTranslationX(XVal);
smoothCornersImage.setTranslationY(YVal);
findViewById(R.id.smooth_text_iv).setTranslationY(-(heightVal - 420) / 2);
findViewById(R.id.detail_text_iv).setTranslationY(reverseHeightVal);
findViewById(R.id.arrow_iv).setTranslationY(reverseHeightVal);
}
});
mTransAnimer = new Animer();
mTransAnimer.setSolver(solverTrans);
mTransAnimer.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
float transVal = (float) AnUtil.mapValueFromRangeToRange(value, 0, 1, 0, 1170);
findViewById(R.id.top_iv).setTranslationY(-transVal);
findViewById(R.id.bottom_iv).setTranslationY(transVal);
}
});
mRadiusAnimer = new Animer();
mRadiusAnimer.setSolver(solverRadius);
mRadiusAnimer.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
smoothCornersImage.setRoundRadius(value);
}
});
mScaleAnimer = new Animer();
mScaleAnimer.setSolver(solverScale);
mScaleAnimer.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
smoothCornersImage.setScaleX(value);
smoothCornersImage.setScaleY(value);
smoothCornersImage.setAlpha(value);
}
});
mScaleButtonAnimer = new Animer();
mScaleButtonAnimer.setSolver(solverButtonScale);
mScaleButtonAnimer.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
findViewById(R.id.arrow_iv).setScaleX(value);
findViewById(R.id.arrow_iv).setScaleY(value);
}
});
mScaleButtonAnimer.setCurrentValue(1);
mScaleAnimer.setCurrentValue(1);
mAlphaAnimer = new Animer();
mAlphaAnimer.setSolver(solverAlpha);
mAlphaAnimer.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
float alphaVal = (float) AnUtil.mapValueFromRangeToRange(value, 0, 1, 1, 0);
float reverseAlpha = (float) AnUtil.mapValueFromRangeToRange(value, 0.5f, 1, 0, 1);
findViewById(R.id.top_iv).setAlpha(alphaVal);
findViewById(R.id.bottom_iv).setAlpha(alphaVal);
findViewById(R.id.smooth_text_iv).setAlpha(alphaVal);
findViewById(R.id.arrow_iv).setAlpha(reverseAlpha);
findViewById(R.id.detail_text_iv).setAlpha(reverseAlpha);
}
});
mNavAnimer = new Animer();
mNavAnimer.setSolver(solverNav);
mNavAnimer.setUpdateListener(new Animer.UpdateListener() {
@Override
public void onUpdate(float value, float velocity, float progress) {
float transVal = (float) AnUtil.mapValueFromRangeToRange(value, 0, 1, 0, 300);
findViewById(R.id.nav_iv).setTranslationY(transVal);
}
});
mAnimerConfiguratorView = (AnConfigView) findViewById(R.id.an_configurator);
AnConfigRegistry.getInstance().addAnimer("Image Rect Animation",mRectAnimer);
AnConfigRegistry.getInstance().addAnimer("Image Scale Animation",mScaleAnimer);
AnConfigRegistry.getInstance().addAnimer("Image Radius Animation",mRadiusAnimer);
AnConfigRegistry.getInstance().addAnimer("Button Scale Animation",mScaleButtonAnimer);
AnConfigRegistry.getInstance().addAnimer("List Trans Animation",mTransAnimer);
AnConfigRegistry.getInstance().addAnimer("Total Alpha Animation",mAlphaAnimer);
AnConfigRegistry.getInstance().addAnimer("Navigation Animation",mNavAnimer);
mAnimerConfiguratorView.refreshAnimerConfigs();
}
private float sWidth, sHeight;
private void getDisplayPoint() {
Point point = new Point();
getWindowManager().getDefaultDisplay().getSize(point);
sWidth = (float) point.x;
sHeight = (float) point.y;
}
private void deleteBars() {
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
}
}
================================================
FILE: app/src/main/java/com/martinrgb/animerexample/ScrollerActivity.java
================================================
package com.martinrgb.animerexample;
import android.content.Context;
import android.content.res.Resources;
import android.opengl.Visibility;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.martinrgb.animer.component.scrollview.AnScrollView;
import com.martinrgb.animer.monitor.AnConfigRegistry;
import com.martinrgb.animer.monitor.AnConfigView;
public class ScrollerActivity extends AppCompatActivity {
private final int ROW_COUNT = 20;
private int[] imageViews = new int[]{R.drawable.img_1,R.drawable.img_2,R.drawable.img_3,R.drawable.img_4};
private AnConfigView mAnimerConfiguratorView;
private int cellSize;
private float screenWidth,screenHeight;
private AnScrollView customScrollViewV,customScrollViewH;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
deleteBars();
setContentView(R.layout.activity_scroller);
measureDisplay();
createLayout();
addAnimerConfig();
}
private void createLayout(){
// Vertical content
ViewGroup contentV = (ViewGroup) findViewById(R.id.content_view_v);
for (int i = 0; i < ROW_COUNT; i++) {
ExampleRowView exampleRowView = new ExampleRowView(getApplicationContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
(int) LinearLayout.LayoutParams.WRAP_CONTENT
);
exampleRowView.getRootLayout().setLayoutParams(params);
exampleRowView.setHeader("Header " + i);
exampleRowView.setSub("Sub " + i);
exampleRowView.setImage(imageViews[i%4]);
contentV.addView(exampleRowView);
}
// Horizontal content
ViewGroup contentH = (ViewGroup) findViewById(R.id.content_view_h);
for (int i = 0; i < ROW_COUNT; i++) {
ExampleRowView exampleRowView = new ExampleRowView(getApplicationContext());
exampleRowView.setHeader("Header " + i);
exampleRowView.setSub("Sub " + i);
exampleRowView.setImage(imageViews[i%4]);
if(i == 0){
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
cellSize,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins( (int)(screenWidth - cellSize)/2, 0, 0, 0);
exampleRowView.setLayoutParams(params);
}
else if(i == ROW_COUNT - 1){
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
cellSize,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins( 0, 0, (int)(screenWidth - cellSize)/2, 0);
exampleRowView.setLayoutParams(params);
}
contentH.addView(exampleRowView);
}
// Vertical sv
customScrollViewV = findViewById(R.id.scrollView_v);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
(int)RelativeLayout.LayoutParams.MATCH_PARENT
);
params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
customScrollViewV.setLayoutParams(params);
customScrollViewV.getScroller().setVertScroll(true);
// Horizontal sv
customScrollViewH = findViewById(R.id.scrollView_h);
params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
(int) screenHeight/2
);
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
customScrollViewH.setLayoutParams(params);
customScrollViewH.getScroller().setVertScroll(false);
customScrollViewH.getScroller().setFixedScroll(true,cellSize);
customScrollViewH.setVisibility(View.INVISIBLE);
}
private void addAnimerConfig(){
mAnimerConfiguratorView = (AnConfigView) findViewById(R.id.an_configurator);
AnConfigRegistry.getInstance().addAnimer("V_Fling",customScrollViewV.getScroller().getFlingAnimer());
AnConfigRegistry.getInstance().addAnimer("V_SpringBack",customScrollViewV.getScroller().getSpringAnimer());
AnConfigRegistry.getInstance().addAnimer("V_FakeFling When Fixed Scroll",customScrollViewV.getScroller().getFakeFlingAnimer());
AnConfigRegistry.getInstance().addAnimer("H_Fling",customScrollViewH.getScroller().getFlingAnimer());
AnConfigRegistry.getInstance().addAnimer("H_SpringBack",customScrollViewH.getScroller().getSpringAnimer());
AnConfigRegistry.getInstance().addAnimer("H_FakeFling When Fixed Scroll",customScrollViewH.getScroller().getFakeFlingAnimer());
mAnimerConfiguratorView.refreshAnimerConfigs();
}
private class ExampleRowView extends LinearLayout {
private final TextView mHeaderView;
private final TextView mSubView;
private final SmoothCornersImage mImageView;
private final LinearLayout root;
public ExampleRowView(Context context) {
super(context);
LayoutInflater inflater = LayoutInflater.from(context);
ViewGroup view = (ViewGroup) inflater.inflate(R.layout.custom_cell_view, this, false);
root = view.findViewById(R.id.root);
mHeaderView = (TextView) view.findViewById(R.id.head_view);
mSubView = (TextView) view.findViewById(R.id.sub_view);
mImageView = view.findViewById(R.id.img_view);
mImageView.setRoundRadius(60);
addView(view);
}
public void setHeader(String text) {
mHeaderView.setText(text);
}
public void setSub(String text) {
mSubView.setText(text);
}
public void setImage(int id) {
mImageView.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(),id));
mImageView.setScaleType(ImageView.ScaleType.MATRIX);
}
public LinearLayout getRootLayout(){
return root;
}
}
private void deleteBars() {
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
}
private void measureDisplay() {
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics ();
display.getMetrics(outMetrics);
float density = getResources().getDisplayMetrics().density;
float dpHeight = outMetrics.heightPixels / density;
float dpWidth = outMetrics.widthPixels / density;
screenHeight = dpToPx(dpHeight,getResources());
screenWidth = dpToPx(dpWidth,getResources());
cellSize = (int) getResources().getDimension(R.dimen.cell_size_dp);
Log.e("inDP","doHeight"+ dpHeight + "dpWidth" + dpWidth);
}
public static int dpToPx(float dp, Resources res) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,res.getDisplayMetrics());
}
}
================================================
FILE: app/src/main/java/com/martinrgb/animerexample/SmoothCornersImage.java
================================================
package com.martinrgb.animerexample;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import androidx.appcompat.widget.AppCompatImageView;
public class SmoothCornersImage extends AppCompatImageView {
private final Paint paint = new Paint();
private final Xfermode mode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
private Bitmap imageBitmap;
private Rect dstRect;
private Matrix matrix;
float cx, cy;
private float WIDTH = 400;
private float HEIGHT = 400;
private float SKETCH_ROUND_RECT_RADIUS = 100f;
private boolean ROUND_TL = true,ROUND_TR = true,ROUND_BL = true,ROUND_BR = true;
private boolean isSquare = false;
public SmoothCornersImage(Context context) {
super(context);
getAttributes(null);
}
public SmoothCornersImage(Context context, AttributeSet attrs) {
super(context, attrs);
getAttributes(attrs);
}
public SmoothCornersImage(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
getAttributes(attrs);
}
private void getAttributes(AttributeSet attr) {
if(attr == null){
return;
}
else{
TypedArray typedArray = getContext()
.getTheme()
.obtainStyledAttributes(attr, R.styleable.SmoothCornersImage, 0, 0);
try {
SKETCH_ROUND_RECT_RADIUS = typedArray.getInteger(R.styleable.SmoothCornersImage_smooth_radius, 20);
isSquare = typedArray.getBoolean(R.styleable.SmoothCornersImage_is_square, false);
} finally {
typedArray.recycle();
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(w != 0 && h != 0){
this.cx = w / 2.0f;
this.cy = h / 2.0f;
dstRect = new Rect(0, 0, w, h);
this.WIDTH = isSquare()?Math.min(w,h):w;
this.HEIGHT = isSquare()?Math.min(w,h):h;
matrix = new Matrix();
Point canvasCenter = new Point((int)this.cx,(int)this.cy);
Point bmpCenter = new Point(this.imageBitmap.getWidth() / 2, this.imageBitmap.getHeight() / 2);
float xRatio = bmpCenter.x/this.cx;
float yRatio = bmpCenter.y/this.cy;
float ratio;
if(xRatio > yRatio){
ratio = 1/yRatio;
}
else {
ratio = 1/xRatio;
}
Log.e("ratio", String.valueOf(ratio));
matrix.postTranslate(cx - bmpCenter.x, cy - bmpCenter.y);
matrix.postScale(ratio,ratio, cx, cy);
}
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
this.imageBitmap = bm;
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
if(drawable instanceof BitmapDrawable)
this.imageBitmap = ((BitmapDrawable)drawable).getBitmap();
else {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
if(width != -1 && height != -1)
this.imageBitmap = Bitmap.createBitmap(width
, height
, Bitmap.Config.ARGB_8888);
else
this.imageBitmap = Bitmap.createBitmap(1
, 1
, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(this.imageBitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight() );
drawable.draw(canvas);
}
}
//############ onDraw ############
@Override
protected void onDraw(Canvas canvas) {
if(imageBitmap != null){
canvas.saveLayer(0,
0,
canvas.getWidth(),
canvas.getHeight(),
paint,
Canvas.ALL_SAVE_FLAG);
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
Path path;
if(isSquare){
if(SKETCH_ROUND_RECT_RADIUS == WIDTH/2){
canvas.translate(cx-WIDTH/2, cy-HEIGHT/2);
drawAndroidRoundRect(0,0,WIDTH,HEIGHT,SKETCH_ROUND_RECT_RADIUS,SKETCH_ROUND_RECT_RADIUS,ROUND_TL,ROUND_TR,ROUND_BL,ROUND_BR,canvas);
}
else{
path = SketchRealSmoothRect(0, 0, WIDTH , WIDTH , SKETCH_ROUND_RECT_RADIUS,SKETCH_ROUND_RECT_RADIUS,
ROUND_TL,ROUND_TR,ROUND_BL,ROUND_BR);
canvas.drawPath(path,paint);
}
}
else{
if(SKETCH_ROUND_RECT_RADIUS == Math.min(WIDTH,HEIGHT)/2){
canvas.translate(cx-WIDTH/2, cy-HEIGHT/2);
drawAndroidRoundRect(0,0,WIDTH,HEIGHT,SKETCH_ROUND_RECT_RADIUS,SKETCH_ROUND_RECT_RADIUS,ROUND_TL,ROUND_TR,ROUND_BL,ROUND_BR,canvas);
}
else{
path = SketchRealSmoothRect(0, 0, WIDTH , HEIGHT , SKETCH_ROUND_RECT_RADIUS,SKETCH_ROUND_RECT_RADIUS,
ROUND_TL,ROUND_TR,ROUND_BL,ROUND_BR);
canvas.drawPath(path,paint);
}
}
paint.setXfermode(mode);
if(imageBitmap.getWidth() != 1){
//canvas.drawBitmap(this.imageBitmap, -(this.imageBitmap.getWidth() - canvas.getWidth())/2, -(this.imageBitmap.getHeight() - canvas.getHeight())/2, paint);
//canvas.drawBitmap(this.imageBitmap, 0,0, paint);
canvas.drawBitmap(this.imageBitmap,matrix,paint);
}
else{
canvas.drawBitmap(this.imageBitmap, null, dstRect, paint);
}
paint.reset();
}
}
//############ Define Path ############
public Path SketchRealSmoothRect(
float left, float top, float right, float bottom, float rx, float ry,
boolean tl, boolean tr, boolean bl, boolean br
){
Path path = new Path();
if (rx < 0) rx = 0;
if (ry < 0) ry = 0;
float width = right - left;
float height = bottom - top;
float posX = cx - width/2;
float posY = cy - height/2;
float r = rx;
float vertexRatio;
if(r/Math.min(width/2,height/2) > 0.5){
float percentage = ((r/Math.min(width/2,height/2)) - 0.5f)/0.4f;
float clampedPer = Math.min(1,percentage);
vertexRatio = 1.f - (1 - 1.104f/1.2819f)*clampedPer;
}
else{
vertexRatio = 1.f;
}
float controlRatio;
if(r/Math.min(width/2,height/2) > 0.6){
float percentage = ((r/Math.min(width/2,height/2)) - 0.6f)/0.3f;
float clampedPer = Math.min(1,percentage);
controlRatio = 1 + (0.8717f/0.8362f - 1)*clampedPer;
}
else{
controlRatio = 1;
}
path.moveTo(posX + width/2 , posY);
if(!tr){
path.lineTo(posX + width, posY);
}
else{
path.lineTo(posX + Math.max(width/2,width - r/100.0f*128.19f*vertexRatio), posY);
path.cubicTo(posX + width - r/100.f*83.62f*controlRatio, posY,posX + width - r/100.f*67.45f,posY + r/100.f*4.64f, posX + width - r/100.f*51.16f, posY + r/100.f*13.36f);
path.cubicTo(posX + width - r/100.f*34.86f, posY + r/100.f*22.07f,posX + width - r/100.f*22.07f,posY + r/100.f*34.86f, posX + width - r/100.f*13.36f, posY + r/100.f*51.16f);
path.cubicTo(posX + width - r/100.f*4.64f, posY + r/100.f*67.45f,posX + width,posY + r/100.f*83.62f*controlRatio, posX + width, posY + Math.min(height/2,r/100.f*128.19f*vertexRatio));
}
if(!br){
path.lineTo(posX + width, posY + height);
}
else{
path.lineTo(posX + width, posY + Math.max(height/2,height - r/100.f*128.19f*vertexRatio));
path.cubicTo(posX + width, posY + height - r/100.f*83.62f*controlRatio,posX + width - r/100.f*4.64f,posY + height - r/100.f*67.45f, posX + width - r/100.f*13.36f, posY + height - r/100.f*51.16f);
path.cubicTo(posX + width - r/100.f*22.07f, posY + height - r/100.f*34.86f,posX + width - r/100.f*34.86f,posY + height - r/100.f*22.07f, posX + width - r/100.f*51.16f, posY + height - r/100.f*13.36f);
path.cubicTo(posX + width - r/100.f*67.45f, posY + height - r/100.f*4.64f,posX + width - r/100.f*83.62f*controlRatio,posY + height, posX + Math.max(width/2,width - r/100.f*128.19f*vertexRatio), posY + height);
}
if(!bl){
path.lineTo(posX, posY + height);
}
else{
path.lineTo(posX + Math.min(width/2,r/100.f*128.19f*vertexRatio), posY + height);
path.cubicTo(posX + r/100.f*83.62f*controlRatio, posY + height,posX + r/100.f*67.45f,posY + height - r/100.f*4.64f, posX + r/100.f*51.16f, posY + height - r/100.f*13.36f);
path.cubicTo(posX + r/100.f*34.86f, posY + height - r/100.f*22.07f,posX + r/100.f*22.07f,posY + height - r/100.f*34.86f, posX + r/100.f*13.36f, posY + height - r/100.f*51.16f);
path.cubicTo(posX + r/100.f*4.64f, posY + height - r/100.f*67.45f,posX ,posY + height - r/100.f*83.62f*controlRatio, posX , posY + Math.max(height/2,height - r/100.f*128.19f*vertexRatio));
}
if(!tl){
path.lineTo(posX, posY);
}
else{
path.lineTo(posX, posY + Math.min(height/2,r/100.f*128.19f*vertexRatio));
path.cubicTo(posX, posY + r/100.f*83.62f*controlRatio,posX + r/100.f*4.64f,posY + r/100.f*67.45f, posX + r/100.f*13.36f, posY + r/100.f*51.16f);
path.cubicTo(posX + r/100.f*22.07f, posY + r/100.f*34.86f,posX + r/100.f*34.86f,posY + r/100.f*22.07f, posX + r/100.f*51.16f, posY + r/100.f*13.36f);
path.cubicTo(posX + r/100.f*67.45f, posY + r/100.f*4.64f,posX + r/100.f*83.62f*controlRatio,posY, posX + Math.min(width/2,r/100.f*128.19f*vertexRatio), posY);
}
path.close();
return path;
}
public void drawAndroidRoundRect(
float left, float top, float right, float bottom, float rx, float ry,
boolean tl, boolean tr, boolean bl, boolean br,Canvas canvas
){
float w = right - left;
float h = bottom - top;
final Rect rect = new Rect((int)left, (int)top, (int)right,(int)bottom);
final RectF rectF = new RectF(rect);
canvas.drawRoundRect(rectF,rx,ry, paint);
if(!tl){
canvas.drawRect(0, 0, w/2, h/2, paint);
Path path = new Path();
path.moveTo(0,h/2);
path.lineTo(0,0);
path.lineTo(w/2,0);
canvas.drawPath(path,paint);
}
if(!tr){
canvas.drawRect(w/2, 0, w, h/2, paint);
Path path = new Path();
path.moveTo(w/2,0);
path.lineTo(w,0);
path.lineTo(w,h/2);
canvas.drawPath(path,paint);
}
if(!bl){
canvas.drawRect(0, h/2, w/2, h, paint);
Path path = new Path();
path.moveTo(0,h/2);
path.lineTo(0,h);
path.lineTo(w/2,h);
canvas.drawPath(path,paint);
}
if(!br){
canvas.drawRect(w/2, h/2, w, h, paint);
Path path = new Path();
path.moveTo(w/2,h);
path.lineTo(w,h);
path.lineTo(w,h/2);
canvas.drawPath(path,paint);
}
}
//############ Getter & Setter ############
public float getRoundRadius() {
return SKETCH_ROUND_RECT_RADIUS;
}
public void setRoundRadius(float radius){
this.SKETCH_ROUND_RECT_RADIUS = Math.max(0,getRadiusInMaxRange(WIDTH,HEIGHT,radius));
this.invalidate();
}
public float getMAXRadius(float width,float height){
float minBorder;
if(width > height){
minBorder = height;
}
else{
minBorder = width;
}
return minBorder/2;
}
private float getRadiusInMaxRange(float width,float height,float radius) {
float realRadius = Math.min(radius, getMAXRadius(width, height));
return realRadius;
}
public PointF getRectSize(){
return new PointF(getRectWidth(),getRectWidth());
}
public void setRectSize(float width,float height){
setRectWidth(width);
setRectHeight(height);
requestLayout();
//this.invalidate();
}
private float getRectWidth() {
return WIDTH;
}
private void setRectWidth(float WIDTH) {
this.WIDTH = WIDTH;
getLayoutParams().width = (int)WIDTH;
}
private float getRectHeight() {
return HEIGHT;
}
private void setRectHeight(float HEIGHT) {
this.HEIGHT = HEIGHT;
getLayoutParams().height = (int)HEIGHT;
}
public boolean isSquare() {
return isSquare;
}
public void setIsSquare(boolean square) {
isSquare = square;
this.invalidate();
}
public void setRectRoundEnable(boolean tl,boolean tr,boolean bl,boolean br){
this.ROUND_TL = tl;
this.ROUND_TR = tr;
this.ROUND_BL = bl;
this.ROUND_BR = br;
this.invalidate();
}
}
================================================
FILE: app/src/main/res/drawable/background_elevation.xml
================================================
-
================================================
FILE: app/src/main/res/drawable/ic_arrow.xml
================================================
================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: app/src/main/res/drawable/myrect.xml
================================================
================================================
FILE: app/src/main/res/drawable-nodpi/mute.xml
================================================
================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_prototype.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_scroller.xml
================================================
================================================
FILE: app/src/main/res/layout/custom_cell_view.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: app/src/main/res/values/attr.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#008577
#00574B
#D81B60
================================================
FILE: app/src/main/res/values/dimension.xml
================================================
100dp
================================================
FILE: app/src/main/res/values/strings.xml
================================================
Animer Example
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/test/java/com/martinrgb/animerexample/ExampleUnitTest.java
================================================
package com.martinrgb.animerexample;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
// classpath 'com.novoda:bintray-release:0.9.2'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Tue May 26 17:09:15 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip
d
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app', ':animer'