public final class

GeneralClickAction

extends java.lang.Object

implements ViewAction

 java.lang.Object

↳androidx.test.espresso.action.GeneralClickAction

Gradle dependencies

compile group: 'androidx.test.espresso', name: 'espresso-core', version: '3.6.1'

  • groupId: androidx.test.espresso
  • artifactId: espresso-core
  • version: 3.6.1

Artifact androidx.test.espresso:espresso-core:3.6.1 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.test.espresso:espresso-core com.android.support.test.espresso:espresso-core

Androidx class mapping:

androidx.test.espresso.action.GeneralClickAction android.support.test.espresso.action.GeneralClickAction

Overview

Enables clicking on views.

Summary

Constructors
publicGeneralClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, PrecisionDescriber precisionDescriber)

publicGeneralClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, PrecisionDescriber precisionDescriber, int inputDevice, int buttonState)

publicGeneralClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, PrecisionDescriber precisionDescriber, int inputDevice, int buttonState, ViewAction rollbackAction)

publicGeneralClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, PrecisionDescriber precisionDescriber, ViewAction rollbackAction)

Methods
public <any>getConstraints()

public java.lang.StringgetDescription()

public voidperform(UiController uiController, View view)

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public GeneralClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, PrecisionDescriber precisionDescriber)

Deprecated: Use GeneralClickAction.GeneralClickAction(Tapper, CoordinatesProvider, PrecisionDescriber, int, int) instead.

public GeneralClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, PrecisionDescriber precisionDescriber, int inputDevice, int buttonState)

public GeneralClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, PrecisionDescriber precisionDescriber, ViewAction rollbackAction)

Deprecated: Use GeneralClickAction.GeneralClickAction(Tapper, CoordinatesProvider, PrecisionDescriber, int, int, ViewAction) instead.

public GeneralClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, PrecisionDescriber precisionDescriber, int inputDevice, int buttonState, ViewAction rollbackAction)

Methods

public <any> getConstraints()

public void perform(UiController uiController, View view)

public java.lang.String getDescription()

Source

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.test.espresso.action;

import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
import static org.hamcrest.Matchers.allOf;

import android.util.Log;
import android.view.View;
import android.view.ViewConfiguration;
import android.webkit.WebView;
import androidx.annotation.Nullable;
import androidx.test.espresso.PerformException;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.util.HumanReadables;
import java.util.Locale;
import org.hamcrest.Matcher;

/** Enables clicking on views. */
public final class GeneralClickAction implements ViewAction {
  private static final String TAG = "GeneralClickAction";

  final CoordinatesProvider coordinatesProvider;
  final Tapper tapper;
  final PrecisionDescriber precisionDescriber;
  @Nullable private final ViewAction rollbackAction;
  private final int inputDevice;
  private final int buttonState;

  /**
   * @deprecated Use {@link #GeneralClickAction(Tapper, CoordinatesProvider, PrecisionDescriber,
   *     int, int)} instead.
   */
  @Deprecated
  public GeneralClickAction(
      Tapper tapper,
      CoordinatesProvider coordinatesProvider,
      PrecisionDescriber precisionDescriber) {
    this(tapper, coordinatesProvider, precisionDescriber, 0, 0, null);
  }

  public GeneralClickAction(
      Tapper tapper,
      CoordinatesProvider coordinatesProvider,
      PrecisionDescriber precisionDescriber,
      int inputDevice,
      int buttonState) {
    this(tapper, coordinatesProvider, precisionDescriber, inputDevice, buttonState, null);
  }

  /**
   * @deprecated Use {@link #GeneralClickAction(Tapper, CoordinatesProvider, PrecisionDescriber,
   *     int, int, ViewAction)} instead.
   */
  @Deprecated
  public GeneralClickAction(
      Tapper tapper,
      CoordinatesProvider coordinatesProvider,
      PrecisionDescriber precisionDescriber,
      ViewAction rollbackAction) {
    this(tapper, coordinatesProvider, precisionDescriber, 0, 0, rollbackAction);
  }

  public GeneralClickAction(
      Tapper tapper,
      CoordinatesProvider coordinatesProvider,
      PrecisionDescriber precisionDescriber,
      int inputDevice,
      int buttonState,
      ViewAction rollbackAction) {
    this.coordinatesProvider = coordinatesProvider;
    this.tapper = tapper;
    this.precisionDescriber = precisionDescriber;
    this.inputDevice = inputDevice;
    this.buttonState = buttonState;
    this.rollbackAction = rollbackAction;
  }

