Skip to content

Commit d76091e

Browse files
committed
Keyevents and improvements in generated code
- Get key press modifiers - Fix CTRL+key presses - Add debug - Use uiAutomatorHelper in command (press) - Format - Fix help dialog button position - Add missing refresh screen in control panel - Add wait for window update (add to culebra generated script) - Handle keys via culebron command - Do not print dump in script where uiAutomatorHelper is available - Add KEY_EVENT
1 parent 1fbad89 commit d76091e

File tree

6 files changed

+430
-34
lines changed

6 files changed

+430
-34
lines changed

src/com/dtmilano/android/controlpanel.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ def __init__(self, parent, culebron, printOperation, value=None, **kwargs):
167167

168168
def command(self):
169169
key = self.value
170-
if key == 'KEYCODE_GOOGLE_NOW':
170+
if self.culebron.vc.uiAutomatorHelper:
171+
self.culebron.command(key)
172+
elif key == 'KEYCODE_GOOGLE_NOW':
171173
self.device.press(Key.GOOGLE_NOW)
172174
self.printOperation(None, Operation.PRESS, Key.GOOGLE_NOW)
173175
elif key == 'KEYCODE_.':
@@ -179,6 +181,7 @@ def command(self):
179181
else:
180182
self.device.press(key)
181183
self.printOperation(None, Operation.PRESS, key)
184+
self.refreshScreen()
182185

183186
def refreshScreen(self):
184187
self.culebron.refresh()

src/com/dtmilano/android/culebron.py

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
'''
3-
Copyright (C) 2012-2019 Diego Torres Milano
3+
Copyright (C) 2012-2022 Diego Torres Milano
44
Created on oct 6, 2014
55
66
Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,6 +31,7 @@
3131
from com.dtmilano.android.common import profileEnd
3232
from com.dtmilano.android.common import profileStart
3333
from com.dtmilano.android.concertina import Concertina
34+
from com.dtmilano.android.keyevent import KEY_EVENT
3435
from com.dtmilano.android.viewclient import ViewClient, View
3536

3637
__version__ = '21.3.0'
@@ -130,6 +131,7 @@ class Operation:
130131
OPEN_QUICK_SETTINGS = 'open_quick_settings'
131132
TYPE = 'type'
132133
PRESS = 'press'
134+
PRESS_UI_AUTOMATOR_HELPER = 'press_ui_automator_helper'
133135
PRESS_BACK = 'press_back'
134136
PRESS_BACK_UI_AUTOMATOR_HELPER = 'press_back_ui_automator_helper'
135137
PRESS_HOME = 'press_home'
@@ -144,6 +146,8 @@ class Operation:
144146
SWIPE_UI_AUTOMATOR_HELPER = 'swipe_ui_automator_helper'
145147
TRAVERSE = 'traverse'
146148
VIEW_SNAPSHOT = 'view_snapshot'
149+
WAIT_FOR_IDLE_UI_AUTOMATOR_HELPER = 'wait_for_idle_ui_automator_helper'
150+
WAIT_FOR_WINDOW_UPDATE_UI_AUTOMATOR_HELPER = 'wait_for_window_update_ui_automator_helper'
147151
WAKE = 'wake'
148152

149153
COMMAND_NAME_OPERATION_MAP = {'flingBackward': FLING_BACKWARD, 'flingForward': FLING_FORWARD,
@@ -167,6 +171,7 @@ class Culebron:
167171

168172
KEYSYM_TO_KEYCODE_MAP = {
169173
'Home': 'HOME',
174+
'abovedot': 'HOME', # Option+H on macOS
170175
'BackSpace': 'BACK',
171176
'Left': 'DPAD_LEFT',
172177
'Right': 'DPAD_RIGHT',
@@ -252,6 +257,7 @@ def __init__(self, vc, device, serialno, printOperation, scale=1, concertina=Fal
252257
'''
253258

