1919
2020'''
2121import StringIO
22+ import json
2223import random
2324import re
2425import time
2526
27+ import numpy
28+
2629from com .dtmilano .android .common import profileEnd
2730from com .dtmilano .android .common import profileStart
2831from com .dtmilano .android .concertina import Concertina
2932
30- __version__ = '13.6.3 '
33+ __version__ = '14.0.0 '
3134
3235import sys
3336import threading
@@ -213,7 +216,7 @@ def checkDependencies():
213216This 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
16131618if TKINTER_AVAILABLE :
16141619 class MainMenu (Tkinter .Menu ):
0 commit comments