xref: /aosp_15_r20/cts/apps/CameraITS/utils/ui_interaction_utils.py (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker# Copyright 2024 The Android Open Source Project
2*b7c941bbSAndroid Build Coastguard Worker#
3*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*b7c941bbSAndroid Build Coastguard Worker#
7*b7c941bbSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*b7c941bbSAndroid Build Coastguard Worker#
9*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*b7c941bbSAndroid Build Coastguard Worker# limitations under the License.
14*b7c941bbSAndroid Build Coastguard Worker"""Utility functions for interacting with a device via the UI."""
15*b7c941bbSAndroid Build Coastguard Worker
16*b7c941bbSAndroid Build Coastguard Workerimport datetime
17*b7c941bbSAndroid Build Coastguard Workerimport logging
18*b7c941bbSAndroid Build Coastguard Workerimport re
19*b7c941bbSAndroid Build Coastguard Workerimport time
20*b7c941bbSAndroid Build Coastguard Workerimport types
21*b7c941bbSAndroid Build Coastguard Workerimport xml.etree.ElementTree as et
22*b7c941bbSAndroid Build Coastguard Worker
23*b7c941bbSAndroid Build Coastguard Workerimport camera_properties_utils
24*b7c941bbSAndroid Build Coastguard Workerimport its_device_utils
25*b7c941bbSAndroid Build Coastguard Worker
26*b7c941bbSAndroid Build Coastguard Worker_DIR_EXISTS_TXT = 'Directory exists'
27*b7c941bbSAndroid Build Coastguard Worker_PERMISSIONS_LIST = ('CAMERA', 'RECORD_AUDIO', 'ACCESS_FINE_LOCATION',
28*b7c941bbSAndroid Build Coastguard Worker                     'ACCESS_COARSE_LOCATION')
29*b7c941bbSAndroid Build Coastguard Worker
30*b7c941bbSAndroid Build Coastguard WorkerACTION_ITS_DO_JCA_CAPTURE = (
31*b7c941bbSAndroid Build Coastguard Worker    'com.android.cts.verifier.camera.its.ACTION_ITS_DO_JCA_CAPTURE'
32*b7c941bbSAndroid Build Coastguard Worker)
33*b7c941bbSAndroid Build Coastguard WorkerACTION_ITS_DO_JCA_VIDEO_CAPTURE = (
34*b7c941bbSAndroid Build Coastguard Worker    'com.android.cts.verifier.camera.its.ACTION_ITS_DO_JCA_VIDEO_CAPTURE'
35*b7c941bbSAndroid Build Coastguard Worker)
36*b7c941bbSAndroid Build Coastguard WorkerACTIVITY_WAIT_TIME_SECONDS = 5
37*b7c941bbSAndroid Build Coastguard WorkerAGREE_BUTTON = 'Agree'
38*b7c941bbSAndroid Build Coastguard WorkerAGREE_AND_CONTINUE_BUTTON = 'Agree and continue'
39*b7c941bbSAndroid Build Coastguard WorkerCANCEL_BUTTON_TXT = 'Cancel'
40*b7c941bbSAndroid Build Coastguard WorkerCAMERA_FILES_PATHS = ('/sdcard/DCIM/Camera',
41*b7c941bbSAndroid Build Coastguard Worker                      '/storage/emulated/0/Pictures')
42*b7c941bbSAndroid Build Coastguard WorkerCAPTURE_BUTTON_RESOURCE_ID = 'CaptureButton'
43*b7c941bbSAndroid Build Coastguard WorkerDONE_BUTTON_TXT = 'Done'
44*b7c941bbSAndroid Build Coastguard WorkerFLASH_MODE_TO_CLICKS = types.MappingProxyType({
45*b7c941bbSAndroid Build Coastguard Worker    'OFF': 3,
46*b7c941bbSAndroid Build Coastguard Worker    'AUTO': 2
47*b7c941bbSAndroid Build Coastguard Worker})
48*b7c941bbSAndroid Build Coastguard WorkerIMG_CAPTURE_CMD = 'am start -a android.media.action.IMAGE_CAPTURE'
49*b7c941bbSAndroid Build Coastguard WorkerITS_ACTIVITY_TEXT = 'Camera ITS Test'
50*b7c941bbSAndroid Build Coastguard WorkerJPG_FORMAT_STR = '.jpg'
51*b7c941bbSAndroid Build Coastguard WorkerOK_BUTTON_TXT = 'OK'
52*b7c941bbSAndroid Build Coastguard WorkerTAKE_PHOTO_CMD = 'input keyevent KEYCODE_CAMERA'
53*b7c941bbSAndroid Build Coastguard WorkerQUICK_SETTINGS_RESOURCE_ID = 'QuickSettingsDropDown'
54*b7c941bbSAndroid Build Coastguard WorkerQUICK_SET_FLASH_RESOURCE_ID = 'QuickSettingsFlashButton'
55*b7c941bbSAndroid Build Coastguard WorkerQUICK_SET_FLIP_CAMERA_RESOURCE_ID = 'QuickSettingsFlipCameraButton'
56*b7c941bbSAndroid Build Coastguard WorkerQUICK_SET_RATIO_RESOURCE_ID = 'QuickSettingsRatioButton'
57*b7c941bbSAndroid Build Coastguard WorkerRATIO_TO_UI_DESCRIPTION = {
58*b7c941bbSAndroid Build Coastguard Worker    '1 to 1 aspect ratio': 'QuickSettingsRatio1:1Button',
59*b7c941bbSAndroid Build Coastguard Worker    '3 to 4 aspect ratio': 'QuickSettingsRatio3:4Button',
60*b7c941bbSAndroid Build Coastguard Worker    '9 to 16 aspect ratio': 'QuickSettingsRatio9:16Button'
61*b7c941bbSAndroid Build Coastguard Worker}
62*b7c941bbSAndroid Build Coastguard WorkerREMOVE_CAMERA_FILES_CMD = 'rm '
63*b7c941bbSAndroid Build Coastguard WorkerUI_DESCRIPTION_BACK_CAMERA = 'Back Camera'
64*b7c941bbSAndroid Build Coastguard WorkerUI_DESCRIPTION_FRONT_CAMERA = 'Front Camera'
65*b7c941bbSAndroid Build Coastguard WorkerUI_OBJECT_WAIT_TIME_SECONDS = datetime.timedelta(seconds=3)
66*b7c941bbSAndroid Build Coastguard WorkerVIEWFINDER_NOT_VISIBLE_PREFIX = 'viewfinder_not_visible'
67*b7c941bbSAndroid Build Coastguard WorkerVIEWFINDER_VISIBLE_PREFIX = 'viewfinder_visible'
68*b7c941bbSAndroid Build Coastguard WorkerWAIT_INTERVAL_FIVE_SECONDS = datetime.timedelta(seconds=5)
69*b7c941bbSAndroid Build Coastguard Worker
70*b7c941bbSAndroid Build Coastguard Worker
71*b7c941bbSAndroid Build Coastguard Workerdef _find_ui_object_else_click(object_to_await, object_to_click):
72*b7c941bbSAndroid Build Coastguard Worker  """Waits for a UI object to be visible. If not, clicks another UI object.
73*b7c941bbSAndroid Build Coastguard Worker
74*b7c941bbSAndroid Build Coastguard Worker  Args:
75*b7c941bbSAndroid Build Coastguard Worker    object_to_await: A snippet-uiautomator selector object to be awaited.
76*b7c941bbSAndroid Build Coastguard Worker    object_to_click: A snippet-uiautomator selector object to be clicked.
77*b7c941bbSAndroid Build Coastguard Worker  """
78*b7c941bbSAndroid Build Coastguard Worker  if not object_to_await.wait.exists(UI_OBJECT_WAIT_TIME_SECONDS):
79*b7c941bbSAndroid Build Coastguard Worker    object_to_click.click()
80*b7c941bbSAndroid Build Coastguard Worker
81*b7c941bbSAndroid Build Coastguard Worker
82*b7c941bbSAndroid Build Coastguard Workerdef verify_ui_object_visible(ui_object, call_on_fail=None):
83*b7c941bbSAndroid Build Coastguard Worker  """Verifies that a UI object is visible.
84*b7c941bbSAndroid Build Coastguard Worker
85*b7c941bbSAndroid Build Coastguard Worker  Args:
86*b7c941bbSAndroid Build Coastguard Worker    ui_object: A snippet-uiautomator selector object.
87*b7c941bbSAndroid Build Coastguard Worker    call_on_fail: [Optional] Callable; method to call on failure.
88*b7c941bbSAndroid Build Coastguard Worker  """
89*b7c941bbSAndroid Build Coastguard Worker  ui_object_visible = ui_object.wait.exists(UI_OBJECT_WAIT_TIME_SECONDS)
90*b7c941bbSAndroid Build Coastguard Worker  if not ui_object_visible:
91*b7c941bbSAndroid Build Coastguard Worker    if call_on_fail is not None:
92*b7c941bbSAndroid Build Coastguard Worker      call_on_fail()
93*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError('UI object was not visible!')
94*b7c941bbSAndroid Build Coastguard Worker
95*b7c941bbSAndroid Build Coastguard Worker
96*b7c941bbSAndroid Build Coastguard Workerdef open_jca_viewfinder(dut, log_path, request_video_capture=False):
97*b7c941bbSAndroid Build Coastguard Worker  """Sends an intent to JCA and open its viewfinder.
98*b7c941bbSAndroid Build Coastguard Worker
99*b7c941bbSAndroid Build Coastguard Worker  Args:
100*b7c941bbSAndroid Build Coastguard Worker    dut: An Android controller device object.
101*b7c941bbSAndroid Build Coastguard Worker    log_path: str; Log path to save screenshots.
102*b7c941bbSAndroid Build Coastguard Worker    request_video_capture: boolean; True if requesting video capture.
103*b7c941bbSAndroid Build Coastguard Worker  Raises:
104*b7c941bbSAndroid Build Coastguard Worker    AssertionError: If JCA viewfinder is not visible.
105*b7c941bbSAndroid Build Coastguard Worker  """
106*b7c941bbSAndroid Build Coastguard Worker  its_device_utils.start_its_test_activity(dut.serial)
107*b7c941bbSAndroid Build Coastguard Worker  call_on_fail = lambda: dut.take_screenshot(log_path, prefix='its_not_found')
108*b7c941bbSAndroid Build Coastguard Worker  verify_ui_object_visible(
109*b7c941bbSAndroid Build Coastguard Worker      dut.ui(text=ITS_ACTIVITY_TEXT),
110*b7c941bbSAndroid Build Coastguard Worker      call_on_fail=call_on_fail
111*b7c941bbSAndroid Build Coastguard Worker  )
112*b7c941bbSAndroid Build Coastguard Worker
113*b7c941bbSAndroid Build Coastguard Worker  # Send intent to ItsTestActivity, which will start the correct JCA activity.
114*b7c941bbSAndroid Build Coastguard Worker  if request_video_capture:
115*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run(
116*b7c941bbSAndroid Build Coastguard Worker        f'adb -s {dut.serial} shell am broadcast -a'
117*b7c941bbSAndroid Build Coastguard Worker        f'{ACTION_ITS_DO_JCA_VIDEO_CAPTURE}'
118*b7c941bbSAndroid Build Coastguard Worker    )
119*b7c941bbSAndroid Build Coastguard Worker  else:
120*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run(
121*b7c941bbSAndroid Build Coastguard Worker        f'adb -s {dut.serial} shell am broadcast -a'
122*b7c941bbSAndroid Build Coastguard Worker        f'{ACTION_ITS_DO_JCA_CAPTURE}'
123*b7c941bbSAndroid Build Coastguard Worker    )
124*b7c941bbSAndroid Build Coastguard Worker  jca_capture_button_visible = dut.ui(
125*b7c941bbSAndroid Build Coastguard Worker      res=CAPTURE_BUTTON_RESOURCE_ID).wait.exists(
126*b7c941bbSAndroid Build Coastguard Worker          UI_OBJECT_WAIT_TIME_SECONDS)
127*b7c941bbSAndroid Build Coastguard Worker  if not jca_capture_button_visible:
128*b7c941bbSAndroid Build Coastguard Worker    dut.take_screenshot(log_path, prefix=VIEWFINDER_NOT_VISIBLE_PREFIX)
129*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Current UI dump: %s', dut.ui.dump())
130*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError('JCA was not started successfully!')
131*b7c941bbSAndroid Build Coastguard Worker  dut.take_screenshot(log_path, prefix=VIEWFINDER_VISIBLE_PREFIX)
132*b7c941bbSAndroid Build Coastguard Worker
133*b7c941bbSAndroid Build Coastguard Worker
134*b7c941bbSAndroid Build Coastguard Workerdef switch_jca_camera(dut, log_path, facing):
135*b7c941bbSAndroid Build Coastguard Worker  """Interacts with JCA UI to switch camera if necessary.
136*b7c941bbSAndroid Build Coastguard Worker
137*b7c941bbSAndroid Build Coastguard Worker  Args:
138*b7c941bbSAndroid Build Coastguard Worker    dut: An Android controller device object.
139*b7c941bbSAndroid Build Coastguard Worker    log_path: str; log path to save screenshots.
140*b7c941bbSAndroid Build Coastguard Worker    facing: str; constant describing the direction the camera lens faces.
141*b7c941bbSAndroid Build Coastguard Worker  Raises:
142*b7c941bbSAndroid Build Coastguard Worker    AssertionError: If JCA does not report that camera has been switched.
143*b7c941bbSAndroid Build Coastguard Worker  """
144*b7c941bbSAndroid Build Coastguard Worker  if facing == camera_properties_utils.LENS_FACING['BACK']:
145*b7c941bbSAndroid Build Coastguard Worker    ui_facing_description = UI_DESCRIPTION_BACK_CAMERA
146*b7c941bbSAndroid Build Coastguard Worker  elif facing == camera_properties_utils.LENS_FACING['FRONT']:
147*b7c941bbSAndroid Build Coastguard Worker    ui_facing_description = UI_DESCRIPTION_FRONT_CAMERA
148*b7c941bbSAndroid Build Coastguard Worker  else:
149*b7c941bbSAndroid Build Coastguard Worker    raise ValueError(f'Unknown facing: {facing}')
150*b7c941bbSAndroid Build Coastguard Worker  dut.ui(res=QUICK_SETTINGS_RESOURCE_ID).click()
151*b7c941bbSAndroid Build Coastguard Worker  _find_ui_object_else_click(dut.ui(desc=ui_facing_description),
152*b7c941bbSAndroid Build Coastguard Worker                             dut.ui(res=QUICK_SET_FLIP_CAMERA_RESOURCE_ID))
153*b7c941bbSAndroid Build Coastguard Worker  if not dut.ui(desc=ui_facing_description).wait.exists(
154*b7c941bbSAndroid Build Coastguard Worker      UI_OBJECT_WAIT_TIME_SECONDS):
155*b7c941bbSAndroid Build Coastguard Worker    dut.take_screenshot(log_path, prefix='failed_to_switch_camera')
156*b7c941bbSAndroid Build Coastguard Worker    logging.debug('JCA UI dump: %s', dut.ui.dump())
157*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError(f'Failed to switch to {ui_facing_description}!')
158*b7c941bbSAndroid Build Coastguard Worker  dut.take_screenshot(
159*b7c941bbSAndroid Build Coastguard Worker      log_path, prefix=f"switched_to_{ui_facing_description.replace(' ', '_')}"
160*b7c941bbSAndroid Build Coastguard Worker  )
161*b7c941bbSAndroid Build Coastguard Worker  dut.ui(res=QUICK_SETTINGS_RESOURCE_ID).click()
162*b7c941bbSAndroid Build Coastguard Worker
163*b7c941bbSAndroid Build Coastguard Worker
164*b7c941bbSAndroid Build Coastguard Workerdef change_jca_aspect_ratio(dut, log_path, aspect_ratio):
165*b7c941bbSAndroid Build Coastguard Worker  """Interacts with JCA UI to change aspect ratio if necessary.
166*b7c941bbSAndroid Build Coastguard Worker
167*b7c941bbSAndroid Build Coastguard Worker  Args:
168*b7c941bbSAndroid Build Coastguard Worker    dut: An Android controller device object.
169*b7c941bbSAndroid Build Coastguard Worker    log_path: str; log path to save screenshots.
170*b7c941bbSAndroid Build Coastguard Worker    aspect_ratio: str; Aspect ratio that JCA supports.
171*b7c941bbSAndroid Build Coastguard Worker      Acceptable values: _RATIO_TO_UI_DESCRIPTION
172*b7c941bbSAndroid Build Coastguard Worker  Raises:
173*b7c941bbSAndroid Build Coastguard Worker    ValueError: If ratio is not supported in JCA.
174*b7c941bbSAndroid Build Coastguard Worker    AssertionError: If JCA does not find the requested ratio.
175*b7c941bbSAndroid Build Coastguard Worker  """
176*b7c941bbSAndroid Build Coastguard Worker  if aspect_ratio not in RATIO_TO_UI_DESCRIPTION:
177*b7c941bbSAndroid Build Coastguard Worker    raise ValueError(f'Testing ratio {aspect_ratio} not supported in JCA!')
178*b7c941bbSAndroid Build Coastguard Worker  dut.ui(res=QUICK_SETTINGS_RESOURCE_ID).click()
179*b7c941bbSAndroid Build Coastguard Worker  # Change aspect ratio in ratio switching menu if needed
180*b7c941bbSAndroid Build Coastguard Worker  if not dut.ui(desc=aspect_ratio).wait.exists(UI_OBJECT_WAIT_TIME_SECONDS):
181*b7c941bbSAndroid Build Coastguard Worker    dut.ui(res=QUICK_SET_RATIO_RESOURCE_ID).click()
182*b7c941bbSAndroid Build Coastguard Worker    try:
183*b7c941bbSAndroid Build Coastguard Worker      dut.ui(res=RATIO_TO_UI_DESCRIPTION[aspect_ratio]).click()
184*b7c941bbSAndroid Build Coastguard Worker    except Exception as e:
185*b7c941bbSAndroid Build Coastguard Worker      dut.take_screenshot(
186*b7c941bbSAndroid Build Coastguard Worker          log_path, prefix=f'failed_to_find{aspect_ratio.replace(" ", "_")}'
187*b7c941bbSAndroid Build Coastguard Worker      )
188*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError(
189*b7c941bbSAndroid Build Coastguard Worker          f'Testing ratio {aspect_ratio} not found in JCA app UI!') from e
190*b7c941bbSAndroid Build Coastguard Worker  dut.ui(res=QUICK_SETTINGS_RESOURCE_ID).click()
191*b7c941bbSAndroid Build Coastguard Worker
192*b7c941bbSAndroid Build Coastguard Worker
193*b7c941bbSAndroid Build Coastguard Workerdef do_jca_video_setup(dut, log_path, facing, aspect_ratio):
194*b7c941bbSAndroid Build Coastguard Worker  """Change video capture settings using the UI.
195*b7c941bbSAndroid Build Coastguard Worker
196*b7c941bbSAndroid Build Coastguard Worker  Selects UI elements to modify settings.
197*b7c941bbSAndroid Build Coastguard Worker
198*b7c941bbSAndroid Build Coastguard Worker  Args:
199*b7c941bbSAndroid Build Coastguard Worker    dut: An Android controller device object.
200*b7c941bbSAndroid Build Coastguard Worker    log_path: str; log path to save screenshots.
201*b7c941bbSAndroid Build Coastguard Worker    facing: str; constant describing the direction the camera lens faces.
202*b7c941bbSAndroid Build Coastguard Worker      Acceptable values: camera_properties_utils.LENS_FACING[BACK, FRONT]
203*b7c941bbSAndroid Build Coastguard Worker    aspect_ratio: str; Aspect ratios that JCA supports.
204*b7c941bbSAndroid Build Coastguard Worker      Acceptable values: _RATIO_TO_UI_DESCRIPTION
205*b7c941bbSAndroid Build Coastguard Worker  """
206*b7c941bbSAndroid Build Coastguard Worker  open_jca_viewfinder(dut, log_path, request_video_capture=True)
207*b7c941bbSAndroid Build Coastguard Worker  switch_jca_camera(dut, log_path, facing)
208*b7c941bbSAndroid Build Coastguard Worker  change_jca_aspect_ratio(dut, log_path, aspect_ratio)
209*b7c941bbSAndroid Build Coastguard Worker
210*b7c941bbSAndroid Build Coastguard Worker
211*b7c941bbSAndroid Build Coastguard Workerdef default_camera_app_setup(device_id, pkg_name):
212*b7c941bbSAndroid Build Coastguard Worker  """Setup Camera app by providing required permissions.
213*b7c941bbSAndroid Build Coastguard Worker
214*b7c941bbSAndroid Build Coastguard Worker  Args:
215*b7c941bbSAndroid Build Coastguard Worker    device_id: serial id of device.
216*b7c941bbSAndroid Build Coastguard Worker    pkg_name: pkg name of the app to setup.
217*b7c941bbSAndroid Build Coastguard Worker  Returns:
218*b7c941bbSAndroid Build Coastguard Worker    Runtime exception from called function or None.
219*b7c941bbSAndroid Build Coastguard Worker  """
220*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Setting up the app with permission.')
221*b7c941bbSAndroid Build Coastguard Worker  for permission in _PERMISSIONS_LIST:
222*b7c941bbSAndroid Build Coastguard Worker    cmd = f'pm grant {pkg_name} android.permission.{permission}'
223*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run_adb_shell_command(device_id, cmd)
224*b7c941bbSAndroid Build Coastguard Worker  allow_manage_storage_cmd = (
225*b7c941bbSAndroid Build Coastguard Worker      f'appops set {pkg_name} MANAGE_EXTERNAL_STORAGE allow'
226*b7c941bbSAndroid Build Coastguard Worker  )
227*b7c941bbSAndroid Build Coastguard Worker  its_device_utils.run_adb_shell_command(device_id, allow_manage_storage_cmd)
228*b7c941bbSAndroid Build Coastguard Worker
229*b7c941bbSAndroid Build Coastguard Worker
230*b7c941bbSAndroid Build Coastguard Workerdef switch_default_camera(dut, facing, log_path):
231*b7c941bbSAndroid Build Coastguard Worker  """Interacts with default camera app UI to switch camera.
232*b7c941bbSAndroid Build Coastguard Worker
233*b7c941bbSAndroid Build Coastguard Worker  Args:
234*b7c941bbSAndroid Build Coastguard Worker    dut: An Android controller device object.
235*b7c941bbSAndroid Build Coastguard Worker    facing: str; constant describing the direction the camera lens faces.
236*b7c941bbSAndroid Build Coastguard Worker    log_path: str; log path to save screenshots.
237*b7c941bbSAndroid Build Coastguard Worker  Raises:
238*b7c941bbSAndroid Build Coastguard Worker    AssertionError: If default camera app does not report that
239*b7c941bbSAndroid Build Coastguard Worker      camera has been switched.
240*b7c941bbSAndroid Build Coastguard Worker  """
241*b7c941bbSAndroid Build Coastguard Worker  flip_camera_pattern = (
242*b7c941bbSAndroid Build Coastguard Worker      r'(switch to|flip camera|switch camera|camera switch|switch)'
243*b7c941bbSAndroid Build Coastguard Worker    )
244*b7c941bbSAndroid Build Coastguard Worker  default_ui_dump = dut.ui.dump()
245*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Default camera UI dump: %s', default_ui_dump)
246*b7c941bbSAndroid Build Coastguard Worker  root = et.fromstring(default_ui_dump)
247*b7c941bbSAndroid Build Coastguard Worker  camera_flip_res = False
248*b7c941bbSAndroid Build Coastguard Worker  for node in root.iter('node'):
249*b7c941bbSAndroid Build Coastguard Worker    resource_id = node.get('resource-id')
250*b7c941bbSAndroid Build Coastguard Worker    content_desc = node.get('content-desc')
251*b7c941bbSAndroid Build Coastguard Worker    if re.search(
252*b7c941bbSAndroid Build Coastguard Worker        flip_camera_pattern, content_desc, re.IGNORECASE
253*b7c941bbSAndroid Build Coastguard Worker    ):
254*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Pattern matches')
255*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Resource id: %s', resource_id)
256*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Flip camera content-desc: %s', content_desc)
257*b7c941bbSAndroid Build Coastguard Worker      camera_flip_res = True
258*b7c941bbSAndroid Build Coastguard Worker      break
259*b7c941bbSAndroid Build Coastguard Worker  if content_desc and resource_id:
260*b7c941bbSAndroid Build Coastguard Worker    if facing == 'front' and camera_flip_res:
261*b7c941bbSAndroid Build Coastguard Worker      if ('rear' in content_desc.lower() or 'rear' in resource_id.lower()
262*b7c941bbSAndroid Build Coastguard Worker          or 'back' in content_desc.lower() or 'back' in resource_id.lower()
263*b7c941bbSAndroid Build Coastguard Worker          ):
264*b7c941bbSAndroid Build Coastguard Worker        logging.debug('Pattern found but camera is already switched.')
265*b7c941bbSAndroid Build Coastguard Worker      else:
266*b7c941bbSAndroid Build Coastguard Worker        dut.ui(desc=content_desc).click.wait()
267*b7c941bbSAndroid Build Coastguard Worker    elif facing == 'rear' and camera_flip_res:
268*b7c941bbSAndroid Build Coastguard Worker      if 'front' in content_desc.lower() or 'front' in resource_id.lower():
269*b7c941bbSAndroid Build Coastguard Worker        logging.debug('Pattern found but camera is already switched.')
270*b7c941bbSAndroid Build Coastguard Worker      else:
271*b7c941bbSAndroid Build Coastguard Worker        dut.ui(desc=content_desc).click.wait()
272*b7c941bbSAndroid Build Coastguard Worker    else:
273*b7c941bbSAndroid Build Coastguard Worker      raise ValueError(f'Unknown facing: {facing}')
274*b7c941bbSAndroid Build Coastguard Worker
275*b7c941bbSAndroid Build Coastguard Worker  dut.take_screenshot(
276*b7c941bbSAndroid Build Coastguard Worker      log_path, prefix=f'switched_to_{facing}_default_camera'
277*b7c941bbSAndroid Build Coastguard Worker  )
278*b7c941bbSAndroid Build Coastguard Worker
279*b7c941bbSAndroid Build Coastguard Worker  if not camera_flip_res:
280*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError('Flip camera resource not found.')
281*b7c941bbSAndroid Build Coastguard Worker
282*b7c941bbSAndroid Build Coastguard Worker
283*b7c941bbSAndroid Build Coastguard Workerdef pull_img_files(device_id, input_path, output_path):
284*b7c941bbSAndroid Build Coastguard Worker  """Pulls files from the input_path on the device to output_path.
285*b7c941bbSAndroid Build Coastguard Worker
286*b7c941bbSAndroid Build Coastguard Worker  Args:
287*b7c941bbSAndroid Build Coastguard Worker    device_id: serial id of device.
288*b7c941bbSAndroid Build Coastguard Worker    input_path: File location on device.
289*b7c941bbSAndroid Build Coastguard Worker    output_path: Location to save the file on the host.
290*b7c941bbSAndroid Build Coastguard Worker  """
291*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Pulling files from the device')
292*b7c941bbSAndroid Build Coastguard Worker  pull_cmd = f'adb -s {device_id} pull {input_path} {output_path}'
293*b7c941bbSAndroid Build Coastguard Worker  its_device_utils.run(pull_cmd)
294*b7c941bbSAndroid Build Coastguard Worker
295*b7c941bbSAndroid Build Coastguard Worker
296*b7c941bbSAndroid Build Coastguard Workerdef launch_and_take_capture(dut, pkg_name, camera_facing, log_path):
297*b7c941bbSAndroid Build Coastguard Worker  """Launches the camera app and takes still capture.
298*b7c941bbSAndroid Build Coastguard Worker
299*b7c941bbSAndroid Build Coastguard Worker  Args:
300*b7c941bbSAndroid Build Coastguard Worker    dut: An Android controller device object.
301*b7c941bbSAndroid Build Coastguard Worker    pkg_name: pkg_name of the default camera app to
302*b7c941bbSAndroid Build Coastguard Worker      be used for captures.
303*b7c941bbSAndroid Build Coastguard Worker    camera_facing: camera lens facing orientation
304*b7c941bbSAndroid Build Coastguard Worker    log_path: str; log path to save screenshots.
305*b7c941bbSAndroid Build Coastguard Worker
306*b7c941bbSAndroid Build Coastguard Worker  Returns:
307*b7c941bbSAndroid Build Coastguard Worker    img_path_on_dut: Path of the captured image on the device
308*b7c941bbSAndroid Build Coastguard Worker  """
309*b7c941bbSAndroid Build Coastguard Worker  device_id = dut.serial
310*b7c941bbSAndroid Build Coastguard Worker  try:
311*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Launching app: %s', pkg_name)
312*b7c941bbSAndroid Build Coastguard Worker    launch_cmd = f'monkey -p {pkg_name} 1'
313*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run_adb_shell_command(device_id, launch_cmd)
314*b7c941bbSAndroid Build Coastguard Worker
315*b7c941bbSAndroid Build Coastguard Worker    # Click OK/Done button on initial pop up windows
316*b7c941bbSAndroid Build Coastguard Worker    if dut.ui(text=AGREE_BUTTON).wait.exists(
317*b7c941bbSAndroid Build Coastguard Worker        timeout=WAIT_INTERVAL_FIVE_SECONDS):
318*b7c941bbSAndroid Build Coastguard Worker      dut.ui(text=AGREE_BUTTON).click.wait()
319*b7c941bbSAndroid Build Coastguard Worker    if dut.ui(text=AGREE_AND_CONTINUE_BUTTON).wait.exists(
320*b7c941bbSAndroid Build Coastguard Worker        timeout=WAIT_INTERVAL_FIVE_SECONDS):
321*b7c941bbSAndroid Build Coastguard Worker      dut.ui(text=AGREE_AND_CONTINUE_BUTTON).click.wait()
322*b7c941bbSAndroid Build Coastguard Worker    if dut.ui(text=OK_BUTTON_TXT).wait.exists(
323*b7c941bbSAndroid Build Coastguard Worker        timeout=WAIT_INTERVAL_FIVE_SECONDS):
324*b7c941bbSAndroid Build Coastguard Worker      dut.ui(text=OK_BUTTON_TXT).click.wait()
325*b7c941bbSAndroid Build Coastguard Worker    if dut.ui(text=DONE_BUTTON_TXT).wait.exists(
326*b7c941bbSAndroid Build Coastguard Worker        timeout=WAIT_INTERVAL_FIVE_SECONDS):
327*b7c941bbSAndroid Build Coastguard Worker      dut.ui(text=DONE_BUTTON_TXT).click.wait()
328*b7c941bbSAndroid Build Coastguard Worker    if dut.ui(text=CANCEL_BUTTON_TXT).wait.exists(
329*b7c941bbSAndroid Build Coastguard Worker        timeout=WAIT_INTERVAL_FIVE_SECONDS):
330*b7c941bbSAndroid Build Coastguard Worker      dut.ui(text=CANCEL_BUTTON_TXT).click.wait()
331*b7c941bbSAndroid Build Coastguard Worker    switch_default_camera(dut, camera_facing, log_path)
332*b7c941bbSAndroid Build Coastguard Worker    time.sleep(ACTIVITY_WAIT_TIME_SECONDS)
333*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Taking photo')
334*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run_adb_shell_command(device_id, TAKE_PHOTO_CMD)
335*b7c941bbSAndroid Build Coastguard Worker    time.sleep(ACTIVITY_WAIT_TIME_SECONDS)
336*b7c941bbSAndroid Build Coastguard Worker    img_path_on_dut = ''
337*b7c941bbSAndroid Build Coastguard Worker    photo_storage_path = ''
338*b7c941bbSAndroid Build Coastguard Worker    for path in CAMERA_FILES_PATHS:
339*b7c941bbSAndroid Build Coastguard Worker      check_path_cmd = (
340*b7c941bbSAndroid Build Coastguard Worker          f'ls {path} && echo "Directory exists" || '
341*b7c941bbSAndroid Build Coastguard Worker          'echo "Directory does not exist"'
342*b7c941bbSAndroid Build Coastguard Worker      )
343*b7c941bbSAndroid Build Coastguard Worker      cmd_output = dut.adb.shell(check_path_cmd).decode('utf-8').strip()
344*b7c941bbSAndroid Build Coastguard Worker      if _DIR_EXISTS_TXT in cmd_output:
345*b7c941bbSAndroid Build Coastguard Worker        photo_storage_path = path
346*b7c941bbSAndroid Build Coastguard Worker        break
347*b7c941bbSAndroid Build Coastguard Worker    find_file_path = (
348*b7c941bbSAndroid Build Coastguard Worker        f'find {photo_storage_path} ! -empty -a ! -name \'.pending*\''
349*b7c941bbSAndroid Build Coastguard Worker        ' -a -type f -iname "*.jpg" -o -iname "*.jpeg"'
350*b7c941bbSAndroid Build Coastguard Worker    )
351*b7c941bbSAndroid Build Coastguard Worker    img_path_on_dut = (
352*b7c941bbSAndroid Build Coastguard Worker        dut.adb.shell(find_file_path).decode('utf-8').strip().lower()
353*b7c941bbSAndroid Build Coastguard Worker    )
354*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Image path on DUT: %s', img_path_on_dut)
355*b7c941bbSAndroid Build Coastguard Worker    if JPG_FORMAT_STR not in img_path_on_dut:
356*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError('Failed to find jpg files!')
357*b7c941bbSAndroid Build Coastguard Worker  finally:
358*b7c941bbSAndroid Build Coastguard Worker    force_stop_app(dut, pkg_name)
359*b7c941bbSAndroid Build Coastguard Worker  return img_path_on_dut
360*b7c941bbSAndroid Build Coastguard Worker
361*b7c941bbSAndroid Build Coastguard Worker
362*b7c941bbSAndroid Build Coastguard Workerdef force_stop_app(dut, pkg_name):
363*b7c941bbSAndroid Build Coastguard Worker  """Force stops an app with given pkg_name.
364*b7c941bbSAndroid Build Coastguard Worker
365*b7c941bbSAndroid Build Coastguard Worker  Args:
366*b7c941bbSAndroid Build Coastguard Worker    dut: An Android controller device object.
367*b7c941bbSAndroid Build Coastguard Worker    pkg_name: pkg_name of the app to be stopped.
368*b7c941bbSAndroid Build Coastguard Worker  """
369*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Closing app: %s', pkg_name)
370*b7c941bbSAndroid Build Coastguard Worker  force_stop_cmd = f'am force-stop {pkg_name}'
371*b7c941bbSAndroid Build Coastguard Worker  dut.adb.shell(force_stop_cmd)
372*b7c941bbSAndroid Build Coastguard Worker
373*b7c941bbSAndroid Build Coastguard Worker
374*b7c941bbSAndroid Build Coastguard Workerdef default_camera_app_dut_setup(device_id, pkg_name):
375*b7c941bbSAndroid Build Coastguard Worker  """Setup the device for testing default camera app.
376*b7c941bbSAndroid Build Coastguard Worker
377*b7c941bbSAndroid Build Coastguard Worker  Args:
378*b7c941bbSAndroid Build Coastguard Worker    device_id: serial id of device.
379*b7c941bbSAndroid Build Coastguard Worker    pkg_name: pkg_name of the app.
380*b7c941bbSAndroid Build Coastguard Worker  Returns:
381*b7c941bbSAndroid Build Coastguard Worker    Runtime exception from called function or None.
382*b7c941bbSAndroid Build Coastguard Worker  """
383*b7c941bbSAndroid Build Coastguard Worker  default_camera_app_setup(device_id, pkg_name)
384*b7c941bbSAndroid Build Coastguard Worker  for path in CAMERA_FILES_PATHS:
385*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run_adb_shell_command(
386*b7c941bbSAndroid Build Coastguard Worker        device_id, f'{REMOVE_CAMERA_FILES_CMD}{path}/*')
387