def send(self, device): if self.view is not None: from device_state import DeviceState width = DeviceState.get_view_width(view_dict=self.view) height = DeviceState.get_view_height(view_dict=self.view) else: width = device.get_width() height = device.get_height() x, y = UIEvent.get_xy(x=self.x, y=self.y, view=self.view) if not x or not y: # If no view and no coordinate specified, use the screen center coordinate x = width / 2 y = height / 2 start_x, start_y = x, y end_x, end_y = x, y duration = 500 if self.direction == "UP": start_y -= height * 2 / 5 end_y += height * 2 / 5 elif self.direction == "DOWN": start_y += height * 2 / 5 end_y -= height * 2 / 5 elif self.direction == "LEFT": start_x -= width * 2 / 5 end_x += width * 2 / 5 elif self.direction == "RIGHT": start_x += width * 2 / 5 end_x -= width * 2 / 5 device.view_drag((start_x, start_y), (end_x, end_y), duration) return True
def get_xy(x, y, view): if x and y: return x, y if view: from device_state import DeviceState return DeviceState.get_view_center(view_dict=view) return x, y
def get_current_state(self): self.logger.debug("getting current device state...") current_state = None try: views = self.get_views() foreground_activity = self.get_top_activity_name() activity_stack = self.get_current_activity_stack() background_services = self.get_service_names() screenshot_path = self.take_screenshot() self.logger.debug("finish getting current device state...") from device_state import DeviceState current_state = DeviceState( self, views=views, foreground_activity=foreground_activity, activity_stack=activity_stack, background_services=background_services, screenshot_path=screenshot_path) except Exception as e: self.logger.warning("exception in get_current_state: %s" % e) import traceback traceback.print_exc() self.logger.debug("finish getting current device state...") self.last_know_state = current_state if not current_state: self.logger.warning("Failed to get current state!") return current_state
def generate_event_based_on_utg(self): """ generate an event based on current device state note: ensure these fields are properly maintained in each transaction: last_event_flag, last_touched_view, last_state, exploited_views, state_transitions @return: AppEvent """ self.save_state_transition(self.last_event_str, self.last_state, self.current_state) if self.device.is_foreground(self.app): # the app is in foreground, clear last_event_flag self.last_event_flag = EVENT_FLAG_STARTED else: number_of_starts = self.last_event_flag.count(EVENT_FLAG_START_APP) # If we have tried too many times but the app is still not started, stop DroidBot if number_of_starts > START_RETRY_THRESHOLD: raise InputInterruptedException("The app cannot be started.") # if app is not started, try start it if self.last_event_flag.endswith(EVENT_FLAG_START_APP): # It seems the app stuck at some state, and cannot be started # just pass to let viewclient deal with this case pass else: start_app_intent = self.app.get_start_intent() self.last_event_flag += EVENT_FLAG_START_APP self.last_event_str = EVENT_FLAG_START_APP return IntentEvent(start_app_intent) # select a view to click view_to_touch = self.select_a_view(self.current_state) # if no view can be selected, restart the app if view_to_touch is None: stop_app_intent = self.app.get_stop_intent() self.last_event_flag += EVENT_FLAG_STOP_APP self.last_event_str = EVENT_FLAG_STOP_APP return IntentEvent(stop_app_intent) view_to_touch_str = view_to_touch['view_str'] if view_to_touch_str.startswith('BACK'): result = KeyEvent('BACK') else: x, y = DeviceState.get_view_center(view_to_touch) result = TouchEvent(x, y) self.last_event_flag += EVENT_FLAG_TOUCH self.last_event_str = view_to_touch_str self.save_explored_view(self.last_state, self.last_event_str) return result
def get_transformed_event(self, device): event_dict = self.event_dict.copy() if 'target_view' in event_dict: target_view = event_dict.pop('target_view') target_view_selector = event_dict.pop('target_view_selector') state = device.get_current_state() matched_view = None for view_dict in state.views: if target_view_selector.match(view_dict): matched_view = view_dict break if matched_view is None: device.logger.warning("target_view no match: %s" % target_view) else: from device_state import DeviceState (event_dict['x'], event_dict['y']) = DeviceState.get_view_center(matched_view) return AppEvent.get_event(event_dict)
def select_a_view(self, state): """ select a view in the view list of given state, let droidbot touch it @param state: DeviceState @return: """ views = [] for view in state.views: if view['enabled'] and len( view['children'] ) == 0 and DeviceState.get_view_size(view) != 0: views.append(view) if not self.no_shuffle: random.shuffle(views) # add a "BACK" view, consider go back last mock_view_back = { 'view_str': 'BACK_%s' % state.foreground_activity, 'text': 'BACK_%s' % state.foreground_activity } views.append(mock_view_back) # first try to find a preferable view for view in views: view_text = view['text'] if view['text'] is not None else '' view_text = view_text.lower().strip() if view_text in self.preferred_buttons and \ (state.foreground_activity, view['view_str']) not in self.explored_views: self.device.logger.info("selected an preferred view: %s" % view['view_str']) return view # try to find a un-clicked view for view in views: if (state.foreground_activity, view['view_str']) not in self.explored_views: self.device.logger.info("selected an un-clicked view: %s" % view['view_str']) return view # if all enabled views have been clicked, try jump to another activity by clicking one of state transitions if not self.no_shuffle: random.shuffle(views) transition_views = { transition[0] for transition in self.state_transitions } for view in views: if view['view_str'] in transition_views: self.device.logger.info("selected a transition view: %s" % view['view_str']) return view # no window transition found, just return a random view # view = views[0] # self.device.logger.info("selected a random view: %s" % view['view_str']) # return view # DroidBot stuck on current state, return None self.device.logger.info("no view could be selected in state: %s" % state.tag) return None