254259
self.vc = vc
260+
self.dump = None
255261
if 'CONCERTINA' in self.vc.debug:
256262
global DEBUG_CONCERTINA
257263
DEBUG_CONCERTINA = self.vc.debug['CONCERTINA'] is not None
@@ -605,15 +611,15 @@ def populateViewTree(self, view):
605611
parent_unique_id = parent.getUniqueId()
606612
except AttributeError:
607613
vuid = view.unique_id
608-
#FIXME
609-
#is_target = view.is_target
614+
# FIXME
615+
# is_target = view.is_target
610616
is_target = False
611617
text = view.text
612618
parent = view.parent
613619
if parent:
614-
#FIXME:
620+
# FIXME:
615621
# parent is an int
616-
#parent_unique_id = parent.unique_id
622+
# parent_unique_id = parent.unique_id
617623
parent_unique_id = 0
618624
if parent is None:
619625
self.viewTree.insert('', tkinter.END, vuid, text=text)
@@ -646,7 +652,8 @@ def findTargets(self):
646652
window = -1
647653
if self.vc:
648654
dump = self.vc.dump(window=window, sleep=0.1)
649-
self.printOperation(None, Operation.DUMP, window, dump)
655+
if not self.vc.uiAutomatorHelper:
656+
self.printOperation(None, Operation.DUMP, window, dump)
650657
else:
651658
dump = []
652659
self.dump = dump
@@ -676,7 +683,7 @@ def findTargets(self):
676683

677684
# FIXME: we are not populating the view tree now
678685
# there are some problems with culebratester2
679-
#if self.vc:
686+
# if self.vc:
680687
# self.vc.traverse(transform=self.populateViewTree)
681688

682689
def getViewContainingPointAndGenerateTestCondition(self, x, y):
@@ -787,7 +794,10 @@ def findBestCandidate(view):
787794
candidate = candidates[0]
788795
self.touchView(candidate, v if candidate != v else None)
789796

790-
self.printOperation(None, Operation.SLEEP, Operation.DEFAULT)
797+
if self.vc.uiAutomatorHelper:
798+
self.printOperation(None, Operation.WAIT_FOR_WINDOW_UPDATE_UI_AUTOMATOR_HELPER, Operation.DEFAULT)
799+
else:
800+
self.printOperation(None, Operation.SLEEP, Operation.DEFAULT)
791801
self.vc.sleep(5)
792802
self.takeScreenshotAndShowItOnWindow()
793803

@@ -975,47 +985,65 @@ def onButton3Pressed(self, event):
975985
print("onButton3Pressed((", event.x, ", ", event.y, "))", file=sys.stderr)
976986
self.showPopupMenu(event)
977987

