From 340e8ac9206e9ca63ed5d573332508ab974866f1 Mon Sep 17 00:00:00 2001 From: Dretch Date: Sun, 30 Apr 2017 19:15:08 +0100 Subject: [PATCH] Allow moving neighbouring thumbs to accommodate new thumb values (for issue #55) --- build.gradle | 2 +- lib/build.gradle | 5 + .../widget/MultiSliderThumbPushTest.java | 189 ++++++++++++++++++ .../java/io/apptik/widget/MultiSlider.java | 46 ++++- 4 files changed, 233 insertions(+), 9 deletions(-) create mode 100644 lib/src/androidTest/java/io/apptik/widget/MultiSliderThumbPushTest.java diff --git a/build.gradle b/build.gradle index 538e7f5..5b04e14 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,6 @@ ext { assertjCore1 = 'org.assertj:assertj-core:1.7.1' assertjCore2 = 'org.assertj:assertj-core:2.5.0' assertjCore3 = 'org.assertj:assertj-core:3.5.2' - mockitoCore = 'org.mockito:mockito-core:2.0.99-beta' + mockitoAndroid = 'org.mockito:mockito-android:2.7.22' junit = 'junit:junit:4.12' } diff --git a/lib/build.gradle b/lib/build.gradle index de71cd7..825d7ae 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -9,6 +9,7 @@ android { targetSdkVersion rootProject.ext.compileSdkVersion versionCode 1 versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } compileOptions { @@ -30,6 +31,10 @@ android { dependencies { compile rootProject.ext.supportDesign + androidTestCompile rootProject.ext.junit + androidTestCompile rootProject.ext.supportAnnotations + androidTestCompile rootProject.ext.supportTestRunner + androidTestCompile rootProject.ext.mockitoAndroid } apply from: 'https://raw.githubusercontent.com/djodjoni/gradle-mvn-push/master/gradle-mvn-push-android.gradle' diff --git a/lib/src/androidTest/java/io/apptik/widget/MultiSliderThumbPushTest.java b/lib/src/androidTest/java/io/apptik/widget/MultiSliderThumbPushTest.java new file mode 100644 index 0000000..93f3c84 --- /dev/null +++ b/lib/src/androidTest/java/io/apptik/widget/MultiSliderThumbPushTest.java @@ -0,0 +1,189 @@ +package io.apptik.widget; + +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; + +@RunWith(AndroidJUnit4.class) +public class MultiSliderThumbPushTest { + + private MultiSlider slider; + private MultiSlider.Thumb thumb0, thumb1, thumb2; + private MultiSlider.OnThumbValueChangeListener listener; + private InOrder listenerInOrder; + + @Before + public void setup() { + + slider = new MultiSlider(InstrumentationRegistry.getTargetContext()); + + slider.removeThumb(0); + slider.removeThumb(0); + + slider.setMin(0); + slider.setMax(20); + slider.setStep(1); + slider.setStepsThumbsApart(2); + + thumb0 = slider.addThumb(2); + thumb1 = slider.addThumb(6); + thumb2 = slider.addThumb(12); + + listener = mock(MultiSlider.OnThumbValueChangeListener.class); + listenerInOrder = inOrder(listener); + slider.setOnThumbValueChangeListener(listener); + } + + private void assertThumbValues(int expectedThumb0, int expectedThumb1, int expectedThumb2) { + assertEquals( + asList(expectedThumb0, expectedThumb1, expectedThumb2), + asList(thumb0.getValue(), thumb1.getValue(), thumb2.getValue())); + } + + @Test + public void testThumbMovedBetweenSelfAndRightNeighbour() { + + thumb1.setValue(8); + + assertThumbValues(2, 8, 12); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb1, 1, 8); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedBetweenSelfAndLeftNeighbour() { + + thumb2.setValue(10); + + assertThumbValues(2, 6, 10); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb2, 2, 10); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedBetweenSelfAndRightNeighbourWithPush() { + + thumb1.setValue(8, true); + + assertThumbValues(2, 8, 12); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb1, 1, 8); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedBetweenSelfAndLeftNeighbourWithPush() { + + thumb2.setValue(10, true); + + assertThumbValues(2, 6, 10); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb2, 2, 10); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedPastRightNeighbour() { + + thumb1.setValue(14); + + assertThumbValues(2, 10, 12); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb1, 1, 10); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedPastLeftNeighbour() { + + thumb1.setValue(0); + + assertThumbValues(2, 4, 12); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb1, 1, 4); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedPastRightNeighbourWithPush() { + + thumb1.setValue(14, true); + + assertThumbValues(2, 14, 16); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb2, 2, 16); + listenerInOrder.verify(listener).onValueChanged(slider, thumb1, 1, 14); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedPastLeftNeighbourWithPush() { + + thumb1.setValue(0, true); + + assertThumbValues(0, 2, 12); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb0, 0, 0); + listenerInOrder.verify(listener).onValueChanged(slider, thumb1, 1, 2); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedPastBothRightNeighbours() { + + thumb0.setValue(14); + + assertThumbValues(4, 6, 12); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb0, 0, 4); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedPastBothLeftNeighbours() { + + thumb2.setValue(0); + + assertThumbValues(2, 6, 8); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb2, 2, 8); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedPastBothRightNeighboursWithPush() { + + thumb0.setValue(14, true); + + assertThumbValues(14, 16, 18); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb2, 2, 18); + listenerInOrder.verify(listener).onValueChanged(slider, thumb1, 1, 16); + listenerInOrder.verify(listener).onValueChanged(slider, thumb0, 0, 14); + listenerInOrder.verifyNoMoreInteractions(); + } + + @Test + public void testThumbMovedPastBothLeftNeighboursWithPush() { + + thumb2.setValue(0, true); + + assertThumbValues(0, 2, 4); + + listenerInOrder.verify(listener).onValueChanged(slider, thumb0, 0, 0); + listenerInOrder.verify(listener).onValueChanged(slider, thumb1, 1, 2); + listenerInOrder.verify(listener).onValueChanged(slider, thumb2, 2, 4); + listenerInOrder.verifyNoMoreInteractions(); + } +} \ No newline at end of file diff --git a/lib/src/main/java/io/apptik/widget/MultiSlider.java b/lib/src/main/java/io/apptik/widget/MultiSlider.java index 85dec5d..e1dbe9e 100644 --- a/lib/src/main/java/io/apptik/widget/MultiSlider.java +++ b/lib/src/main/java/io/apptik/widget/MultiSlider.java @@ -312,20 +312,31 @@ public int getValue() { } /** - * Manually set a thumb value + * Manually set a thumb value, optionally moving neighbouring thumbs to make space. * * @param value + * @param pushNeighbours * @return */ - public Thumb setValue(int value) { + public Thumb setValue(int value, boolean pushNeighbours) { if(mThumbs.contains(this)) { - setThumbValue(this, value, false); + setThumbValue(this, value, pushNeighbours, false); } else { this.value = value; } return this; } + /** + * Manually set a thumb value + * + * @param value + * @return + */ + public Thumb setValue(int value) { + return setValue(value, false); + } + public String getTag() { return tag; } @@ -788,7 +799,6 @@ private int optThumbValue(Thumb thumb, int value) { if (thumb == null || thumb.getThumb() == null) return value; int currIdx = mThumbs.indexOf(thumb); - if (mThumbs.size() > currIdx + 1 && value > mThumbs.get(currIdx + 1).getValue() - mStepsThumbsApart * mStep) { value = mThumbs.get(currIdx + 1).getValue() - mStepsThumbsApart * mStep; @@ -820,11 +830,16 @@ private int optThumbValue(Thumb thumb, int value) { * * @param thumb the thumb which value is going to be changed * @param value the new value - * @param fromUser if the request is coming form the user or the client + * @param fromUser if the request is coming from the user or the client */ - private synchronized void setThumbValue(Thumb thumb, int value, boolean fromUser) { + private synchronized void setThumbValue(Thumb thumb, int value, boolean pushNeighbours, + boolean fromUser) { if (thumb == null || thumb.getThumb() == null) return; + if (pushNeighbours) { + pushNeighbouringThumbs(thumb, value, fromUser); + } + value = optThumbValue(thumb, value); if (value != thumb.getValue()) { @@ -837,8 +852,23 @@ private synchronized void setThumbValue(Thumb thumb, int value, boolean fromUser updateThumb(thumb, getWidth(), getHeight()); } - private synchronized void setThumbValue(int thumb, int value, boolean fromUser) { - setThumbValue(mThumbs.get(thumb), value, fromUser); + private synchronized void setThumbValue(Thumb thumb, int value, boolean fromUser) { + setThumbValue(thumb, value, false, fromUser); + } + + private void pushNeighbouringThumbs(Thumb thumb, int value, boolean fromUser) { + int currIdx = mThumbs.indexOf(thumb); + + int maxLeftValue = value - mStepsThumbsApart * mStep; + int minRightValue = value + mStepsThumbsApart * mStep; + + if (currIdx > 0 && mThumbs.get(currIdx - 1).getValue() > maxLeftValue) { + setThumbValue(mThumbs.get(currIdx - 1), maxLeftValue, true, fromUser); + } + + if (currIdx + 1 < mThumbs.size() && mThumbs.get(currIdx + 1).getValue() < minRightValue) { + setThumbValue(mThumbs.get(currIdx + 1), minRightValue, true, fromUser); + } } private void updateTrackBounds(int w, int h) {