  @Override
  public Matcher<View> getConstraints() {
    Matcher<View> standardConstraint = isDisplayingAtLeast(90);
    if (rollbackAction != null) {
      return allOf(standardConstraint, rollbackAction.getConstraints());
    } else {
      return standardConstraint;
    }
  }

  @Override
  public void perform(UiController uiController, View view) {
    float[] coordinates = coordinatesProvider.calculateCoordinates(view);
    float[] precision = precisionDescriber.describePrecision();

    Tapper.Status status = Tapper.Status.FAILURE;
    int loopCount = 0;
    // Native event injection is quite a tricky process. A tap is actually 2
    // seperate motion events which need to get injected into the system. Injection
    // makes an RPC call from our app under test to the Android system server, the
    // system server decides which window layer to deliver the event to, the system
    // server makes an RPC to that window layer, that window layer delivers the event
    // to the correct UI element, activity, or window object. Now we need to repeat
    // that 2x. for a simple down and up. Oh and the down event triggers timers to
    // detect whether or not the event is a long vs. short press. The timers are
    // removed the moment the up event is received (NOTE: the possibility of eventTime
    // being in the future is totally ignored by most motion event processors).
    //
    // Phew.
    //
    // The net result of this is sometimes we'll want to do a regular tap, and for
    // whatever reason the up event (last half) of the tap is delivered after long
    // press timeout (depending on system load) and the long press behaviour is
    // displayed (EG: show a context menu). There is no way to avoid or handle this more
    // gracefully. Also the longpress behavour is app/widget specific. So if you have
    // a seperate long press behaviour from your short press, you can pass in a
    // 'RollBack' ViewAction which when executed will undo the effects of long press.

    while (status != Tapper.Status.SUCCESS && loopCount < 3) {
      try {
        status = tapper.sendTap(uiController, coordinates, precision, inputDevice, buttonState);
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(
              TAG,
              "perform: "
                  + String.format(
                      Locale.ROOT,
                      "%s - At Coordinates: %d, %d and precision: %d, %d",
                      this.getDescription(),
                      (int) coordinates[0],
                      (int) coordinates[1],
                      (int) precision[0],
                      (int) precision[1]));
        }
      } catch (RuntimeException re) {
        throw new PerformException.Builder()
            .withActionDescription(
                String.format(
                    Locale.ROOT,
                    "%s - At Coordinates: %d, %d and precision: %d, %d",
                    this.getDescription(),
                    (int) coordinates[0],
                    (int) coordinates[1],
                    (int) precision[0],
                    (int) precision[1]))
            .withViewDescription(HumanReadables.describe(view))
            .withCause(re)
            .build();
      }

      int duration = ViewConfiguration.getPressedStateDuration();
      // ensures that all work enqueued to process the tap has been run.
      if (duration > 0) {
        uiController.loopMainThreadForAtLeast(duration);
      }
      if (status == Tapper.Status.WARNING) {
        if (rollbackAction != null) {
          rollbackAction.perform(uiController, view);
        } else {
          break;
        }
      }
      loopCount++;
    }
    if (status == Tapper.Status.FAILURE) {
      throw new PerformException.Builder()
          .withActionDescription(this.getDescription())
          .withViewDescription(HumanReadables.describe(view))
          .withCause(
              new RuntimeException(
                  String.format(
                      Locale.ROOT,
                      "Couldn't click at: %s,%s precision: %s, %s . Tapper: %s coordinate"
                          + " provider: %s precision describer: %s. Tried %s times. With Rollback?"
                          + " %s",
                      coordinates[0],
                      coordinates[1],
                      precision[0],
                      precision[1],
                      tapper,
                      coordinatesProvider,
                      precisionDescriber,
                      loopCount,
                      rollbackAction != null)))
          .build();
    }

    if (tapper == Tap.SINGLE && view instanceof WebView) {
      // WebViews will not process click events until double tap
      // timeout. Not the best place for this - but good for now.
      uiController.loopMainThreadForAtLeast(ViewConfiguration.getDoubleTapTimeout());
    }
  }

  @Override
  public String getDescription() {
    // TODO(brettchabot): Implicitly using the default locale is a common source of bugs:
    return tapper.toString().toLowerCase() + " click";
  }
}