Skip to content

Commit c399d74

Browse files
committed
Introduced concertina configuration
- [some] events probabilities can be defined in JSON configuration file
1 parent e2c5d6e commit c399d74

File tree

6 files changed

+245
-153
lines changed

6 files changed

+245
-153
lines changed

.idea/dictionaries/diego.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/culebra__Guc___scale_0_5.xml

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/com/dtmilano/android/culebron.py

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@
1919
2020
'''
2121
import StringIO
22+
import json
2223
import random
2324
import re
2425
import time
2526

27+
import numpy
28+
2629
from com.dtmilano.android.common import profileEnd
2730
from com.dtmilano.android.common import profileStart
2831
from com.dtmilano.android.concertina import Concertina
2932

30-
__version__ = '13.6.3'
33+
__version__ = '14.0.0'
3134

3235
import sys
3336
import threading
@@ -213,7 +216,7 @@ def checkDependencies():
213216
This is usually installed by python package. Check your distribution details.
214217
''')
215218

216-
def __init__(self, vc, device, serialno, printOperation, scale=1, concertina=False):
219+
def __init__(self, vc, device, serialno, printOperation, scale=1, concertina=False, concertinaConfigFile=None):
217220
'''
218221
Culebron constructor.
219222
@@ -227,8 +230,10 @@ def __init__(self, vc, device, serialno, printOperation, scale=1, concertina=Fal
227230
@type printOperation: method
228231
@param scale: the scale of the device screen used to show it on the window
229232
@type scale: float
230-
@:param concertina: bool
231-
@:type concertina: enable concertina mode (see documentation)
233+
@param concertina: enable concertina mode (see documentation)
234+
@type concertina: bool
235+
@param concertinaConfigFile: configuration file for concertina
236+
@type concertinaConfigFile: str
232237
'''
233238

234239
self.vc = vc
@@ -238,10 +243,12 @@ def __init__(self, vc, device, serialno, printOperation, scale=1, concertina=Fal
238243
self.serialno = serialno
239244
self.scale = scale
240245
self.concertina = concertina
246+
self.concertinaConfig = dict()
247+
self.concertinaConfigFile = concertinaConfigFile
241248
self.window = Tkinter.Tk()
242249
try:
243250
f = resource_filename(Requirement.parse("androidviewclient"),
244-
"share/pixmaps/culebra.png")
251+
"share/pixmaps/culebra.png")
245252
icon = ImageTk.PhotoImage(file=f)
246253
except:
247254
icon = None
@@ -914,7 +921,7 @@ def onKeyPressed(self, event):
914921
keysym = event.keysym
915922

916923
if len(char) == 0 and not (
917-
keysym in Culebron.KEYSYM_TO_KEYCODE_MAP or keysym in Culebron.KEYSYM_CULEBRON_COMMANDS):
924+
keysym in Culebron.KEYSYM_TO_KEYCODE_MAP or keysym in Culebron.KEYSYM_CULEBRON_COMMANDS):
918925
if DEBUG_KEY:
919926
print >> sys.stderr, "returning because len(char) == 0"
920927
return
@@ -1238,15 +1245,14 @@ def drag(self, start, end, duration, steps, units=Unit.DIP):
12381245
end = (x1, y1)
12391246
if self.vc.uiAutomatorHelper:
12401247
self.printOperation(None, Operation.SWIPE_UI_AUTOMATOR_HELPER, x0, y0, x1, y1, steps, units,
1241-
self.device.display['orientation'])
1248+
self.device.display['orientation'])
12421249
else:
12431250
self.printOperation(None, Operation.DRAG, start, end, duration, steps, units,
1244-
self.device.display['orientation'])
1251+
self.device.display['orientation'])
12451252
self.printOperation(None, Operation.SLEEP, 1)
12461253
time.sleep(1)
12471254
self.takeScreenshotAndShowItOnWindow()
12481255

