1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.platform.helpers; 18 19 import android.app.Instrumentation; 20 import android.app.UiAutomation; 21 import android.content.Context; 22 import android.media.session.MediaController; 23 import android.media.session.MediaSessionManager; 24 import android.media.session.PlaybackState; 25 import android.os.SystemClock; 26 import android.platform.helpers.exceptions.UnknownUiException; 27 import android.support.test.uiautomator.By; 28 import android.support.test.uiautomator.Direction; 29 import android.support.test.uiautomator.UiObject; 30 import android.support.test.uiautomator.UiObject2; 31 import android.support.test.uiautomator.UiObjectNotFoundException; 32 import android.support.test.uiautomator.UiScrollable; 33 import android.support.test.uiautomator.UiSelector; 34 35 import java.util.List; 36 import java.util.regex.Pattern; 37 38 public class MediaCenterHelperImpl extends AbstractAutoStandardAppHelper 39 implements IAutoMediaHelper { 40 // Wait Time 41 private static final int UI_RESPONSE_WAIT_MS = 5000; 42 private static final int SHORT_RESPONSE_WAIT_MS = 1000; 43 44 private static final String MEDIA_LAUNCH_COMMAND = 45 "am start -a android.car.intent.action.MEDIA_TEMPLATE -e " 46 + "android.car.intent.extra.MEDIA_COMPONENT "; 47 48 private MediaSessionManager mMediaSessionManager; 49 private UiAutomation mUiAutomation; 50 MediaCenterHelperImpl(Instrumentation instr)51 public MediaCenterHelperImpl(Instrumentation instr) { 52 super(instr); 53 mUiAutomation = instr.getUiAutomation(); 54 mUiAutomation.adoptShellPermissionIdentity("android.permission.MEDIA_CONTENT_CONTROL"); 55 mMediaSessionManager = 56 (MediaSessionManager) 57 instr.getContext().getSystemService(Context.MEDIA_SESSION_SERVICE); 58 } 59 60 /** {@inheritDoc} */ 61 @Override getPackage()62 public String getPackage() { 63 return getApplicationConfig(AutoConfigConstants.MEDIA_CENTER_PACKAGE); 64 } 65 66 /** {@inheritDoc} */ open()67 public void open() { 68 openMediaApp(getApplicationConfig(AutoConfigConstants.MEDIA_ACTIVITY)); 69 } 70 openMediaApp(String packagename)71 private void openMediaApp(String packagename) { 72 pressHome(); 73 waitForIdle(); 74 executeShellCommand(MEDIA_LAUNCH_COMMAND + packagename); 75 } 76 77 /** {@inheritDoc} */ playMedia()78 public void playMedia() { 79 if (!isPlaying()) { 80 UiObject2 playButton = 81 findUiObject( 82 getResourceFromConfig( 83 AutoConfigConstants.MEDIA_CENTER, 84 AutoConfigConstants.MEDIA_CENTER_SCREEN, 85 AutoConfigConstants.PLAY_PAUSE_BUTTON)); 86 if (playButton == null) { 87 throw new UnknownUiException("Unable to find play/pause button"); 88 } 89 clickAndWaitForIdleScreen(playButton); 90 SystemClock.sleep(UI_RESPONSE_WAIT_MS); 91 } 92 } 93 94 /** {@inheritDoc} */ playPauseMediaFromHomeScreen()95 public void playPauseMediaFromHomeScreen() { 96 UiObject2 playButton = 97 findUiObject( 98 getResourceFromConfig( 99 AutoConfigConstants.MEDIA_CENTER, 100 AutoConfigConstants.MEDIA_CENTER_ON_HOME_SCREEN, 101 AutoConfigConstants.PLAY_PAUSE_BUTTON)); 102 if (playButton == null) { 103 throw new UnknownUiException("Unable to find play button from home screen"); 104 } 105 clickAndWaitForIdleScreen(playButton); 106 SystemClock.sleep(UI_RESPONSE_WAIT_MS); 107 } 108 109 /** {@inheritDoc} */ pauseMedia()110 public void pauseMedia() { 111 if (isPlaying()) { 112 UiObject2 pauseButton = 113 findUiObject( 114 getResourceFromConfig( 115 AutoConfigConstants.MEDIA_CENTER, 116 AutoConfigConstants.MEDIA_CENTER_SCREEN, 117 AutoConfigConstants.PLAY_PAUSE_BUTTON)); 118 if (pauseButton == null) { 119 throw new UnknownUiException("Unable to find pause button"); 120 } 121 clickAndWaitForIdleScreen(pauseButton); 122 SystemClock.sleep(UI_RESPONSE_WAIT_MS); 123 } 124 } 125 126 /** {@inheritDoc} */ clickNextTrack()127 public void clickNextTrack() { 128 UiObject2 nextTrackButton = 129 findUiObject( 130 getResourceFromConfig( 131 AutoConfigConstants.MEDIA_CENTER, 132 AutoConfigConstants.MEDIA_CENTER_SCREEN, 133 AutoConfigConstants.NEXT_BUTTON)); 134 if (nextTrackButton == null) { 135 throw new UnknownUiException("Unable to find next track button"); 136 } 137 clickAndWaitForIdleScreen(nextTrackButton); 138 SystemClock.sleep(UI_RESPONSE_WAIT_MS); 139 } 140 141 /** {@inheritDoc} */ clickNextTrackFromHomeScreen()142 public void clickNextTrackFromHomeScreen() { 143 UiObject2 nextTrackButton = 144 findUiObject( 145 getResourceFromConfig( 146 AutoConfigConstants.MEDIA_CENTER, 147 AutoConfigConstants.MEDIA_CENTER_ON_HOME_SCREEN, 148 AutoConfigConstants.NEXT_BUTTON)); 149 if (nextTrackButton == null) { 150 throw new UnknownUiException("Unable to find next track button from home screen"); 151 } 152 clickAndWaitForIdleScreen(nextTrackButton); 153 SystemClock.sleep(UI_RESPONSE_WAIT_MS); 154 } 155 156 /** {@inheritDoc} */ clickPreviousTrack()157 public void clickPreviousTrack() { 158 UiObject2 previousTrackButton = 159 findUiObject( 160 getResourceFromConfig( 161 AutoConfigConstants.MEDIA_CENTER, 162 AutoConfigConstants.MEDIA_CENTER_SCREEN, 163 AutoConfigConstants.PREVIOUS_BUTTON)); 164 if (previousTrackButton == null) { 165 throw new UnknownUiException("Unable to find previous track button"); 166 } 167 clickAndWaitForIdleScreen(previousTrackButton); 168 SystemClock.sleep(UI_RESPONSE_WAIT_MS); 169 } 170 171 /** {@inheritDoc} */ clickPreviousTrackFromHomeScreen()172 public void clickPreviousTrackFromHomeScreen() { 173 UiObject2 previousTrackButton = 174 findUiObject( 175 getResourceFromConfig( 176 AutoConfigConstants.MEDIA_CENTER, 177 AutoConfigConstants.MEDIA_CENTER_ON_HOME_SCREEN, 178 AutoConfigConstants.PREVIOUS_BUTTON)); 179 if (previousTrackButton == null) { 180 throw new UnknownUiException("Unable to find previous track button"); 181 } 182 clickAndWaitForIdleScreen(previousTrackButton); 183 SystemClock.sleep(UI_RESPONSE_WAIT_MS); 184 } 185 186 /** {@inheritDoc} */ clickShuffleAll()187 public void clickShuffleAll() { 188 UiObject2 shufflePlaylistButton = 189 findUiObject( 190 getResourceFromConfig( 191 AutoConfigConstants.MEDIA_CENTER, 192 AutoConfigConstants.MEDIA_CENTER_SCREEN, 193 AutoConfigConstants.SHUFFLE_BUTTON)); 194 if (shufflePlaylistButton == null) { 195 throw new UnknownUiException("Unable to find shuffle playlist button"); 196 } 197 clickAndWaitForIdleScreen(shufflePlaylistButton); 198 } 199 200 /** Click the nth instance among the visible menu items */ clickMenuItem(int instance)201 public void clickMenuItem(int instance) { 202 if (!mDevice.hasObject(By.scrollable(true))) { 203 // Menu is not open 204 return; 205 } 206 UiScrollable menuList = new UiScrollable(new UiSelector().scrollable(true)); 207 menuList.setAsVerticalList(); 208 try { 209 UiObject menuListItem = 210 menuList.getChildByInstance(new UiSelector().clickable(true), instance); 211 menuListItem.clickAndWaitForNewWindow(UI_RESPONSE_WAIT_MS); 212 waitForIdle(); 213 } catch (UiObjectNotFoundException e) { 214 throw new UnknownUiException("Unable to find menu item", e); 215 } 216 } 217 218 /** {@inheritDoc} */ openMenuWith(String... menuOptions)219 public void openMenuWith(String... menuOptions) { 220 for (String menu : menuOptions) { 221 UiObject2 menuButton = findUiObject(By.text(menu)); 222 if (menuButton != null) { 223 clickAndWaitForIdleScreen(menuButton); 224 waitForGone(By.text(menu)); 225 } else { 226 try { 227 UiObject menuItem_object = selectByName(menu); 228 menuItem_object.clickAndWaitForNewWindow(); 229 } catch (UiObjectNotFoundException exception) { 230 throw new UnknownUiException("Unable to find the menu item"); 231 } 232 } 233 SystemClock.sleep(SHORT_RESPONSE_WAIT_MS); 234 } 235 } 236 237 /** {@inheritDoc} */ openNowPlayingWith(String trackName)238 public void openNowPlayingWith(String trackName) { 239 UiObject2 nowPlayingButton = 240 findUiObject( 241 getResourceFromConfig( 242 AutoConfigConstants.MEDIA_CENTER, 243 AutoConfigConstants.MEDIA_CENTER_SCREEN, 244 AutoConfigConstants.PLAY_QUEUE_BUTTON)); 245 if (nowPlayingButton == null) { 246 throw new UnknownUiException("Unable to find Now playing button"); 247 } 248 clickAndWaitForIdleScreen(nowPlayingButton); 249 waitForWindowUpdate(getApplicationConfig(AutoConfigConstants.MEDIA_CENTER_PACKAGE)); 250 UiObject2 playTrackName = findUiObject(By.text(trackName)); 251 if (playTrackName != null) { 252 clickAndWaitForIdleScreen(playTrackName); 253 } else { 254 try { 255 UiObject menuItem_object = selectByName(trackName); 256 menuItem_object.clickAndWaitForNewWindow(); 257 } catch (UiObjectNotFoundException exception) { 258 throw new UnknownUiException("Unable to find the trackname from Now playing list"); 259 } 260 } 261 } 262 263 /** {@inheritDoc} */ getMediaTrackName()264 public String getMediaTrackName() { 265 String track; 266 UiObject2 mediaControl = 267 findUiObject( 268 getResourceFromConfig( 269 AutoConfigConstants.MEDIA_CENTER, 270 AutoConfigConstants.MEDIA_CENTER_SCREEN, 271 AutoConfigConstants.MINIMIZED_MEDIA_CONTROLS)); 272 if (mediaControl != null) { 273 track = getMediaTrackNameFromMinimizedControl(); 274 } else { 275 track = getMediaTrackNameFromPlayback(); 276 } 277 return track; 278 } 279 280 /** {@inheritDoc} */ getMediaTrackNameFromHomeScreen()281 public String getMediaTrackNameFromHomeScreen() { 282 String trackName; 283 UiObject2 trackNameText = 284 findUiObject( 285 getResourceFromConfig( 286 AutoConfigConstants.MEDIA_CENTER, 287 AutoConfigConstants.MEDIA_CENTER_ON_HOME_SCREEN, 288 AutoConfigConstants.TRACK_NAME)); 289 if (trackNameText == null) { 290 throw new UnknownUiException("Unable to find track name from Home Screen"); 291 } 292 trackName = trackNameText.getText(); 293 return trackName; 294 } 295 getMediaTrackNameFromMinimizedControl()296 private String getMediaTrackNameFromMinimizedControl() { 297 String trackName; 298 UiObject2 trackNameText = 299 findUiObject( 300 getResourceFromConfig( 301 AutoConfigConstants.MEDIA_CENTER, 302 AutoConfigConstants.MEDIA_CENTER_SCREEN, 303 AutoConfigConstants.TRACK_NAME_MINIMIZED_CONTROL)); 304 if (trackNameText == null) { 305 throw new UnknownUiException("Unable to find track name from minimized control"); 306 } 307 trackName = trackNameText.getText(); 308 return trackName; 309 } 310 getMediaTrackNameFromPlayback()311 private String getMediaTrackNameFromPlayback() { 312 String trackName; 313 waitForIdle(); 314 UiObject2 trackNameText = 315 findUiObject( 316 getResourceFromConfig( 317 AutoConfigConstants.MEDIA_CENTER, 318 AutoConfigConstants.MEDIA_CENTER_SCREEN, 319 AutoConfigConstants.TRACK_NAME)); 320 if (trackNameText == null) { 321 throw new UnknownUiException("Unable to find track name from now playing"); 322 } 323 trackName = trackNameText.getText(); 324 return trackName; 325 } 326 327 /** {@inheritDoc} */ goBackToMediaHomePage()328 public void goBackToMediaHomePage() { 329 minimizeNowPlaying(); 330 UiObject2 back_btn = 331 findUiObject( 332 getResourceFromConfig( 333 AutoConfigConstants.MEDIA_CENTER, 334 AutoConfigConstants.MEDIA_CENTER_SCREEN, 335 AutoConfigConstants.BACK_BUTTON)); 336 while (back_btn != null) { 337 clickAndWaitForIdleScreen(back_btn); 338 back_btn = 339 findUiObject( 340 getResourceFromConfig( 341 AutoConfigConstants.MEDIA_CENTER, 342 AutoConfigConstants.MEDIA_CENTER_SCREEN, 343 AutoConfigConstants.BACK_BUTTON)); 344 } 345 } 346 347 /** 348 * Minimize the Now Playing window. 349 */ minimizeNowPlaying()350 private void minimizeNowPlaying() { 351 UiObject2 trackNameText = 352 findUiObject( 353 getResourceFromConfig( 354 AutoConfigConstants.MEDIA_CENTER, 355 AutoConfigConstants.MEDIA_CENTER_SCREEN, 356 AutoConfigConstants.TRACK_NAME)); 357 if (trackNameText != null) { 358 trackNameText.swipe(Direction.DOWN, 1.0f, 500); 359 } 360 } 361 362 /** 363 * Scrolls through the list in search of the provided menu 364 * 365 * @param menu : menu to search 366 * @return UiObject found for the menu searched 367 */ selectByName(String menu)368 private UiObject selectByName(String menu) throws UiObjectNotFoundException { 369 UiObject menuListItem = null; 370 UiScrollable menuList = new UiScrollable(new UiSelector().scrollable(true)); 371 menuList.setAsVerticalList(); 372 menuListItem = 373 menuList.getChildByText( 374 new UiSelector().className(android.widget.TextView.class.getName()), menu); 375 mDevice.waitForIdle(); 376 return menuListItem; 377 } 378 379 /** {@inheritDoc} */ 380 @Override isPlaying()381 public boolean isPlaying() { 382 List<MediaController> controllers = mMediaSessionManager.getActiveSessions(null); 383 if (controllers.size() == 0) { 384 throw new RuntimeException("Unable to find Media Controller"); 385 } 386 PlaybackState state = controllers.get(0).getPlaybackState(); 387 return state.getState() == PlaybackState.STATE_PLAYING; 388 } 389 390 /** {@inheritDoc} */ 391 @Override getMediaAppTitle()392 public String getMediaAppTitle() { 393 UiObject2 mediaAppTitle = findUiObject(getResourceFromConfig( 394 AutoConfigConstants.MEDIA_CENTER, 395 AutoConfigConstants.MEDIA_APP, 396 AutoConfigConstants.MEDIA_APP_TITLE)); 397 if (mediaAppTitle == null) { 398 throw new UnknownUiException("Unable to find Media app title text."); 399 } 400 return mediaAppTitle.getText(); 401 } 402 403 /** {@inheritDoc} */ 404 @Override openMediaAppMenuItems()405 public void openMediaAppMenuItems() { 406 List<UiObject2> menuItemElements = findUiObjects(getResourceFromConfig( 407 AutoConfigConstants.MEDIA_CENTER, 408 AutoConfigConstants.MEDIA_APP, 409 AutoConfigConstants.MEDIA_APP_DROP_DOWN_MENU)); 410 if (menuItemElements.size() == 0) { 411 throw new UnknownUiException("Unable to find Media drop down."); 412 } 413 // Media menu drop down is the last item in Media App Screen 414 int positionOfMenuItemDropDown = menuItemElements.size() - 1; 415 clickAndWaitForIdleScreen(menuItemElements.get(positionOfMenuItemDropDown)); 416 } 417 418 /** {@inheritDoc} */ 419 @Override areMediaAppsPresent(List<String> mediaAppsNames)420 public boolean areMediaAppsPresent(List<String> mediaAppsNames) { 421 UiObject2 mediaAppPageTitle = 422 findUiObject( 423 getResourceFromConfig( 424 AutoConfigConstants.MEDIA_CENTER, 425 AutoConfigConstants.MEDIA_APPS_GRID, 426 AutoConfigConstants.MEDIA_APPS_GRID_TITLE)); 427 if (mediaAppPageTitle == null) { 428 throw new RuntimeException("Media apps grid activity not open."); 429 } 430 if (mediaAppsNames == null || mediaAppsNames.size() == 0) { 431 return false; 432 } 433 // Scroll and find media apps in Media App Grid 434 for (String expectedApp : mediaAppsNames) { 435 UiObject2 mediaApp = 436 scrollAndFindUiObject( 437 By.text(Pattern.compile(expectedApp, Pattern.CASE_INSENSITIVE))); 438 if (mediaApp == null || !mediaApp.getText().equals(expectedApp)) { 439 return false; 440 } 441 } 442 return true; 443 } 444 445 /** 446 * {@inheritDoc} 447 */ 448 @Override openApp(String appName)449 public void openApp(String appName) { 450 SystemClock.sleep(SHORT_RESPONSE_WAIT_MS); // to avoid stale object error 451 UiObject2 app = scrollAndFindUiObject(By.text(appName)); 452 if (app != null) { 453 clickAndWaitForIdleScreen(app); 454 } else { 455 throw new IllegalStateException(String.format("App %s cannot be found", appName)); 456 } 457 } 458 459 /** 460 * {@inheritDoc} 461 */ 462 @Override openMediaAppSettingsPage()463 public void openMediaAppSettingsPage() { 464 List<UiObject2> mediaAppMenuItem = findUiObjects(getResourceFromConfig( 465 AutoConfigConstants.MEDIA_CENTER, 466 AutoConfigConstants.MEDIA_APP, 467 AutoConfigConstants.MEDIA_APP_DROP_DOWN_MENU)); 468 if (mediaAppMenuItem.size() == 0) { 469 throw new UnknownUiException("Unable to find Media App menu items."); 470 } 471 // settings page is 2nd last item in Menu Item list 472 int settingsItemPosition = mediaAppMenuItem.size() - 2; 473 clickAndWaitForIdleScreen(mediaAppMenuItem.get(settingsItemPosition)); 474 } 475 476 /** {@inheritDoc} */ 477 @Override getMediaAppUserNotLoggedInErrorMessage()478 public String getMediaAppUserNotLoggedInErrorMessage() { 479 UiObject2 noLoginMsg = findUiObject(getResourceFromConfig( 480 AutoConfigConstants.MEDIA_CENTER, 481 AutoConfigConstants.MEDIA_APP, 482 AutoConfigConstants.MEDIA_APP_NO_LOGIN_MSG)); 483 if (noLoginMsg == null) { 484 throw new UnknownUiException("Unable to find Media app error text."); 485 } 486 return noLoginMsg.getText(); 487 } 488 } 489