1010import re
1111import socket
1212import os
13+ import java
1314from com .android .monkeyrunner import MonkeyDevice
1415
1516DEBUG = False
2122VIEW_SERVER_HOST = 'localhost'
2223VIEW_SERVER_PORT = 4939
2324
25+ # this is probably the only reliable way of determining the OS in monkeyrunner
26+ os_name = java .lang .System .getProperty ('os.name' )
27+ if os_name .startswith ('Windows' ):
28+ ADB = 'adb.exe'
29+ else :
30+ ADB = 'adb'
31+
2432OFFSET = 50
2533
2634class View :
2735 '''
2836 View class
2937 '''
30-
38+
39+ GET_VISIBILITY_PROPERTY = 'getVisibility()'
40+ LAYOUT_TOP_MARGIN_PROPERTY = 'layout:layout_topMargin'
41+
3142 def __init__ (self , map , device ):
3243 '''
3344 Constructor
@@ -86,27 +97,32 @@ def innerMethod():
8697
8798 def __call__ (self , * args , ** kwargs ):
8899 if DEBUG :
89- print "__call__(%s)" % (args if args else None )
100+ print >> sys . stderr , "__call__(%s)" % (args if args else None )
90101
91102 def getX (self ):
92103 x = 0
93- if self .map [ 'getVisibility()' ] == 'VISIBLE' :
104+ if self .GET_VISIBILITY_PROPERTY in self . map and self . map [ self . GET_VISIBILITY_PROPERTY ] == 'VISIBLE' :
94105 x += int (self .map ['layout:mLeft' ])
95- x += OFFSET / 2
106+ # x += OFFSET/2
96107 return x
97108
98109 def getY (self ):
99110 y = 0
100- if self .map [ 'getVisibility()' ] == 'VISIBLE' :
111+ if self .GET_VISIBILITY_PROPERTY in self . map and self . map [ self . GET_VISIBILITY_PROPERTY ] == 'VISIBLE' :
101112 y += int (self .map ['layout:mTop' ])
113+
114+ #if self.LAYOUT_TOP_MARGIN_PROPERTY in self.map:
115+ # if DEBUG:
116+ # print >>sys.stderr, " adding top margin=%d" % int(self.map[self.LAYOUT_TOP_MARGIN_PROPERTY])
117+ # y += int(self.map[self.LAYOUT_TOP_MARGIN_PROPERTY])
102118 return y
103119
104120 def getXY (self ):
105121 '''
106122 Returns the coordinates of this View
107123 '''
108124
109- # FIXME: this usually don 't return the real coordinates of the View but the coordinates
125+ # FIXME: this usually doesn 't return the real coordinates of the View but the coordinates
110126 # relative to its parent, so to obtain the real coordinates the View root should
111127 # have to be traversed to the root adding the coordinates for every child
112128 x = self .getX ()
@@ -125,15 +141,24 @@ def getXY(self):
125141 parent = parent .parent
126142 return (x , y + hy )
127143
144+ def getCoords (self ):
145+ '''
146+ Gets the coords of the View
147+ '''
148+ (x , y ) = self .getXY ();
149+ w = int (self .map ['layout:getWidth()' ])
150+ h = int (self .map ['layout:getHeight()' ])
151+ return ((x , y ), (x + w , y + h ))
152+
128153 def touch (self , type = MonkeyDevice .DOWN_AND_UP ):
129154 '''
130155 Touches this View
131156 '''
132157
133158 (x , y ) = self .getXY ()
134159 if DEBUG :
135- print >> sys .stderr , "should click @ (%d, %d)" % (x , y )
136- self .device .touch (x , y , type )
160+ print >> sys .stderr , "should touch @ (%d, %d)" % (x + OFFSET / 2 , y + OFFSET / 2 )
161+ self .device .touch (x + OFFSET / 2 , y + OFFSET / 2 , type )
137162
138163 def allPossibleNamesWithColon (self , name ):
139164 l = []
@@ -183,7 +208,7 @@ class ViewClient:
183208 mapping is created.
184209 '''
185210
186- def __init__ (self , device , adb = os .path .join (ANDROID_HOME , 'platform-tools' , 'adb' )):
211+ def __init__ (self , device , adb = os .path .join (ANDROID_HOME , 'platform-tools' , ADB )):
187212 '''
188213 Constructor
189214 '''
@@ -223,15 +248,19 @@ def serviceResponse(self, response):
223248 def setViews (self , received ):
224249 self .views = received .split ("\n " )
225250 if DEBUG :
226- print "there are %d views in this dump" % len (self .views )
227-
251+ print >> sys . stderr , "there are %d views in this dump" % len (self .views )
252+
228253 def __splitAttrs (self , str , addViewToViewsById = False ):
229254 '''
230255 Splits the view attributes in str and optionally adds the view id to the viewsById list.
231256 Returns the attributes map.
232257 '''
233258
234259 idRE = re .compile ("(?P<viewId>id/\S+)" )
260+ # FIXME: this split is incorrect if for example a text:mText contains spaces
261+ # maybe something like this should be used and the count the chars specified
262+ # and cut it
263+ #textRE = re.compile("(?P<attr>\S+)(\(\))?=\d+,(?P<text>.+)")
235264 attrRE = re .compile ("(?P<attr>\S+)(\(\))?=\d+,(?P<val>\S+)" )
236265 hashRE = re .compile ("(?P<class>\S+)@(?P<oid>[0-9a-f]+)" )
237266
@@ -241,8 +270,9 @@ def __splitAttrs(self, str, addViewToViewsById=False):
241270 if m :
242271 viewId = m .group ('viewId' )
243272 if DEBUG :
244- print "found %s" % viewId
245-
273+ print >> sys .stderr , "found %s" % viewId
274+
275+ # FIXME: this split is incorrect if for example a text:mText contains spaces
246276 for attr in str .split ():
247277 m = attrRE .match (attr )
248278 if m :
@@ -254,9 +284,14 @@ def __splitAttrs(self, str, addViewToViewsById=False):
254284 attrs ['oid' ] = m .group ('oid' )
255285 else :
256286 if DEBUG :
257- print attr , "doesn't match"
287+ print >> sys . stderr , attr , "doesn't match"
258288
259289 if addViewToViewsById :
290+ if not viewId :
291+ # If the view has NO_ID we are assigning a default id here (id/no_id) which is
292+ # immediatelly incremented if another view with no id was found before to generate
293+ # a unique id
294+ viewId = "id/no_id"
260295 if viewId in self .viewsById :
261296 # sometimes the view ids are not unique, so let's generate a unique id here
262297 i = 1
@@ -267,9 +302,11 @@ def __splitAttrs(self, str, addViewToViewsById=False):
267302 i += 1
268303 viewId = newId
269304 if DEBUG :
270- print "adding viewById %s" % viewId
271- if viewId :
272- self .viewsById [viewId ] = attrs
305+ print >> sys .stderr , "adding viewById %s" % viewId
306+ # We are assigning a new attribute to keep the original id preserved, which could have
307+ # been NO_ID repeated multiple times
308+ attrs ['uniqueId' ] = viewId
309+ self .viewsById [viewId ] = attrs
273310
274311 return attrs
275312
@@ -301,7 +338,7 @@ def traverse(self, root, indent=""):
301338 if not root :
302339 return
303340
304- print "%s%s" % (indent , root )
341+ print >> sys . stderr , "%s%s" % (indent , root )
305342
306343 for ch in root .children :
307344 self .traverse (ch , indent = indent + " " )
@@ -323,9 +360,9 @@ def dump(self, windowId=-1):
323360
324361 s .close ()
325362 if DEBUG_RECEIVED :
326- print
327- print received
328- print
363+ print >> sys . stderr
364+ print >> sys . stderr , received
365+ print >> sys . stderr
329366 self .setViews (received )
330367 self .parseTree (self .views )
331368
@@ -348,10 +385,10 @@ def findViewByTag(self, tag):
348385 return self .findViewWithAttribute ('getTag()' , tag )
349386
350387 def findViewWithAttributeInTree (self , attr , val , root ):
351- if DEBUG : print "findViewWitAttributeInTree: checking if root=%s has attr=%s == %s" % (root .__smallStr__ (), attr , val )
388+ if DEBUG : print >> sys . stderr , "findViewWitAttributeInTree: checking if root=%s has attr=%s == %s" % (root .__smallStr__ (), attr , val )
352389
353390 if root and attr in root .map and root .map [attr ] == val :
354- if DEBUG : print "findViewWitAttributeInTree: FOUND: %s" % root .__smallStr__ ()
391+ if DEBUG : print >> sys . stderr , "findViewWitAttributeInTree: FOUND: %s" % root .__smallStr__ ()
355392 return root
356393 else :
357394 for ch in root .children :
@@ -381,4 +418,4 @@ def getViewIds(self):
381418 except :
382419 print "Don't expect this to do anything"
383420
384-
421+
0 commit comments