diff options
-rw-r--r-- | platform_tools/android/skp_gen/android_skp_capture.py | 178 | ||||
-rw-r--r-- | platform_tools/android/skp_gen/apps/geniewidget.json | 15 |
2 files changed, 193 insertions, 0 deletions
diff --git a/platform_tools/android/skp_gen/android_skp_capture.py b/platform_tools/android/skp_gen/android_skp_capture.py new file mode 100644 index 0000000000..3045b9f97d --- /dev/null +++ b/platform_tools/android/skp_gen/android_skp_capture.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python + +# Copyright 2015 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + + +from __future__ import with_statement + +# Imports the monkeyrunner modules used by this program +from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice + +import ast +import os +import subprocess +import time + + +# Time to wait between performing UI actions and capturing the SKP. +WAIT_FOR_SKP_CAPTURE = 1 + + +class DragAction: + """Action describing a touch drag.""" + def __init__(self, start, end, duration, points): + self.start = start + self.end = end + self.duration = duration + self.points = points + + def run(self, device): + """Perform the action.""" + return device.drag(self.start, self.end, self.duration, self.points) + + +class PressAction: + """Action describing a button press.""" + def __init__(self, button, press_type): + self.button = button + self.press_type = press_type + + def run(self, device): + """Perform the action.""" + return device.press(self.button, self.press_type) + + +def parse_action(action_dict): + """Parse a dict describing an action and return an Action object.""" + if action_dict['type'] == 'drag': + return DragAction(tuple(action_dict['start']), + tuple(action_dict['end']), + action_dict['duration'], + action_dict['points']) + elif action_dict['type'] == 'press': + return PressAction(action_dict['button'], action_dict['press_type']) + else: + raise TypeError('Unsupported action type: %s' % action_dict['type']) + + +class App: + """Class which describes an app to launch and actions to run.""" + def __init__(self, name, package, activity, app_launch_delay, actions): + self.name = name + self.package = package + self.activity = activity + self.app_launch_delay = app_launch_delay + self.run_component = '%s/%s' % (self.package, self.activity) + self.actions = [parse_action(a) for a in actions] + + def launch(self, device): + """Launch the app on the device.""" + device.startActivity(component=self.run_component) + time.sleep(self.app_launch_delay) + + def kill(self): + """Kill the app.""" + adb_shell('am force-stop %s' % self.package) + + +def check_output(cmd): + """Convenience implementation of subprocess.check_output.""" + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if proc.wait() != 0: + raise Exception('Command failed: %s' % ' '.join(cmd)) + return proc.communicate()[0] + + +def adb_shell(cmd): + """Run the given ADB shell command and emulate the exit code.""" + output = check_output(['adb', 'shell', cmd + '; echo $?']).strip() + lines = output.splitlines() + if lines[-1] != '0': + raise Exception('ADB command failed: %s\n\nOutput:\n%s' % (cmd, output)) + return '\n'.join(lines[:-1]) + + +def remote_file_exists(filename): + """Return True if the given file exists on the device and False otherwise.""" + try: + adb_shell('test -f %s' % filename) + return True + except Exception: + return False + + +def capture_skp(skp_file, package, device): + """Capture an SKP.""" + remote_path = '/data/data/%s/cache/%s' % (package, os.path.basename(skp_file)) + try: + adb_shell('rm %s' % remote_path) + except Exception: + if remote_file_exists(remote_path): + raise + + adb_shell('setprop debug.hwui.capture_frame_as_skp %s' % remote_path) + try: + # Spin, wait for the SKP to be written. + timeout = 10 # Seconds + start = time.time() + device.drag((300, 300), (300, 350), 1, 10) # Dummy action to force a draw. + while not remote_file_exists(remote_path): + if time.time() - start > timeout: + raise Exception('Timed out waiting for SKP capture.') + time.sleep(1) + + # Pull the SKP from the device. + cmd = ['adb', 'pull', remote_path, skp_file] + check_output(cmd) + + finally: + adb_shell('setprop debug.hwui.capture_frame_as_skp ""') + + +def load_app(filename): + """Load the JSON file describing an app and return an App instance.""" + with open(filename) as f: + app_dict = ast.literal_eval(f.read()) + return App(app_dict['name'], + app_dict['package'], + app_dict['activity'], + app_dict['app_launch_delay'], + app_dict['actions']) + + +def main(): + """Capture SKPs for all apps.""" + device = MonkeyRunner.waitForConnection() + + # TODO(borenet): Kill all apps. + device.wake() + device.drag((600, 600), (10, 10), 0.2, 10) + + apps_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'apps') + app_files = [os.path.join(apps_dir, app) for app in os.listdir(apps_dir)] + + for app_file in app_files: + app = load_app(app_file) + print app.name + print ' Package %s' % app.package + app.launch(device) + print ' Launched activity %s' % app.activity + + for action in app.actions: + print ' %s' % action.__class__.__name__ + action.run(device) + + time.sleep(WAIT_FOR_SKP_CAPTURE) + print ' Capturing SKP.' + skp_file = '%s.skp' % app.name + capture_skp(skp_file, app.package, device) + print ' Wrote SKP to %s' % skp_file + print + app.kill() + + +if __name__ == '__main__': + main() diff --git a/platform_tools/android/skp_gen/apps/geniewidget.json b/platform_tools/android/skp_gen/apps/geniewidget.json new file mode 100644 index 0000000000..290e1baeba --- /dev/null +++ b/platform_tools/android/skp_gen/apps/geniewidget.json @@ -0,0 +1,15 @@ +{ + "name": "geniewidget", + "package": "com.google.android.apps.genie.geniewidget", + "activity": "com.google.android.apps.genie.geniewidget.activities.NewsActivity", + "app_launch_delay": 3, + "actions": [ + { + "type": "drag", + "start": [600, 600], + "end": [10, 10], + "duration": 0.2, + "points": 10 + } + ] +} |