#! /usr/bin/env python3 ''' Copyright (C) 2012-2021 Diego Torres Milano Created on Feb 3, 2012 @author: diego ''' from __future__ import print_function __version__ = '20.4.4' import getopt import os import sys try: sys.path.insert(0, os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src')) except: pass from com.dtmilano.android.viewclient import ViewClient, View, ViewClientOptions from com.dtmilano.android.common import debugArgsToDict HELP = 'help' VERBOSE = 'verbose' VERSION = 'version' DEBUG = 'debug' IGNORE_SECURE_DEVICE = 'ignore-secure-device' IGNORE_VERSION_CHECK = 'ignore-version-check' FORCE_VIEW_SERVER_USE = 'force-view-server-use' DO_NOT_START_VIEW_SERVER = 'do-not-start-view-server' DO_NOT_IGNORE_UIAUTOMATOR_KILLED = 'do-not-ignore-uiautomator-killed' WINDOW = 'window' ALL = 'all' UNIQUE_ID = 'uniqueId' POSITION = 'position' BOUNDS = 'bounds' CONTENT_DESCRIPTION = 'content-description' TAG = 'tag' CENTER = 'center' SAVE_SCREENSHOT = 'save-screenshot' SAVE_VIEW_SCREENSHOTS = 'save-view-screenshots' DO_NOT_DUMP_VIEWS = 'do-not-dump-views' DEVICE_ART = 'device-art' DROP_SHADOW = 'drop-shadow' SCREEN_GLARE = 'glare' USE_UIAUTOMATOR_HELPER = 'use-uiautomator-helper' MAP = { 'a': View.__str__, ALL: View.__str__, 'i': ViewClient.TRAVERSE_CITUI, UNIQUE_ID: ViewClient.TRAVERSE_CITUI, 'x': ViewClient.TRAVERSE_CITPS, POSITION: ViewClient.TRAVERSE_CITPS, 'b': ViewClient.TRAVERSE_CITB, BOUNDS: ViewClient.TRAVERSE_CITB, 'd': ViewClient.TRAVERSE_CITCD, CONTENT_DESCRIPTION: ViewClient.TRAVERSE_CITCD, 'g': ViewClient.TRAVERSE_CITG, TAG: ViewClient.TRAVERSE_CITG, 'c': ViewClient.TRAVERSE_CITC, CENTER: ViewClient.TRAVERSE_CITC, 'W': ViewClient.TRAVERSE_CITCDS, SAVE_VIEW_SCREENSHOTS: ViewClient.TRAVERSE_CITCDS, 'D': ViewClient.TRAVERSE_S, DO_NOT_DUMP_VIEWS: ViewClient.TRAVERSE_S } USAGE = 'usage: %s [OPTION]... [serialno]' SHORT_OPTS = 'HVvIEFSkw:aixbdgcf:W:DA:ZBhX:' LONG_OPTS = [HELP, VERBOSE, VERSION, IGNORE_SECURE_DEVICE, IGNORE_VERSION_CHECK, FORCE_VIEW_SERVER_USE, DO_NOT_START_VIEW_SERVER, DO_NOT_IGNORE_UIAUTOMATOR_KILLED, WINDOW + '=', ALL, UNIQUE_ID, POSITION, BOUNDS, CONTENT_DESCRIPTION, TAG, CENTER, SAVE_SCREENSHOT + '=', SAVE_VIEW_SCREENSHOTS + '=', DO_NOT_DUMP_VIEWS, DEVICE_ART + '=', DROP_SHADOW, SCREEN_GLARE, USE_UIAUTOMATOR_HELPER, DEBUG + '=' ] LONG_OPTS_ARG = {WINDOW: 'WINDOW', SAVE_SCREENSHOT: 'FILE', SAVE_VIEW_SCREENSHOTS: 'DIR', DEVICE_ART: 'MODEL', DEBUG: 'LIST'} OPTS_HELP = { 'H': 'prints this help', 'V': 'verbose comments', 'I': 'ignore secure device', 'F': 'force view server use (even if UiAutomator present)', 'S': 'don\'t start ViewServer', 'k': 'don\'t ignore UiAutomator killed', 'w': 'dump WINDOW content (default: -1, all windows)', 'a': 'dump all information about Views', 'i': 'dump View unique IDs', 'x': 'dump View positions', 'b': 'dump View bounds', 'd': 'dump View content descriptions', 'g': 'dump View tags', 'c': 'dump View centers', 'f': 'save screenshot to file', 'W': 'save View screenshots to files in directory', 'E': 'ignores ADB version check', 'D': 'don\'t dump views, only useful if you specified -f or -W', 'A': 'device art model to frame screenshot (auto: autodetected)', 'Z': 'drop shadow for device art screenshot', 'B': 'screen glare over screenshot', 'h': 'use UiAutomatorHelper Android app', 'X': 'debug options', } def shortAndLongOptions(): ''' @return: the list of corresponding (short-option, long-option) tuples ''' short_opts = SHORT_OPTS.replace(':', '') if len(short_opts) != len(LONG_OPTS): raise Exception('There is a mismatch between short and long options') t = tuple(short_opts) + tuple(LONG_OPTS) l2 = int(len(t) / 2) sl = [] for i in range(l2): sl.append((t[i], t[i + l2])) return sl def usage(exitVal=1): print(USAGE % progname, file=sys.stderr) print("Try '%s --help' for more information." % progname, file=sys.stderr) sys.exit(exitVal) def _help(): print(USAGE % progname, file=sys.stderr) print(file=sys.stderr) print("Options:", file=sys.stderr) for so, lo in shortAndLongOptions(): o = ' -%c, --%s' % (so, lo) if lo[-1] == '=': o += LONG_OPTS_ARG[lo[:-1]] try: o = '%-34s %-45s' % (o, OPTS_HELP[so]) except: pass print(o, file=sys.stderr) sys.exit(0) def version(): print('{} version {}'.format(progname, __version__)) sys.exit(0) # __main__ progname = os.path.basename(sys.argv[0]) try: opts, args = getopt.getopt(sys.argv[1:], SHORT_OPTS, LONG_OPTS) sys.argv[1:] = args except getopt.GetoptError as e: print('ERROR:', str(e), file=sys.stderr) usage() kwargs1 = {VERBOSE: False, 'ignoresecuredevice': False, 'ignoreversioncheck': False} kwargs2 = {ViewClientOptions.FORCE_VIEW_SERVER_USE: False, ViewClientOptions.START_VIEW_SERVER: True, ViewClientOptions.AUTO_DUMP: False, ViewClientOptions.IGNORE_UIAUTOMATOR_KILLED: True, ViewClientOptions.COMPRESSED_DUMP: True, ViewClientOptions.USE_UIAUTOMATOR_HELPER: False, ViewClientOptions.DEBUG: {}, } options = {WINDOW: -1, SAVE_SCREENSHOT: None, SAVE_VIEW_SCREENSHOTS: None, DO_NOT_DUMP_VIEWS: False, DEVICE_ART: None, DROP_SHADOW: False, SCREEN_GLARE: False} transform = ViewClient.TRAVERSE_CIT for o, a in opts: o = o.strip('-') if o in ['H', HELP]: _help() elif o in ['V', VERBOSE]: kwargs1[VERBOSE] = True elif o in ['v', VERSION]: version() elif o in ['I', IGNORE_SECURE_DEVICE]: kwargs1['ignoresecuredevice'] = True elif o in ['E', IGNORE_VERSION_CHECK]: kwargs1['ignoreversioncheck'] = True elif o in ['F', FORCE_VIEW_SERVER_USE]: kwargs2[ViewClientOptions.FORCE_VIEW_SERVER_USE] = True elif o in ['S', DO_NOT_START_VIEW_SERVER]: kwargs2[ViewClientOptions.START_VIEW_SERVER] = False elif o in ['k', DO_NOT_IGNORE_UIAUTOMATOR_KILLED]: kwargs2[ViewClientOptions.IGNORE_UIAUTOMATOR_KILLED] = False elif o in ['w', WINDOW]: options[WINDOW] = a elif o in ['f', SAVE_SCREENSHOT]: options[SAVE_SCREENSHOT] = a elif o in ['W', SAVE_VIEW_SCREENSHOTS]: options[SAVE_VIEW_SCREENSHOTS] = a transform = MAP[o] elif o in ['A', DEVICE_ART]: options[DEVICE_ART] = a elif o in ['Z', DROP_SHADOW]: options[DROP_SHADOW] = True elif o in ['B', SCREEN_GLARE]: options[SCREEN_GLARE] = True elif o in ['h', USE_UIAUTOMATOR_HELPER]: kwargs2[ViewClientOptions.USE_UIAUTOMATOR_HELPER] = True elif o in ['D', DO_NOT_DUMP_VIEWS]: options[DO_NOT_DUMP_VIEWS] = True transform = MAP[o] elif o in ['X', DEBUG]: kwargs2[ViewClientOptions.DEBUG] = debugArgsToDict(a) else: transform = MAP[o] if options[DO_NOT_DUMP_VIEWS]: transform = MAP[DO_NOT_DUMP_VIEWS] vc = ViewClient(*ViewClient.connectToDeviceOrExit(**kwargs1), **kwargs2) if options[WINDOW] == 'list': vc.list() sys.exit(0) if options[SAVE_SCREENSHOT]: vc.device.reconnect = True # (not options[DO_NOT_DUMP_VIEWS]) ext = os.path.splitext(options[SAVE_SCREENSHOT])[1][1:].upper() _format = ext if ext == 'JPG': _format = 'JPEG' vc.writeImageToFile(options[SAVE_SCREENSHOT], _format=_format, deviceart=options[DEVICE_ART], dropshadow=options[DROP_SHADOW], screenglare=options[SCREEN_GLARE]) if not options[DO_NOT_DUMP_VIEWS] or options[SAVE_VIEW_SCREENSHOTS]: vc.dump(window=options[WINDOW]) ViewClient.imageDirectory = options[SAVE_VIEW_SCREENSHOTS] vc.traverse(transform=transform) if kwargs2[ViewClientOptions.USE_UIAUTOMATOR_HELPER]: try: vc.uiAutomatorHelper.quit() except: pass