1# Copyright 2015 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import time
7
8from telemetry.core import util
9
10
11class ActionNotSupported(Exception):
12  pass
13
14
15class AndroidActionRunner(object):
16  """Provides an API for interacting with an android device.
17
18  This makes use of functionality provided by the android input command. None
19  of the gestures here are guaranteed to be performant for telemetry tests and
20  there is no official support for this API.
21
22  TODO(ariblue): Replace this API with a better implementation for interacting
23  with native components.
24  """
25
26  def __init__(self, platform_backend):
27    self._platform_backend = platform_backend
28
29  def SmoothScrollBy(self, left_start_coord, top_start_coord, direction,
30                     scroll_distance):
31    """Perform gesture to scroll down on the android device.
32    """
33    if direction not in ['down', 'up', 'left', 'right']:
34      raise ActionNotSupported('Invalid scroll direction: %s' % direction)
35
36    # This velocity is slower so that the exact distance we specify is the
37    # distance the page travels.
38    duration = scroll_distance
39
40    # Note that the default behavior is swiping up for scrolling down.
41    if direction == 'down':
42      left_end_coord = left_start_coord
43      top_end_coord = top_start_coord - scroll_distance
44    elif direction == 'up':
45      left_end_coord = left_start_coord
46      top_end_coord = top_start_coord + scroll_distance
47    elif direction == 'right':
48      left_end_coord = left_start_coord - scroll_distance
49      top_end_coord = top_start_coord
50    elif direction == 'left':
51      left_end_coord = left_start_coord + scroll_distance
52      top_end_coord = top_start_coord
53
54    self.InputSwipe(left_start_coord, top_start_coord, left_end_coord,
55                    top_end_coord, duration)
56
57  def Wait(self, seconds):
58    """Wait for the number of seconds specified.
59
60    Args:
61      seconds: The number of seconds to wait.
62    """
63    time.sleep(seconds)
64
65  def InputText(self, string):
66    """Convert the characters of the string into key events and send to device.
67
68    Args:
69      string: The string to send to the device.
70    """
71    self._platform_backend.device.RunShellCommand('input text %s' % string)
72
73  def InputKeyEvent(self, key):
74    """Send a single key input to the device.
75
76    Args:
77      key: A key code number or name that will be sent to the device
78    """
79    self._platform_backend.device.RunShellCommand('input keyevent %s' % key)
80
81  def InputTap(self, x_coord, y_coord):
82    """Perform a tap input at the given coordinates.
83
84    Args:
85      x_coord: The x coordinate of the tap event.
86      y_coord: The y coordinate of the tap event.
87    """
88    self._platform_backend.device.RunShellCommand('input tap %s %s' % (x_coord,
89                                                                       y_coord))
90
91  def InputSwipe(self, left_start_coord, top_start_coord, left_end_coord,
92                 top_end_coord, duration):
93    """Perform a swipe input.
94
95    Args:
96      left_start_coord: The horizontal starting coordinate of the gesture
97      top_start_coord: The vertical starting coordinate of the gesture
98      left_end_coord: The horizontal ending coordinate of the gesture
99      top_end_coord: The vertical ending coordinate of the gesture
100      duration: The length of time of the swipe in milliseconds
101    """
102    self._platform_backend.device.RunShellCommand(
103        'input swipe %s %s %s %s %s' % (left_start_coord, top_start_coord,
104                                        left_end_coord, top_end_coord,
105                                        duration))
106
107  def InputPress(self):
108    """Perform a press input."""
109    self._platform_backend.device.RunShellCommand('input press')
110
111  def InputRoll(self, dx, dy):
112    """Perform a roll input. This sends a simple zero-pressure move event.
113
114    Args:
115      dx: Change in the x coordinate due to move.
116      dy: Change in the y coordinate due to move.
117    """
118    self._platform_backend.device.RunShellCommand('input roll %s %s' % (dx, dy))
119
120  def TurnScreenOn(self):
121    """If device screen is off, turn screen on.
122    If the screen is already on, log a warning and return immediately.
123
124    Raises:
125      Timeout: If the screen is off and device fails to turn screen on.
126    """
127    self._platform_backend.device.SetScreen(True)
128    util.WaitFor(self._platform_backend.device.IsScreenOn, 5)
129
130  def TurnScreenOff(self):
131    """If device screen is on, turn screen off.
132    If the screen is already off, log a warning and return immediately.
133
134    Raises:
135      Timeout: If the screen is on and device fails to turn screen off.
136    """
137
138    def is_screen_off():
139      return not self._platform_backend.device.IsScreenOn()
140
141    self._platform_backend.device.SetScreen(False)
142    util.WaitFor(is_screen_off, 5)
143
144  def UnlockScreen(self):
145    """If device screen is locked, unlocks it.
146    If the device is not locked, log a warning and return immediately.
147
148    Raises:
149      Timeout: If device fails to unlock screen.
150    """
151
152    def is_screen_unlocked():
153      return not self._platform_backend.IsScreenLocked()
154
155    if self._platform_backend.IsScreenLocked():
156      self._platform_backend.device.RunShellCommand('input keyevent 82')
157    else:
158      logging.warning('Screen not locked when expected.')
159      return
160
161    util.WaitFor(is_screen_unlocked, 5)
162