# -*- coding:utf-8 -*- import time,sys,os from UAT_tree import UATTree from UAT_log import error,info,debug from UAT_PathManage import UATPathManage from UAT_runnerCommand import UATRunnerCommand from ssat_sdk.python_uiautomator import PyUIAutomator,DirectionManageAndroid,FocusManageAndroid ERROR = True INFO = True DEBUG = True ''' 采用uiautomator技术,处理界面定位问题,用于移动焦点到目标组件上 ''' class FocusCommand(): cls = "FocusCommand" UIObjParam = { "text": None, # MASK_TEXT, "textContains": None, # MASK_TEXTCONTAINS, "textMatches": None, # MASK_TEXTMATCHES, "textStartsWith": None, # MASK_TEXTSTARTSWITH, "className": None, # MASK_CLASSNAME "classNameMatches": None, # MASK_CLASSNAMEMATCHES "description": None, # MASK_DESCRIPTION "descriptionContains": None, # MASK_DESCRIPTIONCONTAINS "descriptionMatches": None, # MASK_DESCRIPTIONMATCHES "descriptionStartsWith": None, # MASK_DESCRIPTIONSTARTSWITH "checkable": None, # MASK_CHECKABLE "checked": None, # MASK_CHECKED "clickable": None, # MASK_CLICKABLE "longClickable": None, # MASK_LONGCLICKABLE, "scrollable": None, # MASK_SCROLLABLE, "enabled": None, # MASK_ENABLED, "focusable": None, # MASK_FOCUSABLE, "focused": None, # MASK_FOCUSED, "selected": None, # MASK_SELECTED, "packageName": None, # MASK_PACKAGENAME, "packageNameMatches": None, # MASK_PACKAGENAMEMATCHES, "resourceId": None, # MASK_RESOURCEID, "resourceIdMatches": None, # MASK_RESOURCEIDMATCHES, "index": None, # MASK_INDEX, "instance": None # MASK_INSTANCE, } UATParamMap = { "text":"text", # MASK_TEXT, "textContains":"textContains", # MASK_TEXTCONTAINS, "textMatch":"textMatches", # MASK_TEXTMATCHES, "textSWith":"textStartsWith", # MASK_TEXTSTARTSWITH, "class":"className", # MASK_CLASSNAME "classMatches":"classNameMatches", # MASK_CLASSNAMEMATCHES "desc":"description", # MASK_DESCRIPTION "descContain":"descriptionContains", # MASK_DESCRIPTIONCONTAINS "descMatch":"descriptionMatches", # MASK_DESCRIPTIONMATCHES "descSWith":"descriptionStartsWith", # MASK_DESCRIPTIONSTARTSWITH "checkable":"checkable", # MASK_CHECKABLE "checked":"checked", # MASK_CHECKED "clickable":"clickable", # MASK_CLICKABLE "lClickable":"longClickable", # MASK_LONGCLICKABLE, "scrollable":"scrollable", # MASK_SCROLLABLE, "enable":"enabled", # MASK_ENABLED, "focusable":"focusable", # MASK_FOCUSABLE, "focused":"focused", # MASK_FOCUSED, "selected":"selected", # MASK_SELECTED, "pkg":"packageName", # MASK_PACKAGENAME, "pkgMatch":"packageNameMatches", # MASK_PACKAGENAMEMATCHES, "resid":"resourceId", # MASK_RESOURCEID, "residMatch":"resourceIdMatches", # MASK_RESOURCEIDMATCHES, "index":"index", # MASK_INDEX, "instance":"instance" # MASK_INSTANCE, } def __init__(self, runnerCommand): self.runnerCommand = runnerCommand self.pyU = PyUIAutomator() self.dm = DirectionManageAndroid() self.fm = FocusManageAndroid(self.pyU, self.dm) self.inLayout= False ''' 在parent界面,将焦点移动到目标option上。 焦点定位:根据layout是不是限制焦点范围,进行焦点组件寻找,焦点组件类型:focus、select、focusView、long-click 目标定位:纯粹是根据optionView配置组建坐标定位 :param parent:当前电视界面的parent字典 :param option:目标option字典 :return True/False ''' def focusOptionView(self, parent, option): #是否采用layout限制焦点判断范围 layout = parent[UATTree.TAB_LAYOUT] print "focusOptionView,layout:",layout self.inLayout = layout[UATTree.Layout_Limit] # 找到目标optionView参数 layoutUIObj = {} optionUIObjParam = self.setObjParam(option[UATTree.TAB_OPTION_VIEW]) if self.inLayout == 1: layoutUIObjParam = self.setObjParam(layout) else: layoutUIObjParam = {} # 获取move key按键 moveKey = parent[UATTree.TAB_MOVE_KEY] if moveKey[UATTree.Max_Try] != "": Max_Try = int(moveKey[UATTree.Max_Try]) else: Max_Try = parent[UATTree.TAB_OPTION].__len__() findDirection = ["down","up"] keyType = UATTree.Key_Event if moveKey[UATTree.Key_Event].__len__() > 1: findDirection = moveKey[UATTree.Key_Event] keyType = UATTree.Key_Event elif moveKey[UATTree.Key_IR].__len__() > 1: findDirection = moveKey[UATTree.Key_IR] keyType = UATTree.Key_IR elif moveKey[UATTree.Key_Input].__len__() > 1: inputCmd = moveKey[UATTree.Key_Input] #TODO input情况的处理 return False else: error(self.cls, "focusTargetOption", option[UATTree.TAB_NAME] + " option 读取 move_key失败", ERROR) return False #获取焦点View和optionview的area参数 osamBounds = option[UATTree.TAB_OPTION_VIEW][UATTree.View_sambounds] fsamBounds = "" #获取焦点view参数 fsParam = option[UATTree.TAB_FOCUS_SELECT] fvParam = option[UATTree.TAB_FOCUSE_VIEW] fvUIParam = self.setObjParam(fvParam) if fsParam[UATTree.FS_Type].__len__() > 1: if fsParam[UATTree.FS_Type].lower() == "focus": fsParam["focused"] = True elif fsParam[UATTree.FS_Type].lower() == "select": fsParam["select"] = True else: error(self.cls,"focusOptionView","Option %s focus-select参数异常。"%option[UATTree.TAB_NAME], ERROR) return False fsamBounds = fsParam[UATTree.View_sambounds] fsUIParam = self.setObjParam(fsParam) return self.toDestObj(optionUIObjParam, fsUIParam,Max_Try,findDirection,keyType,layoutUIObjParam,fsamBounds,osamBounds) elif fvUIParam.__len__() > 0: fsamBounds = fvParam[UATTree.View_sambounds] return self.toDestObj(optionUIObjParam, fvUIParam,Max_Try,findDirection,keyType,layoutUIObjParam,fsamBounds,osamBounds) else: error(self.cls, "focusOptionView", "Option %s focusView参数异常。" % option[UATTree.TAB_NAME], ERROR) return False def toDestObj(self, destUIParam, focusObjParam, Max_Try=10, findDirections=["down","up"], keyType=UATTree.Key_Event ,layoutUIParam={},fsamBounds="",osamBounds=""): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 print "toDestObj,enter:",Max_Try,findDirections,keyType count = 0 Max_Try = Max_Try directIndex = 0 while (True): if count >= Max_Try and directIndex >= findDirections.__len__(): break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) print "toDestObj,focusObjParam:",focusObjParam focusedUIObject = self.pyU.getUiObject2(focusObjParam) focusedBounds = focusedUIObject.info['bounds'] # print "focusedBounds:",focusedBounds if layoutUIParam.__len__() > 0: layoutUIObj = self.pyU.getUiObject2(layoutUIParam) destUIObject = layoutUIObj.child(**destUIParam) else: destUIObject = self.pyU.getUiObject2(destUIParam) if destUIObject: print "focusOptionView,focusedBounds,destUIBounds:",focusedBounds, destUIObject.info['bounds'] try: destBounds = destUIObject.info['bounds'] # print "destBounds:", destBounds if self.hasFocusDest(focusedBounds, destBounds, ): print '成功聚焦到目标焦点:',destUIParam return True else: count = count + 1 direction = self.dm.getTargetDirection(focusedBounds, destBounds) self.dm.goOneStep(self.pyU, direction, keyType) except Exception,e: print "toDestObj,e:",e # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 else: directIndex = directIndex + 1 self.fm.pressKeyByType(findDirections[directIndex], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.fm.pressKeyByType(findDirections[directIndex], keyType) else: directIndex = directIndex + 1 self.fm.pressKeyByType(findDirections[directIndex], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 根据配置的样本焦点框和OptionView 区域坐标,计算是否聚焦 ''' def hasFocusDest(self,focusedBounds, destBounds, fsamBounds="",osamBounds=""): if fsamBounds.__len__() < 1 or osamBounds.__len__()<1: return self.dm.isHasAnotherBounds(focusedBounds, destBounds) else:#在焦点框bounds特别大时,同时包含多个目标optionView时,用此方法判断是否选中。 fsamBounds = self.strToBounds(fsamBounds) osamBounds = self.strToBounds(osamBounds) sdx=fsamBounds[0][0] - osamBounds[0][0] sdy=fsamBounds[0][1] - osamBounds[0][1] sdrate = self.calPointAngle(fsamBounds) focusBounds = self.strToBounds(focusedBounds) destBounds = self.strToBounds(destBounds) dx = focusBounds[0][0] - destBounds[0][0] dy = focusBounds[0][1] - destBounds[0][1] drate = dy / dx if (drate - sdrate) < 5: return True else: return False ''' 计算点p1相对点p2的角度 ''' def calPointAngle(self, p1, p2): dx = p1[0]-p2[0] dy = p1[1]-p2[1] ''' 将‘[801,116][1280,180]’转成数组[[801,116][1280,180]] ''' def strToBounds(self,bstr): char = "[]" print "strToBounds,bstr:",bstr keyIndex = 1 # key.__len__()-1 为去掉"["的处理 i1 = bstr.find(char[0]) i2 = bstr.find(char[1]) if i1 == -1 or i2 == -1: return [] str1 = bstr[i1: i2+1] str2 = bstr[i2+1 : bstr.__len__()] return [eval(str1),eval(str2)] ''' 根据传入的uat界面obj参数,转换成UIObject参数 参数均用字典存储。 ''' def setObjParam(self, uatObjParam): uiObjParam = {} for uatKey in uatObjParam: uatParam = uatObjParam[uatKey] if self.UATParamMap.has_key(uatKey) and uatParam is not None and str(uatParam).__len__() > 0: uiObjParam[self.UATParamMap[uatKey]] = uatParam return uiObjParam ''' 检测option组件,在电视上是否被选中了 :param option 数据字典 :return -1:未进入option的页面,找不到option UIObject;0:进入了option的页面,未选中option;1 已经选中option -2:代表未找到焦点 ''' def checkOptionChoose(self, option): optionUIObj = self.pyU.getUiObject(text=option["optionView"][UATTree.View_Text], resourceId=option["optionView"][UATTree.View_ID], description=option["optionView"][UATTree.View_Desc]) if optionUIObj is None or optionUIObj.count == 0: return -1 curUIObj = self.getChooseUIObj(option) if curUIObj is None or curUIObj.count == 0: return -2 if self.dm.isHasAnotherBounds(curUIObj.info["bounds"], optionUIObj.info['bounds']): return 1 else: return 0 ''' 根据option配置的焦点方式,返回当前页面焦点组件 ''' def getChooseUIObj(self, option): print "getChooseUIObj option:", option["focus-select"] if option["focus-select"]["type"].__len__() > 1: chooseType = option["focus-select"][UATTree.FS_Type] if chooseType.lower() == "focus": return self.pyU.getFocusedUIObject() elif chooseType.lower() == "select": return self.pyU.getSelectedUIObject() elif chooseType.lower() == "no": resId = option[UATTree.TAB_OPTION_VIEW][UATTree.View_ID] text = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Text] className = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Class] desc = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Desc] return self.pyU.getUiObject(resourceId=resId, text=text, className=className, description=desc) else: error(self.cls, "getChooseUIObj", option["name"] + " option focus-select属性配置异常", ERROR) return None elif option["focuseView"][UATTree.Focus_Text].__len__() > 0 \ or option["focuseView"][UATTree.Focus_ID].__len__() > 1 \ or option["focuseView"][UATTree.Focus_Desc].__len__() > 0: return self.pyU.getUiObject(text=option["focuseView"][UATTree.Focus_Text], resourceId=option["focuseView"][UATTree.Focus_ID], description=option["focuseView"][UATTree.Focus_Desc]) else: error(self.cls, "getChooseUIObj", option["name"] + " option 需要配置 focus-select或者FocusView", ERROR) return None ''' 检测parent,在电视上是否被选中了。一个option被选中,表示选中 :param option 数据字典 :return -3:代表UIView判断未通过; -2:代表选中了parent,但未找到焦点; -1:未进入parent的页面,找不到parent layout; 0:进入了parent的页面,未选中parent; 1:已经选中parent ''' def checkParentChoose(self, parent): debug(self.cls, "checkParentChoose", "parent:" + parent["name"], DEBUG) chooseTypeDict = {} layoutResId = parent["layout"][UATTree.Layout_ID] uiView = parent[UATTree.TAB_UI_VIEW] uiViewResId = uiView[UATTree.View_ID] uiViewText = uiView[UATTree.View_Text] uiViewDesc = uiView[UATTree.View_Desc] if layoutResId == "" and uiViewResId == "" and uiViewText == "" and uiViewDesc == "": debug(self.cls, "checkParentChoose", "Warning:Parent %s的Layout resId和UIView信息获取失败!!请注意检查UATree文件!!!" % parent['name'], DEBUG) return -1 elif layoutResId != "": # 如果存在UIView信息,则先判断UIView if uiViewResId != "" or uiViewText != "" or uiViewDesc != "": isExist = self.checkUIViewExist(uiView) if isExist is False: info(self.cls, "checkParentChoose", "当前页面不存在Parent:%s的UIView组件,判断该界面非此parent" % parent['name'], INFO) return -3 else: info(self.cls, "checkParentChoose", "已识别出Parent:%s的UIView组件" % parent['name'], INFO) description = parent["layout"][UATTree.Layout_Desc] if description != "": layoutUIObj = self.pyU.getUiObject(resourceId=parent["layout"][UATTree.Layout_ID], description=parent["layout"][UATTree.Layout_Desc]) else: layoutUIObj = self.pyU.getUiObject(resourceId=parent["layout"][UATTree.Layout_ID]) if layoutUIObj is None or layoutUIObj.count == 0: # debug(self.cls, "checkParentChoose", "parent isn't " + parent["name"], DEBUG) return -1 # print "checkParentChoose, layoutUIObj:",layoutUIObj.info debug(self.cls, "checkParentChoose", "layoutUIObj:" + str(layoutUIObj.info), DEBUG) for optionName in parent["option"]: option = parent["option"][optionName] if option["focus-select"]["type"].__len__() > 1: chooseTypeDict[option["focus-select"][UATTree.FS_Type]] = option elif option["focuseView"][UATTree.Focus_Text].__len__() > 0 \ or option["focuseView"][UATTree.Focus_ID].__len__() > 1 \ or option["focuseView"][UATTree.Focus_Desc].__len__() > 0: chooseTypeDict[option["focuseView"][UATTree.Focus_ID] + option["focuseView"][UATTree.Focus_Text] + option["focuseView"][UATTree.Focus_Desc]] = option info(self.cls, "checkParentChoose", str(chooseTypeDict), INFO) for key in chooseTypeDict.keys(): option = chooseTypeDict[key] chooseUIObj = self.getChooseUIObj(option) # TODO 如果选中效果,是没有type[no],例如快捷键,如何处理? if chooseUIObj is None or chooseUIObj.count == 0: return -2 debug(self.cls, "checkParentChoose", "chooseUIObj:" + str(chooseUIObj.info), DEBUG) # 获取到chooseUIObj后,与layout对比坐标,确认焦点是否存在于layout之中 layoutBounds = layoutUIObj.info['bounds'] choosingBounds = chooseUIObj.info['bounds'] if not self.dm.isHasAnotherBounds(layoutBounds, choosingBounds): info(self.cls, "checkParentChoose", "识别出parent %s的Layout,但焦点不在该Layout之中,判断界面未处于该parent" % (parent['name']), INFO) return -2 # 如果该页面text属性不为空,则确认该parent下所有option的text,是否存在于当前页面中;如果text属性为空,则判断index属性 for optionName in parent["option"]: option = parent["option"][optionName] # 如果有text属性则确认Text,是否能对比成功 optionText = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Text] optionId = option[UATTree.TAB_OPTION_VIEW][UATTree.View_ID] optionIndex = option[UATTree.TAB_OPTION_VIEW][UATTree.View_Index] if optionText.__len__() > 0: info(self.cls, "checkParentChoose", "checking parent %s text %s" % (parent['name'], optionText), INFO) textObj = self.pyU.getUiObject(text=optionText) elif optionId.__len__() > 0: info(self.cls, "checkParentChoose", "checking parent %s optionId %s" % (parent['name'], optionId), INFO) textObj = self.pyU.getUiObject(resourceId=optionId) elif optionIndex != "": # index属性无法用于判断option,如果在以index属性作为判断依据的页面,则直接返回在这个页面 info(self.cls, "checkParentChoose", "已找到目标parent %s,该页面为Index item页面,无法判断具体option" % parent['name'], INFO) return 1 else: error(self.cls, "checkParentChoose", "当前参数不足以判断option %s是否存在" % (option['name']), ERROR) continue if textObj.exists: info(self.cls, "checkParentChoose", "已找到目标parent %s,并识别出该parent下的option %s" % (parent['name'], option['name']), INFO) return 1 else: info(self.cls, "checkParentChoose", "return 0", INFO) return 0 else: isExist = self.checkUIViewExist(uiView) if not isExist: return -3 else: info(self.cls, "checkParentChoose", "识别出parent %s的UIView参数,判断处于该界面中" % (parent['name']), INFO) return 1 ''' 检测某个弹窗是否存在。 弹窗相关的参数放在UIView中配置。 当存在resId参数时,使用resId获取对象并获取文本,再与text做比较; 当不存在resId参数时,使用text直接获取对象,判断对象是否存在。 与checkParentChoose判断不一致,做特殊处理。 ''' def checkDialogExist(self, dialog): debug(self.cls, "checkDialogExist", "dialog:" + dialog["name"], DEBUG) dialogText = dialog[UATTree.TAB_UI_VIEW][UATTree.UIView_Text] dialogResId = dialog[UATTree.TAB_UI_VIEW][UATTree.UIView_ID] # print "checkDialogExist.dialogText:", dialogText # print "checkDialogExist.dialogResId:", dialogResId if dialogResId != "": textObj = self.pyU.getUiObject(resourceId=dialogResId) if textObj.exists: objText = textObj.info['text'] # print "checkDialogExist.objText:", objText if dialogText in objText: return 1 else: return 0 else: return 0 else: textObj = self.pyU.getUiObject(text=dialogText) if textObj.exists: return 1 else: return 0 def checkUIViewExist(self, uiView): resid = uiView[UATTree.View_ID] text = uiView[UATTree.View_Text] description = uiView[UATTree.View_Desc] uiViewObj = self.pyU.getUiObject(resourceId=resid, text=text, description=description) if not uiViewObj.exists: return False else: return True if __name__ == "__main__": uatPathManage = UATPathManage() pyU = PyUIAutomator() dm = DirectionManageAndroid() fm = FocusManageAndroid(pyU, dm) runnerCmd = UATRunnerCommand(uatPathManage, pyU, dm, fm) focusCmd = FocusCommand(runnerCmd) focusCmd.strToBounds('[801,116][1280,180]') # option = focusCmd.runnerCommand.uatPathManage.uatData.getOption("av_devices_settings") # parent = focusCmd.runnerCommand.uatPathManage.uatData.getParentByOption(option) # focusCmd.focusOptionView(parent, option) # print "dump_hierarchy 0:",time.time() # print focusCmd.pyU.dump_hierarchy() # print "dump_hierarchy 1:",time.time() # uiobjg = focusCmd.pyU.getUiObject()