978-
def command(self, keycode):
979-
'''
988+
def command(self, keycode: str) -> None:
989+
"""
980990
Presses a key.
981991
Generates the actual key press on the device and prints the line in the script.
982-
'''
983992
984-
self.device.press(keycode)
985-
self.printOperation(None, Operation.PRESS, keycode)
993+
:param keycode the keycode name
994+
"""
995+
996+
if self.vc.uiAutomatorHelper:
997+
try:
998+
if not keycode.startswith('KEYCODE_'):
999+
keycode = f'KEYCODE_{keycode}'
1000+
self.vc.uiAutomatorHelper.ui_device.press_key_code(KEY_EVENT[f'{keycode}'])
1001+
self.printOperation(None, Operation.PRESS_UI_AUTOMATOR_HELPER, keycode)
1002+
except Exception as e:
1003+
print(e, file=sys.stderr)
1004+
else:
1005+
self.device.press(keycode)
1006+
self.printOperation(None, Operation.PRESS, keycode)
9861007

9871008
def onKeyPressed(self, event):
9881009
if DEBUG_KEY:
9891010
print("onKeyPressed(", repr(event), ")", file=sys.stderr)
990-
print(" event", type(event.char), len(event.char), repr(
991-
event.char), "keysym=", event.keysym, "keycode=", event.keycode, event.type, "state=", event.state,
1011+
print(f' char: type={type(event.char)} len={len(event.char)} repr={repr(event.char)}\n' +
1012+
f' keysym={event.keysym}, keycode={event.keycode}, type={event.type}, state={event.state}',
9921013
file=sys.stderr)
9931014
print(" events disabled:", self.areEventsDisabled, file=sys.stderr)
9941015
if self.areEventsDisabled:
9951016
if DEBUG_KEY:
996-
print("ignoring event", file=sys.stderr)
1017+
print(" ignoring event", file=sys.stderr)
9971018
self.canvas.update_idletasks()
9981019
return
9991020

10001021
char = event.char
10011022
keysym = event.keysym
1023+
state = event.state
1024+
1025+
# Manual way to get the modifiers
1026+
# https://stackoverflow.com/a/34482048/236465
1027+
ctrl = (state & 0x4) != 0
1028+
alt = (state & 0x8) != 0 or (state & 0x80) != 0
1029+
shift = (state & 0x1) != 0
1030+
1031+
if ctrl:
1032+
try:
1033+
return getattr(self, f'onCtrl{keysym.upper()}')(event)
1034+
except AttributeError:
1035+
pass
10021036

10031037
if len(char) == 0 and not (
10041038
keysym in Culebron.KEYSYM_TO_KEYCODE_MAP or keysym in Culebron.KEYSYM_CULEBRON_COMMANDS):
10051039
if DEBUG_KEY:
1006-
print("returning because len(char) == 0", file=sys.stderr)
1040+
print(f' returning because len(char) == 0 keysym={keysym}', file=sys.stderr)
10071041
return
10081042

10091043
###
10101044
### internal commands: no output to generated script
10111045
###
1012-
try:
1013-
handler = getattr(self, 'onCtrl%s' % self.UPPERCASE_CHARS[ord(char) - 1])
1014-
except:
1015-
handler = None
1016-
if handler:
1017-
return handler(event)
1018-
elif keysym == 'F1':
1046+
if keysym == 'F1':
10191047
self.showHelp()
10201048
return
10211049
elif keysym == 'F5':
@@ -1056,7 +1084,7 @@ def onKeyPressed(self, event):
10561084
else:
10571085
self.command(Culebron.KEYSYM_TO_KEYCODE_MAP[keysym])
10581086
# ALT-M
1059-
elif keysym == 'm' and event.state == 24:
1087+
elif keysym == 'm' and alt:
10601088
if DEBUG_KEY:
10611089
print("Sending MENU", file=sys.stderr)
10621090
self.command('MENU')
@@ -1072,8 +1100,6 @@ def onKeyPressed(self, event):
10721100
pass
10731101
else:
10741102
self.command(char)
1075-
# commented out (profile)
1076-
# time.sleep(1)
10771103
self.takeScreenshotAndShowItOnWindow()
10781104

10791105
def wake(self):
@@ -1152,7 +1178,8 @@ def saveViewSnapshot(self, view):
11521178

11531179
if not view:
11541180
raise ValueError("view must be provided to take snapshot")
1155-
filename = self.snapshotDir + os.sep + '${serialno}-' + View.variableNameFromId(view) + '-${timestamp}' + '.' + self.snapshotFormat.lower()
1181+
filename = self.snapshotDir + os.sep + '${serialno}-' + View.variableNameFromId(
1182+
view) + '-${timestamp}' + '.' + self.snapshotFormat.lower()
11561183
d = FileDialog(self, self.device.substituteDeviceTemplate(filename))
11571184
saveAsFilename = d.askSaveAsFilename()
11581185
if saveAsFilename:
@@ -2306,7 +2333,7 @@ def buttonBox(self):
23062333
self.bind("<Return>", self.onDismiss)
23072334
self.bind("<Escape>", self.onDismiss)
23082335

2309-
box.grid(row=1, column=1)
2336+
box.grid(row=2, column=1)
23102337

23112338
def onDismiss(self, event=None):
23122339
# put focus back to the parent window's canvas

0 commit comments

Comments
 (0)