# -*- coding:utf-8 -*- import time from ssat_sdk.ocr_convert import OCRConvert from ssat_sdk.device_manage.capturecard_manager import CCardManager from ssat_sdk.uiautomator2.ext import htmlreport # from uiautomator2.ext import htmlreport from ssat_sdk import uiautomator2 as u2, LoggingUtil, inspect, FeatureDetect, sat_environment, getSATTmpDIR, ImageCMP from ssat_sdk.UATree.UAT_tree import UATTree from ssat_sdk.tv_operator import TvOperator # import uiautomator2 as u2 import os import re from ssat_sdk.utils import LoggingUtil from ssat_sdk.utils.string_util import getDigitFromString from ssat_sdk.sat_environment import getAdbDeviceSatatus pyFileName = os.path.split(__file__)[-1] def get_current_function_name(): return inspect.stack()[1][3] ''' 预置条件:保证执行PC和测试电视adb连接 各个API说明的例子中到达self.pyUIAutomator 是PyUIAutomator类的实例 self.pyUIAutomator = PyUIAutomator() ''' class PyUIAutomator(): def __init__(self, serial=None): try: self.u = u2.connect_usb(serial) #得到UIAutomatorServer实例对象 self.serial = serial except Exception,e: print e # hrp = htmlreport.HTMLReport(self.u, 'report') # try: # hrp.patch_click() # except Exception, e: # print e # pass # self.u.app_stop_all() def reConnect_usb(self): self.u = u2.connect_usb(self.serial) # hrp = htmlreport.HTMLReport(self.u, 'report') # try: # hrp.patch_click() # except Exception, e: # print e # pass # self.u.app_stop_all() ''' 废弃,改用sat_environment.getAdbDeviceSatatus() ''' def checkAdbConnected(self): return u2.check_adb_connect() def listenAdbConnected(self): count = 5 while getAdbDeviceSatatus() is False and count >0: count -= 1 LoggingUtil.printLog("PyUIAutomator","listenAdbConnected,Count %d adb not connected!"%count) # self.reConnect_usb() time.sleep(1) def killAdb(self): return u2.killAdb() def getAdbType(self): return u2.check_adb_connect_type() ''' 作用:获取UIAutomator 连接对象 后续可以使用该对象使用原生方法 ''' def getPyUIAutomator(self): self.listenAdbConnected() return self.u ''' 作用:Android times次按键 key:常用按键的字符串 主要的键值字段:home、back、left、right、up、down、center、menu、search、enter、delete ( or del)、 recent (recent apps)、volume_up、volume_down、volume_mute、camera、power times :按键次数 默认为1次 duration:按键时间间隔 默认为1.0s ''' def pressKeyTimes(self, key, times=1, duration=1.0): self.listenAdbConnected() self.u.pressKeyTimes(key, times, duration) ''' 用uiautomator view属性键作为paramDIct的键,传入的paramDict里的参数,必须有效。 参考session.Selector里的__fields字典 ''' def getUiObject2(self, paramDict): # print "getUiObject2,paramDict:",paramDict uiObject = None if paramDict.__len__>0: try: uiObject = self.u(**paramDict) except Exception,e: print "UIAutomator未启动,请确认是否为开机状态" uiObject = None # print uiObject,uiObject.info return uiObject ''' 作用:获取UiObject对象 参数: className:控件类型(例如:android.widget.TextView) resourceId:控件的Id(例如:lh.oslauncher:id/tv_title) text:控件上的文件(例如:'主页') description:控件描述 instance:实例参数 注意:className、resourceId、text 、description、instance是利用 UIautomatoview.bat工具查看控件属性看到的 其中 className对应 UIautomatoview.bat中的 class resourceId对应 UIautomatoview.bat中的 resource-id description对应 UIautomatoview.bat中的 content-desc instance对应 UIautomatoview.bat中的 index 例子:uiObject =self.pyUIAutomator.getUiObject(resourceId="com.google.android.tvlauncher:id/banner_image", description=u"Play 电影", className = 'android.widget.ImageView') ''' def getUiObject(self, className='', resourceId='', text='', description='', bounds = '',instance=-1): if text != "": if type(text) is not type([]): obj = self.getUiObjectTest(className,resourceId,text,description,bounds,instance) return obj if type(text) is type([]): count = 0 for t in text: try: obj = self.getUiObjectTest(className,resourceId,t,description,bounds,instance) objInfo = obj.info print "文本%s对应的option已找到"%t return obj except Exception,e: nextText = text[count+1] print"文本%s对应的option未找到,匹配下一个文本%s"%(t,nextText) count += 1 print "所有文本均已匹配完毕,未找到对应option" class object(): exists = False obj = object() return obj else: obj = self.getUiObjectTest(className,resourceId,text,description,bounds,instance) return obj def getUiObjectTest(self, className='', resourceId='', text='', description='', bounds = '',instance=-1): self.listenAdbConnected() # print 'className:', className # print 'resourceId:', resourceId # print 'text:', text # print 'description:', description # print 'instance:', instance uiObject = None try: if className == "" and resourceId == "" and text == "" and description == "" and\ bounds == "" and instance == -1: print "没有参数带入,找不到对象" return uiObject if className != "" and resourceId != "" and text != "" and description != "" and \ bounds != "" and instance != -1: uiObject = self.u(className=className, resourceId=resourceId, text=text, description=description, bounds=bounds,instance=instance) # 缺少一个元素的 #缺instance if className != "" and resourceId != "" and text != "" and description != "" and \ bounds != "" and instance == -1: uiObject = self.u(className=className, resourceId=resourceId, text=text, bounds = bounds, description=description) #缺bounds if className != "" and resourceId != "" and text != "" and description != "" and \ bounds == "" and instance != -1: uiObject = self.u(className=className, resourceId=resourceId, text=text, description=description,instance = instance) #缺description if className != "" and resourceId != "" and text != "" and description == "" and\ bounds != "" and instance != -1: uiObject = self.u(className=className, resourceId=resourceId, text=text, bounds=bounds,instance=instance) #缺text if className != "" and resourceId != "" and text == "" and description != "" and\ bounds != "" and instance != -1: uiObject = self.u(className=className, resourceId=resourceId, description=description, bounds=bounds,instance=instance) #缺resouceId if className != "" and resourceId == "" and text != "" and description != "" and \ bounds!="" and instance != -1: uiObject = self.u(className=className, text=text, description=description, bounds=bounds,instance=instance) # lack of className if className == "" and resourceId != "" and text != "" and description != "" and \ bounds!="" and instance != -1: uiObject = self.u(resourceId=resourceId, text=text, description=description, bounds=bounds,instance=instance) # 缺少两个元素的 # lack of className and resourceId if className == "" and resourceId == "" and text != "" and description != "" and \ bounds!="" and instance != -1: uiObject = self.u(text=text, description=description,bounds=bounds, instance=instance) # lack of className and text if className == "" and resourceId != "" and text == "" and description != "" and \ bounds != "" and instance != -1: uiObject = self.u(resourceId=resourceId, description=description, bounds=bounds, instance=instance) #lack of className and description if className == "" and resourceId != "" and text != "" and description == "" and \ bounds!="" and instance != -1: uiObject = self.u(resourceId=resourceId, text=text,bounds=bounds, instance=instance) # lack of className and bounds if className == "" and resourceId != "" and text != "" and description != "" and \ bounds == "" and instance != -1: uiObject = self.u(resourceId=resourceId, text=text, description=description, instance=instance) # lack of className and instance if className == "" and resourceId != "" and text != "" and description != "" and \ bounds != "" and instance == -1: uiObject = self.u(resourceId=resourceId, text=text, description=description, bounds=bounds) # lack of resourceId and text if className != "" and resourceId == "" and text == "" and description != "" and \ bounds!="" and instance != -1: uiObject = self.u(className=className, description=description,bounds=bounds, instance=instance) # lack of resourceId and description if className != "" and resourceId == "" and text != "" and description == "" and \ bounds!="" and instance != -1: uiObject = self.u(className=className, text=text,bounds = bounds, instance=instance) # lack of resourceId and bounds if className != "" and resourceId != "" and text == "" and description != "" and \ bounds == "" and instance != -1: uiObject = self.u(className=className, text=text, description=description, instance=instance) # lack of resourceId and instance if className != "" and resourceId == "" and text != "" and description != "" and \ bounds != "" and instance == -1: uiObject = self.u(className=className, text=text, description=description, bounds=bounds) # lack of text and description if className != "" and resourceId != "" and text == "" and description == "" and \ bounds!="" and instance != -1: uiObject = self.u(className=className, resourceId=resourceId, bounds=bounds, instance=instance) # lack of text and bounds if className != "" and resourceId != "" and text == "" and description != "" and \ bounds=="" and instance != -1: uiObject = self.u(className=className, resourceId=resourceId, description=description,instance=instance) # lack of text and instance if className != "" and resourceId != "" and text == "" and description != "" and \ bounds!="" and instance == -1: uiObject = self.u(className=className, resourceId=resourceId, description=description,bounds=bounds) # lack of description and bounds if className != "" and resourceId != "" and text != "" and description == "" and \ bounds=="" and instance != -1: uiObject = self.u(className=className, resourceId=resourceId, text = text, instance=instance) # lack of description and instance if className != "" and resourceId != "" and text != "" and description == "" and \ bounds!="" and instance == -1: uiObject = self.u(className=className, resourceId=resourceId, text = text, bounds=bounds) # lack of bounds and instance if className != "" and resourceId != "" and text != "" and description != "" and \ bounds=="" and instance == -1: uiObject = self.u(className=className, resourceId=resourceId, text = text, description=description) # 缺少3个元素 # lack of className and resourceId and text if className == "" and resourceId == "" and text == "" and description != "" and \ bounds!="" and instance != -1: uiObject = self.u(description=description, bounds=bounds,instance=instance) # lack of className and resourceId and description if className == "" and resourceId == "" and text != "" and description == "" and \ bounds!="" and instance != -1: uiObject = self.u(text = text,bounds=bounds,instance=instance) # lack of className and resourceId and bounds if className == "" and resourceId == "" and text != "" and description != "" and \ bounds =="" and instance != -1: uiObject = self.u(text = text,description=description,instance=instance) # lack of className and resourceId and instance if className == "" and resourceId == "" and text != "" and description != "" and \ bounds!="" and instance == -1: uiObject = self.u(text = text,description=description,bounds=bounds) # lack of className and text and description if className == "" and resourceId != "" and text == "" and description == "" and \ bounds!="" and instance != -1: uiObject = self.u(resourceId=resourceId,bounds=bounds,instance=instance) # lack of className and text and bounds if className == "" and resourceId != "" and text == "" and description != "" and \ bounds=="" and instance != -1: uiObject = self.u(resourceId=resourceId,description=description,instance=instance) # lack of className and text and instance if className == "" and resourceId != "" and text == "" and description != "" and \ bounds!="" and instance == -1: uiObject = self.u(resourceId=resourceId,description=description,bounds=bounds) # lack of className and description and bounds if className == "" and resourceId != "" and text != "" and description == "" and \ bounds=="" and instance != -1: uiObject = self.u(resourceId=resourceId, text = text,instance=instance) # lack of className and description and instance if className == "" and resourceId != "" and text != "" and description == "" and \ bounds!="" and instance == -1: uiObject = self.u(resourceId=resourceId, text = text,bounds=bounds) # lack of resourceId and text and description if className != "" and resourceId == "" and text == "" and description == "" and \ bounds!="" and instance != -1: uiObject = self.u(className=className,bounds=bounds,instance=instance) # lack of resourceId and text and bounds if className != "" and resourceId == "" and text == "" and description != "" and \ bounds=="" and instance != -1: uiObject = self.u(className=className,description=description,instance=instance) # lack of resourceId and text and instance if className != "" and resourceId == "" and text == "" and description != "" and \ bounds!="" and instance == -1: uiObject = self.u(className=className,description=description,bounds=bounds) # lack of resourceId and description and bounds if className != "" and resourceId == "" and text != "" and description == "" and \ bounds =="" and instance != -1: uiObject = self.u(className=className,text = text,instance=instance) # lack of resourceId and description and instance if className != "" and resourceId == "" and text != "" and description == "" and \ bounds!="" and instance == -1: uiObject = self.u(className=className, text = text,bounds=bounds) # lack of resourceId and bounds and instance if className != "" and resourceId == "" and text != "" and description != "" and \ bounds =="" and instance == -1: uiObject = self.u(className=className, text = text,description=description) # lack of text and description and bounds if className != "" and resourceId != "" and text == "" and description == "" and \ bounds=="" and instance != -1: uiObject = self.u(className=className, resourceId=resourceId,instance=instance) # lack of text and description and instance if className != "" and resourceId != "" and text == "" and description == "" and \ bounds!="" and instance == -1: uiObject = self.u(className=className, resourceId=resourceId,bounds=bounds) # lack of description and bounds and instance if className != "" and resourceId != "" and text != "" and description == "" and \ bounds =="" and instance == -1: uiObject = self.u(className=className, resourceId=resourceId, text = text) # 缺少4个元素 # lack of className and resourceId and text and description if className == "" and resourceId == "" and text == "" and description == "" and \ bounds !="" and instance != -1: uiObject = self.u(bounds=bounds,instance=instance) # lack of className and resourceId and text and bounds if className == "" and resourceId == "" and text == "" and description != "" and \ bounds =="" and instance != -1: uiObject = self.u(description=description,instance=instance) # lack of className and resourceId and text and instance if className == "" and resourceId == "" and text == "" and description != "" and \ bounds !="" and instance == -1: uiObject = self.u(description=description,bounds=bounds) # lack of className and resourceId and description and bounds if className == "" and resourceId == "" and text != "" and description == "" and \ bounds =="" and instance != -1: uiObject = self.u(text = text,instance=instance) # lack of className and resourceId and description and instance if className == "" and resourceId == "" and text != "" and description == "" and \ bounds !="" and instance == -1: uiObject = self.u(text = text,bounds=bounds) # lack of resourceid and text and description and bounds if className != "" and resourceId == "" and text == "" and description == "" and \ bounds =="" and instance != -1: uiObject = self.u(className=className,instance=instance) # lack of resourceid and text and description and instance if className != "" and resourceId == "" and text == "" and description == "" and \ bounds !="" and instance == -1: uiObject = self.u(className=className,bounds=bounds) # lack of text and description and bounds and instance if className != "" and resourceId != "" and text == "" and description == "" and \ bounds =="" and instance == -1: uiObject = self.u(className=className, resourceId=resourceId) # 缺少5个元素的 # only className if className != "" and resourceId == "" and text == "" and description == "" and \ bounds =="" and instance == -1: uiObject = self.u(className=className) # only resourceid if className == "" and resourceId != "" and text == "" and description == "" and \ bounds =="" and instance == -1: uiObject = self.u(resourceId=resourceId) # only text if className == "" and resourceId == "" and text != "" and description == "" and \ bounds =="" and instance == -1: uiObject = self.u(text = text) # only description if className == "" and resourceId == "" and text == "" and description != "" and \ bounds =="" and instance == -1: uiObject = self.u(description=description) # only bounds if className == "" and resourceId == "" and text == "" and description == "" and \ bounds !="" and instance == -1: uiObject = self.u(bounds=bounds) # only instance if className == "" and resourceId == "" and text == "" and description == "" and \ bounds =="" and instance == -1: uiObject = self.u(instance=instance) return uiObject except Exception, e: print "异常:",e return uiObject ''' 作用:判断当前界面中指定控件是否存在 参数: className:控件类型(例如:android.widget.TextView) resourceId:控件的Id(例如:lh.oslauncher:id/tv_title) text:控件上的文件(例如:'主页') description:控件描述 instance:实例参数 注意:className、resourceId、text 是利用WEditor工具查看控件属性看到的 例子:isExists = self.pyUIAutomator.isElementExist("android.widget.TextView", resourceId="com.apps.weather:id/today_tv_city", text='鞍山') ''' def isElementExist(self, className='', resourceId='', text='', description='', instance=-1): self.listenAdbConnected() try: uiObject = self.getUiObject(className, resourceId, text, description, instance) print 'uiObject:', uiObject if uiObject: isExists = uiObject.exists() print 'isExists:', isExists else: isExists = False except Exception, e: print e isExists = False return isExists ''' 作用:获取指定控件的文字属性 参数: className:控件类型(例如:android.widget.TextView) resourceId:控件的Id(例如:lh.oslauncher:id/tv_title) 注意:className、resourceId 是利用WEditor工具查看控件属性看到的 例子: str_play_time = self.pyUIAutomator.getTextById("android.widget.TextView", resourceId="com.apps.usbmediaplayer:id/playingTime") ''' def getTextById(self, className, resourceId): self.listenAdbConnected() try: text = self.u(className=className, resourceId=resourceId).get_text() except Exception, e: print e text = '' return text ''' 作用:执行adb命令 参数:adbCmd:要被执行的adb命令 注意:adbCmd不需要输入adb shell 例子:self.pyUIAutomator.adb_shell('am start -n com.apps.factory.ui/.designmenu.DesignMenuActivity') ''' def adb_shell(self, adbCmd): self.listenAdbConnected() try: self.u.adb_shell(adbCmd) except Exception, e: print e ''' 作用:设置android.widget.EditText 的文字内容 参数:text 要输入到EditText的文字内容 注意:保证焦点在该EditText,处于可以输入的状态 例子:self.pyUIAutomator.send_text(u'鞍山') ''' def send_text(self, text): self.listenAdbConnected() try: self.u.set_fastinput_ime(True) # 切换成FastInputIME输入法 self.u.clear_text() self.u.send_keys(unicode(text)) # adb广播输入 self.u.set_fastinput_ime(False) # 切换成正常的输入法 except Exception, e: print e ''' 作用:向电视机发送action动作 参数:actin 要执行的动作 注意:焦点在搜索界面输入框中,在搜索界面输入完搜索内容,执行该搜索动作 例子:小米谷歌商店搜索应用 搜索 keep应用 self.pyUIAutomator.send_text("kee") self.pyUIAutomator.send_action("search") ''' def send_action(self, action): self.listenAdbConnected() try: self.u.set_fastinput_ime(True) # 切换成FastInputIME输入法 self.u.send_action(action) self.u.set_fastinput_ime(False) # 切换成正常的输入法 except Exception, e: print e ''' 作用:保存界面截图到指定地址 参数:pngPath 要保存图片的全路径名称 注意:保证该图片路径所在文件夹存在 例子:self.pyUIAutomator.screenshot( r'E:\svn\SAT\SAT_Runner\btc_runner_se\runner\smoking\report\imgs\testUSB_Desk_Focused.png') ''' def screenshot(self, pngPath): self.listenAdbConnected() try: self.u.screenshot(pngPath) except Exception, e: print e ''' 作用:在Android 一个土司提示框 方便在电视机中看到跑到哪里 参数: toastText:土司的内容 toastTime:土司存留时间 注意: 例子:self.pyUIAutomator.makeToast('测试开始', 3) ''' def makeToast(self, toastText, toastTime=1): self.listenAdbConnected() try: self.u.make_toast(toastText, toastTime) except Exception, e: print e ''' 作用:启动指定包名的应用的指定Activity 参数: pkg_name:指定的包名 activity:指定的activity名 注意: 例子:self.pyUIAutomator.startApp('com.apps.usbmediaplayer') self.pyUIAutomator.startApp('com.apps.usbmediaplayer','com.apps.usbmediaplayer.activity.MainActivity') ''' def startApp(self, pkg_name, activity=None): self.listenAdbConnected() try: if activity: self.u.app_start(pkg_name, activity=activity) else: self.u.app_start(pkg_name) except Exception, e: print e ''' 作用:关闭某一指定包名的APP 参数: pkg_name:指定的包名 注意: 例子:self.pyUIAutomator.stopApp('com.apps.usbmediaplayer') ''' def stopApp(self, pkg_name): self.listenAdbConnected() try: self.u.app_stop(pkg_name) except Exception, e: print e ''' 作用:关闭Android系统中所有的运行程序 参数: 注意: 例子:self.pyUIAutomator.stopAllApps() ''' def stopAllApps(self): self.listenAdbConnected() try: self.u.app_stop_all() except Exception, e: print e ''' 作用:停止uiautomator守护程序,允许其他测试框架如 appium 运行 参数: 注意: 例子:self.pyUIAutomator.stopUIAutomatorService() ''' def stopUIAutomatorService(self): self.listenAdbConnected() try: self.u.service("uiautomator").stop() except Exception, e: print e ''' 作用:根据传入的chooseType,获取当前界面聚焦的对象 参数: 注意: 例子:self.pyUIAutomator.getChoosedUIObject("focus") ''' def getChoosedUIObject(self, chooseType, resourceId='', text=''): if chooseType.lower() == "focus": return self.getFocusedUIObject(resourceId=resourceId, text=text) elif chooseType.lower() == "select": return self.getSelectedUIObject(resourceId=resourceId, text=text) else: return None ''' 作用:获取当前界面的focused的UIObject对象 参数: 注意: 例子:self.pyUIAutomator.getFocusedUIObject() ''' def getFocusedUIObject(self, resourceId='', text=''): self.listenAdbConnected() focusedUIObject = None try: if resourceId != "" and text != "": focusedUIObject = self.u(resourceId=resourceId, text=text, focused=True) elif resourceId == "" and text != "": focusedUIObject = self.u(text=text, focused=True) elif resourceId != "" and text == "": focusedUIObject = self.u(resourceId=resourceId, focused=True) else: focusedUIObject = self.u(focused=True) except Exception, e: print "getFocusedUIObject Error:", e return focusedUIObject ''' 作用:获取当前界面的selected的UIObject对象 参数: 注意: 例子:self.pyUIAutomator.getFocusedUIObject() ''' def getSelectedUIObject(self, resourceId='', text=''): self.listenAdbConnected() try: if resourceId != "" and text != "": selectedUIObject = self.u(resourceId=resourceId, text=text, selected=True) elif resourceId == "" and text != "": selectedUIObject = self.u(text=text, selected=True) elif resourceId != "" and text == "": selectedUIObject = self.u(resourceId=resourceId, selected=True) else: selectedUIObject = self.u(selected=True) return selectedUIObject except Exception, e: print "getSelectedUIObject Error:", e return None ''' 作用:在脚本运行过程中,监听弹框的出现,如果出现则按按钮使其消失,使得脚本中获取元素控件时不被遮挡报错 参数:watcherName 监听器名称 dialogText 弹框的text buttonText 使弹框消失的按钮text 注意: 1.这个函数只是创建监听器,这些监听器生效使用u.startWatchers(),监听器失效使用u.stopWatchers(); 2.一般使用在脚本运行实际执行界面前 声明监听器、调用 u.startWatchers(),在脚本 结束时候调用u.stopWatchers() 例子:self.pyUIAutomator.getFocusedUIObject() u = PyUIAutomator() u.createDilogWatcher("Normal Dialog Watcher", "我是一个普通Dialog", "确定") u.startWatchers() #这30S延时代表脚本执行 time.sleep(30) u.stopWatchers() ''' def createDilogWatcher(self, watcherName, dialogText, buttonText): self.listenAdbConnected() self.u.watcher(watcherName).when(text=dialogText).click(text=buttonText) ''' 作用:使得创建的弹框监听器生效 参数: 注意: ''' def startWatchers(self): self.listenAdbConnected() self.u.watchers.watched = True ''' 作用:使得创建的弹框监听器失效 参数: 注意: ''' def stopWatchers(self): self.listenAdbConnected() self.u.watchers.watched = False def dump_hierarchy(self): self.listenAdbConnected() return self.u.dump_hierarchy() class DirectionManageAndroid(): def __init__(self): self.className = self.__class__.__name__ self.grid = None self.remote = TvOperator() def goOneStep(self, pyUIAutomator, direction, keyType = UATTree.Key_Event): # keyDict = {'U': "up", "D": "down", "L": "left", "R": "right"} print "goOneStep,direction:", direction directionArr = direction.split("-") for direct in directionArr: LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), u"script:sendKey 执行按键:%s" % (direct)) # self.custSendKey(keyDict[direct]) if keyType == UATTree.Key_Event: pyUIAutomator.pressKeyTimes(direct) elif keyType == UATTree.Key_IR: self.remote.sendKey(direct) # def custSendKey(self, keyEvent): # if type(keyEvent) == type('') or type(keyEvent) == type(u""): # self.operator.sendKey(keyEvent) # if type(keyEvent) == type([]): # try: # self.operator.sendKey(keyEvent[0], keyEvent[1], keyEvent[2]) # except Exception: # LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), # u'script:sendKey ERROR 双击按键数组%s格式出错,请按照格式:[按键字符,按键次数,按键间隔]' % ( # keyEvent)) ''' 计算目标区域在当前焦点区域的方位. | | 左上角 | 上 | 右上角 | | ----------------------------------- | | | | 左 | 焦点区域 | 右 | | ----------------------------------- | | 左下角 | 下 | 右下角 | | 区域的方位,按上图,分成4面位方向和4角位方向。 4面位方向:上、下、左、右。只要目标区域,和4面位方向区域有交集,就属于面位方向。 4角位方向:左上角、右上角、左下角、右下角。目标区域全部在4角位方向区域,才是角位方向。 方位定义:up、down、left、right、left-up、left-down、right-up、right-down :param areaL:当前焦点所在区域 :param areaT:目标区域 :return direction。字符串类型,方位:up、down、left、right、left-up、left-down、right-up、right-down ''' ImgRectLimit = 10000 def getTargetDirection(self, focusedBounds, destBounds, moveKey=["down","up"]): areaL = (focusedBounds['left'], focusedBounds['top'], focusedBounds['right'], focusedBounds['bottom']) areaT = (destBounds['left'], destBounds['top'], destBounds['right'], destBounds['bottom']) print "getTargetDirection:", areaL, areaT LoggingUtil.getDebugLogger().info(pyFileName, self.className, get_current_function_name(), u'script:focusMatch 当前聚焦区域坐标:%s 目标聚焦区域坐标:%s' % (str(areaL), str(areaT))) if self.hasSameArea(areaL,areaT): focusCP = [(areaL[2] + areaL[0])/2,(areaL[3]+areaL[1])/2] destCP = [(areaT[2] + areaT[0])/2,(areaT[3]+areaT[1])/2] return self.getPointDirection(focusCP,destCP) # 确定4个面位方位的区域的x,y轴范围 up = [areaL[0], 0, areaL[2], areaL[1]] down = [areaL[0], areaL[3], areaL[2], DirectionManageAndroid.ImgRectLimit] left = [0, areaL[1], areaL[0], areaL[3]] right = [areaL[2], areaL[1], DirectionManageAndroid.ImgRectLimit, areaL[3]] # 检测目标区域是否和4个面位方向区域有重叠部分 up_direct = self.hasSameArea(up, areaT) if up_direct: return "up" down_direct = self.hasSameArea(down, areaT) if down_direct: return "down" left_direct = self.hasSameArea(left, areaT) if left_direct: return "left" right_direct = self.hasSameArea(right, areaT) if right_direct: return "right" # 计算目标区域坐上角点的方位,x,y差值确定角位方向 dL_Ux = areaT[0] - areaL[0] dL_Uy = areaT[1] - areaL[1] if (dL_Ux > 0) and (dL_Uy > 0): if "down" in moveKey: return "down-right" else: return "right-down" if (dL_Ux < 0) and (dL_Uy > 0): if "down" in moveKey: return "down-left" else: return "left-down" if (dL_Ux > 0) and (dL_Uy < 0): if "down" in moveKey: return "up-right" else: return "right-up" if (dL_Ux < 0) and (dL_Uy < 0): if "down" in moveKey: return "up-left" else: return "left-up" ''' 计算点p2相对p1的方位。按p1 45°斜线划分上下左右。 ''' def getPointDirection(self,p1,p2): print "getPointDirection,p1,p2:",p1,p2 dx = p2[0] - p1[0] dy = p2[1] - p1[1] if dx >= 0 and abs(dy) < abs(dx): return "right" if dx <= 0 and abs(dy) < abs(dx): return "left" if dy >= 0 and abs(dy) >= abs(dx): return "down" if dy <= 0 and abs(dy) >= abs(dx): return "up" return "center" ''' 检测两个区域是否有重叠部分 ''' def hasSameArea(self, area1, area2): # 计算各区域宽、高 area1_W = area1[2] - area1[0] area1_H = area1[3] - area1[1] area2_W = area2[2] - area2[0] area2_H = area2[3] - area2[1] # 计算目标区域坐上角点的方位 dL_Ux = area1[0] - area2[0] dL_Uy = area1[1] - area2[1] # 计算x坐标,判断是否有重叠可能。 x_enable = False if dL_Ux > 0: if (abs(dL_Ux) - area2_W) >= 0: x_enable = False else: x_enable = True else: if (abs(dL_Ux) - area1_W) >= 0: x_enable = False else: x_enable = True # 计算y坐标,判断是否有重叠可能。 y_enable = False if dL_Uy > 0: if (abs(dL_Uy) - area2_H) >= 0: y_enable = False else: y_enable = True else: if (abs(dL_Uy) - area1_H) >= 0: y_enable = False else: y_enable = True # 如果x坐标、y坐标都有可能重叠,则一定重叠。反之,没有重叠。 return x_enable and y_enable def isSameBounds(self, BoundA, BoundB): areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom']) areaB = (BoundB['left'], BoundB['top'], BoundB['right'], BoundB['bottom']) if areaA[0] == areaB[0] \ and areaA[1] == areaB[1] \ and areaA[2] == areaB[2] \ and areaA[3] == areaB[3]: return True else: return False def isHasAnotherBounds(self, BoundA, BoundB): areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom']) areaB = (BoundB['left'], BoundB['top'], BoundB['right'], BoundB['bottom']) if areaA[0] <= areaB[0] and areaA[1] <= areaB[1] and areaA[2] >= areaB[2] and areaA[3] >= areaB[3]: return True else: return False def isRelativePositionBounds(self, initFocusedArea, initDestTextArea, BoundA, BoundB): # initFocusedArea = ( # initFocusedBound['left'], initFocusedBound['top'], initFocusedBound['right'], initFocusedBound['bottom']) # initDestTextArea = ( # initFocusedBound['left'], initDestTextBound['top'], initDestTextBound['right'], initDestTextBound['bottom']) areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom']) areaB = (BoundB['left'], BoundB['top'], BoundB['right'], BoundB['bottom']) # if areaA[0] < areaB[0] and areaA[1] < areaB[1] and areaA[2] > areaB[2] and areaA[3] >areaB[3]: # return True # else: # return False if initFocusedArea[0] - initDestTextArea[0] == areaA[0] - areaB[0] and initFocusedArea[1] - initDestTextArea[ 1] == areaA[1] - areaB[1] and initFocusedArea[2] - initDestTextArea[2] == areaA[2] - areaB[2] and \ initFocusedArea[3] - initDestTextArea[3] == areaA[3] - areaB[3]: return True else: return False # 比较两个Bound面积大小 def isBiggerBound(self, boundA, boundB): # print 'boundA:',boundA # print 'boundB:',boundB if ((boundA["bottom"] - boundA["top"]) * (boundA["right"] - boundA["left"])) >= (( boundB["bottom"] - boundB["top"]) * ( boundB["right"] - boundB["left"])): return True else: return False def getRelativeBounds(self, standBounds_view1, standBounds_view2, currentBound_view1): currentBound_view2 = {} currentBound_view2["top"] = standBounds_view2["top"] - standBounds_view1["top"] + currentBound_view1["top"] currentBound_view2["left"] = standBounds_view2["left"] - standBounds_view1["left"] + currentBound_view1["left"] currentBound_view2["right"] = standBounds_view2["right"] - standBounds_view1["right"] + currentBound_view1[ "right"] currentBound_view2["bottom"] = standBounds_view2["bottom"] - standBounds_view1["bottom"] + currentBound_view1[ "bottom"] return currentBound_view2 class FocusManageAndroid(): # u是PyUIAutomator对象 # focusManage 是DirectionManageAndroid对象 def __init__(self, u, directionManageAndroid): self.className = self.__class__.__name__ self.u = u self.directionManageAndroid = directionManageAndroid self.ReverseDirctionDict = {"up": "down", "down": "up", "left": "right", "right": "left"} self.featureDetect = FeatureDetect() self.ccard = CCardManager() self.imgCMP = ImageCMP() self.ocr = OCRConvert() self.remote = TvOperator() ''' 作用:在RecyclerView 中 focused控件包含文字控件 用于聚焦目标焦点 参数: text:目标文字的文字内容 className:目标文字的className findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: 小米信源设置界面 聚焦到Device Settings/Sound mode className = "android.widget.TextView" focusManageAndroid.toDestFocusByText_className_for_RecyclerView("News", className) ''' # 在RecyclerView 中 focused控件包含文字控件 def toDestFocusByText_className_for_RecyclerView(self, text, className, findDirection="down", Max_Try=10, keyType = UATTree.Key_Event): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) focusedUIObject = self.u.getFocusedUIObject() focusedBounds = focusedUIObject.info['bounds'] # print "focusedBounds:",focusedBounds destUIObject = self.u.getUiObject(text=text, className=className) if destUIObject: try: destBounds = destUIObject.info['bounds'] # print "destBounds:", destBounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print '成功聚焦到目标焦点:', text return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:在RecyclerView 中 focused控件包含文字控件 用于聚焦目标焦点 参数: text:目标文字的文字内容 textResourceId:目标文字的resourceId findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: 小米信源设置界面 聚焦到Device Settings focusManageAndroid.toDestFocusByText_for_RecyclerView("Device Settings","com.android.tv:id/title") ''' # 在RecyclerView 中 focused控件包含文字控件 def toDestFocusByText_for_RecyclerView(self, text, textResourceId="", findDirection="down", Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict={}): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) focusedUIObject = self.u.getFocusedUIObject() try: focusedBounds = focusedUIObject.info['bounds'] except Exception, e: print "未能获取到当前聚焦焦点,聚焦框丢失,判断聚焦失败!!" return False print "focusedBounds:",focusedBounds if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId) if destUIObject: try: destBounds = destUIObject.info['bounds'] print "destBounds:", destBounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print '成功聚焦到目标焦点:', text return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:在RecyclerView 中 focused控件包含文字控件 用于聚焦目标焦点,传入的resourceId为聚焦焦点的resId时的处理函数 参数: text:目标文字的文字内容 focuseResId:整个聚焦焦点的resourceId findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: 小米俄罗斯设置界面 存在复数个聚焦焦点时,使用此函数,可以规避复数焦点的问题 ''' def toDestFocusByText_with_FocuseResourceId(self, text, focuseResId, findDirection="down", Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict = {}): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): if count >= Max_Try and Reversecount >= Reverse_Max_Try: break if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) focusedUIObject = self.u.getFocusedUIObject(resourceId=focuseResId) try: focusedBounds = focusedUIObject.info['bounds'] except Exception, e: print "未能获取到当前聚焦焦点,聚焦框丢失,判断聚焦失败!!" return False print "focusedBounds:",focusedBounds if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) destUIObject = self.u.getUiObject(text=text) if destUIObject: try: destBounds = destUIObject.info['bounds'] print "destBounds:", destBounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print '成功聚焦到目标焦点:', text return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:在RecyclerVi或listView 中 selected控件包含文字控件 用于聚焦目标焦点 参数: text:目标文字的文字内容 textResourceId:目标文字的resourceId listViewResourceId:RecyclerVi或listView的resourceId findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: 2841项目信源列表聚焦 聚焦到HDMI1 focusManageAndroid.toDestSelectByText_for_RecyclerView("HDMI1","com.android.tv:id/title","com.android.tv:id/scene_transition_common") ''' def toDestSelectByText_for_RecyclerView(self, text, textResourceId='', optionViewResoucreId='', listViewResourceId='', findDirection="down", Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict={}): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): if count >= Max_Try and Reversecount >= Reverse_Max_Try: break if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) listViewUIObject = self.u.getUiObject(resourceId=listViewResourceId) print "listViewUIObject.info:", listViewUIObject.info if optionViewResoucreId != '': selectedUIObject = listViewUIObject.child(selected=True, resourceId=optionViewResoucreId) else: selectedUIObject = listViewUIObject.child(selected=True) print "selectedUIObject.info:", selectedUIObject.info selectedBounds = selectedUIObject.info['bounds'] # print "focusedBounds:",focusedBounds if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId) if destUIObject: destBounds = destUIObject.info['bounds'] # print "destBounds:", destBounds if self.directionManageAndroid.isHasAnotherBounds(selectedBounds, destBounds): print '成功聚焦到目标焦点:', text return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(selectedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:在界面中 中 focused控件包含content-des控件 用于聚焦目标焦点 参数: description:目标描述的文字内容 descriptionResourceId:目标描述的resourceId findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: 小米APP 聚焦到YouTube descriptionResourceId = "com.google.android.tvlauncher:id/banner_image" focusManageAndroid.toDestFocusByDescription_for_RecyclerView("YouTube",descriptionResourceId=descriptionResourceId) ''' # 在RecyclerView 中 focused控件包含文字控件 def toDestFocusByDescription_for_RecyclerView(self, description, descriptionResourceId="", findDirection="down", Max_Try=10 ,keyType = UATTree.Key_Event, hb_keyDict = {}): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): print "count:", count if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) focusedUIObject = self.u.getFocusedUIObject() focusedBounds = focusedUIObject.info['bounds'] # print "focusedBounds:",focusedBounds if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) destUIObject = self.u.getUiObject(description=description, resourceId=descriptionResourceId) if destUIObject: try: destBounds = destUIObject.info['bounds'] # print "destBounds:", destBounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print '成功聚焦到目标焦点:', description return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception, e: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False # 在RecyclerView 中 focused控件包含bounds控件 def toDestFocusByBounds_for_RecyclerView(self, bounds, boundsResourceId="", findDirection="down", Max_Try=10, keyType=UATTree.Key_Event, hb_keyDict={}): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): print "count:", count if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) focusedUIObject = self.u.getFocusedUIObject() focusedBounds = focusedUIObject.info['bounds'] if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) try: destBounds = bounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds,destBounds): print "success to focus the target:",bounds return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds,destBounds) self.directionManageAndroid.goOneStep(self.u,direction,keyType) except Exception,e: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False # class&Bounds def toDestFocusByClassBounds_for_RecyclerView(self, className, bounds,classResourceId="", findDirection="down", Max_Try=10, keyType=UATTree.Key_Event, hb_keyDict={}): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): print "count:", count if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) focusedUIObject = self.u.getFocusedUIObject() print "zhouyifocusedUIObject",focusedUIObject print "zhouyifocusedUIObject.info", focusedUIObject.info if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) destUIObject = self.u.getUiObject(className=className, bounds = bounds,resourceId=classResourceId) print "destUIObject",destUIObject try: destUIObjectInfo = destUIObject.info print "destUIObjectInfo",destUIObjectInfo except Exception,e: print "异常:",e # print "destUIObject.info",destUIObjectInfo try: # destBounds = destUIObject.info['bounds'] destBounds = bounds print "destBounds:", destBounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds,destBounds): print "c&b success to focus the target:",bounds return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds,destBounds) self.directionManageAndroid.goOneStep(self.u,direction,keyType) except Exception,e: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False # class def toDestFocusByClass_for_RecyclerView(self, className, classResourceId="", findDirection="down", Max_Try=10, keyType=UATTree.Key_Event, hb_keyDict={}): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): print "count:", count if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) focusedUIObject = self.u.getFocusedUIObject() print "zhouyifocusedUIObject", focusedUIObject print "zhouyifocusedUIObject.info", focusedUIObject.info if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) destUIObject = self.u.getUiObject(className=className, resourceId=classResourceId) print "destUIObject", destUIObject try: destUIObjectInfo = destUIObject.info print "destUIObjectInfo", destUIObjectInfo except Exception, e: print "异常:", e # print "destUIObject.info",destUIObjectInfo try: destBounds = destUIObject.info['bounds'] print "destBounds:", destBounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print '成功聚焦到目标焦点:', bounds return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception, e: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False # zhouyi def toDestTargetByBounds_for_RecyclerView(self, bounds, chooseType, Max_Try=20, keyType=UATTree.Key_Event, findFocusCount=0, hb_keyDict={}): count = 0 while (True): if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) # targetUIObject = self.u.getUiObject(bounds=bounds) # print "targetUIObject",targetUIObject # print "zhouyibounds",bounds # print "targetUIObject.info:", targetUIObject.info # destBounds = targetUIObject.info['bounds'] destBounds = bounds print "目标坐标:", destBounds if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) try: if chooseType.lower() == "focus": choosingUIObject = self.u.getFocusedUIObject() objBounds = choosingUIObject.info['bounds'] elif chooseType.lower() == "select": choosingUIObject = self.u.getSelectedUIObject() objBounds = choosingUIObject.info['bounds'] print "当前坐标:", objBounds except Exception, e: print "获取焦点失败!Error:", e if findFocusCount < 3: self.u.pressKeyTimes("down") return self.toDestTargetByBounds_for_RecyclerView(bounds, chooseType, findFocusCount=findFocusCount + 1) else: print "尝试%s次操作!!!仍未能找到聚焦点!!!无法聚焦到目标" % findFocusCount return False if self.directionManageAndroid.isHasAnotherBounds(objBounds, destBounds): print "已聚焦至目标组件!!!" return True else: if count >= Max_Try: print "已尝试至最大次数%s次,仍未能聚焦至目标组件!!!" % Max_Try return False direction = self.directionManageAndroid.getTargetDirection(objBounds, destBounds) print "目标方位:", direction self.directionManageAndroid.goOneStep(self.u, direction, keyType) count += 1 ''' 作用:在界面中 中 focused控件中既没有包含content-des控件属性,也没有text控件属性, 采用单张图片模板匹配 用于聚焦目标焦点 参数: singleImagePath:目标聚焦的单张小图片的地址 findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: 小米内销APP(既没有文字text属性也没有描述属性content-des) 聚焦到ATX APP singleImagePath = "D:\\ATX_APP.jpg" focusManageAndroid.toDestFocusBySingleImage_for_RecyclerView(singleImagePath) ''' # 在RecyclerView 中 focused控件包含文字控件 def toDestFocusBySingleImage_for_RecyclerView(self, singleImagePath, findDirection="down", Max_Try=10 ,keyType = UATTree.Key_Event): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): print "count:", count if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) focusedUIObject = self.u.getFocusedUIObject() focusedBounds = focusedUIObject.info['bounds'] srceenImgPath = os.path.join(sat_environment.getSATTmpDIR(), "SingleImage_runpath.png") self.ccard.takePicture(srceenImgPath) resultDict = self.featureDetect.matchSingleImage(srceenImgPath, [0, 0, 1920, 1080], singleImagePath) # destUIObject = self.u.getUiObject(description=description, resourceId=descriptionResourceId) if resultDict: try: destBounds_coordinate = resultDict['coordinate'] # areaA = (BoundA['left'], BoundA['top'], BoundA['right'], BoundA['bottom']) destBounds = {"left": destBounds_coordinate[0], "top": destBounds_coordinate[1], "right": destBounds_coordinate[2], "bottom": destBounds_coordinate[3]} # print "destBounds:", destBounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print '成功聚焦到目标焦点:', singleImagePath return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception, e: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:在界面中 中 focused控件中既没有包含content-des控件属性,也没有text控件属性, 采用OCR识别文字 用于聚焦目标焦点 参数: text:需要OCR识别的文字 textResourceId:文字区域的resourceId recyclerViewResourceId:列表recyclerView的resourceId ocrTextBorder:为OCR识别更准确,文字识别坐标需要扩充的边界的值(默认为10) OCRDict:OCR识别文字的属性 默认为百度中英文识别 findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: ''' # 在RecyclerView 中 focused控件包含文字控件 def toDestFocusByOCRText_for_RecyclerView(self, text, textResourceId, recyclerViewResourceId, ocrTextBorder=10, OCRDict={"OCR_lan": "CHN_ENG", "OCR_type": 10000}, findDirection="down", Max_Try=10, keyType = UATTree.Key_Event): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): print "count:", count if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) focusedUIObject = self.u.getFocusedUIObject() focusedBounds = focusedUIObject.info['bounds'] # 寻找目标焦点坐标 childbound = None recyclerViewUIObject = self.u.getUiObject(resourceId=recyclerViewResourceId) for i in range(recyclerViewUIObject.info["childCount"]): childUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=textResourceId) try: childbound = childUIObject.info["bounds"] # 对扩大后的图片坐标进行边界判断 childbound_Area_left = childbound["left"] - 10 if childbound_Area_left < 0: childbound_Area_left = 0 childbound_Area_top = childbound["top"] - 10 if childbound_Area_top < 0: childbound_Area_top = 0 childbound_Area_right = childbound["right"] + 10 if childbound_Area_right > 1920: childbound_Area_right = 1920 childbound_Area_bottom = childbound["bottom"] - 10 if childbound_Area_bottom > 1080: childbound_Area_bottom = 1080 childbound_Area = (childbound_Area_left, childbound_Area_top, childbound_Area_right, childbound_Area_bottom) # 小图保存地址 tmpPic = os.path.join(getSATTmpDIR(), "uiautomator_text_ocr.png") # 整个界面图片保存地址 srceenImgPath = os.path.join(sat_environment.getSATTmpDIR(), "uiautomator_OCR_runpath.png") self.u.screenshot(srceenImgPath) # self.ccard.takePicture(srceenImgPath) # 切割小图片 self.imgCMP.saveCropPic(srceenImgPath, tmpPic, ( childbound_Area[0], childbound_Area[1], childbound_Area[2], childbound_Area[3])) # ocr 识别 textValueCUR = self.ocr.getStr(tmpPic, OCRDict["OCR_lan"], OCRDict["OCR_type"]) print "textValueCUR:", textValueCUR print "destText:", text if self.ocr.cmpOcrStr(textValueCUR, text): break else: childbound = None except Exception: childbound = None pass if childbound: try: destBounds = childbound if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print '成功聚焦到目标焦点:', text return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception, e: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:在界面中 中 focused控件中既没有包含content-des控件属性,也没有text控件属性, 一般这种控件是自定义控件 class类型是android.view.View 采用OCR识别文字 用于聚焦目标焦点 参数: text:需要OCR识别的文字 visableViewResourceId:可以通过UIAutomator框架获取到的控件的id initTextArea: 确定的一个区域下的文字坐标 initVisableViewArea:确定的一个区域下的可获取控件的坐标 ocrTextBorder:为OCR识别更准确,文字识别坐标需要扩充的边界的值(默认为10) OCRDict:OCR识别文字的属性 默认为百度中英文识别 findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子:小米内销APP测试 #文字内容 text = "应用商店" #图标的resourceId visableViewResourceId = "com.mitv.tvhome:id/di_img" #非聚焦的app文字坐标 initTextArea = [90, 392, 229, 424] #非聚焦的app图片坐标 initVisableViewArea = [90, 257, 227, 382] focusManageAndroid.toDestFocusRelativeOCRText__for_RecyclerView(text=text, visableViewResourceId=visableViewResourceId, initTextArea=initTextArea, initVisableViewArea=initVisableViewArea) ''' # 在RecyclerView 中 focused控件包含文字控件 def toDestFocusRelativeOCRText__for_RecyclerView(self, text, visableViewResourceId, initTextArea, initVisableViewArea, ocrTextBorder=5, OCRDict={"OCR_lan": "CHN_ENG", "OCR_type": 10000}, findDirection="down", Max_Try=10, keyType = UATTree.Key_Event): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 initTextBounds = {"left": initTextArea[0], "top": initTextArea[1], "right": initTextArea[2], "bottom": initTextArea[3]} initvisableViewBounds = {"left": initVisableViewArea[0], "top": initVisableViewArea[1], "right": initVisableViewArea[2], "bottom": initVisableViewArea[3]} while (True): print "count:", count if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) focusedUIObject = self.u.getFocusedUIObject() focusedBounds = focusedUIObject.info['bounds'] # 寻找目标焦点坐标 childbound = None childCountUIObject = self.u.getUiObject(resourceId=visableViewResourceId) print "childCountUIObject.info", childCountUIObject.info childCount = childCountUIObject.count print "列表包含的子控件数量:", childCount for i in range(childCount): print "第%s次查找目标bounds" % (str(i)) visableViewObject = self.u.getUiObject(instance=i, resourceId=visableViewResourceId) try: visableViewBound = visableViewObject.info["bounds"] print "initvisableViewBounds:", initvisableViewBounds print "initTextBounds:", initTextBounds print "visableViewBound:", visableViewBound childbound = self.directionManageAndroid.getRelativeBounds(initvisableViewBounds, initTextBounds, visableViewBound) print "childbound:", childbound # return False # childbound = childUIObject.info["bounds"] # 对扩大后的图片坐标进行边界判断 childbound_Area_left = childbound["left"] - 10 if childbound_Area_left < 0: childbound_Area_left = 0 childbound_Area_top = childbound["top"] - 10 if childbound_Area_top < 0: childbound_Area_top = 0 childbound_Area_right = childbound["right"] + 10 if childbound_Area_right > 1920: childbound_Area_right = 1920 childbound_Area_bottom = childbound["bottom"] + 10 if childbound_Area_bottom > 1080: childbound_Area_bottom = 1080 childbound_Area = [childbound_Area_left, childbound_Area_top, childbound_Area_right, childbound_Area_bottom] print "childbound_Area:", childbound_Area # 小图保存地址 tmpPic = os.path.join(getSATTmpDIR(), "uiautomator_text_ocr.png") # 整个界面图片保存地址 srceenImgPath = os.path.join(sat_environment.getSATTmpDIR(), "uiautomator_OCR_runpath.png") self.u.screenshot(srceenImgPath) # self.ccard.takePicture(srceenImgPath) # 切割小图片 self.imgCMP.saveCropPic(srceenImgPath, tmpPic, ( childbound_Area[0], childbound_Area[1], childbound_Area[2], childbound_Area[3])) # ocr 识别 textValueCUR = self.ocr.getStr(tmpPic, OCRDict["OCR_lan"], OCRDict["OCR_type"]) print "textValueCUR:", textValueCUR print "destText:", text if self.ocr.cmpOcrStr(textValueCUR, text): break else: childbound = None except Exception, e: print e childbound = None pass print "childbound:", childbound # return False if childbound: try: destBounds = childbound if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print '成功聚焦到目标焦点:', text return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception, e: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:在RecyclerView 中 focused控件和文字控件同级 相对位置固定 用于聚焦目标焦点 参数: text:目标文字的文字内容 textResourceId:目标文字的resourceId initFocusedArea:聚焦情况下的focused控件的坐标区域(x1,y1,x2,y2) initDestTextArea:聚焦情况下的text控件的坐标区域(x1,y1,x2,y2) findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: 小米信源设置 的声音自定义界面 Device /Settings Sound mode /Custom/ initFocusedArea = (822, 180, 1259, 221) initDestTextArea = (822, 116, 1259, 180) focusManageAndroid.toDestFocusRelativeText_for_RecyclerView("3500Hz: 0dB", "com.android.tv:id/title", initFocusedArea, initDestTextArea) ''' # 在RecyclerView 中 focused控件和文字控件同级 def toDestFocusRelativeText_for_RecyclerView(self, text, textResourceId, initFocusedArea, initDestTextArea, findDirection="down", Max_Try=10, keyType = UATTree.Key_Event): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) focusedUIObject = self.u.getFocusedUIObject() focusedBounds = focusedUIObject.info['bounds'] # print "focusedBounds:",focusedBounds destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId) if destUIObject: try: destBounds = destUIObject.info['bounds'] # print "destBounds:", destBounds if self.directionManageAndroid.isRelativePositionBounds(initFocusedArea, initDestTextArea, focusedBounds, destBounds): print '成功聚焦到目标焦点:', text return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:聚焦效果View控件 包含 目标文本控件 用于聚焦目标焦点 参数: text:目标文字的文字内容 textResourceId:目标文字的resourceId FocusViewResourceId:聚焦效果View的resourceId findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: 小米USB界面上方Tab聚焦方法 focusManageAndroid.toDestFocusByText_for_FocusView("Devices", "com.xiaomi.mitv.mediaexplorer:id/dev", "com.xiaomi.mitv.mediaexplorer:id/tab_cursor", findDirection="right", Max_Try=5) ''' # 聚焦效果View控件 包含 目标文本控件 def toDestFocusByText_for_FocusView(self, text, FocusViewResourceId='', FocusViewClass='',FocusViewDesc='',findDirection="down", Max_Try=10, keyType = UATTree.Key_Event, hb_keyDict={}): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) if FocusViewResourceId is not '': focusedUIObject = self.u.getUiObject(resourceId=FocusViewResourceId) else: focusedUIObject = self.u.getUiObject(description=FocusViewDesc,className=FocusViewClass) focusedBounds = focusedUIObject.info['bounds'] print "focusedBounds:", focusedBounds if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) destUIObject = self.u.getUiObject(text=text) if destUIObject: try: destBounds = destUIObject.info['bounds'] print "destBounds:", destBounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print '成功聚焦到目标焦点:', text return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection] ,keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection] ,keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:RecyclerView 中焦点没有focused属性 只是子项中的View有放大效果 参数: text:目标文字的文字内容 textResourceId:目标文字的resourceId zoomViewResourceId:f放大效果View的resourceId recyclerViewResourceId:列表recyclerView 的resourceId findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查找次数 注意: 例子: 小米USB Images 浏览界面 focusManageAndroid.toDestZoomByText_for_RecyclerView("Pattern", "com.xiaomi.mitv.mediaexplorer:id/album_item_name", "com.xiaomi.mitv.mediaexplorer:id/album_item_bg", "com.xiaomi.mitv.mediaexplorer:id/all_albums_view") ''' # RecyclerView 中焦点没有focused属性 只是子项中的View有放大效果 def toDestZoomByText_for_RecyclerView(self, text, textResourceId, zoomViewResourceId, recyclerViewResourceId, findDirection="down", Max_Try=20, keyType=UATTree.Key_Event): # 按照传入的方向寻找Max_Try次,如果仍未聚焦到选中area,则按照传入方向的反方向 反向寻找 2*Max_Try 次 count = 0 Max_Try = Max_Try Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) # focusedUIObject = self.u.getUiObject(resourceId=FocusViewResourceId) # focusedBounds = focusedUIObject.info['bounds'] # print "focusedBounds:", focusedBounds destUIObject = self.u.getUiObject(text=text, resourceId=textResourceId) if destUIObject: try: # 获取目标文本的bounds destBounds = destUIObject.info['bounds'] # print "destBounds:", destBounds zoomUiObject = self.u.getUiObject(resourceId=recyclerViewResourceId) # print "uiObject.info: ", uiObject.info childCount = zoomUiObject.info['childCount'] zoomBound = {u'top': 0, u'left': 0, u'right': 0, u'bottom': 0} zoomIndex = -1 for i in range(childCount): childObject = zoomUiObject.child_by_instance(i, resourceId=zoomViewResourceId) # print "childObject.info:", childObject.info childounds = childObject.info['bounds'] # print "child:", str(i), "bounds:", childounds if self.directionManageAndroid.isBiggerBound(childounds, zoomBound): zoomBound = childounds zoomIndex = i zoomTextBound = zoomUiObject.child_by_instance(i, resourceId=textResourceId).info[ "bounds"] zoomText = zoomUiObject.child_by_instance(i, resourceId=textResourceId).info["text"] # print zoomBound, zoomIndex, zoomTextBound, zoomText print "toDestZoomByText_for_RecyclerView 当前聚焦文字:", zoomText if str(zoomText) == str(text): print '成功聚焦到目标焦点:', text return True # if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): # print '成功聚焦到目标焦点:', text # return True else: count = count + 1 direction = self.directionManageAndroid.getTargetDirection(zoomTextBound, destBounds) self.directionManageAndroid.goOneStep(self.u, direction, keyType) except Exception: # 出现控件出现一半的时候,获取控件信息会报错 if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) # 如果界面中没有目标文字的控件出现,则按照传入的方向寻找Max_Try次;仍然未找到 则反方向寻找2*Max_Try次 else: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到目标焦点!!" % (str(Max_Try)) return False ''' 作用:获取RecyclerView 中所有文字名称的列表 参数: recyclerViewResourceId:列表recyclerView 的resourceId textResourceId:item中文本区域的的resourceId findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查看次数,执行Max_Try 次findDirection 后 仍无新控件被刷出 则返回所有文件的列表 name_list:最终返回的列表 注意: 例子: 小米USB Music 浏览界面 获取所有的音乐文件 recyclerViewResourceId = "com.xiaomi.mitv.mediaexplorer:id/all_music_view" textResourceId = "com.xiaomi.mitv.mediaexplorer:id/item_title" recyclerViewItemNameList = focusManageAndroid.getRecyclerViewItemNameList(recyclerViewResourceId,textResourceId) print "recyclerViewItemNameList:", recyclerViewItemNameList ''' def getRecyclerViewItemNameList(self, recyclerViewResourceId, textResourceId, findDirection="down", Max_Try=10, name_list=[], keyType = UATTree.Key_Event): count = 0 isNOTChange = False old_name_list = name_list while (True): if count > Max_Try and isNOTChange: return name_list break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) recyclerViewUIObject = self.u.getUiObject(resourceId=recyclerViewResourceId) for i in range(recyclerViewUIObject.info["childCount"]): childUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=textResourceId) try: childText = childUIObject.info["text"] if childText not in name_list: name_list.append(childUIObject.info["text"]) except Exception: pass if old_name_list == name_list: isNOTChange = True count = count + 1 self.pressKeyByType(findDirection, keyType) else: isNOTChange = False self.pressKeyByType(findDirection, keyType) self.getRecyclerViewItemNameList(recyclerViewResourceId, textResourceId, findDirection, Max_Try, name_list) ''' 作用:获取RecyclerView 中所有文字名称的列表 参数: keyText:寻找目标Text的key的Text keyResourceId:寻找目标Text的key的resourceId recyclerViewResourceId:列表recyclerView 的resourceId textResourceId:目标text的中文本区域的的resourceId findDirection: 目标文字控件被隐藏时寻找的方向 Max_Try:最多查看次数,执行Max_Try 次findDirection 后 仍无新控件被刷出 则返回"" 注意: 例子: 小米USB Music 音乐播放界面 通过音乐名称获取音乐时长 keyResourceId = "com.xiaomi.mimusic2:id/item_textview" recyclerViewResourceId = "com.xiaomi.mimusic2:id/music_list_view" textResourceId = "com.xiaomi.mimusic2:id/music_list_item_duration" findText = focusManageAndroid.getRecyclerViewItemTextByKeyText("鸭子", keyResourceId, recyclerViewResourceId, textResourceId) print "findText:", findText ''' def getRecyclerViewItemTextByKeyText(self, keyText, keyResourceId, recyclerViewResourceId, textResourceId, findDirection="down", Max_Try=20, keyType = UATTree.Key_Event): count = 0 Reversecount = 0 Reverse_Max_Try = Max_Try * 2 while (True): if count >= Max_Try and Reversecount >= Reverse_Max_Try: break # 等待聚焦效果刷新完成,稳定之后再获取相关的属性 time.sleep(0.5) recyclerViewUIObject = self.u.getUiObject(resourceId=recyclerViewResourceId) iCounnt = -1 findText = "" print "recyclerViewUIObject.info:", recyclerViewUIObject.info for i in range(recyclerViewUIObject.info["childCount"]): print "i:", i try: childUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=keyResourceId) if str(childUIObject.info["text"]) == str(keyText): iCounnt = i findUIObject = recyclerViewUIObject.child_by_instance(i, resourceId=textResourceId) findText = findUIObject.info["text"] return str(findText) except Exception: iCounnt = -1 pass if iCounnt == -1: if count < Max_Try: count = count + 1 self.pressKeyByType(findDirection, keyType) else: Reversecount = Reversecount + 1 self.pressKeyByType(self.ReverseDirctionDict[findDirection], keyType) print "执行%s 次查找,仍未找到keyText:%s!!" % (str(Max_Try), str(keyText)) return "" def focusFirstItemFromRecyclerView(self, recyclerView_resId, child_class, Max_Try=20, keyType = UATTree.Key_Event): return self.focusItemByIndexFromRecyclerView(recyclerView_resId, child_class, 0, Max_Try, keyType) def focusItemByIndexFromRecyclerView(self, recyclerView_resId, child_class, target_index, Max_Try=20, keyType = UATTree.Key_Event, hb_keyDict={}): count = 0 recyclerView = self.u.getUiObject(resourceId=recyclerView_resId) while(True): if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) firstItemUIObject = recyclerView.child_by_instance(target_index, className = child_class) print "firstItemUIObject.info:", firstItemUIObject.info if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) destBounds = firstItemUIObject.info['bounds'] print "目标坐标:", destBounds focusedUIObject = self.u.getFocusedUIObject() focusedBounds = focusedUIObject.info['bounds'] print "当前坐标:",focusedBounds if self.directionManageAndroid.isHasAnotherBounds(focusedBounds, destBounds): print "已将焦点归位至第一位!!!" return True else: if count >= Max_Try: print "已尝试至最大次数%s次,仍未能使焦点归位至第一位!!!"%Max_Try return False direction = self.directionManageAndroid.getTargetDirection(focusedBounds, destBounds) print "目标方位:",direction self.directionManageAndroid.goOneStep(self.u, direction, keyType) count += 1 def focusTargetSeekBar(self, target_resId, chooseType, Max_Try=20, keyType = UATTree.Key_Event, findFocusCount = 0): count = 0 while(True): targetUIObject = self.u.getUiObject(resourceId=target_resId) print "targetUIObject.info:", targetUIObject.info destBounds = targetUIObject.info['bounds'] print "目标坐标:", destBounds try: if chooseType.lower() == "focus": choosingUIObject = self.u.getFocusedUIObject() objBounds = choosingUIObject.info['bounds'] elif chooseType.lower() == "select": choosingUIObject = self.u.getSelectedUIObject() objBounds = choosingUIObject.info['bounds'] print "当前坐标:",objBounds except Exception,e: print "获取焦点失败!Error:",e if findFocusCount < 3: self.u.pressKeyTimes("down") return self.focusTargetSeekBar(target_resId, chooseType, findFocusCount = findFocusCount + 1) else: print "尝试%s次操作!!!仍未能找到聚焦点!!!无法聚焦到目标"%findFocusCount return False if self.directionManageAndroid.isHasAnotherBounds(objBounds, destBounds): print "已聚焦至目标seekbar!!!" return True else: if count >= Max_Try: print "已尝试至最大次数%s次,仍未能聚焦至目标seekbar!!!"%Max_Try return False direction = self.directionManageAndroid.getTargetDirection(objBounds, destBounds) print "目标方位:",direction self.directionManageAndroid.goOneStep(self.u, direction, keyType) count += 1 def toDestTargetByResourceId_for_RecyclerView(self, resourceId, chooseType, Max_Try=20, keyType = UATTree.Key_Event, findFocusCount = 0, hb_keyDict={}): count = 0 while(True): if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) targetUIObject = self.u.getUiObject(resourceId=resourceId) print "targetUIObject.info:", targetUIObject.info destBounds = targetUIObject.info['bounds'] print "目标坐标:", destBounds if hb_keyDict != {}: # 执行心跳按键 self.executeHeartBeatKey(hb_keyDict) try: if chooseType.lower() == "focus": choosingUIObject = self.u.getFocusedUIObject() objBounds = choosingUIObject.info['bounds'] elif chooseType.lower() == "select": choosingUIObject = self.u.getSelectedUIObject() objBounds = choosingUIObject.info['bounds'] print "当前坐标:",objBounds except Exception,e: print "获取焦点失败!Error:",e if findFocusCount < 3: self.u.pressKeyTimes("down") return self.toDestTargetByResourceId_for_RecyclerView(resourceId, chooseType, findFocusCount = findFocusCount + 1) else: print "尝试%s次操作!!!仍未能找到聚焦点!!!无法聚焦到目标"%findFocusCount return False if self.directionManageAndroid.isHasAnotherBounds(objBounds, destBounds): print "已聚焦至目标组件!!!" return True else: if count >= Max_Try: print "已尝试至最大次数%s次,仍未能聚焦至目标组件!!!"%Max_Try return False direction = self.directionManageAndroid.getTargetDirection(objBounds, destBounds) print "目标方位:",direction self.directionManageAndroid.goOneStep(self.u, direction, keyType) count += 1 def pressKeyByType(self, keyName, keyType = UATTree.Key_Event, times = 1, duration = 1.0): print "pressKeyByType:",keyName,keyType if keyType == UATTree.Key_Event: return self.u.pressKeyTimes(keyName, times, duration) elif keyType == UATTree.Key_IR: return self.remote.sendKey(keyName, times, duration) else: return False ''' 执行心跳按键的函数。传入UATree里parent['others']['heartbeat_key']中的参数,会根据参数的配置执行心跳按键 ''' def executeHeartBeatKey(self, hb_keyDict): try: eventList = hb_keyDict["event"] irList = hb_keyDict["ir"] if eventList != "": LoggingUtil.printLog("Executing heartbeat_key by EventKey!!!KeyList:%s" % eventList) for key in eventList: self.u.pressKeyTimes(key) elif irList != "": LoggingUtil.printLog("Executing heartbeat_key by irKey!!!KeyList:%s" % irList) for key in irList: self.remote.sendKey(key) except Exception,e: LoggingUtil.printLog(u"[executeHeartBeatKey]: 心跳按键配置异常,无法正常执行心跳按键。\n %s"%e) if __name__ == "__main__": u = PyUIAutomator() dm = DirectionManageAndroid() fm = FocusManageAndroid(u, dm) fm.toDestFocusByText_with_FocuseResourceId(text="Picture", focuseResId="com.mediatek.wwtv.tvcenter:id/menu_item_frame") # directionMange = DirectionManageAndroid() # focusManage = FocusManageAndroid(u, directionMange) # focusObj = focusManage.u.getFocusedUIObject() # print "focusObj.info:",focusObj.info # childCount = focusObj.info["childCount"] # for i in range(childCount): # childUI = focusObj.child_by_instance(i, className = "android.widget.TextView") # print childUI.info["text"] # focusManage.toDestFocusByText_for_RecyclerView(text="Picture mode",textResourceId="", # findDirection="down") # focusManage.focusTargetSeekBar("com.android.tv.settings:id/seekbar_freq_300") # recyclerViewUIObject = u.getUiObject(resourceId="com.xiaomi.mitv.mediaexplorer:id/photo_list") # print "recyclerViewUIObject.info", recyclerViewUIObject.info # for i in range(recyclerViewUIObject.info["childCount"]): # childUI = recyclerViewUIObject.child_by_instance(i) # print "cound %s child.info:"%i, childUI.info # focusManage.focusFirstItemFromRecyclerView(recyclerViewUIObject, child_class="android.widget.LinearLayout") # focusedUIObject = pyui.getFocusedUIObject() # print 'focusedUIObject:', focusedUIObject # if focusedUIObject: # print focusedUIObject.info