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