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