() {
73 | @Override
74 | public int compare(View left, View right) {
75 | double leftHorizontalDistance = Utils.horizontalDistance(comparisonPoint, Utils.viewToPoint(left));
76 | double leftVerticalDistance = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(left));
77 | double rightHorizontalDistance = Utils.horizontalDistance(comparisonPoint, Utils.viewToPoint(right));
78 | double rightVerticalDistance = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(right));
79 |
80 | if (leftVerticalDistance < rightVerticalDistance ||
81 | leftVerticalDistance == rightVerticalDistance &&
82 | leftHorizontalDistance < rightHorizontalDistance) {
83 | return -1;
84 | }
85 | return 1;
86 | }
87 | });
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/willowtreeapps/spruce/dynamics/DynamicAnimation.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.dynamics
24 |
25 | /**
26 | * Creates [SpruceFlingAnimation] for a property that can be accessed via the provided setter and getter.
27 | * For example, the following sample code creates a [SpruceFlingAnimation] for the alpha property of a
28 | * [View] object named `view`:
29 | * `flingAnimationOf(view::setAlpha, view::getAlpha)`
30 | *
31 | * @param setter The function that mutates the property being animated
32 | * @param getter The function that returns the value of the property
33 | * @return [SpruceFlingAnimation]
34 | */
35 | fun flingAnimationOf(setter: (Float) -> Unit, getter: () -> Float): SpruceFlingAnimation {
36 | return SpruceFlingAnimation(createFloatValueHolder(setter, getter))
37 | }
38 |
39 | /**
40 | * Creates [SpruceSpringAnimation] for a property that can be accessed via the provided setter and getter.
41 | * If finalPosition is not [Float.NaN] then create [SpruceSpringAnimation] with
42 | * [SpringForce.mFinalPosition].
43 | *
44 | * @param setter The function that mutates the property being animated
45 | * @param getter The function that returns the value of the property
46 | * @param finalPosition [SpringForce.mFinalPosition] Final position of spring.
47 | * @return [SpruceSpringAnimation]
48 | */
49 | fun springAnimationOf(
50 | setter: (Float) -> Unit,
51 | getter: () -> Float,
52 | finalPosition: Float = Float.NaN
53 | ): SpruceSpringAnimation {
54 | val valueHolder = createFloatValueHolder(setter, getter)
55 | return if (finalPosition.isNaN()) {
56 | SpruceSpringAnimation(valueHolder)
57 | } else {
58 | SpruceSpringAnimation(valueHolder, finalPosition)
59 | }
60 | }
61 |
62 | /**
63 | * Updates or applies spring force properties like [SpringForce.mDampingRatio],
64 | * [SpringForce.mFinalPosition] and stiffness on SpringAnimation.
65 | *
66 | * If [SpruceSpringAnimation.mSpring] is null in case [SpruceSpringAnimation] is created without final position
67 | * it will be created and attached to [SpruceSpringAnimation]
68 | *
69 | * @param func lambda with receiver on [SpringForce]
70 | * @return [SpruceSpringAnimation]
71 | */
72 | inline fun SpruceSpringAnimation.withSpringForceProperties(
73 | func: SpringForce.() -> Unit
74 | ): SpruceSpringAnimation {
75 | if (spring == null) {
76 | spring = SpringForce()
77 | }
78 | spring.func()
79 | return this
80 | }
81 |
82 | private fun createFloatValueHolder(setter: (Float) -> Unit, getter: () -> Float): FloatValueHolder {
83 | return object : FloatValueHolder() {
84 | override fun getValue(): Float {
85 | return getter.invoke()
86 | }
87 |
88 | override fun setValue(value: Float) {
89 | setter.invoke(value)
90 | }
91 | }
92 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/willowtreeapps/spruce/dynamics/FloatPropertyCompat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.dynamics;
24 |
25 | import android.util.FloatProperty;
26 |
27 | import androidx.annotation.RequiresApi;
28 |
29 | /**
30 | * FloatPropertyCompat is an abstraction that can be used to represent a mutable float value that
31 | * is held in a host object. To access this float value, {@link #setValue(Object, float)} and getter
32 | * {@link #getValue(Object)} need to be implemented. Both the setter and the getter take the
33 | * primitive float type and avoids autoboxing and other overhead associated with the
34 | * Float class.
35 | *
36 | *
For API 24 and later, {@link FloatProperty} instances can be converted to
37 | * {@link FloatPropertyCompat} through
38 | * {@link FloatPropertyCompat#createFloatPropertyCompat(FloatProperty)}.
39 | *
40 | * @param the class on which the Property is declared
41 | */
42 | public abstract class FloatPropertyCompat {
43 | final String mPropertyName;
44 |
45 | /**
46 | * A constructor that takes an identifying name.
47 | */
48 | public FloatPropertyCompat(String name) {
49 | mPropertyName = name;
50 | }
51 |
52 | /**
53 | * Create a {@link FloatPropertyCompat} wrapper for a {@link FloatProperty} object. The new
54 | * {@link FloatPropertyCompat} instance will access and modify the property value of
55 | * {@link FloatProperty} through the {@link FloatProperty} instance's setter and getter.
56 | *
57 | * @param property FloatProperty instance to be wrapped
58 | * @param the class on which the Property is declared
59 | * @return a new {@link FloatPropertyCompat} wrapper for the given {@link FloatProperty} object
60 | */
61 | @RequiresApi(24)
62 | public static FloatPropertyCompat createFloatPropertyCompat(
63 | final FloatProperty property) {
64 | return new FloatPropertyCompat(property.getName()) {
65 | @Override
66 | public float getValue(T object) {
67 | return property.get(object);
68 | }
69 |
70 | @Override
71 | public void setValue(T object, float value) {
72 | property.setValue(object, value);
73 | }
74 | };
75 | }
76 |
77 | /**
78 | * Returns the current value that this property represents on the given object.
79 | *
80 | * @param object object which this property represents
81 | * @return the current property value of the given object
82 | */
83 | public abstract float getValue(T object);
84 |
85 | /**
86 | * Sets the value on object which this property represents.
87 | *
88 | * @param object object which this property represents
89 | * @param value new value of the property
90 | */
91 | public abstract void setValue(T object, float value);
92 | }
93 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/willowtreeapps/spruce/sort/ContinuousSort.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.sort;
24 |
25 | import android.graphics.PointF;
26 | import android.view.View;
27 | import android.view.ViewGroup;
28 |
29 | import java.util.ArrayList;
30 | import java.util.Collections;
31 | import java.util.Comparator;
32 | import java.util.List;
33 |
34 | public class ContinuousSort extends RadialSort {
35 |
36 | private final long duration;
37 | private final boolean reversed;
38 |
39 | /**
40 | * Establishes the delay between object animations and their starting position based on distance,
41 | * delay, and a value from the Position enum
42 | *
43 | * @param interObjectDelay delay between object animations
44 | * @param reversed flag to indicate if the animation should be reversed
45 | * @param position enum value of the position the animation should start from
46 | */
47 | public ContinuousSort(long interObjectDelay, boolean reversed, Position position) {
48 | super(interObjectDelay, reversed, position);
49 | this.duration = interObjectDelay;
50 | this.reversed = reversed;
51 | }
52 |
53 | @Override
54 | public List getViewListWithTimeOffsets(ViewGroup parent, List children) {
55 | final PointF comparisonPoint = getDistancePoint(parent, children);
56 |
57 | double maxDistance = 0;
58 | for (View v : children) {
59 | double distance = getDistanceBetweenPoints(Utils.viewToPoint(v), comparisonPoint);
60 | if (distance > maxDistance) {
61 | maxDistance = distance;
62 | }
63 | }
64 |
65 | List timedViews = new ArrayList<>();
66 | for (View view : children) {
67 | double normalizedDistance;
68 | double viewDistance = getDistanceBetweenPoints(Utils.viewToPoint(view), comparisonPoint);
69 | if (reversed) {
70 | normalizedDistance = (maxDistance - viewDistance) / maxDistance;
71 | } else {
72 | normalizedDistance = viewDistance / maxDistance;
73 | }
74 |
75 | long offset = Math.round(duration * normalizedDistance);
76 | timedViews.add(new SpruceTimedView(view, offset));
77 | }
78 |
79 | return timedViews;
80 | }
81 |
82 | @Override
83 | public void sortChildren(ViewGroup parent, List children) {
84 | final PointF comparisonPoint = getDistancePoint(parent, children);
85 | Collections.sort(children, new Comparator() {
86 | @Override
87 | public int compare(View left, View right) {
88 | double leftDistance = getDistanceBetweenPoints(Utils.viewToPoint(left), comparisonPoint);
89 | double rightDistance = getDistanceBetweenPoints(Utils.viewToPoint(right), comparisonPoint);
90 | return Double.compare(leftDistance, rightDistance);
91 | }
92 | });
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/willowtreeapps/spruce/sort/RadialSort.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.sort;
24 |
25 | import android.graphics.PointF;
26 | import android.view.View;
27 | import android.view.ViewGroup;
28 |
29 | import java.util.List;
30 |
31 | public class RadialSort extends DistancedSort {
32 |
33 | private final Position position;
34 |
35 | public enum Position {
36 | TOP_LEFT,
37 | TOP_MIDDLE,
38 | TOP_RIGHT,
39 | LEFT,
40 | MIDDLE,
41 | RIGHT,
42 | BOTTOM_LEFT,
43 | BOTTOM_MIDDLE,
44 | BOTTOM_RIGHT
45 | }
46 |
47 | /**
48 | * Establishes the delay between object animations and their starting position based on distance,
49 | * delay, and a value from the Position enum
50 | *
51 | * @param interObjectDelay delay between object animations
52 | * @param reversed flag to indicate if the animation should be reversed
53 | * @param position enum value of the position the animation should start from
54 | */
55 | public RadialSort(long interObjectDelay, boolean reversed, Position position) {
56 | super(interObjectDelay, reversed);
57 | if (position == null) {
58 | throw new NullPointerException("Position can't be null and must be a valid type");
59 | }
60 | this.position = position;
61 | }
62 |
63 | @Override
64 | public PointF getDistancePoint(ViewGroup parent, List children) {
65 | PointF distancePoint;
66 |
67 | switch (position) {
68 | case TOP_LEFT:
69 | distancePoint = new PointF(0, 0);
70 | break;
71 | case TOP_MIDDLE:
72 | distancePoint = new PointF(parent.getWidth() / 2, 0);
73 | break;
74 | case TOP_RIGHT:
75 | distancePoint = new PointF(parent.getWidth(), 0);
76 | break;
77 | case LEFT:
78 | distancePoint = new PointF(0, parent.getHeight() / 2);
79 | break;
80 | case MIDDLE:
81 | distancePoint = new PointF(parent.getWidth() / 2, parent.getHeight() / 2);
82 | break;
83 | case RIGHT:
84 | distancePoint = new PointF(parent.getWidth(), parent.getHeight() / 2);
85 | break;
86 | case BOTTOM_LEFT:
87 | distancePoint = new PointF(0, parent.getHeight());
88 | break;
89 | case BOTTOM_MIDDLE:
90 | distancePoint = new PointF(parent.getWidth() / 2, parent.getHeight());
91 | break;
92 | case BOTTOM_RIGHT:
93 | distancePoint = new PointF(parent.getWidth(), parent.getHeight());
94 | break;
95 | default:
96 | throw new AssertionError("Must be a valid Position argument type");
97 | }
98 |
99 | return super.translate(distancePoint, children);
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/lib/src/test/java/com/willowtreeapps/spruce/exclusion/ExclusionHelperTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.exclusion;
24 |
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 |
28 | import org.junit.Assert;
29 | import org.junit.Before;
30 | import org.junit.Test;
31 | import org.junit.runner.RunWith;
32 | import org.mockito.Mockito;
33 | import org.robolectric.RobolectricTestRunner;
34 |
35 | import java.util.ArrayList;
36 | import java.util.List;
37 |
38 |
39 | @RunWith(RobolectricTestRunner.class)
40 | public class ExclusionHelperTest {
41 |
42 | private ViewGroup mockParent;
43 |
44 | @Before
45 | public void setup() {
46 | mockParent = Mockito.mock(ViewGroup.class);
47 | }
48 |
49 | @Test
50 | public void test_normal_exclusion_with_empty_list() {
51 | ExclusionHelper helper = new ExclusionHelper();
52 | List idList = new ArrayList<>();
53 | helper.initialize(idList, ExclusionHelper.NORMAL_MODE);
54 | Assert.assertEquals(helper.filterViews(mockParent).size(), idList.size());
55 | Assert.assertNotEquals(helper.filterViews(mockParent).size(), 1);
56 |
57 | }
58 |
59 | @Test
60 | public void test_r_l_exclusion_with_empty_list() {
61 | ExclusionHelper helper = new ExclusionHelper();
62 | List idList = new ArrayList<>();
63 | helper.initialize(idList, ExclusionHelper.R_L_MODE);
64 | Assert.assertEquals(helper.filterViews(mockParent).size(), idList.size());
65 | Assert.assertNotEquals(helper.filterViews(mockParent).size(), 1);
66 |
67 | }
68 |
69 |
70 | @Test
71 | public void test_normal_exclusion_success() {
72 | ExclusionHelper helper = new ExclusionHelper();
73 | List idList = new ArrayList<>();
74 | idList.add(2);
75 | idList.add(7);
76 | helper.initialize(idList, ExclusionHelper.NORMAL_MODE);
77 | ExclusionTestHelper.addViews(mockParent, 1, 2, 5, 7, 9);
78 | Assert.assertNotEquals(helper.filterViews(mockParent).size(), idList.size());
79 | Assert.assertEquals(helper.filterViews(mockParent).size(), idList.size() + 1);
80 |
81 | for (View view : helper.filterViews(mockParent)) {
82 | Assert.assertFalse(idList.contains(view.getId()));
83 | }
84 | }
85 |
86 | @Test
87 | public void test_r_l_exclusion_success() {
88 | ExclusionHelper helper = new ExclusionHelper();
89 | List idList = new ArrayList<>();
90 | idList.add(5);
91 | idList.add(7);
92 |
93 | List positionList = new ArrayList<>();
94 | positionList.add(2);
95 | positionList.add(3);
96 |
97 |
98 | helper.initialize(positionList, ExclusionHelper.R_L_MODE);
99 | ExclusionTestHelper.addViews(mockParent, 1, 2, 5, 7, 9);
100 | Assert.assertNotEquals(helper.filterViews(mockParent).size(), idList.size());
101 | Assert.assertEquals(helper.filterViews(mockParent).size(), idList.size() + 1);
102 |
103 | for (View view : helper.filterViews(mockParent)) {
104 | Assert.assertFalse(idList.contains(view.getId()));
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/willowtreeapps/spruce/animation/DefaultAnimations.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.animation;
24 |
25 | import android.animation.Animator;
26 | import android.animation.ObjectAnimator;
27 | import android.animation.PropertyValuesHolder;
28 | import android.view.View;
29 |
30 | import com.willowtreeapps.spruce.dynamics.SpruceDynamics;
31 | import com.willowtreeapps.spruce.dynamics.SpruceSpringAnimation;
32 | import com.willowtreeapps.spruce.dynamics.SpringForce;
33 |
34 | /**
35 | * Convenience methods for retrieving default view animators
36 | */
37 | public class DefaultAnimations {
38 |
39 | private static final float GROW_SCALE = 1.5F;
40 | private static final float SHRINK_SCALE = 0.1F;
41 | private static final float ORIGINAL_SCALE = 1.0F;
42 | private static final float FADE_AWAY_TO = 0.0F;
43 | private static final float FADE_IN_TO = 1.0F;
44 | private static final float FADE_FROM = 0.0F;
45 | private static final float START_ROTATION = 0F;
46 | private static final float END_ROTATION = 360F;
47 |
48 | public static Animator growAnimator(View view, long duration) {
49 | return ObjectAnimator.ofPropertyValuesHolder(view,
50 | PropertyValuesHolder.ofFloat(View.SCALE_X, GROW_SCALE, ORIGINAL_SCALE),
51 | PropertyValuesHolder.ofFloat(View.SCALE_Y, GROW_SCALE, ORIGINAL_SCALE))
52 | .setDuration(duration);
53 | }
54 |
55 | public static Animator shrinkAnimator(View view, long duration) {
56 | return ObjectAnimator.ofPropertyValuesHolder(view,
57 | PropertyValuesHolder.ofFloat(View.SCALE_X, SHRINK_SCALE, ORIGINAL_SCALE),
58 | PropertyValuesHolder.ofFloat(View.SCALE_Y, SHRINK_SCALE, ORIGINAL_SCALE))
59 | .setDuration(duration);
60 | }
61 |
62 | public static Animator fadeAwayAnimator(View view, long duration) {
63 | return ObjectAnimator.ofFloat(view, View.ALPHA, FADE_AWAY_TO)
64 | .setDuration(duration);
65 | }
66 |
67 | public static Animator fadeInAnimator(View view, long duration) {
68 | return ObjectAnimator.ofFloat(view, View.ALPHA, FADE_FROM, FADE_IN_TO)
69 | .setDuration(duration);
70 | }
71 |
72 | public static Animator spinAnimator(View view, long duration) {
73 | return ObjectAnimator.ofFloat(view, View.ROTATION, START_ROTATION, END_ROTATION)
74 | .setDuration(duration);
75 | }
76 |
77 | public static SpruceSpringAnimation dynamicTranslationUpwards(View view) {
78 | SpruceSpringAnimation tranUp = new SpruceSpringAnimation(view, SpruceDynamics.TRANSLATION_Y)
79 | .setStartValue(200f);
80 | tranUp.setSpring(new SpringForce());
81 | tranUp.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY);
82 | tranUp.getSpring().setStiffness(SpringForce.STIFFNESS_LOW);
83 | tranUp.getSpring().setFinalPosition(FADE_FROM);
84 | return tranUp;
85 | }
86 |
87 | public static SpruceSpringAnimation dynamicFadeIn(View view) {
88 | SpruceSpringAnimation tranUp = new SpruceSpringAnimation(view, SpruceDynamics.ALPHA)
89 | .setStartValue(FADE_FROM);
90 | tranUp.setSpring(new SpringForce());
91 | tranUp.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY);
92 | tranUp.getSpring().setStiffness(SpringForce.STIFFNESS_MEDIUM);
93 | tranUp.getSpring().setFinalPosition(FADE_IN_TO);
94 | return tranUp;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/lib/src/test/java/com/willowtreeapps/spruce/sort/ContinuousSortTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.sort;
24 |
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 |
28 | import org.junit.Assert;
29 | import org.junit.Before;
30 | import org.junit.Test;
31 | import org.junit.runner.RunWith;
32 | import org.mockito.Mockito;
33 | import org.robolectric.RobolectricTestRunner;
34 |
35 | import java.util.List;
36 |
37 | @RunWith(RobolectricTestRunner.class)
38 | public class ContinuousSortTest {
39 |
40 | private ViewGroup mockParent = Mockito.mock(ViewGroup.class);
41 | private List mockChildren;
42 |
43 | @Before
44 | public void setup() {
45 | mockChildren = TestHelper.setupMockChildren();
46 | }
47 |
48 | @Test
49 | public void test_positive_inter_object_delay() {
50 | List resultViews = new ContinuousSort(/*interObjectDelay=*/1,
51 | /*reversed=*/false,
52 | RadialSort.Position.TOP_LEFT)
53 | .getViewListWithTimeOffsets(mockParent, mockChildren);
54 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset());
55 | Assert.assertEquals(1, resultViews.get(1).getTimeOffset());
56 | Assert.assertEquals(1, resultViews.get(2).getTimeOffset());
57 | }
58 |
59 | @Test
60 | public void test_positive_inter_object_delay_with_reversed() {
61 | List resultViews = new ContinuousSort(/*interObjectDelay=*/1,
62 | /*reversed=*/true,
63 | RadialSort.Position.TOP_LEFT)
64 | .getViewListWithTimeOffsets(mockParent, mockChildren);
65 | Assert.assertEquals(1, resultViews.get(0).getTimeOffset());
66 | Assert.assertEquals(1, resultViews.get(1).getTimeOffset());
67 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset());
68 | }
69 |
70 | @Test
71 | public void test_inter_object_delay_of_zero() {
72 | List resultViews = new ContinuousSort(/*interObjectDelay=*/0,
73 | /*reversed=*/false,
74 | RadialSort.Position.TOP_LEFT)
75 | .getViewListWithTimeOffsets(mockParent, mockChildren);
76 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset());
77 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset());
78 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset());
79 | }
80 |
81 | @Test
82 | public void test_negative_inter_object_delay() {
83 | List resultViews = new ContinuousSort(/*interObjectDelay=*/-1,
84 | /*reversed=*/false,
85 | RadialSort.Position.TOP_LEFT)
86 | .getViewListWithTimeOffsets(mockParent, mockChildren);
87 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset());
88 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset());
89 | Assert.assertEquals(-1, resultViews.get(2).getTimeOffset());
90 | }
91 |
92 | @Test
93 | public void test_negative_inter_object_delay_with_reversed() {
94 | List resultViews = new ContinuousSort(/*interObjectDelay=*/-1,
95 | /*reversed=*/true,
96 | RadialSort.Position.TOP_LEFT)
97 | .getViewListWithTimeOffsets(mockParent, mockChildren);
98 | Assert.assertEquals(-1, resultViews.get(0).getTimeOffset());
99 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset());
100 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset());
101 | }
102 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/willowtreeapps/spurceexampleapp/helpers/InterpolatorSelector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spurceexampleapp.helpers;
24 |
25 | import android.os.Build;
26 | import android.view.animation.Interpolator;
27 | import android.view.animation.LinearInterpolator;
28 |
29 | import androidx.annotation.RequiresApi;
30 |
31 | import com.willowtreeapps.spruce.interpolators.SpruceInterpolators;
32 |
33 | import java.util.HashMap;
34 | import java.util.Map;
35 |
36 | /**
37 | * This class is used to get the interpolator from {@link com.willowtreeapps.spruce.interpolators.SpruceInterpolators}
38 | *
39 | * This class is a helper for the Interpolation selection in {@link com.willowtreeapps.spurceexampleapp.fragments.ControlsFragment}
40 | */
41 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
42 | public class InterpolatorSelector {
43 |
44 | private final Map interpolatorMap = new HashMap<>();
45 |
46 | public InterpolatorSelector() {
47 | initializeHashMap();
48 | }
49 |
50 | /**
51 | * This hash map is used to the {@link android.app.AlertDialog} in
52 | * {@link com.willowtreeapps.spurceexampleapp.fragments.ControlsFragment}
53 | */
54 | private void initializeHashMap() {
55 | interpolatorMap.put(0, SpruceInterpolators.EASE);
56 | interpolatorMap.put(1, SpruceInterpolators.EASE_IN);
57 | interpolatorMap.put(2, SpruceInterpolators.EASE_OUT);
58 | interpolatorMap.put(3, SpruceInterpolators.EASE_IN_OUT);
59 | interpolatorMap.put(4, SpruceInterpolators.EASE_IN_QUAD);
60 | interpolatorMap.put(5, SpruceInterpolators.EASE_IN_CUBIC);
61 | interpolatorMap.put(6, SpruceInterpolators.EASE_IN_QUART);
62 | interpolatorMap.put(7, SpruceInterpolators.EASE_IN_QUINT);
63 | interpolatorMap.put(8, SpruceInterpolators.EASE_IN_SINE);
64 | interpolatorMap.put(9, SpruceInterpolators.EASE_IN_EXPO);
65 | interpolatorMap.put(10, SpruceInterpolators.EASE_IN_CIRC);
66 | interpolatorMap.put(11, SpruceInterpolators.EASE_IN_BACK);
67 | interpolatorMap.put(12, SpruceInterpolators.EASE_OUT_QUAD);
68 | interpolatorMap.put(13, SpruceInterpolators.EASE_OUT_CUBIC);
69 | interpolatorMap.put(14, SpruceInterpolators.EASE_OUT_QUART);
70 | interpolatorMap.put(15, SpruceInterpolators.EASE_OUT_QUINT);
71 | interpolatorMap.put(16, SpruceInterpolators.EASE_OUT_SINE);
72 | interpolatorMap.put(17, SpruceInterpolators.EASE_OUT_EXPO);
73 | interpolatorMap.put(18, SpruceInterpolators.EASE_OUT_CIRC);
74 | interpolatorMap.put(19, SpruceInterpolators.EASE_OUT_BACK);
75 | interpolatorMap.put(20, SpruceInterpolators.EASE_IN_OUT_QUAD);
76 | interpolatorMap.put(21, SpruceInterpolators.EASE_IN_OUT_CUBIC);
77 | interpolatorMap.put(22, SpruceInterpolators.EASE_IN_OUT_QUART);
78 | interpolatorMap.put(23, SpruceInterpolators.EASE_IN_OUT_QUINT);
79 | interpolatorMap.put(24, SpruceInterpolators.EASE_IN_OUT_SINE);
80 | interpolatorMap.put(25, SpruceInterpolators.EASE_IN_OUT_EXPO);
81 | interpolatorMap.put(26, SpruceInterpolators.EASE_IN_OUT_CIRC);
82 | interpolatorMap.put(27, SpruceInterpolators.EASE_IN_OUT_BACK);
83 | }
84 |
85 | /**
86 | * This method returns the interpolator for a specific position from the {@link HashMap}
87 | *
88 | * @param position position from the {@link android.app.AlertDialog}
89 | * @return {@link Interpolator} that is selected view {@link android.app.AlertDialog}
90 | */
91 | public Interpolator getInterpolatorMap(int position) {
92 | if (interpolatorMap.containsKey(position)) {
93 | return interpolatorMap.get(position);
94 | } else {
95 | return new LinearInterpolator();
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/app/src/main/java/com/willowtreeapps/spurceexampleapp/fragments/ViewFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spurceexampleapp.fragments;
24 |
25 | import android.content.Context;
26 | import android.content.res.Resources;
27 | import android.os.Bundle;
28 | import android.util.TypedValue;
29 | import android.view.LayoutInflater;
30 | import android.view.View;
31 | import android.view.ViewGroup;
32 | import android.view.ViewTreeObserver;
33 | import android.widget.GridLayout;
34 |
35 | import androidx.annotation.Nullable;
36 | import androidx.fragment.app.Fragment;
37 |
38 | import com.willowtreeapps.spurceexampleapp.R;
39 | import com.willowtreeapps.spurceexampleapp.widgets.CardLayout;
40 |
41 | import java.util.ArrayList;
42 | import java.util.List;
43 |
44 |
45 | public class ViewFragment extends Fragment {
46 |
47 | private OnParentAndChildCreationListener listener;
48 | private GridLayout parent;
49 | private final List children = new ArrayList<>();
50 |
51 | public static ViewFragment newInstance() {
52 | return new ViewFragment();
53 | }
54 |
55 | @Nullable
56 | @Override
57 | public View onCreateView(LayoutInflater inflater, ViewGroup container, @Nullable Bundle savedInstanceState) {
58 | ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.view_fragment, container, false);
59 | parent = rootView.findViewById(R.id.view_group_to_animate);
60 |
61 | final int CHILD_VIEW_COUNT = parent.getColumnCount() * parent.getRowCount();
62 |
63 | for (int i = 0; i < CHILD_VIEW_COUNT; i++) {
64 | CardLayout childView = new CardLayout(getContext());
65 | //setting ID for the exclusion logic.
66 | childView.setId(i);
67 | parent.addView(childView);
68 | children.add(childView);
69 | }
70 |
71 | parent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
72 | @Override
73 | public void onGlobalLayout() {
74 | Resources res = getResources();
75 | int tileMargins = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, res.getDisplayMetrics()));
76 | final int childWidth = parent.getWidth() / parent.getColumnCount() - (tileMargins * 2);
77 | final int childHeight = parent.getHeight() / parent.getRowCount() - (tileMargins * 2);
78 |
79 | for (int i = 0; i < parent.getChildCount(); i++) {
80 | View childView = parent.getChildAt(i);
81 | GridLayout.LayoutParams params = (GridLayout.LayoutParams) childView.getLayoutParams();
82 | params.width = childWidth;
83 | params.height = childHeight;
84 | params.setMargins(tileMargins, tileMargins, tileMargins, tileMargins);
85 | childView.setLayoutParams(params);
86 | }
87 | parent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
88 | }
89 | });
90 |
91 | listener.onParentAndChildrenPrepared(parent, children);
92 |
93 | return rootView;
94 | }
95 |
96 | @Override
97 | public void onAttach(Context context) {
98 | super.onAttach(context);
99 |
100 | try {
101 | listener = (OnParentAndChildCreationListener) context;
102 | } catch (ClassCastException e) {
103 | throw new ClassCastException(context.toString() + " must implement OnParentAndChildCreationListener");
104 | }
105 | }
106 |
107 | public interface OnParentAndChildCreationListener {
108 | void onParentAndChildrenPrepared(ViewGroup parent, List children);
109 | }
110 |
111 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/willowtreeapps/spruce/sort/SnakeSort.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.sort;
24 |
25 | import android.graphics.PointF;
26 | import android.view.View;
27 | import android.view.ViewGroup;
28 |
29 | import java.util.ArrayList;
30 | import java.util.Collections;
31 | import java.util.Comparator;
32 | import java.util.List;
33 |
34 | public class SnakeSort extends CorneredSort {
35 |
36 | private final long interObjectDelay;
37 | private final boolean reversed;
38 |
39 | /**
40 | * Animate child views from side to side (based on the provided corner parameter), alternating left to right and right to left on each row.
41 | *
42 | * @param interObjectDelay long delay between objects
43 | * @param reversed boolean indicating if the selection is reversed
44 | * @param corner {@link com.willowtreeapps.spruce.sort.CorneredSort.Corner Corner} value to start from
45 | */
46 | public SnakeSort(long interObjectDelay, boolean reversed, Corner corner) {
47 | super(interObjectDelay, reversed, corner);
48 | this.interObjectDelay = interObjectDelay;
49 | this.reversed = reversed;
50 | }
51 |
52 | @Override
53 | public List getViewListWithTimeOffsets(ViewGroup parent, List children) {
54 | final PointF comparisonPoint = getDistancePoint(parent, children);
55 | List timedViews = new ArrayList<>();
56 | long currentTimeOffset = 0;
57 |
58 | // Calculate all possible vertical distances from the point of comparison.
59 | final List verticalDistances = new ArrayList<>();
60 | for (View child: children) {
61 | float d = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(child));
62 | if (!verticalDistances.contains(d)) {
63 | verticalDistances.add(d);
64 | }
65 | }
66 |
67 | // Sort these so we can find the row index by the vertical distance.
68 | Collections.sort(verticalDistances);
69 |
70 | Collections.sort(children, new Comparator() {
71 | @Override
72 | public int compare(View left, View right) {
73 | double leftHorizontalDistance = Utils.horizontalDistance(comparisonPoint, Utils.viewToPoint(left));
74 | double leftVerticalDistance = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(left));
75 | double rightHorizontalDistance = Utils.horizontalDistance(comparisonPoint, Utils.viewToPoint(right));
76 | double rightVerticalDistance = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(right));
77 |
78 | // Difference in vertical distance takes priority.
79 | if (leftVerticalDistance < rightVerticalDistance) {
80 | return -1;
81 | } else if (leftVerticalDistance > rightVerticalDistance) {
82 | return 1;
83 | }
84 |
85 | // If the are in the same row, find the row index.
86 | int row = verticalDistances.indexOf((float) leftVerticalDistance);
87 | if (leftHorizontalDistance < rightHorizontalDistance) {
88 | return row % 2 == 0 ? -1: 1;
89 | } else {
90 | return row % 2 == 0 ? 1: -1;
91 | }
92 | }
93 | });
94 |
95 | if (reversed) {
96 | Collections.reverse(children);
97 | }
98 |
99 | for (View view : children) {
100 | timedViews.add(new SpruceTimedView(view, currentTimeOffset));
101 | currentTimeOffset += interObjectDelay;
102 | }
103 |
104 | return timedViews;
105 | }
106 |
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 | Spruce
25 | Recycler Example
26 | List View Example
27 |
28 |
29 | Sort Example
30 | Recycler View Example
31 | List View Example
32 |
33 | Default Sort
34 | Cornered Sort
35 | Continuous Sort
36 | Continuous Weighted Sort
37 | Inline Sort
38 | Linear Sort
39 | Radial Sort
40 | Random Sort
41 | Snake Sort
42 |
43 | Bottom to Top
44 | Top to Bottom
45 | Left to Right
46 | Right to Left
47 |
48 | Top Left
49 | Top Middle
50 | Top Right
51 | Right
52 | Middle
53 | Left
54 | Bottom Left
55 | Bottom Middle
56 | Bottom Right
57 |
58 | Light
59 | Medium
60 | Heavy
61 |
62 | Reversed
63 |
64 | Vertical
65 | Horizontal
66 | 0s
67 | 0.1s
68 | 2s
69 | Duration
70 | Delay
71 | Sort function:
72 | Sort Function selection
73 | Select
74 | Cancel
75 |
76 |
77 | Code Sample
78 | new DefaultSort(%1$d);
79 | new CorneredSort(%1$d, %2$s, CorneredSort.Corner.%3$s);
80 | new ContinuousSort(%1$d, %2$s, RadialSort.Position.%3$s);
81 | new ContinuousWeightedSort(%1$d, %2$s, RadialSort.Position.%3$s, %4$s, %5$s);
82 | new InlineSort(%1$d, %2$s, CorneredSort.Corner.%3$s);
83 | new LinearSort(%1$d, %2$s, LinearSort.Direction.%3$s);
84 | new RadialSort(%1$d, %2$s, RadialSort.Position.%3$s);
85 | new RandomSort(%1$d);
86 | new SnakeSort(%1$d, %2$s, CorneredSort.Corner.%3$s);
87 | Sort Controls
88 | exclude views
89 | Interpolator
90 | linear
91 |
92 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/continuous_radio_group.xml:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
27 |
28 |
32 |
33 |
39 |
40 |
45 |
46 |
50 |
51 |
56 |
57 |
63 |
64 |
69 |
70 |
71 |
72 |
73 |
74 |
80 |
81 |
86 |
87 |
91 |
92 |
97 |
98 |
104 |
105 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/willowtreeapps/spruce/sort/CorneredSort.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.sort;
24 |
25 | import android.graphics.PointF;
26 | import android.view.View;
27 | import android.view.ViewGroup;
28 |
29 | import java.util.ArrayList;
30 | import java.util.Collections;
31 | import java.util.Comparator;
32 | import java.util.List;
33 |
34 | public class CorneredSort extends DistancedSort {
35 |
36 | public enum Corner {
37 | TOP_LEFT,
38 | TOP_RIGHT,
39 | BOTTOM_LEFT,
40 | BOTTOM_RIGHT
41 | }
42 |
43 | private final long interObjectDelay;
44 | private final Corner corner;
45 | private final boolean reversed;
46 |
47 | /**
48 | * Animates views in a corner like fashion. The views near the starting corner will animate first.
49 | *
50 | * @param interObjectDelay long delay between objects
51 | * @param reversed boolean
52 | * @param corner Corner enum value {@link Corner corner}
53 | */
54 | public CorneredSort(long interObjectDelay, boolean reversed, Corner corner) {
55 | super(interObjectDelay, reversed);
56 | if (corner == null) {
57 | throw new NullPointerException("Corner can't be null and must be a valid type");
58 | }
59 | this.interObjectDelay = interObjectDelay;
60 | this.corner = corner;
61 | this.reversed = reversed;
62 | }
63 |
64 | @Override
65 | public List getViewListWithTimeOffsets(ViewGroup parent, List children) {
66 | final PointF comparisonPoint = getDistancePoint(parent, children);
67 | List timedViews = new ArrayList<>();
68 | long currentTimeOffset = 0;
69 |
70 | double lastDistance = 0;
71 | for (View view : children) {
72 | double viewDistance = getDistanceBetweenPoints(Utils.viewToPoint(view), comparisonPoint);
73 | if (Math.floor(lastDistance) != Math.floor(viewDistance)) {
74 | lastDistance = viewDistance;
75 | currentTimeOffset += interObjectDelay;
76 | }
77 | timedViews.add(new SpruceTimedView(view, currentTimeOffset));
78 | }
79 |
80 | return timedViews;
81 | }
82 |
83 | @Override
84 | public void sortChildren(ViewGroup parent, List children) {
85 | final PointF comparisonPoint = getDistancePoint(parent, children);
86 | Collections.sort(children, new Comparator() {
87 | @Override
88 | public int compare(View left, View right) {
89 | double leftDistance = Math.abs(comparisonPoint.x - left.getX()) + Math.abs(comparisonPoint.y - left.getY());
90 | double rightDistance = Math.abs(comparisonPoint.x - right.getX()) + Math.abs(comparisonPoint.y - right.getY());
91 | if (reversed) {
92 | return Double.compare(rightDistance, leftDistance);
93 | }
94 | return Double.compare(leftDistance, rightDistance);
95 | }
96 | });
97 | }
98 |
99 | @Override
100 | public PointF getDistancePoint(ViewGroup parent, List children) {
101 | PointF distancePoint;
102 |
103 | switch (corner) {
104 | case TOP_LEFT:
105 | distancePoint = new PointF(0, 0);
106 | break;
107 | case TOP_RIGHT:
108 | distancePoint = new PointF(parent.getWidth(), 0);
109 | break;
110 | case BOTTOM_LEFT:
111 | distancePoint = new PointF(0, parent.getHeight());
112 | break;
113 | case BOTTOM_RIGHT:
114 | distancePoint = new PointF(parent.getWidth(), parent.getHeight());
115 | break;
116 | default:
117 | throw new AssertionError("Must be a valid Corner argument type");
118 | }
119 |
120 | return distancePoint;
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/app/src/main/java/com/willowtreeapps/spurceexampleapp/SpruceActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spurceexampleapp;
24 |
25 | import android.content.Intent;
26 | import android.os.Bundle;
27 | import android.view.Menu;
28 | import android.view.MenuItem;
29 | import android.view.View;
30 | import android.view.ViewGroup;
31 | import android.widget.ArrayAdapter;
32 | import android.widget.Spinner;
33 |
34 | import com.willowtreeapps.spurceexampleapp.fragments.ControlsFragment;
35 | import com.willowtreeapps.spurceexampleapp.fragments.ViewFragment;
36 | import com.willowtreeapps.spurceexampleapp.pager.VerticalViewPager;
37 |
38 | import java.util.ArrayList;
39 | import java.util.List;
40 |
41 | import androidx.annotation.Nullable;
42 | import androidx.appcompat.app.AppCompatActivity;
43 | import androidx.appcompat.widget.Toolbar;
44 | import androidx.fragment.app.Fragment;
45 | import androidx.fragment.app.FragmentManager;
46 | import androidx.fragment.app.FragmentStatePagerAdapter;
47 |
48 |
49 | public class SpruceActivity extends AppCompatActivity
50 | implements ViewFragment.OnParentAndChildCreationListener {
51 |
52 | public ViewGroup parent;
53 | public List children = new ArrayList<>();
54 | public Spinner sortDropDown;
55 |
56 | @Override
57 | protected void onCreate(@Nullable Bundle savedInstanceState) {
58 | super.onCreate(savedInstanceState);
59 |
60 | setContentView(R.layout.fragment_pager);
61 |
62 | FragmentManager fm = getSupportFragmentManager();
63 |
64 | VerticalViewPager verticalPager = findViewById(R.id.vertical_pager);
65 | VerticalPagerAdapter adapter = new VerticalPagerAdapter(fm);
66 | verticalPager.setAdapter(adapter);
67 |
68 | Toolbar toolBar = findViewById(R.id.tool_bar);
69 | setSupportActionBar(toolBar);
70 | if (getSupportActionBar() != null) {
71 | getSupportActionBar().setDisplayShowTitleEnabled(false);
72 | }
73 |
74 | sortDropDown = findViewById(R.id.sort_selection);
75 | ArrayAdapter spinnerAdapter = ArrayAdapter.createFromResource(this,
76 | R.array.sort_functions,
77 | R.layout.spinner_item);
78 | spinnerAdapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);
79 | sortDropDown.setAdapter(spinnerAdapter);
80 | }
81 |
82 | @Override
83 | public boolean onCreateOptionsMenu(Menu menu) {
84 | getMenuInflater().inflate(R.menu.menu_main, menu);
85 | return super.onCreateOptionsMenu(menu);
86 | }
87 |
88 | @Override
89 | public boolean onOptionsItemSelected(MenuItem item) {
90 | switch (item.getItemId()) {
91 | case R.id.sort_option:
92 | break;
93 | case R.id.recycler_option:
94 | startActivity(new Intent(this, RecyclerActivity.class)
95 | .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
96 | break;
97 | case R.id.list_view_option:
98 | startActivity(new Intent(this, ListViewActivity.class)
99 | .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
100 | break;
101 | }
102 | return super.onOptionsItemSelected(item);
103 | }
104 |
105 | @Override
106 | public void onParentAndChildrenPrepared(ViewGroup parent, List children) {
107 | this.parent = parent;
108 | this.children = children;
109 | }
110 |
111 | private class VerticalPagerAdapter extends FragmentStatePagerAdapter {
112 |
113 | VerticalPagerAdapter(FragmentManager fm) {
114 | super(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
115 | }
116 |
117 | @Override
118 | public Fragment getItem(int position) {
119 | switch (position) {
120 | case 1:
121 | return ControlsFragment.newInstance();
122 | default:
123 | return ViewFragment.newInstance();
124 | }
125 | }
126 |
127 | @Override
128 | public int getCount() {
129 | return 2;
130 | }
131 |
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/lib/src/test/java/com/willowtreeapps/spruce/sort/DistancedSortTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Spruce
3 | *
4 | * Copyright (c) 2017 WillowTree, Inc.
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | * THE SOFTWARE.
20 | *
21 | */
22 |
23 | package com.willowtreeapps.spruce.sort;
24 |
25 | import android.graphics.PointF;
26 | import android.view.View;
27 | import android.view.ViewGroup;
28 |
29 | import org.hamcrest.CoreMatchers;
30 | import org.junit.Assert;
31 | import org.junit.Before;
32 | import org.junit.Test;
33 | import org.junit.runner.RunWith;
34 | import org.mockito.Mockito;
35 | import org.robolectric.RobolectricTestRunner;
36 |
37 | import java.util.ArrayList;
38 | import java.util.List;
39 |
40 | @RunWith(RobolectricTestRunner.class)
41 | public class DistancedSortTest {
42 |
43 | private ViewGroup mockParent;
44 | private List mockChildren;
45 |
46 | @Before
47 | public void setup() {
48 | mockParent = Mockito.mock(ViewGroup.class);
49 | mockChildren = TestHelper.setupMockChildren();
50 | }
51 |
52 | @Test
53 | public void test_positive_inter_object_delay() {
54 | List resultViews = new DistancedSort(/*interObjectDelay=*/1,
55 | /*reversed=*/false)
56 | .getViewListWithTimeOffsets(mockParent, mockChildren);
57 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset());
58 | Assert.assertEquals(1, resultViews.get(1).getTimeOffset());
59 | Assert.assertEquals(2, resultViews.get(2).getTimeOffset());
60 | }
61 |
62 | @Test
63 | public void test_inter_object_delay_of_zero() {
64 | List resultViews = new DistancedSort(/*interObjectDelay=*/0,
65 | /*reversed=*/false)
66 | .getViewListWithTimeOffsets(mockParent, mockChildren);
67 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset());
68 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset());
69 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset());
70 | }
71 |
72 | @Test
73 | public void test_negative_inter_object_delay() {
74 | List resultViews = new DistancedSort(/*interObjectDelay=*/-1,
75 | /*reversed=*/false)
76 | .getViewListWithTimeOffsets(mockParent, mockChildren);
77 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset());
78 | Assert.assertEquals(-1, resultViews.get(1).getTimeOffset());
79 | Assert.assertEquals(-2, resultViews.get(2).getTimeOffset());
80 | }
81 |
82 | @Test
83 | public void test_get_distance_point_returns_a_point() {
84 | ViewGroup mockParent = Mockito.mock(ViewGroup.class);
85 | List mockChildren = new ArrayList<>();
86 | mockChildren.add(Mockito.mock(View.class));
87 |
88 | DistancedSort distancedSort = new DistancedSort(/*interObjectDelay=*/0, /*reversed=*/false);
89 | Assert.assertThat(distancedSort.getDistancePoint(mockParent, mockChildren), CoreMatchers.instanceOf(PointF.class));
90 | }
91 |
92 | @Test
93 | public void test_translate_returns_a_point() {
94 | List mockChildren = new ArrayList<>();
95 | mockChildren.add(Mockito.mock(View.class));
96 |
97 | DistancedSort distancedSort = new DistancedSort(/*interObjectDelay=*/0, /*reversed=*/false);
98 | Assert.assertThat(distancedSort.translate(new PointF(0, 0), mockChildren), CoreMatchers.instanceOf(PointF.class));
99 | }
100 |
101 | @Test
102 | public void test_get_distance_between_points_returns_a_double() {
103 | DistancedSort distancedSort = new DistancedSort(/*interObjectDelay=*/0, /*reversed=*/false);
104 | PointF firstPoint = Mockito.mock(PointF.class);
105 | PointF secondPoint = Mockito.mock(PointF.class);
106 |
107 | Assert.assertThat(distancedSort.getDistanceBetweenPoints(firstPoint, secondPoint), CoreMatchers.instanceOf(Double.class));
108 | }
109 |
110 | @Test
111 | public void test_get_view_list_with_time_offsets_returns_correct_number_of_children() {
112 | ViewGroup mockParent = Mockito.mock(ViewGroup.class);
113 | List mockChildren = new ArrayList<>();
114 | for (int i = 0; i < 3; i++) {
115 | mockChildren.add(Mockito.mock(View.class));
116 | }
117 | List resultViews = new DistancedSort(/*interObjectDelay=*/0, /*reversed=*/false).getViewListWithTimeOffsets(mockParent, mockChildren);
118 | Assert.assertEquals(mockChildren.size(), resultViews.size());
119 | }
120 |
121 | }
--------------------------------------------------------------------------------