)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.catrobat.paintroid.test.espresso.util;
-
-import android.view.View;
-
-import androidx.test.espresso.action.CoordinatesProvider;
-
-public class PositionCoordinatesProvider implements CoordinatesProvider {
- private final float xCoordinate;
- private final float yCoordinate;
-
- public PositionCoordinatesProvider(float x, float y) {
- this.xCoordinate = x;
- this.yCoordinate = y;
- }
-
- public static CoordinatesProvider at(float x, float y) {
- return new PositionCoordinatesProvider(x, y);
- }
-
- @Override
- public float[] calculateCoordinates(View view) {
- return calculateViewOffset(view, xCoordinate, yCoordinate);
- }
-
- public static float[] calculateViewOffset(View view, float x, float y) {
- final int[] screenLocation = new int[2];
- view.getLocationOnScreen(screenLocation);
-
- final float touchX = screenLocation[0] + x;
- final float touchY = screenLocation[1] + y;
- return new float[] {touchX, touchY};
- }
-}
diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.kt
new file mode 100644
index 0000000000..f687395783
--- /dev/null
+++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.kt
@@ -0,0 +1,42 @@
+/**
+ * Paintroid: An image manipulation application for Android.
+ * Copyright (C) 2010-2015 The Catrobat Team
+ * (//developer.catrobat.org/credits>)
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see //www.gnu.org/licenses/>.
+ */
+package org.catrobat.paintroid.test.espresso.util
+
+import android.view.View
+import androidx.test.espresso.action.CoordinatesProvider
+
+class PositionCoordinatesProvider(private val xCoordinate: Float, private val yCoordinate: Float) : CoordinatesProvider {
+ override fun calculateCoordinates(view: View): FloatArray = calculateViewOffset(view, xCoordinate, yCoordinate)
+
+ companion object {
+ @JvmStatic
+ fun at(x: Float, y: Float): CoordinatesProvider = PositionCoordinatesProvider(x, y)
+ @JvmStatic
+ fun calculateViewOffset(view: View, x: Float, y: Float): FloatArray {
+ val screenLocation = IntArray(2)
+ view.getLocationOnScreen(screenLocation)
+ val touchX = screenLocation[0] + x
+ val touchY = screenLocation[1] + y
+ return floatArrayOf(touchX, touchY)
+ }
+ }
+}
diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.java
deleted file mode 100644
index 60bece8b8b..0000000000
--- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Paintroid: An image manipulation application for Android.
- * Copyright (C) 2010-2015 The Catrobat Team
- * ()
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.catrobat.paintroid.test.espresso.util;
-
-import android.graphics.PointF;
-import android.util.Log;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ProgressBar;
-import android.widget.SeekBar;
-
-import org.hamcrest.Matcher;
-
-import static org.catrobat.paintroid.test.espresso.util.CustomSwiper.ACCURATE;
-import static org.hamcrest.Matchers.is;
-
-import java.lang.reflect.Method;
-
-import androidx.test.espresso.UiController;
-import androidx.test.espresso.ViewAction;
-import androidx.test.espresso.ViewAssertion;
-import androidx.test.espresso.action.CoordinatesProvider;
-import androidx.test.espresso.action.GeneralClickAction;
-import androidx.test.espresso.action.GeneralLocation;
-import androidx.test.espresso.action.GeneralSwipeAction;
-import androidx.test.espresso.action.MotionEvents;
-import androidx.test.espresso.action.Press;
-import androidx.test.espresso.action.ScrollToAction;
-import androidx.test.espresso.action.Swipe;
-import androidx.test.espresso.action.Tap;
-import androidx.test.espresso.action.Tapper;
-import androidx.test.espresso.matcher.ViewMatchers;
-import androidx.viewpager.widget.ViewPager;
-
-import static androidx.test.espresso.action.ViewActions.actionWithAssertions;
-import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
-import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
-import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
-import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
-
-public final class UiInteractions {
-
- private UiInteractions() {
- }
-
- public static ViewAction unconstrainedScrollTo() {
- return actionWithAssertions(new UnconstrainedScrollToAction());
- }
-
- public static ViewAction waitFor(final long millis) {
- return new ViewAction() {
- @Override
- public Matcher getConstraints() {
- return isRoot();
- }
-
- @Override
- public String getDescription() {
- return "Wait for " + millis + " milliseconds.";
- }
-
- @Override
- public void perform(UiController uiController, final View view) {
- uiController.loopMainThreadForAtLeast(millis);
- }
- };
- }
-
- public static ViewAssertion assertRecyclerViewCount(final int expectedCount) {
- return (view, noViewFoundException) -> {
- if (noViewFoundException != null) {
- throw noViewFoundException;
- }
-
- org.catrobat.paintroid.ui.LayerAdapter adapter = (org.catrobat.paintroid.ui.LayerAdapter) ((androidx.recyclerview.widget.RecyclerView) view).getAdapter();
- if (adapter != null) {
- assertThat(adapter.getItemCount(), is(expectedCount));
- }
- };
- }
-
- public static ViewAction setProgress(final int progress) {
- return new ViewAction() {
-
- @Override
- public Matcher getConstraints() {
- return isAssignableFrom(SeekBar.class);
- }
-
- @Override
- public String getDescription() {
- return "Set a progress";
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- try {
- Method privateSetProgressMethod = ProgressBar.class.getDeclaredMethod("setProgressInternal", Integer.TYPE, Boolean.TYPE, Boolean.TYPE);
- privateSetProgressMethod.setAccessible(true);
- privateSetProgressMethod.invoke(view, progress, true, true);
- } catch (ReflectiveOperationException e) {
- Log.e("SET PROGRESS", "could not set progress");
- }
- }
- };
- }
-
- public static ViewAction clickOutside(final Direction direction) {
- return actionWithAssertions(
- new GeneralClickAction(Tap.SINGLE, view -> {
- android.graphics.Rect r = new android.graphics.Rect();
- view.getGlobalVisibleRect(r);
- switch (direction) {
- case ABOVE:
- return new float[]{r.centerX(), r.top - 50};
- case BELOW:
- return new float[]{r.centerX(), r.bottom + 50};
- case LEFT:
- return new float[]{r.left - 50, r.centerY()};
- case RIGHT:
- return new float[]{r.right + 50, r.centerY()};
- }
- return null;
- }, Press.FINGER, 0, 1)
- );
- }
-
- public static ViewAction touchAt(final CoordinatesProvider provider) {
- return touchAt(provider, Tap.SINGLE);
- }
-
- public static ViewAction touchAt(final PointF coordinates) {
- return touchAt(coordinates, Tap.SINGLE);
- }
-
- public static ViewAction touchAt(final int x, final int y) {
- return touchAt((float) x, (float) y);
- }
-
- public static ViewAction touchAt(final float x, final float y) {
- return touchAt(x, y, Tap.SINGLE);
- }
-
- public static ViewAction touchLongAt(final CoordinatesProvider provider) {
- return touchAt(provider, Tap.LONG);
- }
-
- public static ViewAction touchLongAt(final PointF coordinates) {
- return touchAt(coordinates, Tap.LONG);
- }
-
- public static ViewAction touchLongAt(final float x, final float y) {
- return touchAt(x, y, Tap.LONG);
- }
-
- public static ViewAction touchAt(final CoordinatesProvider provider, final Tapper tapStyle) {
- return actionWithAssertions(
- new GeneralClickAction(tapStyle, provider, Press.FINGER, 0, 0));
- }
-
- public static ViewAction touchAt(final PointF coordinates, final Tapper tapStyle) {
- return touchAt(coordinates.x, coordinates.y, tapStyle);
- }
-
- public static ViewAction touchAt(final float x, final float y, final Tapper tapStyle) {
- return actionWithAssertions(
- new GeneralClickAction(tapStyle, PositionCoordinatesProvider.at(x, y), Press.FINGER, 0, 0)
- );
- }
-
- public static ViewAction touchCenterLeft() {
- return new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_LEFT, Press.FINGER, 0, 0);
- }
-
- public static ViewAction touchCenterTop() {
- return new GeneralClickAction(Tap.SINGLE, GeneralLocation.TOP_CENTER, Press.FINGER, 0, 0);
- }
-
- public static ViewAction touchCenterBottom() {
- return new GeneralClickAction(Tap.SINGLE, GeneralLocation.BOTTOM_CENTER, Press.FINGER, 0, 0);
- }
-
- public static ViewAction touchCenterMiddle() {
- return new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER, 0, 0);
- }
-
- public static ViewAction touchCenterRight() {
- return new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_RIGHT, Press.FINGER, 0, 0);
- }
-
- public static ViewAction swipe(PointF start, PointF end) {
- return swipe((int) start.x, (int) start.y, (int) end.x, (int) end.y);
- }
-
- public static ViewAction swipe(float startX, float startY, float endX, float endY) {
- return swipe((int) startX, (int) startY, (int) endX, (int) endY);
- }
-
- public static ViewAction swipe(int startX, int startY, int endX, int endY) {
- return swipe(PositionCoordinatesProvider.at(startX, startY), PositionCoordinatesProvider.at(endX, endY));
- }
-
- public static ViewAction swipe(CoordinatesProvider startCoordinatesProvider, CoordinatesProvider endCoordinatesProvider) {
- return new GeneralSwipeAction(Swipe.FAST, startCoordinatesProvider, endCoordinatesProvider, Press.FINGER);
- }
-
- public static ViewAction swipeAccurate(CoordinatesProvider startCoordinatesProvider, CoordinatesProvider endCoordinatesProvider) {
- return new GeneralSwipeAction(ACCURATE, startCoordinatesProvider, endCoordinatesProvider, Press.FINGER);
- }
-
- public static ViewAction selectViewPagerPage(final int pos) {
- return new ViewAction() {
- @Override
- public Matcher getConstraints() {
- return isAssignableFrom(ViewPager.class);
- }
-
- @Override
- public String getDescription() {
- return "select page in ViewPager";
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- ((ViewPager) view).setCurrentItem(pos);
- }
- };
- }
-
- public enum Direction {
- ABOVE,
- BELOW,
- LEFT,
- RIGHT
- }
-
- private static class UnconstrainedScrollToAction implements ViewAction {
- private final ViewAction action = new ScrollToAction();
-
- @Override
- public Matcher getConstraints() {
- return withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE);
- }
-
- @Override
- public String getDescription() {
- return action.getDescription();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- action.perform(uiController, view);
- }
- }
-
- public static class DefinedLongTap implements Tapper {
-
- private final int longPressTimeout;
-
- DefinedLongTap(int longPressTimeout) {
- this.longPressTimeout = longPressTimeout;
- }
-
- public static Tapper withPressTimeout(final int longPressTimeout) {
- return new DefinedLongTap(longPressTimeout);
- }
-
- @Override
- public Status sendTap(
- UiController uiController, float[] coordinates, float[] precision, int inputDevice,
- int buttonState) {
- MotionEvent downEvent = MotionEvents.sendDown(uiController, coordinates, precision,
- inputDevice, buttonState).down;
- try {
- // Duration before a press turns into a long press.
- // Factor 1.5 is needed, otherwise a long press is not safely detected.
- // See android.test.TouchUtils longClickView
- long longPressTimeout = (long) (this.longPressTimeout * 1.5f);
- uiController.loopMainThreadForAtLeast(longPressTimeout);
-
- if (!MotionEvents.sendUp(uiController, downEvent)) {
- MotionEvents.sendCancel(uiController, downEvent);
- return Status.FAILURE;
- }
- } finally {
- downEvent.recycle();
- }
- return Status.SUCCESS;
- }
-
- /**
- * @deprecated use other sendTap instead
- */
- @Deprecated
- @Override
- public Status sendTap(UiController uiController, float[] coordinates, float[] precision) {
- return sendTap(uiController, coordinates, precision, InputDevice.SOURCE_UNKNOWN,
- MotionEvent.BUTTON_PRIMARY);
- }
- }
-
- public static class PressAndReleaseActions {
-
- static MotionEvent motionEvent = null;
-
- public static PressAction pressAction(DrawingSurfaceLocationProvider coordinates) {
- return new PressAction(coordinates);
- }
-
- public static ReleaseAction releaseAction() {
- return new ReleaseAction();
- }
-
- public static void tearDownPressAndRelease() {
- motionEvent = null;
- }
-
- public static class PressAction implements ViewAction {
-
- DrawingSurfaceLocationProvider coordinates;
-
- PressAction(DrawingSurfaceLocationProvider coords) {
- this.coordinates = coords;
- }
-
- @Override
- public Matcher getConstraints() {
- return isDisplayingAtLeast(90);
- }
-
- @Override
- public String getDescription() {
- return "Press Action";
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- if (motionEvent != null) {
- throw new AssertionError("Only one view can be held at a time");
- }
- float[] coords = coordinates.calculateCoordinates(view);
- float[] precision = Press.FINGER.describePrecision();
-
- motionEvent = MotionEvents.sendDown(uiController, coords, precision).down;
- }
- }
-
- public static class ReleaseAction implements ViewAction {
-
- @Override
- public Matcher getConstraints() {
- return isDisplayingAtLeast(90);
- }
-
- @Override
- public String getDescription() {
- return "Release";
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- if (motionEvent == null) {
- throw new AssertionError("Only one view can be held at a time");
- }
- MotionEvents.sendUp(uiController, motionEvent);
- }
- }
- }
-}
diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.kt
new file mode 100644
index 0000000000..4a19745683
--- /dev/null
+++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.kt
@@ -0,0 +1,333 @@
+/*
+ * Paintroid: An image manipulation application for Android.
+ * Copyright (C) 2010-2015 The Catrobat Team
+ * ()
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.catrobat.paintroid.test.espresso.util
+
+import android.graphics.PointF
+import android.graphics.Rect
+import android.util.Log
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.View
+import android.widget.ProgressBar
+import android.widget.SeekBar
+import androidx.recyclerview.widget.RecyclerView
+import androidx.test.espresso.NoMatchingViewException
+import androidx.test.espresso.UiController
+import androidx.test.espresso.ViewAction
+import androidx.test.espresso.ViewAssertion
+import androidx.test.espresso.action.CoordinatesProvider
+import androidx.test.espresso.action.GeneralClickAction
+import androidx.test.espresso.action.GeneralLocation
+import androidx.test.espresso.action.GeneralSwipeAction
+import androidx.test.espresso.action.MotionEvents
+import androidx.test.espresso.action.Press
+import androidx.test.espresso.action.ScrollToAction
+import androidx.test.espresso.action.Swipe
+import androidx.test.espresso.action.Tap
+import androidx.test.espresso.action.Tapper
+import androidx.test.espresso.action.ViewActions
+import androidx.test.espresso.matcher.ViewMatchers
+import androidx.viewpager.widget.ViewPager
+import org.catrobat.paintroid.ui.LayerAdapter
+import org.hamcrest.Matcher
+import org.hamcrest.Matchers
+import java.lang.Boolean
+import kotlin.AssertionError
+import kotlin.Deprecated
+import kotlin.Float
+import kotlin.FloatArray
+import kotlin.Int
+import kotlin.Long
+import kotlin.String
+import kotlin.floatArrayOf
+
+object UiInteractions {
+ fun unconstrainedScrollTo(): ViewAction = ViewActions.actionWithAssertions(UnconstrainedScrollToAction())
+
+ fun waitFor(millis: Long): ViewAction {
+ return object : ViewAction {
+ override fun getConstraints(): Matcher {
+ return ViewMatchers.isRoot()
+ }
+
+ override fun getDescription(): String {
+ return "Wait for $millis milliseconds."
+ }
+
+ override fun perform(uiController: UiController, view: View) {
+ uiController.loopMainThreadForAtLeast(millis)
+ }
+ }
+ }
+
+ fun assertRecyclerViewCount(expectedCount: Int): ViewAssertion {
+ return ViewAssertion { view: View, noViewFoundException: NoMatchingViewException? ->
+ if (noViewFoundException != null) {
+ throw noViewFoundException
+ }
+ val adapter = (view as RecyclerView).adapter as LayerAdapter?
+ if (adapter != null) {
+ ViewMatchers.assertThat(
+ adapter.itemCount,
+ Matchers.`is`(expectedCount)
+ )
+ }
+ }
+ }
+
+ fun setProgress(progress: Int): ViewAction {
+ return object : ViewAction {
+ override fun getConstraints(): Matcher {
+ return ViewMatchers.isAssignableFrom(SeekBar::class.java)
+ }
+
+ override fun getDescription(): String {
+ return "Set a progress"
+ }
+
+ override fun perform(uiController: UiController, view: View) {
+ try {
+ val privateSetProgressMethod =
+ ProgressBar::class.java.getDeclaredMethod(
+ "setProgressInternal",
+ Integer.TYPE,
+ Boolean.TYPE,
+ Boolean.TYPE
+ )
+ privateSetProgressMethod.isAccessible = true
+ privateSetProgressMethod.invoke(view, progress, true, true)
+ } catch (e: ReflectiveOperationException) {
+ Log.e("SET PROGRESS", "could not set progress")
+ }
+ }
+ }
+ }
+
+ fun clickOutside(direction: Direction?): ViewAction? {
+ return ViewActions.actionWithAssertions(
+ GeneralClickAction(Tap.SINGLE, CoordinatesProvider { view: View ->
+ val r = Rect()
+ view.getGlobalVisibleRect(r)
+ when (direction) {
+ Direction.ABOVE -> return@CoordinatesProvider floatArrayOf(
+ r.centerX().toFloat(),
+ (r.top - 50).toFloat()
+ )
+ Direction.BELOW -> return@CoordinatesProvider floatArrayOf(
+ r.centerX().toFloat(),
+ (r.bottom + 50).toFloat()
+ )
+ Direction.LEFT -> return@CoordinatesProvider floatArrayOf(
+ (r.left - 50).toFloat(),
+ r.centerY().toFloat()
+ )
+ Direction.RIGHT -> return@CoordinatesProvider floatArrayOf(
+ (r.right + 50).toFloat(),
+ r.centerY().toFloat()
+ )
+ }
+ null
+ }, Press.FINGER, 0, 1)
+ )
+ }
+ fun touchAt(x: Int, y: Int): ViewAction = touchAt(x.toFloat(), y.toFloat())
+
+ fun touchLongAt(provider: CoordinatesProvider?): ViewAction = touchAt(provider, Tap.LONG)
+
+ fun touchLongAt(coordinates: PointF): ViewAction = touchAt(coordinates, Tap.LONG)
+
+ fun touchLongAt(x: Float, y: Float): ViewAction = touchAt(x, y, Tap.LONG)
+
+ @JvmOverloads
+ fun touchAt(provider: CoordinatesProvider?, tapStyle: Tapper? = Tap.SINGLE): ViewAction {
+ return ViewActions.actionWithAssertions(
+ GeneralClickAction(tapStyle, provider, Press.FINGER, 0, 0)
+ )
+ }
+
+ @JvmOverloads
+ fun touchAt(coordinates: PointF, tapStyle: Tapper? = Tap.SINGLE): ViewAction = touchAt(coordinates.x, coordinates.y, tapStyle)
+
+ @JvmOverloads
+ fun touchAt(x: Float, y: Float, tapStyle: Tapper? = Tap.SINGLE): ViewAction {
+ return ViewActions.actionWithAssertions(
+ GeneralClickAction(tapStyle, PositionCoordinatesProvider.at(x, y), Press.FINGER, 0, 0)
+ )
+ }
+
+ fun touchCenterLeft(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_LEFT, Press.FINGER, 0, 0)
+
+ fun touchCenterTop(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.TOP_CENTER, Press.FINGER, 0, 0)
+
+ fun touchCenterBottom(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.BOTTOM_CENTER, Press.FINGER, 0, 0)
+
+ fun touchCenterMiddle(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER, 0, 0)
+
+ fun touchCenterRight(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_RIGHT, Press.FINGER, 0, 0)
+
+ fun swipe(start: PointF, end: PointF): ViewAction = swipe(start.x.toInt(), start.y.toInt(), end.x.toInt(), end.y.toInt())
+
+ fun swipe(startX: Float, startY: Float, endX: Float, endY: Float): ViewAction = swipe(startX.toInt(), startY.toInt(), endX.toInt(), endY.toInt())
+
+ fun swipe(startX: Int, startY: Int, endX: Int, endY: Int): ViewAction {
+ return swipe(
+ PositionCoordinatesProvider.at(startX.toFloat(), startY.toFloat()),
+ PositionCoordinatesProvider.at(endX.toFloat(), endY.toFloat())
+ )
+ }
+
+ fun swipe(
+ startCoordinatesProvider: CoordinatesProvider?,
+ endCoordinatesProvider: CoordinatesProvider?
+ ): ViewAction {
+ return GeneralSwipeAction(
+ Swipe.FAST,
+ startCoordinatesProvider,
+ endCoordinatesProvider,
+ Press.FINGER
+ )
+ }
+
+ fun swipeAccurate(
+ startCoordinatesProvider: CoordinatesProvider?,
+ endCoordinatesProvider: CoordinatesProvider?
+ ): ViewAction {
+ return GeneralSwipeAction(
+ CustomSwiper.ACCURATE,
+ startCoordinatesProvider,
+ endCoordinatesProvider,
+ Press.FINGER
+ )
+ }
+
+ fun selectViewPagerPage(pos: Int): ViewAction {
+ return object : ViewAction {
+ override fun getConstraints(): Matcher {
+ return ViewMatchers.isAssignableFrom(ViewPager::class.java)
+ }
+
+ override fun getDescription(): String {
+ return "select page in ViewPager"
+ }
+
+ override fun perform(uiController: UiController, view: View) {
+ (view as ViewPager).currentItem = pos
+ }
+ }
+ }
+
+ enum class Direction {
+ ABOVE, BELOW, LEFT, RIGHT
+ }
+
+ private class UnconstrainedScrollToAction : ViewAction {
+ private val action: ViewAction = ScrollToAction()
+ override fun getConstraints(): Matcher = ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)
+
+ override fun getDescription(): String = action.description
+
+ override fun perform(uiController: UiController, view: View) = action.perform(uiController, view)
+ }
+
+ class DefinedLongTap internal constructor(private val longPressTimeout: Int) : Tapper {
+ override fun sendTap(
+ uiController: UiController,
+ coordinates: FloatArray,
+ precision: FloatArray,
+ inputDevice: Int,
+ buttonState: Int
+ ): Tapper.Status {
+ val downEvent = MotionEvents.sendDown(
+ uiController, coordinates, precision,
+ inputDevice, buttonState
+ ).down
+ try {
+ // Duration before a press turns into a long press.
+ // Factor 1.5 is needed, otherwise a long press is not safely detected.
+ // See android.test.TouchUtils longClickView
+ val longPressTimeout = (longPressTimeout * 1.5f).toLong()
+ uiController.loopMainThreadForAtLeast(longPressTimeout)
+ if (!MotionEvents.sendUp(uiController, downEvent)) {
+ MotionEvents.sendCancel(uiController, downEvent)
+ return Tapper.Status.FAILURE
+ }
+ } finally {
+ downEvent.recycle()
+ }
+ return Tapper.Status.SUCCESS
+ }
+
+ @Deprecated("use other sendTap instead")
+ override fun sendTap(
+ uiController: UiController,
+ coordinates: FloatArray,
+ precision: FloatArray
+ ): Tapper.Status {
+ return sendTap(
+ uiController, coordinates, precision, InputDevice.SOURCE_UNKNOWN,
+ MotionEvent.BUTTON_PRIMARY
+ )
+ }
+
+ companion object {
+ fun withPressTimeout(longPressTimeout: Int): Tapper = DefinedLongTap(longPressTimeout)
+ }
+ }
+
+ object PressAndReleaseActions {
+ var motionEvent: MotionEvent? = null
+ fun pressAction(coordinates: DrawingSurfaceLocationProvider): PressAction = PressAction(coordinates)
+
+ fun releaseAction(): ReleaseAction = ReleaseAction()
+
+ fun tearDownPressAndRelease() {
+ motionEvent = null
+ }
+
+ class PressAction internal constructor(var coordinates: DrawingSurfaceLocationProvider) :
+ ViewAction {
+
+ override fun getConstraints(): Matcher = ViewMatchers.isDisplayingAtLeast(90)
+
+ override fun getDescription(): String = "Press Action"
+
+ override fun perform(uiController: UiController, view: View) {
+ if (motionEvent != null) {
+ throw AssertionError("Only one view can be held at a time")
+ }
+ val coords = coordinates.calculateCoordinates(view)
+ val precision = Press.FINGER.describePrecision()
+ motionEvent = MotionEvents.sendDown(uiController, coords, precision).down
+ }
+ }
+
+ class ReleaseAction : ViewAction {
+ override fun getConstraints(): Matcher = ViewMatchers.isDisplayingAtLeast(90)
+
+ override fun getDescription(): String = "Release"
+
+ override fun perform(uiController: UiController, view: View) {
+ if (motionEvent == null) {
+ throw AssertionError("Only one view can be held at a time")
+ }
+ MotionEvents.sendUp(uiController, motionEvent)
+ }
+ }
+ }
+}
diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.java
deleted file mode 100644
index 3fc0475ff4..0000000000
--- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.java
+++ /dev/null
@@ -1,600 +0,0 @@
-/**
- * Paintroid: An image manipulation application for Android.
- * Copyright (C) 2010-2015 The Catrobat Team
- * ()
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.catrobat.paintroid.test.espresso.util;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.graphics.drawable.VectorDrawable;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.WindowManager;
-import android.widget.Adapter;
-import android.widget.AdapterView;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.TableRow;
-import android.widget.TextView;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.test.espresso.NoMatchingViewException;
-import androidx.test.espresso.Root;
-import androidx.test.espresso.ViewAssertion;
-import androidx.test.espresso.matcher.BoundedMatcher;
-import androidx.test.espresso.matcher.ViewMatchers;
-import androidx.test.espresso.util.HumanReadables;
-import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-import static androidx.core.util.Preconditions.checkNotNull;
-import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
-
-public final class UiMatcher {
-
- private UiMatcher() {
- }
-
- public static Matcher atPosition(final int position, @NonNull final Matcher itemMatcher) {
- checkNotNull(itemMatcher);
- return new BoundedMatcher(RecyclerView.class) {
- @Override
- public void describeTo(Description description) {
- description.appendText("has item at position " + position + ": ");
- itemMatcher.describeTo(description);
- }
-
- @Override
- protected boolean matchesSafely(final RecyclerView view) {
- RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(position);
- if (viewHolder == null) {
- // has no item on such position
- return false;
- }
- return itemMatcher.matches(viewHolder.itemView);
- }
- };
- }
-
- public static Matcher withIndex(final Matcher matcher, final int index) {
- return new TypeSafeMatcher() {
- int currentIndex = 0;
-
- @Override
- public void describeTo(Description description) {
- description.appendText("with index: ");
- description.appendValue(index);
- matcher.describeTo(description);
- }
-
- @Override
- public boolean matchesSafely(View view) {
- return matcher.matches(view) && currentIndex++ == index;
- }
- };
- }
-
- public static Matcher hasTypeFace(final Typeface typeface) {
- return new TypeSafeMatcher() {
-
- @Override
- protected boolean matchesSafely(final View view) {
- return view instanceof TextView && ((TextView) view).getTypeface() == typeface;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("the selected TextView doesn't have the TypeFace:" + typeface);
- }
- };
- }
-
- public static Matcher hasChildPosition(final int position) {
- return new TypeSafeMatcher() {
- @Override
- public void describeTo(Description description) {
- description.appendText("is child #" + position);
- }
-
- @Override
- public boolean matchesSafely(View view) {
- ViewParent viewParent = view.getParent();
-
- if (!(viewParent instanceof ViewGroup)) {
- return false;
- }
-
- ViewGroup viewGroup = (ViewGroup) viewParent;
- return viewGroup.indexOfChild(view) == position;
- }
- };
- }
-
- public static Matcher hasTablePosition(final int rowIndex, final int columnIndex) {
- return new TypeSafeMatcher() {
- @Override
- public void describeTo(Description description) {
- description.appendText("is child in cell @(" + rowIndex + "|" + columnIndex + ")");
- }
-
- @Override
- public boolean matchesSafely(View view) {
- ViewParent tableRow = view.getParent();
- if (!(tableRow instanceof ViewGroup)) {
- return false;
- }
- if (((ViewGroup) tableRow).indexOfChild(view) != columnIndex) {
- return false;
- }
-
- ViewParent tableLayout = tableRow.getParent();
- if (!(tableLayout instanceof ViewGroup)) {
- return false;
- }
-
- return ((ViewGroup) tableLayout).indexOfChild((TableRow) tableRow) == rowIndex;
- }
- };
- }
-
- public static Matcher withBackgroundColor(final Matcher colorMatcher) {
-
- return new TypeSafeMatcher() {
- @Override
- protected boolean matchesSafely(View view) {
- ColorDrawable colorDrawable = (ColorDrawable) view.getBackground();
-
- if (colorDrawable == null) {
- return false;
- }
-
- int bgColor = colorDrawable.getColor();
-
- return colorMatcher.matches(bgColor);
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("with background color: ");
- colorMatcher.describeTo(description);
- }
- };
- }
-
- public static Matcher withBackgroundColor(final int color) {
-
- return new TypeSafeMatcher() {
- @Override
- protected boolean matchesSafely(View view) {
- Drawable background = view.getBackground();
-
- if (background == null) {
- return false;
- }
-
- if (background instanceof ColorDrawable) {
- return color == ((ColorDrawable) background).getColor();
- } else if (background instanceof LayerDrawable) {
- LayerDrawable layerDrawable = (LayerDrawable) background;
- Drawable drawable = layerDrawable.getDrawable(0);
- return drawable instanceof ColorDrawable
- && color == ((ColorDrawable) drawable).getColor();
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("with background color: " + color);
- }
- };
- }
-
- public static Matcher withTextColor(final Matcher colorMatcher) {
-
- return new TypeSafeMatcher() {
- @Override
- protected boolean matchesSafely(View view) {
- if (!(view instanceof TextView)) {
- return false;
- }
-
- TextView textView = (TextView) view;
-
- int textColor = textView.getCurrentTextColor();
-
- return colorMatcher.matches(textColor);
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("with text color: ");
- colorMatcher.describeTo(description);
- }
- };
- }
-
- public static Matcher withTextColor(final int color) {
-
- return new TypeSafeMatcher() {
- @Override
- protected boolean matchesSafely(View view) {
- if (!(view instanceof TextView)) {
- return false;
- }
-
- TextView textView = (TextView) view;
-
- int textColor = textView.getCurrentTextColor();
-
- return textColor == color;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("with text color: " + color);
- }
- };
- }
-
- public static Matcher withProgress(final int progress) {
-
- return new TypeSafeMatcher() {
- @Override
- protected boolean matchesSafely(View view) {
- if (!(view instanceof SeekBar)) {
- return false;
- }
-
- SeekBar seekbarView = (SeekBar) view;
-
- int seekbarProgress = seekbarView.getProgress();
-
- return seekbarProgress == progress;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("with progress: " + progress);
- }
- };
- }
-
- public static Matcher withBackground(final int resourceId) {
-
- return new TypeSafeMatcher() {
- String resourceName;
-
- @Override
- protected boolean matchesSafely(View target) {
- Resources resources = target.getContext().getResources();
- resourceName = resources.getResourceEntryName(resourceId);
-
- if (!(target instanceof ImageView)) {
- return false;
- }
-
- Drawable expectedDrawable = resources.getDrawable(resourceId);
- Drawable targetDrawable = target.getBackground();
-
- if (expectedDrawable == null || targetDrawable == null) {
- return false;
- }
-
- Bitmap expectedBitmap = ((BitmapDrawable) expectedDrawable).getBitmap();
-
- if (targetDrawable instanceof BitmapDrawable) {
- Bitmap bitmap = ((BitmapDrawable) targetDrawable).getBitmap();
- return bitmap.sameAs(expectedBitmap);
- } else if (targetDrawable instanceof StateListDrawable) {
- Bitmap bitmap = ((BitmapDrawable) targetDrawable.getCurrent()).getBitmap();
- return bitmap.sameAs(expectedBitmap);
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("with drawable from resource id: ");
- description.appendValue(resourceId);
- if (resourceName != null) {
- description.appendText("[");
- description.appendText(resourceName);
- description.appendText("]");
- }
- }
- };
- }
-
- public static Matcher withChildren(final Matcher numberOfChildrenMatcher) {
-
- return new TypeSafeMatcher() {
-
- @Override
- protected boolean matchesSafely(View target) {
- if (!(target instanceof ViewGroup)) {
- return false;
- }
-
- return numberOfChildrenMatcher.matches(((ViewGroup) target).getChildCount());
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("with children # is ");
- numberOfChildrenMatcher.describeTo(description);
- }
- };
- }
-
- public static Matcher