1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.android.apps.common.testing.ui.espresso.action; 18 19import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isDisplayingAtLeast; 20 21import com.google.android.apps.common.testing.ui.espresso.PerformException; 22import com.google.android.apps.common.testing.ui.espresso.UiController; 23import com.google.android.apps.common.testing.ui.espresso.ViewAction; 24import com.google.android.apps.common.testing.ui.espresso.util.HumanReadables; 25 26import android.view.View; 27import android.view.ViewConfiguration; 28 29import org.hamcrest.Matcher; 30 31/** 32 * Enables swiping across a view. 33 */ 34public final class GeneralSwipeAction implements ViewAction { 35 36 /** Maximum number of times to attempt sending a swipe action. */ 37 private static final int MAX_TRIES = 3; 38 39 /** The minimum amount of a view that must be displayed in order to swipe across it. */ 40 private static final int VIEW_DISPLAY_PERCENTAGE = 90; 41 42 private final CoordinatesProvider startCoordinatesProvider; 43 private final CoordinatesProvider endCoordinatesProvider; 44 private final Swiper swiper; 45 private final PrecisionDescriber precisionDescriber; 46 47 public GeneralSwipeAction(Swiper swiper, CoordinatesProvider startCoordinatesProvider, 48 CoordinatesProvider endCoordinatesProvider, PrecisionDescriber precisionDescriber) { 49 this.swiper = swiper; 50 this.startCoordinatesProvider = startCoordinatesProvider; 51 this.endCoordinatesProvider = endCoordinatesProvider; 52 this.precisionDescriber = precisionDescriber; 53 } 54 55 @Override 56 public Matcher<View> getConstraints() { 57 return isDisplayingAtLeast(VIEW_DISPLAY_PERCENTAGE); 58 } 59 60 @Override 61 public void perform(UiController uiController, View view) { 62 float[] startCoordinates = startCoordinatesProvider.calculateCoordinates(view); 63 float[] endCoordinates = endCoordinatesProvider.calculateCoordinates(view); 64 float[] precision = precisionDescriber.describePrecision(); 65 66 Swiper.Status status = Swiper.Status.FAILURE; 67 68 for (int tries = 0; tries < MAX_TRIES && status != Swiper.Status.SUCCESS; tries++) { 69 try { 70 status = swiper.sendSwipe(uiController, startCoordinates, endCoordinates, precision); 71 } catch (RuntimeException re) { 72 throw new PerformException.Builder() 73 .withActionDescription(this.getDescription()) 74 .withViewDescription(HumanReadables.describe(view)) 75 .withCause(re) 76 .build(); 77 } 78 79 // ensures that all work enqueued to process the swipe has been run. 80 uiController.loopMainThreadForAtLeast(ViewConfiguration.getPressedStateDuration()); 81 } 82 83 if (status == Swiper.Status.FAILURE) { 84 throw new PerformException.Builder() 85 .withActionDescription(getDescription()) 86 .withViewDescription(HumanReadables.describe(view)) 87 .withCause(new RuntimeException(String.format( 88 "Couldn't swipe from: %s,%s to: %s,%s precision: %s, %s . Swiper: %s " 89 + "start coordinate provider: %s precision describer: %s. Tried %s times", 90 startCoordinates[0], 91 startCoordinates[1], 92 endCoordinates[0], 93 endCoordinates[1], 94 precision[0], 95 precision[1], 96 swiper, 97 startCoordinatesProvider, 98 precisionDescriber, 99 MAX_TRIES))) 100 .build(); 101 } 102 } 103 104 @Override 105 public String getDescription() { 106 return swiper.toString().toLowerCase() + " swipe"; 107 } 108} 109