1249-
12501256
def enableEvents(self):
12511257
if self.permanentlyDisableEvents:
12521258
return
@@ -1260,7 +1266,6 @@ def enableEvents(self):
12601266
self.canvas.bind("<Key>", self.onKeyPressed)
12611267
self.areEventsDisabled = False
12621268

1263-
12641269
def disableEvents(self, permanently=False):
12651270
self.permanentlyDisableEvents = permanently
12661271
if self.canvas is not None:
@@ -1274,7 +1279,6 @@ def disableEvents(self, permanently=False):
12741279
# self.canvas.unbind("<Control-Key-S>")
12751280
self.canvas.unbind("<Key>")
12761281

1277-
12781282
def toggleTargets(self):
12791283
if DEBUG:
12801284
print >> sys.stderr, "toggletargets: aretargetsmarked=", self.areTargetsMarked
@@ -1283,7 +1287,6 @@ def toggleTargets(self):
12831287
else:
12841288
self.unmarkTargets()
12851289

1286-
12871290
def markTargets(self):
12881291
if DEBUG:
12891292
print >> sys.stderr, "marktargets: aretargetsmarked=", self.areTargetsMarked
@@ -1299,7 +1302,6 @@ def markTargets(self):
12991302
c += 1
13001303
self.areTargetsMarked = True
13011304

