1# Copyright (C) 2023 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import logging 16import re 17import time 18from utilities import constants 19from utilities.common_utils import CommonUtils 20 21 22class MediaUtils: 23 """A utility that provides Media controls for handheld device and HU.""" 24 25 def __init__(self, target, discoverer): 26 self.target = target 27 self.discoverer = discoverer 28 self.common_utils = CommonUtils(self.target, self.discoverer) 29 30 # Execute any shell command on phone device 31 def execute_shell_on_device(self, shell_command): 32 self.target.log.info( 33 'Executing shell command: <%s> on phone device <%s>', 34 shell_command, 35 self.target.serial, 36 ) 37 return self.target.adb.shell(shell_command) 38 39 # Execute any shell command on HU 40 def execute_shell_on_hu_device(self, shell_command): 41 self.target.log.info( 42 'Executing shell command: <%s> on HU device <%s>', 43 shell_command, 44 self.discoverer.serial, 45 ) 46 return self.discoverer.adb.shell(shell_command) 47 48 # Start YouTube Music app on phone device 49 def open_youtube_music_app(self): 50 logging.info("Open YouTube Music on phone device") 51 self.execute_shell_on_device( 52 constants.DEFAULT_YOUTUBE_MUSIC_PLAYLIST 53 ) 54 55 # Stop YouTube Music app on phone device 56 def close_youtube_music_app(self): 57 logging.info("Close YouTube Music on phone device") 58 self.execute_shell_on_device( 59 constants.STOP_YOUTUBE_MEDIA_SHELL 60 ) 61 62 # Press TAB button on phone device 63 def press_tab_on_device(self): 64 logging.info("Press <TAB> on phone device") 65 self.execute_shell_on_device(constants.KEYCODE_TAB) 66 67 # Press ENTER button on phone device 68 def press_enter_on_device(self): 69 logging.info("Press <ENTER> on phone device") 70 self.execute_shell_on_device(constants.KEYCODE_ENTER) 71 72 # Press NEXT song on phone device 73 def press_next_song_button(self): 74 logging.info("Press <NEXT SONG> button on phone device") 75 self.execute_shell_on_device(constants.KEYCODE_MEDIA_NEXT) 76 77 # Press PREVIOUS song on phone device 78 def press_previous_song_button(self): 79 logging.info("Press <PREVIOUS SONG> button on phone device") 80 self.execute_shell_on_device(constants.KEYCODE_MEDIA_PREVIOUS) 81 82 # Press PAUSE song on phone device 83 def press_pause_song_button(self): 84 logging.info("Press <PAUSE> button on phone device") 85 self.execute_shell_on_device(constants.KEYCODE_MEDIA_PAUSE) 86 87 # Press PLAY song on phone device 88 def press_play_song_button(self): 89 logging.info("Press <PLAY> button on phone device") 90 self.execute_shell_on_device(constants.KEYCODE_MEDIA_PLAY) 91 92 # Press STOP playing song on phone device 93 def press_stop_playing_song_button(self): 94 logging.info("Press <STOP> playing song button on phone device") 95 self.execute_shell_on_device(constants.KEYCODE_MEDIA_STOP) 96 97 # Get song metadata from phone device 98 def get_song_metadata(self): 99 logging.info("Getting song metadata from phone device") 100 # get dumpsys 101 dumpsys_metadata = self.execute_shell_on_device(constants.GET_DUMPSYS_METADATA).decode( 102 'utf8') 103 104 # compile regex 105 regex_pattern = re.compile(constants.SONG_METADATA_PATTERN) 106 107 # match regex 108 actual_dumpsys_metadata = regex_pattern.findall(dumpsys_metadata) 109 110 # check if dumpsys_metadata is not empty 111 if len(actual_dumpsys_metadata) < 1: 112 logging.info('dumpsys media_session metadata is empty after matching with RegEx ' + 113 'pattern <%s>', constants.SONG_METADATA_PATTERN) 114 return constants.NULL_VALUE 115 logging.info('Actual dumpsys media_session on phone device metadata: %s"', 116 actual_dumpsys_metadata) 117 118 # assign actual_song_metadata after '=' sign in actual_dumpsys_metadata 119 # if actual_dumpsys_metadata contains less than 3 'null', 120 # and split on '=' is an array with more than 1 element 121 actual_song_metadata = [x.split('=', 1)[1] for x in actual_dumpsys_metadata if 122 x.count(constants.NULL_VALUE) < 3 and len(x.split('=', 1)) > 1] 123 logging.info("Actual song metadata on phone device: %s", actual_song_metadata) 124 125 # assign parsed_song_metadata 126 parsed_song_metadata = actual_song_metadata[0] if len( 127 actual_song_metadata) > 0 else constants.NULL_VALUE 128 logging.info("Parsed song metadata on phone device: %s", parsed_song_metadata) 129 return parsed_song_metadata 130 131 # Get song title from phone device 132 def get_song_title_from_phone(self): 133 logging.info("Getting song title from phone device") 134 time.sleep(constants.YOUTUBE_SYNC_TIME) 135 song_metadata_array = self.get_song_metadata().split(',') 136 actual_song_title = song_metadata_array[0] 137 logging.info("Actual song title on phone device: %s", actual_song_title) 138 return actual_song_title 139 140 # Get song title from HU 141 def get_song_title_from_hu(self): 142 logging.info("Getting song title from HU") 143 actual_song_title = self.discoverer.mbs.getMediaTrackName() 144 logging.info("Actual song title on HU: %s", actual_song_title) 145 return actual_song_title 146 147 # Click on NEXT track on HU 148 def click_next_track_on_hu(self): 149 logging.info("Click on NEXT track on HU") 150 self.discoverer.mbs.clickNextTrack() 151 time.sleep(constants.WAIT_FOR_LOAD) 152 153 # Click on PREVIOUS track on HU 154 def click_previous_track_on_hu(self): 155 logging.info("Click on PREVIOUS track on HU") 156 self.discoverer.mbs.clickPreviousTrack() 157 time.sleep(constants.WAIT_FOR_LOAD) 158 159 # Open Media app on HU 160 def open_media_app_on_hu(self): 161 logging.info("Open Media app on HU") 162 self.discoverer.mbs.openBluetoothMediaApp() 163 164 # Pause Media app on HU 165 def pause_media_on_hu(self): 166 logging.info("Press PAUSE button on HU") 167 self.discoverer.mbs.pauseMedia() 168 169 # Play Media app on HU 170 def play_media_on_hu(self): 171 logging.info("Press PLAY button on HU") 172 self.discoverer.mbs.playMedia() 173 174 # Click on CANCEL button on Connect BT Audio page on HU 175 def click_on_cancel_bt_audio_connection_button_on_hu(self): 176 logging.info("Click on Cancel Bluetooth Audio connection button on HU") 177 self.discoverer.mbs.cancelBluetoothAudioConncetion() 178 179 # Reboot HU 180 def reboot_hu(self): 181 logging.info("Starting HU reboot...") 182 self.execute_shell_on_hu_device(constants.REBOOT) 183 184 # Is song playing on HU 185 def is_song_playing_on_hu(self): 186 logging.info("Checking is song playing on HU") 187 actual_song_playing_status = self.discoverer.mbs.isPlaying() 188 logging.info("Is song playing: %s", actual_song_playing_status) 189 return actual_song_playing_status 190 191 # Maximize playing song 192 def maximize_now_playing(self): 193 logging.info("Maximizing playing song on HU") 194 self.discoverer.mbs.maximizeNowPlaying() 195 196 # Minimize playing song 197 def minimize_now_playing(self): 198 logging.info("Maximizing playing song on HU") 199 self.discoverer.mbs.minimizeNowPlaying() 200 201 # Open playlist 202 def open_media_playlist(self): 203 logging.info("Open Playlist content on HU") 204 self.discoverer.mbs.openMediaAppMenuItems() 205 206 # Scroll playlist to the button 207 def scroll_playlist_to_the_button(self): 208 logging.info("Scroll Playlist to the button on HU") 209 self.discoverer.mbs.scrollPlayListDown() 210 211 # Select first visible song from playlist 212 def select_song_from_playlist(self): 213 logging.info("Select song from playlist on HU") 214 self.discoverer.mbs.clickOnSongFromPlaylist() 215 216 # Get playing Album title 217 def get_album_title_on_hu(self): 218 logging.info("Getting Album title on HU") 219 actual_album_title = self.discoverer.mbs.getAlbumTitle() 220 logging.info("Actual Album title on HU: <%s>", actual_album_title) 221 return actual_album_title 222 223 # Get Artist title on HU 224 def get_artist_title_on_hu(self): 225 logging.info("Getting Artist title on HU") 226 actual_artist_title = self.discoverer.mbs.getArtistrTitle() 227 logging.info("Actual Artist title on HU: <%s>", actual_artist_title) 228 return actual_artist_title 229 230 # Get current song playing time on HU 231 def get_current_song_playing_time_on_hu(self): 232 logging.info("Getting current song playing time on HU") 233 actual_current_song_playing_time = self.discoverer.mbs.getSongCurrentPlayingTime() 234 logging.info("Actual current song playing time on HU: <%s>", 235 actual_current_song_playing_time) 236 return actual_current_song_playing_time 237 238 def get_current_song_max_playing_time_on_hu(self): 239 logging.info("Getting current song max playing time on HU") 240 actual_current_song_max_playing_time = self.discoverer.mbs.getCurrentSongMaxPlayingTime() 241 logging.info("Actual current song max playing time on HU: <%s>", 242 actual_current_song_max_playing_time) 243 return actual_current_song_max_playing_time 244 245 # Check is NOW PLAYING label displayed 246 def is_now_playing_label_displayed(self): 247 logging.info("Checking is <Now Playing> label displayed on HU") 248 actual_is_now_playing_label_display_status = self.discoverer.mbs.isNowPlayingLabelVisible() 249 logging.info("<Now Playing> label displayed: <%s>", 250 actual_is_now_playing_label_display_status) 251 return actual_is_now_playing_label_display_status 252 253 # Check is PLAYLIST icon visible 254 def is_playlist_icon_visible(self): 255 logging.info("Checking is Playlist icon displayed on HU") 256 actual_is_playlist_icon_displayed = self.discoverer.mbs.isPlaylistIconVisible() 257 logging.info("Playlist icon displayed: <%s>", 258 actual_is_playlist_icon_displayed) 259 return actual_is_playlist_icon_displayed 260 261 # Click on Playlist icon 262 def click_on_playlist_icon(self): 263 logging.info("Click on Playlist icon on HU") 264 self.discoverer.mbs.clickOnPlaylistIcon() 265 266 # Open Youtube Music app on HU 267 def open_youtube_music_app_on_hu(self): 268 logging.info("Open <%s> app on HU", constants.YOUTUBE_MUSIC_APP) 269 self.common_utils.click_on_ui_element_with_text(constants.YOUTUBE_MUSIC_APP) 270 271 # Open Bluetooth Audio app on HU 272 def open_bluetooth_audio_app_on_hu(self): 273 logging.info("Open <%s> app on HU", constants.BLUETOOTH_AUDIO_APP) 274 self.common_utils.click_on_ui_element_with_text(constants.BLUETOOTH_AUDIO_APP) 275 276 # Open Media Apps menu 277 def open_media_apps_menu(self): 278 logging.info("Open Media apps menu items on HU") 279 self.discoverer.mbs.openMediaAppMenuItems() 280 281 # Open Bluetooth player 282 def open_bluetooth_player(self): 283 logging.info("Open <%s> on HU", constants.BLUETOOTH_PLAYER) 284 self.common_utils.click_on_ui_element_with_text(constants.BLUETOOTH_PLAYER) 285 286 # Open Radio app 287 def open_radio_app(self): 288 logging.info("Open <%s> app on HU", constants.RADIO_APP) 289 self.common_utils.click_on_ui_element_with_text(constants.RADIO_APP) 290 291 # Tune FM Radio on HU 292 def tune_fm_radio_on_hu(self, fm_frequency): 293 if re.match(constants.FM_FREQUENCY_PATTERN, fm_frequency): 294 logging.info("Tune FM Radio on HU for frequency <%s>", fm_frequency) 295 logging.info("Select <%s> range for radio", constants.RADIO_FM_RANGE) 296 self.common_utils.click_on_ui_element_with_text(constants.RADIO_FM_RANGE) 297 for x in fm_frequency: 298 self.common_utils.click_on_ui_element_with_text(x) 299 time.sleep(constants.WAIT_ONE_SEC) 300 logging.info("Confirm selected %s frequency on HU", constants.RADIO_FM_RANGE) 301 self.common_utils.click_on_ui_element_with_text(constants.CONFIRM_RADIO_FREQUENCY) 302 else: 303 logging.error("Invalid FM Radio frequency. Expected pattern: <%s>", 304 constants.FM_FREQUENCY_PATTERN) 305