1302-
13031305
def markTarget(self, x1, y1, x2, y2, color='#ff00ff'):
13041306
'''
13051307
@return the id of the rectangle added
@@ -1312,11 +1314,9 @@ def markTarget(self, x1, y1, x2, y2, color='#ff00ff'):
13121314
self.markedTargetIds[_id] = (x1, y1, x2, y2)
13131315
return _id
13141316

1315-
13161317
def unmarkTarget(self, _id):
13171318
self.canvas.delete(_id)
13181319

1319-
13201320
def unmarkTargets(self):
13211321
if not self.areTargetsMarked:
13221322
return
@@ -1325,31 +1325,27 @@ def unmarkTargets(self):
13251325
self.markedTargetIds = {}
13261326
self.areTargetsMarked = False
13271327

1328-
13291328
def setDragDialogShowed(self, showed):
13301329
self.isDragDialogShowed = showed
13311330
if showed:
13321331
pass
13331332
else:
13341333
self.isGrabbingTouch = False
13351334

1336-
13371335
def drawTouchedPoint(self, x, y):
13381336
if DEBUG:
13391337
print >> sys.stderr, "drawTouchedPoint(", x, ",", y, ")"
13401338
size = 50
13411339
return self.canvas.create_oval((x - size) * self.scale, (y - size) * self.scale, (x + size) * self.scale,
13421340
(y + size) * self.scale, fill=Color.MAGENTA)
13431341

1344-
13451342
def drawDragLine(self, x0, y0, x1, y1):
13461343
if DEBUG:
13471344
print >> sys.stderr, "drawDragLine(", x0, ",", y0, ",", x1, ",", y1, ")"
13481345
width = 15
13491346
return self.canvas.create_line(x0 * self.scale, y0 * self.scale, x1 * self.scale, y1 * self.scale, width=width,
13501347
fill=Color.MAGENTA, arrow="last", arrowshape=(50, 50, 30), dash=(50, 25))
13511348

1352-
13531349
def executeCommandAndRefresh(self, command):
13541350
self.showVignette()
13551351
if DEBUG:
@@ -1369,18 +1365,15 @@ def executeCommandAndRefresh(self, command):
13691365
# FIXME: perhaps refresh() should be invoked here just in case size or orientation changed
13701366
self.takeScreenshotAndShowItOnWindow()
13711367

1372-
13731368
def changeLanguage(self):
13741369
code = tkSimpleDialog.askstring("Change language", "Enter the language code")
13751370
self.vc.uiDevice.changeLanguage(code)
13761371
self.printOperation(None, Operation.CHANGE_LANGUAGE, code)
13771372
self.refresh()
13781373

1379-
13801374
def setOnTouchListener(self, listener):
13811375
self.onTouchListener = listener
13821376

1383-
13841377
def setGrab(self, state):
13851378
if DEBUG:
13861379
print >> sys.stderr, "Culebron.setGrab(%s)" % state
@@ -1392,7 +1385,6 @@ def setGrab(self, state):
13921385
else:
13931386
self.hideMessageArea()
13941387

1395-
13961388
@staticmethod
13971389
def isClickableCheckableOrFocusable(v):
13981390
if DEBUG_ISCCOF:
@@ -1417,24 +1409,22 @@ def isClickableCheckableOrFocusable(v):
14171409
pass
14181410
return False
14191411

1420-
14211412
def mainloop(self):
14221413
self.window.title("%s v%s" % (Culebron.APPLICATION_NAME, __version__))
14231414
self.window.resizable(width=Tkinter.FALSE, height=Tkinter.FALSE)
14241415
self.window.lift()
14251416
if self.concertina:
1417+
self.readConcertinaConfig(self.concertinaConfigFile)
14261418
self.concertinaLoop()
14271419
else:
14281420
self.window.mainloop()
14291421

1430-
14311422
def concertinaLoop(self):
14321423
random.seed()
14331424
self.disableEvents(permanently=True)
14341425
self.concertinaLoopCallback(dontinteract=True)
14351426
self.window.mainloop()
14361427

1437-
14381428
def concertinaLoopCallback(self, dontinteract=False):
14391429
if not dontinteract:
14401430
if DEBUG_CONCERTINA:
@@ -1444,11 +1434,13 @@ def concertinaLoopCallback(self, dontinteract=False):
14441434
rand = random.random()
14451435
if DEBUG_CONCERTINA:
14461436
print >> sys.stderr, "CONCERTINA: random=%f" % rand
1447-
if rand > 0.85:
1437+
probabilities = self.concertinaConfig['probabilities']
1438+
if rand > (1 - probabilities['systemKeys']):
14481439
# Send key events
1449-
k = random.choice(['ENTER', 'BACK', 'HOME', 'MENU'])
1440+
systemKeys = self.concertinaConfig['systemKeys']
1441+
k = numpy.random.choice(systemKeys['keys'], 1, p=systemKeys['probabilities'])[0]
14501442
if DEBUG_CONCERTINA:
1451-
print >> sys.stderr, "CONCERTINA: key=" + k
1443+
print >> sys.stderr, "CONCERTINA: system key=" + k
14521444
# DEBUG ONLY!
14531445
# print >> sys.stderr, "Not sending key event"
14541446
self.command(k)
@@ -1503,7 +1495,7 @@ def concertinaLoopCallback(self, dontinteract=False):
15031495
Concertina.sayRandomText()
15041496
time.sleep(5)
15051497
elif random.choice(['SCROLL', 'TOUCH']) == 'SCROLL' and (
1506-
isScrollable or parent.isScrollable() or parentClass == 'android.widget.ScrollView'):
1498+
isScrollable or parent.isScrollable() or parentClass == 'android.widget.ScrollView'):
15071499
# NOTE: The order here is important because some EditText are inside ScrollView's and we want to
15081500
# capture the case of other ScrollViews
15091501
if isScrollable:
@@ -1561,7 +1553,6 @@ def concertinaLoopCallback(self, dontinteract=False):
15611553
print >> sys.stderr, "CONCERTINA: No target views"
15621554
self.window.after(5000, self.concertinaLoopCallback)
15631555

1564-
15651556
def getViewContainingPointAndLongTouch(self, x, y):
15661557
# FIXME: this method is almost exactly as getViewContainingPointAndTouch()
15671558
if DEBUG:
@@ -1609,6 +1600,20 @@ def findBestCandidate(view):
16091600
self.vc.sleep(5)
16101601
self.takeScreenshotAndShowItOnWindow()
16111602

1603+
def readConcertinaConfig(self, concertinaConfigFile):
1604+
if concertinaConfigFile:
1605+
self.concertinaConfig = json.load(open(concertinaConfigFile))
1606+
if 'probabilities' not in self.concertinaConfig:
1607+
self.concertinaConfig['probabilities'] = dict()
1608+
self.concertinaConfig['probabilities']['systemKeys'] = 0.14
1609+
self.concertinaConfig['probabilities']['other'] = 0.86
1610+
if 'systemKeys' not in self.concertinaConfig:
1611+
self.concertinaConfig['systemKeys'] = dict()
1612+
self.concertinaConfig['systemKeys']['keys'] = ['ENTER', 'BACK', 'HOME', 'MENU']
1613+
n = float(len(self.concertinaConfig['systemKeys']['keys']))
1614+
self.concertinaConfig['systemKeys']['probabilities'] = [1 / n for k in
1615+
self.concertinaConfig['systemKeys']['keys']]
1616+
16121617

16131618
if TKINTER_AVAILABLE:
16141619
class MainMenu(Tkinter.Menu):

src/com/dtmilano/android/viewclient.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4341,9 +4341,10 @@ class CulebraOptions:
43414341
NULL_BACK_END = 'null-back-end'
43424342
USE_UIAUTOMATOR_HELPER = 'use-uiautomator-helper'
43434343
CONCERTINA = 'concertina'
4344+
CONCERTINA_CONFIG = 'concertina-config'
43444345
INSTALL_APK = 'install-apk'
43454346

4346-
SHORT_OPTS = 'HVvIEFSkw:i:t:d:rCUM:j:D:K:R:a:o:pf:W:GuP:Os:mLA:ZB0hc1:'
4347+
SHORT_OPTS = 'HVvIEFSkw:i:t:d:rCUM:j:D:K:R:a:o:pf:W:GuP:Os:mLA:ZB0hcJ:1:'
43474348
LONG_OPTS = [HELP, VERBOSE, VERSION, IGNORE_SECURE_DEVICE, IGNORE_VERSION_CHECK, FORCE_VIEW_SERVER_USE,
43484349
DO_NOT_START_VIEW_SERVER,
43494350
DO_NOT_IGNORE_UIAUTOMATOR_KILLED,
@@ -4365,6 +4366,7 @@ class CulebraOptions:
43654366
NULL_BACK_END,
43664367
USE_UIAUTOMATOR_HELPER,
43674368
CONCERTINA,
4369+
CONCERTINA_CONFIG + '=',
43684370
INSTALL_APK + '=',
43694371
]
43704372
LONG_OPTS_ARG = {WINDOW: 'WINDOW',
@@ -4377,6 +4379,7 @@ class CulebraOptions:
43774379
SCALE: 'FLOAT',
43784380
SERIALNO: 'LIST',
43794381
DEVICE_ART: 'MODEL',
4382+
CONCERTINA_CONFIG: 'FILENAME',
43804383
INSTALL_APK: 'FILENAME'}
43814384
OPTS_HELP = {
43824385
'H': 'prints this help',
@@ -4413,6 +4416,7 @@ class CulebraOptions:
44134416
'0': 'use a null back-end (no View tree obtained)',
44144417
'h': 'use UiAutomatorHelper',
44154418
'c': 'enable concertina mode (EXPERIMENTAL)',
4419+
'J': 'concertina config file (JSON)',
44164420
'1': 'install APK as precondition (use with -U)',
44174421
}
44184422

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"probabilities": {
3+
"systemKeys": 1.0,
4+
"other": 0.0
5+
},
6+
"systemKeys": {
7+
"keys": [
8+
"ENTER",
9+
"BACK",
10+
"HOME",
11+
"MENU"
12+
],
13+
"probabilities": [
14+
0.33,
15+
0.33,
16+
0,
17+
0.34
18+
]
19+
}
20+
}

0 commit comments

Comments
 (0)