├── settings.gradle
├── .gitignore
├── screenshots
├── phone.png
├── android_tv.png
├── icon-web.png
├── android_auto.png
├── android_wear_1.png
├── android_wear_2.png
├── phone_cast_dialog.png
├── phone_lockscreen.png
└── phone_fullscreen_player.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── mobile
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── bg.png
│ │ │ │ ├── default_background.xml
│ │ │ │ ├── fullscreen_toolbar_bg_gradient.xml
│ │ │ │ ├── fullscreen_bg_gradient.xml
│ │ │ │ └── ic_equalizer_white_36dp.xml
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── ic_menu.png
│ │ │ │ ├── ic_star_on.png
│ │ │ │ ├── ic_by_genre.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_star_off.png
│ │ │ │ ├── ic_default_art.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_launcher_white.png
│ │ │ │ ├── ic_close_black_24dp.png
│ │ │ │ ├── ic_pause_black_36dp.png
│ │ │ │ ├── ic_allmusic_black_24dp.png
│ │ │ │ ├── ic_skip_next_white_24dp.png
│ │ │ │ ├── ic_skip_next_white_48dp.png
│ │ │ │ ├── ic_equalizer1_white_36dp.png
│ │ │ │ ├── ic_equalizer2_white_36dp.png
│ │ │ │ ├── ic_equalizer3_white_36dp.png
│ │ │ │ ├── ic_play_arrow_black_36dp.png
│ │ │ │ ├── uamp_ic_pause_white_24dp.png
│ │ │ │ ├── uamp_ic_pause_white_48dp.png
│ │ │ │ ├── ic_info_outline_grey600_48dp.png
│ │ │ │ ├── ic_playlist_music_black_24dp.png
│ │ │ │ ├── ic_skip_previous_white_24dp.png
│ │ │ │ ├── ic_skip_previous_white_48dp.png
│ │ │ │ ├── uamp_ic_play_arrow_white_24dp.png
│ │ │ │ └── uamp_ic_play_arrow_white_48dp.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── ic_menu.png
│ │ │ │ ├── ic_star_on.png
│ │ │ │ ├── ic_by_genre.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_star_off.png
│ │ │ │ ├── ic_default_art.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_launcher_white.png
│ │ │ │ ├── ic_close_black_24dp.png
│ │ │ │ ├── ic_pause_black_36dp.png
│ │ │ │ ├── ic_allmusic_black_24dp.png
│ │ │ │ ├── ic_skip_next_white_24dp.png
│ │ │ │ ├── ic_skip_next_white_48dp.png
│ │ │ │ ├── ic_equalizer1_white_36dp.png
│ │ │ │ ├── ic_equalizer2_white_36dp.png
│ │ │ │ ├── ic_equalizer3_white_36dp.png
│ │ │ │ ├── ic_play_arrow_black_36dp.png
│ │ │ │ ├── uamp_ic_pause_white_24dp.png
│ │ │ │ ├── uamp_ic_pause_white_48dp.png
│ │ │ │ ├── ic_info_outline_grey600_48dp.png
│ │ │ │ ├── ic_playlist_music_black_24dp.png
│ │ │ │ ├── ic_skip_previous_white_24dp.png
│ │ │ │ ├── ic_skip_previous_white_48dp.png
│ │ │ │ ├── uamp_ic_play_arrow_white_24dp.png
│ │ │ │ └── uamp_ic_play_arrow_white_48dp.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── ic_menu.png
│ │ │ │ ├── banner_tv.png
│ │ │ │ ├── ic_by_genre.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_star_off.png
│ │ │ │ ├── ic_star_on.png
│ │ │ │ ├── ic_default_art.png
│ │ │ │ ├── ic_launcher_white.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_close_black_24dp.png
│ │ │ │ ├── ic_pause_black_36dp.png
│ │ │ │ ├── ic_allmusic_black_24dp.png
│ │ │ │ ├── ic_equalizer1_white_36dp.png
│ │ │ │ ├── ic_equalizer2_white_36dp.png
│ │ │ │ ├── ic_equalizer3_white_36dp.png
│ │ │ │ ├── ic_play_arrow_black_36dp.png
│ │ │ │ ├── ic_skip_next_white_24dp.png
│ │ │ │ ├── ic_skip_next_white_48dp.png
│ │ │ │ ├── uamp_ic_pause_white_24dp.png
│ │ │ │ ├── uamp_ic_pause_white_48dp.png
│ │ │ │ ├── ic_skip_previous_white_24dp.png
│ │ │ │ ├── ic_skip_previous_white_48dp.png
│ │ │ │ ├── ic_info_outline_grey600_48dp.png
│ │ │ │ ├── ic_playlist_music_black_24dp.png
│ │ │ │ ├── uamp_ic_play_arrow_white_24dp.png
│ │ │ │ └── uamp_ic_play_arrow_white_48dp.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── ic_menu.png
│ │ │ │ ├── ic_by_genre.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_star_off.png
│ │ │ │ ├── ic_star_on.png
│ │ │ │ ├── ic_default_art.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_launcher_white.png
│ │ │ │ ├── ic_close_black_24dp.png
│ │ │ │ ├── ic_pause_black_36dp.png
│ │ │ │ ├── ic_allmusic_black_24dp.png
│ │ │ │ ├── ic_skip_next_white_24dp.png
│ │ │ │ ├── ic_skip_next_white_48dp.png
│ │ │ │ ├── ic_equalizer1_white_36dp.png
│ │ │ │ ├── ic_equalizer2_white_36dp.png
│ │ │ │ ├── ic_equalizer3_white_36dp.png
│ │ │ │ ├── ic_play_arrow_black_36dp.png
│ │ │ │ ├── uamp_ic_pause_white_24dp.png
│ │ │ │ ├── uamp_ic_pause_white_48dp.png
│ │ │ │ ├── ic_info_outline_grey600_48dp.png
│ │ │ │ ├── ic_playlist_music_black_24dp.png
│ │ │ │ ├── ic_skip_previous_white_24dp.png
│ │ │ │ ├── ic_skip_previous_white_48dp.png
│ │ │ │ ├── uamp_ic_play_arrow_white_24dp.png
│ │ │ │ └── uamp_ic_play_arrow_white_48dp.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ │ ├── ic_menu.png
│ │ │ │ ├── ic_star_on.png
│ │ │ │ ├── ic_by_genre.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_star_off.png
│ │ │ │ ├── ic_default_art.png
│ │ │ │ ├── ic_notification.png
│ │ │ │ ├── ic_launcher_white.png
│ │ │ │ ├── ic_close_black_24dp.png
│ │ │ │ ├── ic_pause_black_36dp.png
│ │ │ │ ├── ic_allmusic_black_24dp.png
│ │ │ │ ├── ic_equalizer1_white_36dp.png
│ │ │ │ ├── ic_equalizer2_white_36dp.png
│ │ │ │ ├── ic_equalizer3_white_36dp.png
│ │ │ │ ├── ic_play_arrow_black_36dp.png
│ │ │ │ ├── ic_skip_next_white_24dp.png
│ │ │ │ ├── ic_skip_next_white_48dp.png
│ │ │ │ ├── uamp_ic_pause_white_24dp.png
│ │ │ │ ├── uamp_ic_pause_white_48dp.png
│ │ │ │ ├── ic_skip_previous_white_24dp.png
│ │ │ │ ├── ic_skip_previous_white_48dp.png
│ │ │ │ ├── ic_info_outline_grey600_48dp.png
│ │ │ │ ├── ic_playlist_music_black_24dp.png
│ │ │ │ ├── uamp_ic_play_arrow_white_24dp.png
│ │ │ │ └── uamp_ic_play_arrow_white_48dp.png
│ │ │ ├── xml
│ │ │ │ └── automotive_app_desc.xml
│ │ │ ├── values
│ │ │ │ ├── ids.xml
│ │ │ │ ├── strings_notifications.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ ├── values-h800dp
│ │ │ │ └── dimens.xml
│ │ │ ├── animator
│ │ │ │ ├── slide_in_from_bottom.xml
│ │ │ │ ├── slide_in_from_left.xml
│ │ │ │ ├── slide_in_from_right.xml
│ │ │ │ ├── slide_out_to_bottom.xml
│ │ │ │ ├── slide_out_to_left.xml
│ │ │ │ └── slide_out_to_right.xml
│ │ │ ├── anim
│ │ │ │ ├── fade_in.xml
│ │ │ │ └── fade_out.xml
│ │ │ ├── animator-v21
│ │ │ │ ├── slide_in_from_bottom.xml
│ │ │ │ ├── slide_in_from_left.xml
│ │ │ │ ├── slide_in_from_right.xml
│ │ │ │ ├── slide_out_to_bottom.xml
│ │ │ │ └── slide_out_to_left.xml
│ │ │ ├── menu
│ │ │ │ ├── main.xml
│ │ │ │ └── drawer.xml
│ │ │ ├── layout
│ │ │ │ ├── tv_vertical_grid.xml
│ │ │ │ ├── tv_playback_controls.xml
│ │ │ │ ├── tv_activity_player.xml
│ │ │ │ ├── nav_header.xml
│ │ │ │ ├── include_toolbar.xml
│ │ │ │ ├── fragment_list.xml
│ │ │ │ ├── activity_player.xml
│ │ │ │ ├── media_list_item.xml
│ │ │ │ ├── activity_placeholder.xml
│ │ │ │ └── fragment_playback_controls.xml
│ │ │ └── values-v21
│ │ │ │ └── styles.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── android
│ │ │ └── uamp
│ │ │ ├── ui
│ │ │ ├── MediaBrowserProvider.java
│ │ │ ├── PlaceholderActivity.java
│ │ │ ├── NowPlayingActivity.java
│ │ │ ├── tv
│ │ │ │ ├── TvVerticalGridActivity.java
│ │ │ │ ├── TvBrowseActivity.java
│ │ │ │ └── TvPlaybackActivity.java
│ │ │ └── MediaItemViewHolder.java
│ │ │ ├── model
│ │ │ ├── MusicProviderSource.java
│ │ │ ├── MutableMediaMetadata.java
│ │ │ └── RemoteJSONSource.java
│ │ │ ├── utils
│ │ │ ├── NetworkHelper.java
│ │ │ ├── TvHelper.java
│ │ │ ├── ResourceHelper.java
│ │ │ ├── WearHelper.java
│ │ │ ├── LogHelper.java
│ │ │ ├── BitmapHelper.java
│ │ │ ├── CarHelper.java
│ │ │ └── MediaIDHelper.java
│ │ │ ├── CastOptionsProvider.java
│ │ │ ├── playback
│ │ │ └── Playback.java
│ │ │ ├── VoiceSearchParams.java
│ │ │ └── AlbumArtCache.java
│ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── android
│ │ │ └── uamp
│ │ │ ├── playback
│ │ │ ├── SimplePlaybackServiceCallback.java
│ │ │ ├── SimpleMetadataUpdateListener.java
│ │ │ └── SimplePlayback.java
│ │ │ ├── TestSetupHelper.java
│ │ │ └── utils
│ │ │ └── SimpleMusicProviderSource.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── android
│ │ └── uamp
│ │ └── utils
│ │ └── MediaIDHelperTest.java
├── .gitignore
├── libs
│ └── licenses
│ │ └── gradle.LICENSE
├── proguard-rules.pro
└── build.gradle
├── docs
└── cast_styled_receiver
│ ├── uamp_cast_logo.png
│ ├── uamp_cast_splash.png
│ ├── uamp_cast_watermark.png
│ └── style.css
├── .google
└── packaging.yaml
├── gradle.properties
├── CONTRIB.md
├── README.md
├── gradlew.bat
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':mobile'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea
4 | .DS_Store
5 | /build
6 | *.iml
7 |
--------------------------------------------------------------------------------
/screenshots/phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/screenshots/phone.png
--------------------------------------------------------------------------------
/screenshots/android_tv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/screenshots/android_tv.png
--------------------------------------------------------------------------------
/screenshots/icon-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/screenshots/icon-web.png
--------------------------------------------------------------------------------
/screenshots/android_auto.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/screenshots/android_auto.png
--------------------------------------------------------------------------------
/screenshots/android_wear_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/screenshots/android_wear_1.png
--------------------------------------------------------------------------------
/screenshots/android_wear_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/screenshots/android_wear_2.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/screenshots/phone_cast_dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/screenshots/phone_cast_dialog.png
--------------------------------------------------------------------------------
/screenshots/phone_lockscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/screenshots/phone_lockscreen.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable/bg.png
--------------------------------------------------------------------------------
/screenshots/phone_fullscreen_player.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/screenshots/phone_fullscreen_player.png
--------------------------------------------------------------------------------
/docs/cast_styled_receiver/uamp_cast_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/docs/cast_styled_receiver/uamp_cast_logo.png
--------------------------------------------------------------------------------
/docs/cast_styled_receiver/uamp_cast_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/docs/cast_styled_receiver/uamp_cast_splash.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_menu.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_menu.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_menu.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_star_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_star_on.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_star_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_star_on.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/banner_tv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/banner_tv.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_menu.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_menu.png
--------------------------------------------------------------------------------
/docs/cast_styled_receiver/uamp_cast_watermark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/docs/cast_styled_receiver/uamp_cast_watermark.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_by_genre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_by_genre.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_star_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_star_off.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_by_genre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_by_genre.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_star_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_star_off.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_by_genre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_by_genre.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_star_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_star_off.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_star_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_star_on.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_by_genre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_by_genre.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_star_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_star_off.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_star_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_star_on.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_star_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_star_on.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_default_art.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_default_art.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_notification.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_default_art.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_default_art.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_notification.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_default_art.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_default_art.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_by_genre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_by_genre.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_star_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_star_off.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_launcher_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_launcher_white.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_launcher_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_launcher_white.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_launcher_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_launcher_white.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_notification.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_default_art.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_default_art.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_default_art.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_default_art.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_close_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_close_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_pause_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_pause_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_close_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_close_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_pause_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_pause_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_close_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_close_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_pause_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_pause_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_launcher_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_launcher_white.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_launcher_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_launcher_white.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_allmusic_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_allmusic_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_skip_next_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_skip_next_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_skip_next_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_skip_next_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_allmusic_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_allmusic_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_skip_next_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_skip_next_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_skip_next_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_skip_next_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_allmusic_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_allmusic_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_close_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_close_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_pause_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_pause_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_close_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_close_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_pause_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_pause_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_equalizer1_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_equalizer1_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_equalizer2_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_equalizer2_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_equalizer3_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_equalizer3_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_play_arrow_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_play_arrow_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/uamp_ic_pause_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/uamp_ic_pause_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/uamp_ic_pause_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/uamp_ic_pause_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_equalizer1_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_equalizer1_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_equalizer2_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_equalizer2_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_equalizer3_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_equalizer3_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_play_arrow_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_play_arrow_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/uamp_ic_pause_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/uamp_ic_pause_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/uamp_ic_pause_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/uamp_ic_pause_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_equalizer1_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_equalizer1_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_equalizer2_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_equalizer2_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_equalizer3_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_equalizer3_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_play_arrow_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_play_arrow_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_skip_next_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_skip_next_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_skip_next_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_skip_next_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/uamp_ic_pause_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/uamp_ic_pause_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/uamp_ic_pause_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/uamp_ic_pause_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_allmusic_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_allmusic_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_skip_next_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_skip_next_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_skip_next_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_skip_next_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_allmusic_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_allmusic_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_info_outline_grey600_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_info_outline_grey600_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_playlist_music_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_playlist_music_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_skip_previous_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_skip_previous_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_skip_previous_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/ic_skip_previous_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_info_outline_grey600_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_info_outline_grey600_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_playlist_music_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_playlist_music_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_skip_previous_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_skip_previous_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_skip_previous_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/ic_skip_previous_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_skip_previous_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_skip_previous_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_skip_previous_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_skip_previous_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_equalizer1_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_equalizer1_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_equalizer2_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_equalizer2_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_equalizer3_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_equalizer3_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_play_arrow_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_play_arrow_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/uamp_ic_pause_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/uamp_ic_pause_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/uamp_ic_pause_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/uamp_ic_pause_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_equalizer1_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_equalizer1_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_equalizer2_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_equalizer2_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_equalizer3_white_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_equalizer3_white_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_play_arrow_black_36dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_play_arrow_black_36dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_skip_next_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_skip_next_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_skip_next_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_skip_next_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/uamp_ic_pause_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/uamp_ic_pause_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/uamp_ic_pause_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/uamp_ic_pause_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/uamp_ic_play_arrow_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/uamp_ic_play_arrow_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/uamp_ic_play_arrow_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-hdpi/uamp_ic_play_arrow_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/uamp_ic_play_arrow_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/uamp_ic_play_arrow_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/uamp_ic_play_arrow_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-mdpi/uamp_ic_play_arrow_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_info_outline_grey600_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_info_outline_grey600_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_playlist_music_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/ic_playlist_music_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/uamp_ic_play_arrow_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/uamp_ic_play_arrow_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/uamp_ic_play_arrow_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xhdpi/uamp_ic_play_arrow_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_info_outline_grey600_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_info_outline_grey600_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_playlist_music_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_playlist_music_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_skip_previous_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_skip_previous_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_skip_previous_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/ic_skip_previous_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_skip_previous_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_skip_previous_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_skip_previous_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_skip_previous_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/uamp_ic_play_arrow_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/uamp_ic_play_arrow_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/uamp_ic_play_arrow_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxhdpi/uamp_ic_play_arrow_white_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_info_outline_grey600_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_info_outline_grey600_48dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_playlist_music_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/ic_playlist_music_black_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/uamp_ic_play_arrow_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/uamp_ic_play_arrow_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/uamp_ic_play_arrow_white_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angebagui/android-UniversalMusicPlayer/master/mobile/src/main/res/drawable-xxxhdpi/uamp_ic_play_arrow_white_48dp.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jun 02 15:18:55 PDT 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable/default_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
9 |
--------------------------------------------------------------------------------
/docs/cast_styled_receiver/style.css:
--------------------------------------------------------------------------------
1 | .background {
2 | background-image: linear-gradient(#000000, #250900);
3 | }
4 |
5 | .logo {
6 | /* max 1280x400, padding-top: 25% */
7 | background-image: url(uamp_cast_logo.png);
8 | background-size: auto 30%;
9 | }
10 |
11 | .progressBar {
12 | background-color: #FF5A26;
13 | }
14 |
15 | .splash {
16 | /* max 1280x400, padding-top: 25% */
17 | background-image: url(uamp_cast_splash.png);
18 | }
19 |
20 | .watermark {
21 | background-image: url(uamp_cast_watermark.png);
22 | background-size: auto 57px;
23 | }
24 |
--------------------------------------------------------------------------------
/mobile/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | #built application files
3 | *.apk
4 | *.ap_
5 |
6 | # files for the dex VM
7 | *.dex
8 |
9 | # Java class files
10 | *.class
11 |
12 | # generated files
13 | bin/
14 | gen/
15 |
16 | # Local configuration file (sdk path, etc)
17 | local.properties
18 |
19 | # Windows thumbnail db
20 | Thumbs.db
21 |
22 | # OSX files
23 | .DS_Store
24 |
25 | # Eclipse project files
26 | .classpath
27 | .project
28 |
29 | # Android Studio
30 | .idea
31 | *.iml
32 |
33 | #.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
34 | .gradle
35 | build/
36 |
--------------------------------------------------------------------------------
/mobile/libs/licenses/gradle.LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2007-2011 the original author or authors
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 |
15 |
--------------------------------------------------------------------------------
/mobile/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/google/home/mangini/tools/android-studio/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/mobile/src/main/res/xml/automotive_app_desc.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values-h800dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | 96dp
20 |
21 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/ui/MediaBrowserProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.ui;
17 |
18 | import android.support.v4.media.MediaBrowserCompat;
19 |
20 | public interface MediaBrowserProvider {
21 | MediaBrowserCompat getMediaBrowser();
22 | }
23 |
--------------------------------------------------------------------------------
/.google/packaging.yaml:
--------------------------------------------------------------------------------
1 | # GOOGLE SAMPLE PACKAGING DATA
2 | #
3 | # This file is used by Google as part of our samples packaging process.
4 | # End users may safely ignore this file. It has no relevance to other systems.
5 | ---
6 | status: PUBLISHED
7 | technologies: [Android, Android Auto, Google Cast, Android Wear, Android TV]
8 | categories: [Getting Started, Media, UI]
9 | languages: [Java]
10 | solutions: [Mobile]
11 |
12 | github: googlesamples/android-UniversalMusicPlayer
13 |
14 | level: INTERMEDIATE
15 |
16 | icon: screenshots/icon-web.png
17 |
18 | apiRefs:
19 | - android:android.media.session.MediaSession
20 | - android:android.media.session.MediaSession.Callback
21 | - android:android.media.session.MediaController
22 | - android:android.service.media.MediaBrowserService
23 | - android:android.media.browse.MediaBrowser
24 | - android:android.app.Notification.MediaStyle
25 |
26 | license: apache2-android
27 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator/slide_in_from_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator/slide_in_from_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator/slide_in_from_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator/slide_out_to_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable/fullscreen_toolbar_bg_gradient.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/mobile/src/main/res/anim/fade_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
23 |
--------------------------------------------------------------------------------
/mobile/src/main/res/anim/fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
23 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values/strings_notifications.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | Stop Casting
19 | Pause
20 | Play
21 | Previous
22 | Next
23 |
24 |
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable/fullscreen_bg_gradient.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
24 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/model/MusicProviderSource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 com.example.android.uamp.model;
18 |
19 | import android.support.v4.media.MediaMetadataCompat;
20 |
21 | import java.util.Iterator;
22 |
23 | public interface MusicProviderSource {
24 | String CUSTOM_METADATA_TRACK_SOURCE = "__SOURCE__";
25 | Iterator iterator();
26 | }
27 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator-v21/slide_in_from_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
24 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator-v21/slide_in_from_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
24 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator-v21/slide_in_from_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
24 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator-v21/slide_out_to_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
24 |
--------------------------------------------------------------------------------
/mobile/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
25 |
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable/ic_equalizer_white_36dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator/slide_out_to_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator/slide_out_to_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/tv_vertical_grid.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
21 |
22 |
27 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/tv_playback_controls.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
20 |
21 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/mobile/src/main/res/animator-v21/slide_out_to_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
20 |
25 |
30 |
31 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/ui/PlaceholderActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.ui;
17 |
18 | import android.os.Bundle;
19 |
20 | import com.example.android.uamp.R;
21 |
22 | /**
23 | * Placeholder activity for features that are not implemented in this sample, but
24 | * are in the navigation drawer.
25 | */
26 | public class PlaceholderActivity extends BaseActivity {
27 |
28 | @Override
29 | public void onCreate(Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 | setContentView(R.layout.activity_placeholder);
32 | initializeToolbar();
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/mobile/src/main/res/menu/drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/mobile/src/androidTest/java/com/example/android/uamp/playback/SimplePlaybackServiceCallback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.playback;
17 |
18 | import android.support.v4.media.session.PlaybackStateCompat;
19 |
20 | class SimplePlaybackServiceCallback implements PlaybackManager.PlaybackServiceCallback {
21 | @Override
22 | public void onPlaybackStart() {
23 |
24 | }
25 |
26 | @Override
27 | public void onNotificationRequired() {
28 |
29 | }
30 |
31 | @Override
32 | public void onPlaybackStop() {
33 |
34 | }
35 |
36 | @Override
37 | public void onPlaybackStateUpdated(PlaybackStateCompat newState) {
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/tv_activity_player.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
22 |
28 |
29 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/nav_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
26 |
27 |
32 |
33 |
--------------------------------------------------------------------------------
/mobile/src/androidTest/java/com/example/android/uamp/playback/SimpleMetadataUpdateListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.playback;
17 |
18 | import android.support.v4.media.MediaMetadataCompat;
19 | import android.support.v4.media.session.MediaSessionCompat;
20 |
21 | import java.util.List;
22 |
23 | public class SimpleMetadataUpdateListener implements QueueManager.MetadataUpdateListener{
24 | @Override
25 | public void onMetadataChanged(MediaMetadataCompat metadata) {
26 | }
27 |
28 | @Override
29 | public void onMetadataRetrieveError() {
30 | }
31 |
32 | @Override
33 | public void onCurrentQueueIndexUpdated(int queueIndex) {
34 | }
35 |
36 | @Override
37 | public void onQueueUpdated(String title, List newQueue) {
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/utils/NetworkHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 com.example.android.uamp.utils;
18 |
19 | import android.content.Context;
20 | import android.net.ConnectivityManager;
21 | import android.net.NetworkInfo;
22 |
23 | /**
24 | * Generic reusable network methods.
25 | */
26 | public class NetworkHelper {
27 | /**
28 | * @param context to use to check for network connectivity.
29 | * @return true if connected, false otherwise.
30 | */
31 | public static boolean isOnline(Context context) {
32 | ConnectivityManager connMgr = (ConnectivityManager)
33 | context.getSystemService(Context.CONNECTIVITY_SERVICE);
34 | NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
35 | return (networkInfo != null && networkInfo.isConnected());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/include_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
24 |
25 |
32 |
33 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2014 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 |
15 |
16 | # Project-wide Gradle settings.
17 |
18 | # IDE (e.g. Android Studio) users:
19 | # Gradle settings configured through the IDE *will override*
20 | # any settings specified in this file.
21 |
22 | # For more details on how to configure your build environment visit
23 | # http://www.gradle.org/docs/current/userguide/build_environment.html
24 |
25 | # Specifies the JVM arguments used for the daemon process.
26 | # The setting is particularly useful for tweaking memory settings.
27 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
28 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
29 |
30 | # When configured, Gradle will run in incubating parallel mode.
31 | # This option should only be used with decoupled projects. More details, visit
32 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
33 | # org.gradle.parallel=true
34 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/CastOptionsProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 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 com.example.android.uamp;
18 |
19 | import android.content.Context;
20 |
21 | import com.google.android.gms.cast.framework.CastOptions;
22 | import com.google.android.gms.cast.framework.OptionsProvider;
23 | import com.google.android.gms.cast.framework.SessionProvider;
24 |
25 | import java.util.List;
26 |
27 | /**
28 | * Specify receiver application ID for cast
29 | */
30 | public class CastOptionsProvider implements OptionsProvider {
31 |
32 | @Override
33 | public CastOptions getCastOptions(Context context) {
34 | return new CastOptions.Builder()
35 | .setReceiverApplicationId(context.getString(R.string.cast_application_id))
36 | .build();
37 | }
38 |
39 | @Override
40 | public List getAdditionalSessionProviders(Context context) {
41 | return null;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/mobile/src/androidTest/java/com/example/android/uamp/TestSetupHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 com.example.android.uamp;
18 |
19 | import com.example.android.uamp.model.MusicProvider;
20 | import com.example.android.uamp.model.MusicProviderSource;
21 | import com.example.android.uamp.utils.SimpleMusicProviderSource;
22 |
23 | import java.util.concurrent.CountDownLatch;
24 |
25 | public class TestSetupHelper {
26 |
27 | public static MusicProvider setupMusicProvider(MusicProviderSource source)
28 | throws Exception {
29 | final CountDownLatch signal = new CountDownLatch(1);
30 | MusicProvider provider = new MusicProvider(source);
31 | provider.retrieveMediaAsync(new MusicProvider.Callback() {
32 | @Override
33 | public void onMusicCatalogReady(boolean success) {
34 | signal.countDown();
35 | }
36 | });
37 | signal.await();
38 | return provider;
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/utils/TvHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 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 | package com.example.android.uamp.utils;
17 |
18 | import android.app.UiModeManager;
19 | import android.content.Context;
20 | import android.content.res.Configuration;
21 |
22 | public class TvHelper {
23 | private static final String TAG = LogHelper.makeLogTag(TvHelper.class);
24 |
25 | /**
26 | * Returns true when running Android TV
27 | *
28 | * @param c Context to detect UI Mode.
29 | * @return true when device is running in tv mode, false otherwise.
30 | */
31 | public static boolean isTvUiMode(Context c) {
32 | UiModeManager uiModeManager = (UiModeManager) c.getSystemService(Context.UI_MODE_SERVICE);
33 | if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
34 | LogHelper.d(TAG, "Running in TV mode");
35 | return true;
36 | } else {
37 | LogHelper.d(TAG, "Running on a non-TV mode");
38 | return false;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/CONTRIB.md:
--------------------------------------------------------------------------------
1 | # How to become a contributor and submit your own code
2 |
3 | ## Contributor License Agreements
4 |
5 | We'd love to accept your sample apps and patches! Before we can take them, we
6 | have to jump a couple of legal hurdles.
7 |
8 | Please fill out either the individual or corporate Contributor License Agreement (CLA).
9 |
10 | * If you are an individual writing original source code and you're sure you
11 | own the intellectual property, then you'll need to sign an [individual CLA]
12 | (https://developers.google.com/open-source/cla/individual).
13 | * If you work for a company that wants to allow you to contribute your work,
14 | then you'll need to sign a [corporate CLA]
15 | (https://developers.google.com/open-source/cla/corporate).
16 |
17 | Follow either of the two links above to access the appropriate CLA and
18 | instructions for how to sign and return it. Once we receive it, we'll be able to
19 | accept your pull requests.
20 |
21 | ## Contributing A Patch
22 |
23 | 1. Submit an issue describing your proposed change to the repo in question.
24 | 1. The repo owner will respond to your issue promptly.
25 | 1. If your proposed change is accepted, and you haven't already done so, sign a
26 | Contributor License Agreement (see details above).
27 | 1. Fork the desired repo, develop and test your code changes.
28 | 1. Ensure that your code adheres to the existing style in the sample to which
29 | you are contributing. Refer to the
30 | [Android Code Style Guide]
31 | (https://source.android.com/source/code-style.html) for the
32 | recommended coding standards for this organization.
33 | 1. Ensure that your code has an appropriate set of unit tests which all pass.
34 | 1. Submit a pull request.
35 |
36 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | #ffff5722
19 | #ffbf360c
20 | #00B8D4
21 | #00B8D4
22 | @color/bt_accent
23 | #ccc
24 | @color/bt_accent
25 | #ffff1600
26 | #feee
27 | #ff333333
28 | #E5CCCCCC
29 | #E5FFFFFF
30 | #ff0097A7
31 | #000000
32 | #DDDDDD
33 | #0096a6
34 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
29 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | 4dp
20 | 16dp
21 |
22 |
23 | 8dp
24 | 64dp
25 | 8dp
26 |
27 |
28 | 32dp
29 | 48dp
30 | 16dp
31 |
32 |
33 | 72dp
34 | 12dp
35 | 60dp
36 |
37 | 50dp
38 |
39 |
40 | 16dp
41 | 16dp
42 |
43 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/model/MutableMediaMetadata.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 com.example.android.uamp.model;
18 |
19 | import android.support.v4.media.MediaMetadataCompat;
20 | import android.text.TextUtils;
21 |
22 | /**
23 | * Holder class that encapsulates a MediaMetadata and allows the actual metadata to be modified
24 | * without requiring to rebuild the collections the metadata is in.
25 | */
26 | public class MutableMediaMetadata {
27 |
28 | public MediaMetadataCompat metadata;
29 | public final String trackId;
30 |
31 | public MutableMediaMetadata(String trackId, MediaMetadataCompat metadata) {
32 | this.metadata = metadata;
33 | this.trackId = trackId;
34 | }
35 |
36 | @Override
37 | public boolean equals(Object o) {
38 | if (this == o) {
39 | return true;
40 | }
41 | if (o == null || o.getClass() != MutableMediaMetadata.class) {
42 | return false;
43 | }
44 |
45 | MutableMediaMetadata that = (MutableMediaMetadata) o;
46 |
47 | return TextUtils.equals(trackId, that.trackId);
48 | }
49 |
50 | @Override
51 | public int hashCode() {
52 | return trackId.hashCode();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/utils/ResourceHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 com.example.android.uamp.utils;
18 |
19 | import android.content.Context;
20 | import android.content.pm.ApplicationInfo;
21 | import android.content.pm.PackageManager;
22 | import android.content.res.Resources;
23 | import android.content.res.TypedArray;
24 |
25 | /**
26 | * Generic reusable methods to handle resources.
27 | */
28 | public class ResourceHelper {
29 | /**
30 | * Get a color value from a theme attribute.
31 | * @param context used for getting the color.
32 | * @param attribute theme attribute.
33 | * @param defaultColor default to use.
34 | * @return color value
35 | */
36 | public static int getThemeColor(Context context, int attribute, int defaultColor) {
37 | int themeColor = 0;
38 | String packageName = context.getPackageName();
39 | try {
40 | Context packageContext = context.createPackageContext(packageName, 0);
41 | ApplicationInfo applicationInfo =
42 | context.getPackageManager().getApplicationInfo(packageName, 0);
43 | packageContext.setTheme(applicationInfo.theme);
44 | Resources.Theme theme = packageContext.getTheme();
45 | TypedArray ta = theme.obtainStyledAttributes(new int[] {attribute});
46 | themeColor = ta.getColor(0, defaultColor);
47 | ta.recycle();
48 | } catch (PackageManager.NameNotFoundException e) {
49 | e.printStackTrace();
50 | }
51 | return themeColor;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/ui/NowPlayingActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.ui;
17 |
18 | import android.app.Activity;
19 | import android.app.UiModeManager;
20 | import android.content.Intent;
21 | import android.content.res.Configuration;
22 | import android.os.Bundle;
23 |
24 | import com.example.android.uamp.ui.tv.TvPlaybackActivity;
25 | import com.example.android.uamp.utils.LogHelper;
26 |
27 | /**
28 | * The activity for the Now Playing Card PendingIntent.
29 | * https://developer.android.com/training/tv/playback/now-playing.html
30 | *
31 | * This activity determines which activity to launch based on the current UI mode.
32 | */
33 | public class NowPlayingActivity extends Activity {
34 |
35 | private static final String TAG = LogHelper.makeLogTag(NowPlayingActivity.class);
36 |
37 | @Override
38 | public void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | LogHelper.d(TAG, "onCreate");
41 | Intent newIntent;
42 | UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
43 | if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
44 | LogHelper.d(TAG, "Running on a TV Device");
45 | newIntent = new Intent(this, TvPlaybackActivity.class);
46 | } else {
47 | LogHelper.d(TAG, "Running on a non-TV Device");
48 | newIntent = new Intent(this, MusicPlayerActivity.class);
49 | }
50 | startActivity(newIntent);
51 | finish();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
25 |
26 |
27 |
43 |
44 |
51 |
52 |
53 |
55 |
56 |
--------------------------------------------------------------------------------
/mobile/src/androidTest/java/com/example/android/uamp/playback/SimplePlayback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.playback;
17 |
18 | import android.support.v4.media.session.MediaSessionCompat;
19 |
20 | class SimplePlayback implements Playback {
21 | @Override
22 | public void start() {
23 |
24 | }
25 |
26 | @Override
27 | public void stop(boolean notifyListeners) {
28 |
29 | }
30 |
31 | @Override
32 | public void setState(int state) {
33 |
34 | }
35 |
36 | @Override
37 | public int getState() {
38 | return 0;
39 | }
40 |
41 | @Override
42 | public boolean isConnected() {
43 | return false;
44 | }
45 |
46 | @Override
47 | public boolean isPlaying() {
48 | return false;
49 | }
50 |
51 | @Override
52 | public int getCurrentStreamPosition() {
53 | return 0;
54 | }
55 |
56 | @Override
57 | public void setCurrentStreamPosition(int pos) {
58 |
59 | }
60 |
61 | @Override
62 | public void updateLastKnownStreamPosition() {
63 |
64 | }
65 |
66 | @Override
67 | public void play(MediaSessionCompat.QueueItem item) {
68 |
69 | }
70 |
71 | @Override
72 | public void pause() {
73 |
74 | }
75 |
76 | @Override
77 | public void seekTo(int position) {
78 |
79 | }
80 |
81 | @Override
82 | public void setCurrentMediaId(String mediaId) {
83 |
84 | }
85 |
86 | @Override
87 | public String getCurrentMediaId() {
88 | return null;
89 | }
90 |
91 | @Override
92 | public void setCallback(Callback callback) {
93 |
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/fragment_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
21 |
22 |
29 |
42 |
43 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/mobile/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | apply plugin: 'com.android.application'
17 |
18 | android {
19 | compileSdkVersion 23
20 | buildToolsVersion "23.0.3"
21 |
22 | defaultConfig {
23 | applicationId "com.example.android.uamp"
24 | minSdkVersion 17
25 | targetSdkVersion 23
26 | versionCode 2
27 | versionName "1.1"
28 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
29 | }
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_7
32 | targetCompatibility JavaVersion.VERSION_1_7
33 | }
34 | lintOptions {
35 | abortOnError true
36 | }
37 | buildTypes {
38 | release {
39 | minifyEnabled false
40 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
41 | }
42 | }
43 | }
44 |
45 | repositories {
46 | flatDir {
47 | dirs 'libs'
48 | }
49 | }
50 |
51 | dependencies {
52 | compile 'com.google.android.gms:play-services-cast-framework:9.4.0'
53 | compile 'com.google.android.support:wearable:1.3.0'
54 | compile 'com.android.support:appcompat-v7:23.4.0'
55 | compile 'com.android.support:cardview-v7:23.4.0'
56 | compile 'com.android.support:mediarouter-v7:23.4.0'
57 | compile 'com.android.support:leanback-v17:23.4.0'
58 | compile 'com.android.support:design:23.4.0'
59 | testCompile 'junit:junit:4.12'
60 | testCompile 'org.mockito:mockito-core:1.10.19'
61 | androidTestCompile 'junit:junit:4.12'
62 | androidTestCompile 'com.android.support:support-annotations:23.4.0'
63 | androidTestCompile 'com.android.support.test:runner:0.4.1'
64 | androidTestCompile 'com.android.support.test:rules:0.4.1'
65 | }
66 |
--------------------------------------------------------------------------------
/mobile/src/androidTest/java/com/example/android/uamp/utils/SimpleMusicProviderSource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 com.example.android.uamp.utils;
18 |
19 | import android.support.v4.media.MediaMetadataCompat;
20 |
21 | import com.example.android.uamp.model.MusicProviderSource;
22 |
23 | import java.util.ArrayList;
24 | import java.util.Iterator;
25 | import java.util.List;
26 |
27 | public class SimpleMusicProviderSource implements MusicProviderSource {
28 |
29 | private List mData = new ArrayList<>();
30 |
31 | public void add(String title, String album, String artist, String genre, String source,
32 | String iconUrl, long trackNumber, long totalTrackCount, long durationMs) {
33 | String id = String.valueOf(source.hashCode());
34 |
35 | //noinspection ResourceType
36 | mData.add(new MediaMetadataCompat.Builder()
37 | .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
38 | .putString(MusicProviderSource.CUSTOM_METADATA_TRACK_SOURCE, source)
39 | .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, album)
40 | .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
41 | .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, durationMs)
42 | .putString(MediaMetadataCompat.METADATA_KEY_GENRE, genre)
43 | .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, iconUrl)
44 | .putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
45 | .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, trackNumber)
46 | .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, totalTrackCount)
47 | .build());
48 | }
49 |
50 | @Override
51 | public Iterator iterator() {
52 | return mData.iterator();
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/utils/WearHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.utils;
17 |
18 | import android.os.Bundle;
19 | import android.support.wearable.media.MediaControlConstants;
20 |
21 | public class WearHelper {
22 | private static final String WEAR_APP_PACKAGE_NAME = "com.google.android.wearable.app";
23 |
24 | public static boolean isValidWearCompanionPackage(String packageName) {
25 | return WEAR_APP_PACKAGE_NAME.equals(packageName);
26 | }
27 |
28 | public static void setShowCustomActionOnWear(Bundle customActionExtras, boolean showOnWear) {
29 | if (showOnWear) {
30 | customActionExtras.putBoolean(
31 | MediaControlConstants.EXTRA_CUSTOM_ACTION_SHOW_ON_WEAR, true);
32 | } else {
33 | customActionExtras.remove(MediaControlConstants.EXTRA_CUSTOM_ACTION_SHOW_ON_WEAR);
34 | }
35 | }
36 |
37 | public static void setUseBackgroundFromTheme(Bundle extras, boolean useBgFromTheme) {
38 | if (useBgFromTheme) {
39 | extras.putBoolean(MediaControlConstants.EXTRA_BACKGROUND_COLOR_FROM_THEME, true);
40 | } else {
41 | extras.remove(MediaControlConstants.EXTRA_BACKGROUND_COLOR_FROM_THEME);
42 | }
43 | }
44 |
45 | public static void setSlotReservationFlags(Bundle extras, boolean reserveSkipToNextSlot,
46 | boolean reserveSkipToPrevSlot) {
47 | if (reserveSkipToPrevSlot) {
48 | extras.putBoolean(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_PREVIOUS, true);
49 | } else {
50 | extras.remove(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_PREVIOUS);
51 | }
52 | if (reserveSkipToNextSlot) {
53 | extras.putBoolean(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_NEXT, true);
54 | } else {
55 | extras.remove(MediaControlConstants.EXTRA_RESERVE_SLOT_SKIP_TO_NEXT);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Universal Android Music Player Sample
2 | =====================================
3 |
4 | This sample shows how to implement an audio media app that works
5 | across multiple form factors and provide a consistent user experience
6 | on Android phones, tablets, Android Auto, Android Wear, Android TV and Google Cast devices.
7 |
8 |
9 | Pre-requisites
10 | --------------
11 |
12 | - Android SDK v17
13 |
14 | Getting Started
15 | ---------------
16 |
17 | This sample uses the Gradle build system. To build this project, use the
18 | "gradlew build" command or use "Import Project" in Android Studio.
19 |
20 | Screenshots
21 | -----------
22 |
23 | 
24 | 
25 | 
26 | 
27 | 
28 | 
29 |
30 | 
31 | 
32 |
33 | Support
34 | -------
35 |
36 | - Google+ Community: https://plus.google.com/communities/105153134372062985968
37 | - Stack Overflow: http://stackoverflow.com/questions/tagged/android
38 |
39 | If you've found an error in this sample, please file an issue:
40 | https://github.com/googlesamples/android-UniversalMusicPlayer/issues
41 |
42 | Patches are encouraged, and may be submitted by forking this project and
43 | submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
44 |
45 | License
46 | -------
47 |
48 | Copyright 2014 The Android Open Source Project, Inc.
49 |
50 | Licensed to the Apache Software Foundation (ASF) under one or more contributor
51 | license agreements. See the NOTICE file distributed with this work for
52 | additional information regarding copyright ownership. The ASF licenses this
53 | file to you under the Apache License, Version 2.0 (the "License"); you may not
54 | use this file except in compliance with the License. You may obtain a copy of
55 | the License at
56 |
57 | http://www.apache.org/licenses/LICENSE-2.0
58 |
59 | Unless required by applicable law or agreed to in writing, software
60 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
61 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
62 | License for the specific language governing permissions and limitations under
63 | the License.
64 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/activity_player.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
26 |
27 |
30 |
31 |
32 |
33 |
39 |
40 |
46 |
47 |
53 |
54 |
55 |
56 |
57 |
58 |
65 |
66 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/media_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
21 |
22 |
33 |
34 |
41 |
42 |
51 |
52 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/activity_placeholder.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
26 |
27 |
30 |
31 |
32 |
33 |
41 |
42 |
48 |
49 |
55 |
56 |
57 |
58 |
59 |
60 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | Universal Music Player
20 | Favorite
21 | Unable to retrieve metadata.
22 | Genres
23 | Songs by genre
24 | %1$s songs
25 | Random music
26 | Cannot connect to server. Please, check your Internet connectivity.
27 | Error Loading Media
28 | Casting to %1$s
29 | Song currently playing
30 | Artist of currently playing song
31 | Extra info for currently playing song
32 | Play item
33 | play or pause
34 | skip to next
35 | skip to previous
36 | Touch to connect to a Google Cast device
37 |
39 | CC1AD845
40 | Home
41 | You may also like
42 | Listen
43 | Open the main menu
44 | Close the main menu
45 | This is a placeholder for your application code.
46 | All Music
47 | Playlists
48 | Now Playing
49 | Background image for album art
50 | Search results
51 | No search results.
52 | Loading…
53 | Play on…
54 |
55 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/utils/LogHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.utils;
17 |
18 | import android.util.Log;
19 |
20 | import com.example.android.uamp.BuildConfig;
21 |
22 | public class LogHelper {
23 |
24 | private static final String LOG_PREFIX = "uamp_";
25 | private static final int LOG_PREFIX_LENGTH = LOG_PREFIX.length();
26 | private static final int MAX_LOG_TAG_LENGTH = 23;
27 |
28 | public static String makeLogTag(String str) {
29 | if (str.length() > MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH) {
30 | return LOG_PREFIX + str.substring(0, MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH - 1);
31 | }
32 |
33 | return LOG_PREFIX + str;
34 | }
35 |
36 | /**
37 | * Don't use this when obfuscating class names!
38 | */
39 | public static String makeLogTag(Class cls) {
40 | return makeLogTag(cls.getSimpleName());
41 | }
42 |
43 |
44 | public static void v(String tag, Object... messages) {
45 | // Only log VERBOSE if build type is DEBUG
46 | if (BuildConfig.DEBUG) {
47 | log(tag, Log.VERBOSE, null, messages);
48 | }
49 | }
50 |
51 | public static void d(String tag, Object... messages) {
52 | // Only log DEBUG if build type is DEBUG
53 | if (BuildConfig.DEBUG) {
54 | log(tag, Log.DEBUG, null, messages);
55 | }
56 | }
57 |
58 | public static void i(String tag, Object... messages) {
59 | log(tag, Log.INFO, null, messages);
60 | }
61 |
62 | public static void w(String tag, Object... messages) {
63 | log(tag, Log.WARN, null, messages);
64 | }
65 |
66 | public static void w(String tag, Throwable t, Object... messages) {
67 | log(tag, Log.WARN, t, messages);
68 | }
69 |
70 | public static void e(String tag, Object... messages) {
71 | log(tag, Log.ERROR, null, messages);
72 | }
73 |
74 | public static void e(String tag, Throwable t, Object... messages) {
75 | log(tag, Log.ERROR, t, messages);
76 | }
77 |
78 | public static void log(String tag, int level, Throwable t, Object... messages) {
79 | if (Log.isLoggable(tag, level)) {
80 | String message;
81 | if (t == null && messages != null && messages.length == 1) {
82 | // handle this common case without the extra cost of creating a stringbuffer:
83 | message = messages[0].toString();
84 | } else {
85 | StringBuilder sb = new StringBuilder();
86 | if (messages != null) for (Object m : messages) {
87 | sb.append(m);
88 | }
89 | if (t != null) {
90 | sb.append("\n").append(Log.getStackTraceString(t));
91 | }
92 | message = sb.toString();
93 | }
94 | Log.println(level, tag, message);
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/utils/BitmapHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.utils;
17 |
18 | import android.graphics.Bitmap;
19 | import android.graphics.BitmapFactory;
20 |
21 | import java.io.BufferedInputStream;
22 | import java.io.IOException;
23 | import java.io.InputStream;
24 | import java.net.HttpURLConnection;
25 | import java.net.URL;
26 |
27 | public class BitmapHelper {
28 | private static final String TAG = LogHelper.makeLogTag(BitmapHelper.class);
29 |
30 | // Max read limit that we allow our input stream to mark/reset.
31 | private static final int MAX_READ_LIMIT_PER_IMG = 1024 * 1024;
32 |
33 | public static Bitmap scaleBitmap(Bitmap src, int maxWidth, int maxHeight) {
34 | double scaleFactor = Math.min(
35 | ((double) maxWidth)/src.getWidth(), ((double) maxHeight)/src.getHeight());
36 | return Bitmap.createScaledBitmap(src,
37 | (int) (src.getWidth() * scaleFactor), (int) (src.getHeight() * scaleFactor), false);
38 | }
39 |
40 | public static Bitmap scaleBitmap(int scaleFactor, InputStream is) {
41 | // Get the dimensions of the bitmap
42 | BitmapFactory.Options bmOptions = new BitmapFactory.Options();
43 |
44 | // Decode the image file into a Bitmap sized to fill the View
45 | bmOptions.inJustDecodeBounds = false;
46 | bmOptions.inSampleSize = scaleFactor;
47 |
48 | return BitmapFactory.decodeStream(is, null, bmOptions);
49 | }
50 |
51 | public static int findScaleFactor(int targetW, int targetH, InputStream is) {
52 | // Get the dimensions of the bitmap
53 | BitmapFactory.Options bmOptions = new BitmapFactory.Options();
54 | bmOptions.inJustDecodeBounds = true;
55 | BitmapFactory.decodeStream(is, null, bmOptions);
56 | int actualW = bmOptions.outWidth;
57 | int actualH = bmOptions.outHeight;
58 |
59 | // Determine how much to scale down the image
60 | return Math.min(actualW/targetW, actualH/targetH);
61 | }
62 |
63 | @SuppressWarnings("SameParameterValue")
64 | public static Bitmap fetchAndRescaleBitmap(String uri, int width, int height)
65 | throws IOException {
66 | URL url = new URL(uri);
67 | BufferedInputStream is = null;
68 | try {
69 | HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
70 | is = new BufferedInputStream(urlConnection.getInputStream());
71 | is.mark(MAX_READ_LIMIT_PER_IMG);
72 | int scaleFactor = findScaleFactor(width, height, is);
73 | LogHelper.d(TAG, "Scaling bitmap ", uri, " by factor ", scaleFactor, " to support ",
74 | width, "x", height, "requested dimension");
75 | is.reset();
76 | return scaleBitmap(scaleFactor, is);
77 | } finally {
78 | if (is != null) {
79 | is.close();
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/fragment_playback_controls.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
22 |
30 |
39 |
49 |
58 |
68 |
69 |
81 |
82 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/playback/Playback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.playback;
17 |
18 | import com.example.android.uamp.MusicService;
19 |
20 | import static android.support.v4.media.session.MediaSessionCompat.QueueItem;
21 |
22 | /**
23 | * Interface representing either Local or Remote Playback. The {@link MusicService} works
24 | * directly with an instance of the Playback object to make the various calls such as
25 | * play, pause etc.
26 | */
27 | public interface Playback {
28 | /**
29 | * Start/setup the playback.
30 | * Resources/listeners would be allocated by implementations.
31 | */
32 | void start();
33 |
34 | /**
35 | * Stop the playback. All resources can be de-allocated by implementations here.
36 | * @param notifyListeners if true and a callback has been set by setCallback,
37 | * callback.onPlaybackStatusChanged will be called after changing
38 | * the state.
39 | */
40 | void stop(boolean notifyListeners);
41 |
42 | /**
43 | * Set the latest playback state as determined by the caller.
44 | */
45 | void setState(int state);
46 |
47 | /**
48 | * Get the current {@link android.media.session.PlaybackState#getState()}
49 | */
50 | int getState();
51 |
52 | /**
53 | * @return boolean that indicates that this is ready to be used.
54 | */
55 | boolean isConnected();
56 |
57 | /**
58 | * @return boolean indicating whether the player is playing or is supposed to be
59 | * playing when we gain audio focus.
60 | */
61 | boolean isPlaying();
62 |
63 | /**
64 | * @return pos if currently playing an item
65 | */
66 | int getCurrentStreamPosition();
67 |
68 | /**
69 | * Set the current position. Typically used when switching players that are in
70 | * paused state.
71 | *
72 | * @param pos position in the stream
73 | */
74 | void setCurrentStreamPosition(int pos);
75 |
76 | /**
77 | * Query the underlying stream and update the internal last known stream position.
78 | */
79 | void updateLastKnownStreamPosition();
80 |
81 | /**
82 | * @param item to play
83 | */
84 | void play(QueueItem item);
85 |
86 | /**
87 | * Pause the current playing item
88 | */
89 | void pause();
90 |
91 | /**
92 | * Seek to the given position
93 | */
94 | void seekTo(int position);
95 |
96 | /**
97 | * Set the current mediaId. This is only used when switching from one
98 | * playback to another.
99 | *
100 | * @param mediaId to be set as the current.
101 | */
102 | void setCurrentMediaId(String mediaId);
103 |
104 | /**
105 | *
106 | * @return the current media Id being processed in any state or null.
107 | */
108 | String getCurrentMediaId();
109 |
110 | interface Callback {
111 | /**
112 | * On current music completed.
113 | */
114 | void onCompletion();
115 | /**
116 | * on Playback status changed
117 | * Implementations can use this callback to update
118 | * playback state on the media sessions.
119 | */
120 | void onPlaybackStatusChanged(int state);
121 |
122 | /**
123 | * @param error to be added to the PlaybackState
124 | */
125 | void onError(String error);
126 |
127 | /**
128 | * @param mediaId being currently played
129 | */
130 | void setCurrentMediaId(String mediaId);
131 | }
132 |
133 | /**
134 | * @param callback to be called
135 | */
136 | void setCallback(Callback callback);
137 | }
138 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/ui/tv/TvVerticalGridActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.ui.tv;
17 |
18 | import android.content.ComponentName;
19 | import android.os.Bundle;
20 | import android.os.RemoteException;
21 | import android.support.v4.app.FragmentActivity;
22 | import android.support.v4.media.MediaBrowserCompat;
23 | import android.support.v4.media.session.MediaControllerCompat;
24 |
25 | import com.example.android.uamp.MusicService;
26 | import com.example.android.uamp.R;
27 | import com.example.android.uamp.utils.LogHelper;
28 |
29 | public class TvVerticalGridActivity extends FragmentActivity
30 | implements TvVerticalGridFragment.MediaFragmentListener {
31 |
32 | private static final String TAG = LogHelper.makeLogTag(TvVerticalGridActivity.class);
33 | public static final String SHARED_ELEMENT_NAME = "hero";
34 | private MediaBrowserCompat mMediaBrowser;
35 | private String mMediaId;
36 | private String mTitle;
37 |
38 | @Override
39 | public void onCreate(Bundle savedInstanceState)
40 | {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.tv_vertical_grid);
43 |
44 | mMediaId = getIntent().getStringExtra(TvBrowseActivity.SAVED_MEDIA_ID);
45 | mTitle = getIntent().getStringExtra(TvBrowseActivity.BROWSE_TITLE);
46 |
47 | getWindow().setBackgroundDrawableResource(R.drawable.bg);
48 |
49 | mMediaBrowser = new MediaBrowserCompat(this,
50 | new ComponentName(this, MusicService.class),
51 | mConnectionCallback, null);
52 | }
53 |
54 | @Override
55 | protected void onStart() {
56 | super.onStart();
57 | LogHelper.d(TAG, "Activity onStart: mMediaBrowser connect");
58 | mMediaBrowser.connect();
59 | }
60 |
61 | @Override
62 | protected void onStop() {
63 | super.onStop();
64 | mMediaBrowser.disconnect();
65 | }
66 |
67 | protected void browse() {
68 | LogHelper.d(TAG, "navigateToBrowser, mediaId=" + mMediaId);
69 | TvVerticalGridFragment fragment = (TvVerticalGridFragment) getSupportFragmentManager()
70 | .findFragmentById(R.id.vertical_grid_fragment);
71 | fragment.setMediaId(mMediaId);
72 | fragment.setTitle(mTitle);
73 | }
74 |
75 | @Override
76 | public MediaBrowserCompat getMediaBrowser() {
77 | return mMediaBrowser;
78 | }
79 |
80 | private final MediaBrowserCompat.ConnectionCallback mConnectionCallback =
81 | new MediaBrowserCompat.ConnectionCallback() {
82 | @Override
83 | public void onConnected() {
84 | LogHelper.d(TAG, "onConnected: session token ",
85 | mMediaBrowser.getSessionToken());
86 |
87 | try {
88 | MediaControllerCompat mediaController = new MediaControllerCompat(
89 | TvVerticalGridActivity.this, mMediaBrowser.getSessionToken());
90 | setSupportMediaController(mediaController);
91 | browse();
92 | } catch (RemoteException e) {
93 | LogHelper.e(TAG, e, "could not connect media controller");
94 | }
95 | }
96 |
97 | @Override
98 | public void onConnectionFailed() {
99 | LogHelper.d(TAG, "onConnectionFailed");
100 | }
101 |
102 | @Override
103 | public void onConnectionSuspended() {
104 | LogHelper.d(TAG, "onConnectionSuspended");
105 | setSupportMediaController(null);
106 | }
107 | };
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/mobile/src/test/java/com/example/android/uamp/utils/MediaIDHelperTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.utils;
17 |
18 | import org.junit.Test;
19 | import org.junit.runner.RunWith;
20 | import org.junit.runners.JUnit4;
21 |
22 | import static org.junit.Assert.assertArrayEquals;
23 | import static org.junit.Assert.assertEquals;
24 | import static org.junit.Assert.assertNull;
25 | import static org.junit.Assert.fail;
26 |
27 | /**
28 | * Unit tests for the {@link MediaIDHelper} class. Exercises the helper methods that
29 | * do MediaID to MusicID conversion and hierarchy (categories) extraction.
30 | */
31 | @RunWith(JUnit4.class)
32 | public class MediaIDHelperTest {
33 |
34 | @Test
35 | public void testNormalMediaIDStructure() throws Exception {
36 | String mediaID = MediaIDHelper.createMediaID("784343", "BY_GENRE", "Classic 70's");
37 | assertEquals("Classic 70's", MediaIDHelper.extractBrowseCategoryValueFromMediaID(mediaID));
38 | assertEquals("784343", MediaIDHelper.extractMusicIDFromMediaID(mediaID));
39 | }
40 |
41 | @Test
42 | public void testSpecialSymbolsMediaIDStructure() throws Exception {
43 | String mediaID = MediaIDHelper.createMediaID("78A_88|X/3", "BY_GENRE", "Classic 70's");
44 | assertEquals("Classic 70's", MediaIDHelper.extractBrowseCategoryValueFromMediaID(mediaID));
45 | assertEquals("78A_88|X/3", MediaIDHelper.extractMusicIDFromMediaID(mediaID));
46 | }
47 |
48 | @Test
49 | public void testNullMediaIDStructure() throws Exception {
50 | String mediaID = MediaIDHelper.createMediaID(null, "BY_GENRE", "Classic 70's");
51 | assertEquals("Classic 70's", MediaIDHelper.extractBrowseCategoryValueFromMediaID(mediaID));
52 | assertNull(MediaIDHelper.extractMusicIDFromMediaID(mediaID));
53 | }
54 |
55 | @Test(expected = IllegalArgumentException.class)
56 | public void testInvalidSymbolsInMediaIDStructure() throws Exception {
57 | fail(MediaIDHelper.createMediaID(null, "BY|GENRE/2", "Classic 70's"));
58 | }
59 |
60 | @Test
61 | public void testCreateBrowseCategoryMediaID() throws Exception {
62 | String browseMediaID = MediaIDHelper.createMediaID(null, "BY_GENRE", "Rock & Roll");
63 | assertEquals("Rock & Roll", MediaIDHelper.extractBrowseCategoryValueFromMediaID(browseMediaID));
64 | String[] categories = MediaIDHelper.getHierarchy(browseMediaID);
65 | assertArrayEquals(categories, new String[]{"BY_GENRE", "Rock & Roll"});
66 | }
67 |
68 | @Test
69 | public void testGetParentOfPlayableMediaID() throws Exception {
70 | String mediaID = MediaIDHelper.createMediaID("23423423", "BY_GENRE", "Rock & Roll");
71 | String expectedParentID = MediaIDHelper.createMediaID(null, "BY_GENRE", "Rock & Roll");
72 | assertEquals(expectedParentID, MediaIDHelper.getParentMediaID(mediaID));
73 | }
74 |
75 | @Test
76 | public void testGetParentOfBrowsableMediaID() throws Exception {
77 | String mediaID = MediaIDHelper.createMediaID(null, "BY_GENRE", "Rock & Roll");
78 | String expectedParentID = MediaIDHelper.createMediaID(null, "BY_GENRE");
79 | assertEquals(expectedParentID, MediaIDHelper.getParentMediaID(mediaID));
80 | }
81 |
82 | @Test
83 | public void testGetParentOfCategoryMediaID() throws Exception {
84 | assertEquals(
85 | MediaIDHelper.MEDIA_ID_ROOT,
86 | MediaIDHelper.getParentMediaID(MediaIDHelper.createMediaID(null, "BY_GENRE")));
87 | }
88 |
89 | @Test
90 | public void testGetParentOfRoot() throws Exception {
91 | assertEquals(
92 | MediaIDHelper.MEDIA_ID_ROOT,
93 | MediaIDHelper.getParentMediaID(MediaIDHelper.MEDIA_ID_ROOT));
94 | }
95 |
96 | @Test(expected=NullPointerException.class)
97 | public void testGetParentOfNull() throws Exception {
98 | //noinspection ConstantConditions
99 | fail(MediaIDHelper.getParentMediaID(null));
100 | }
101 |
102 | }
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/ui/tv/TvBrowseActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.ui.tv;
17 |
18 | import android.content.ComponentName;
19 | import android.content.Intent;
20 | import android.os.Bundle;
21 | import android.os.RemoteException;
22 | import android.support.annotation.NonNull;
23 | import android.support.v4.app.FragmentActivity;
24 | import android.support.v4.media.MediaBrowserCompat;
25 | import android.support.v4.media.session.MediaControllerCompat;
26 |
27 | import com.example.android.uamp.MusicService;
28 | import com.example.android.uamp.R;
29 | import com.example.android.uamp.utils.LogHelper;
30 |
31 | /**
32 | * Main activity for the Android TV user interface.
33 | */
34 | public class TvBrowseActivity extends FragmentActivity
35 | implements TvBrowseFragment.MediaFragmentListener {
36 |
37 | private static final String TAG = LogHelper.makeLogTag(TvBrowseActivity.class);
38 | public static final String SAVED_MEDIA_ID="com.example.android.uamp.MEDIA_ID";
39 | public static final String BROWSE_TITLE = "com.example.android.uamp.BROWSE_TITLE";
40 |
41 | private MediaBrowserCompat mMediaBrowser;
42 |
43 | private String mMediaId;
44 | private String mBrowseTitle;
45 |
46 | @Override
47 | public void onCreate(Bundle savedInstanceState) {
48 | super.onCreate(savedInstanceState);
49 | LogHelper.d(TAG, "Activity onCreate");
50 |
51 | setContentView(R.layout.tv_activity_player);
52 |
53 | mMediaBrowser = new MediaBrowserCompat(this,
54 | new ComponentName(this, MusicService.class),
55 | mConnectionCallback, null);
56 | }
57 |
58 | @Override
59 | protected void onSaveInstanceState(@NonNull Bundle outState) {
60 | if (mMediaId != null) {
61 | outState.putString(SAVED_MEDIA_ID, mMediaId);
62 | outState.putString(BROWSE_TITLE, mBrowseTitle);
63 | }
64 | super.onSaveInstanceState(outState);
65 | }
66 |
67 | @Override
68 | protected void onStart() {
69 | super.onStart();
70 | LogHelper.d(TAG, "Activity onStart");
71 | mMediaBrowser.connect();
72 | }
73 |
74 | @Override
75 | protected void onStop() {
76 | super.onStop();
77 | LogHelper.d(TAG, "Activity onStop");
78 | if (mMediaBrowser != null) {
79 | mMediaBrowser.disconnect();
80 | }
81 | }
82 |
83 | @Override
84 | public boolean onSearchRequested() {
85 | startActivity(new Intent(this, TvBrowseActivity.class));
86 | return true;
87 | }
88 |
89 | protected void navigateToBrowser(String mediaId) {
90 | LogHelper.d(TAG, "navigateToBrowser, mediaId=" + mediaId);
91 | TvBrowseFragment fragment =
92 | (TvBrowseFragment) getSupportFragmentManager().findFragmentById(R.id.main_browse_fragment);
93 | fragment.initializeWithMediaId(mediaId);
94 | mMediaId = mediaId;
95 | if (mediaId == null) {
96 | mBrowseTitle = getResources().getString(R.string.home_title);
97 | }
98 | fragment.setTitle(mBrowseTitle);
99 | }
100 |
101 | @Override
102 | public MediaBrowserCompat getMediaBrowser() {
103 | return mMediaBrowser;
104 | }
105 |
106 | private final MediaBrowserCompat.ConnectionCallback mConnectionCallback =
107 | new MediaBrowserCompat.ConnectionCallback() {
108 | @Override
109 | public void onConnected() {
110 | LogHelper.d(TAG, "onConnected: session token ",
111 | mMediaBrowser.getSessionToken());
112 | try {
113 | MediaControllerCompat mediaController = new MediaControllerCompat(
114 | TvBrowseActivity.this, mMediaBrowser.getSessionToken());
115 | setSupportMediaController(mediaController);
116 | navigateToBrowser(mMediaId);
117 | } catch (RemoteException e) {
118 | LogHelper.e(TAG, e, "could not connect media controller");
119 | }
120 | }
121 |
122 | @Override
123 | public void onConnectionFailed() {
124 | LogHelper.d(TAG, "onConnectionFailed");
125 | }
126 |
127 | @Override
128 | public void onConnectionSuspended() {
129 | LogHelper.d(TAG, "onConnectionSuspended");
130 | setSupportMediaController(null);
131 | }
132 | };
133 | }
134 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/VoiceSearchParams.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp;
17 |
18 | import android.os.Build;
19 | import android.os.Bundle;
20 | import android.provider.MediaStore;
21 | import android.text.TextUtils;
22 |
23 | /**
24 | * For more information about voice search parameters,
25 | * check https://developer.android.com/guide/components/intents-common.html#PlaySearch
26 | */
27 | public final class VoiceSearchParams {
28 |
29 | public final String query;
30 | public boolean isAny;
31 | public boolean isUnstructured;
32 | public boolean isGenreFocus;
33 | public boolean isArtistFocus;
34 | public boolean isAlbumFocus;
35 | public boolean isSongFocus;
36 | public String genre;
37 | public String artist;
38 | public String album;
39 | public String song;
40 |
41 | /**
42 | * Creates a simple object describing the search criteria from the query and extras.
43 | * @param query the query parameter from a voice search
44 | * @param extras the extras parameter from a voice search
45 | */
46 | public VoiceSearchParams(String query, Bundle extras) {
47 | this.query = query;
48 |
49 | if (TextUtils.isEmpty(query)) {
50 | // A generic search like "Play music" sends an empty query
51 | isAny = true;
52 | } else {
53 | if (extras == null) {
54 | isUnstructured = true;
55 | } else {
56 | String genreKey;
57 | if (Build.VERSION.SDK_INT >= 21) {
58 | genreKey = MediaStore.EXTRA_MEDIA_GENRE;
59 | } else {
60 | genreKey = "android.intent.extra.genre";
61 | }
62 |
63 | String mediaFocus = extras.getString(MediaStore.EXTRA_MEDIA_FOCUS);
64 | if (TextUtils.equals(mediaFocus, MediaStore.Audio.Genres.ENTRY_CONTENT_TYPE)) {
65 | // for a Genre focused search, only genre is set:
66 | isGenreFocus = true;
67 | genre = extras.getString(genreKey);
68 | if (TextUtils.isEmpty(genre)) {
69 | // Because of a bug on the platform, genre is only sent as a query, not as
70 | // the semantic-aware extras. This check makes it future-proof when the
71 | // bug is fixed.
72 | genre = query;
73 | }
74 | } else if (TextUtils.equals(mediaFocus, MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE)) {
75 | // for an Artist focused search, both artist and genre are set:
76 | isArtistFocus = true;
77 | genre = extras.getString(genreKey);
78 | artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST);
79 | } else if (TextUtils.equals(mediaFocus, MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE)) {
80 | // for an Album focused search, album, artist and genre are set:
81 | isAlbumFocus = true;
82 | album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM);
83 | genre = extras.getString(genreKey);
84 | artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST);
85 | } else if (TextUtils.equals(mediaFocus, MediaStore.Audio.Media.ENTRY_CONTENT_TYPE)) {
86 | // for a Song focused search, title, album, artist and genre are set:
87 | isSongFocus = true;
88 | song = extras.getString(MediaStore.EXTRA_MEDIA_TITLE);
89 | album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM);
90 | genre = extras.getString(genreKey);
91 | artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST);
92 | } else {
93 | // If we don't know the focus, we treat it is an unstructured query:
94 | isUnstructured = true;
95 | }
96 | }
97 | }
98 | }
99 |
100 | @Override
101 | public String toString() {
102 | return "query=" + query
103 | + " isAny=" + isAny
104 | + " isUnstructured=" + isUnstructured
105 | + " isGenreFocus=" + isGenreFocus
106 | + " isArtistFocus=" + isArtistFocus
107 | + " isAlbumFocus=" + isAlbumFocus
108 | + " isSongFocus=" + isSongFocus
109 | + " genre=" + genre
110 | + " artist=" + artist
111 | + " album=" + album
112 | + " song=" + song;
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/utils/CarHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.utils;
17 |
18 | import android.app.UiModeManager;
19 | import android.content.Context;
20 | import android.content.res.Configuration;
21 | import android.os.Bundle;
22 |
23 | import com.example.android.uamp.MusicService;
24 |
25 | public class CarHelper {
26 | private static final String TAG = LogHelper.makeLogTag(CarHelper.class);
27 |
28 | private static final String AUTO_APP_PACKAGE_NAME = "com.google.android.projection.gearhead";
29 |
30 | // Use these extras to reserve space for the corresponding actions, even when they are disabled
31 | // in the playbackstate, so the custom actions don't reflow.
32 | private static final String SLOT_RESERVATION_SKIP_TO_NEXT =
33 | "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT";
34 | private static final String SLOT_RESERVATION_SKIP_TO_PREV =
35 | "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS";
36 | private static final String SLOT_RESERVATION_QUEUE =
37 | "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_QUEUE";
38 |
39 | /**
40 | * Action for an intent broadcast by Android Auto when a media app is connected or
41 | * disconnected. A "connected" media app is the one currently attached to the "media" facet
42 | * on Android Auto. So, this intent is sent by AA on:
43 | *
44 | * - connection: when the phone is projecting and at the moment the app is selected from the
45 | * list of media apps
46 | * - disconnection: when another media app is selected from the list of media apps or when
47 | * the phone stops projecting (when the user unplugs it, for example)
48 | *
49 | * The actual event (connected or disconnected) will come as an Intent extra,
50 | * with the key MEDIA_CONNECTION_STATUS (see below).
51 | */
52 | public static final String ACTION_MEDIA_STATUS = "com.google.android.gms.car.media.STATUS";
53 |
54 | /**
55 | * Key in Intent extras that contains the media connection event type (connected or disconnected)
56 | */
57 | public static final String MEDIA_CONNECTION_STATUS = "media_connection_status";
58 |
59 | /**
60 | * Value of the key MEDIA_CONNECTION_STATUS in Intent extras used when the current media app
61 | * is connected.
62 | */
63 | public static final String MEDIA_CONNECTED = "media_connected";
64 |
65 | /**
66 | * Value of the key MEDIA_CONNECTION_STATUS in Intent extras used when the current media app
67 | * is disconnected.
68 | */
69 | public static final String MEDIA_DISCONNECTED = "media_disconnected";
70 |
71 |
72 | public static boolean isValidCarPackage(String packageName) {
73 | return AUTO_APP_PACKAGE_NAME.equals(packageName);
74 | }
75 |
76 | public static void setSlotReservationFlags(Bundle extras, boolean reservePlayingQueueSlot,
77 | boolean reserveSkipToNextSlot, boolean reserveSkipToPrevSlot) {
78 | if (reservePlayingQueueSlot) {
79 | extras.putBoolean(SLOT_RESERVATION_QUEUE, true);
80 | } else {
81 | extras.remove(SLOT_RESERVATION_QUEUE);
82 | }
83 | if (reserveSkipToPrevSlot) {
84 | extras.putBoolean(SLOT_RESERVATION_SKIP_TO_PREV, true);
85 | } else {
86 | extras.remove(SLOT_RESERVATION_SKIP_TO_PREV);
87 | }
88 | if (reserveSkipToNextSlot) {
89 | extras.putBoolean(SLOT_RESERVATION_SKIP_TO_NEXT, true);
90 | } else {
91 | extras.remove(SLOT_RESERVATION_SKIP_TO_NEXT);
92 | }
93 | }
94 |
95 | /**
96 | * Returns true when running Android Auto or a car dock.
97 | *
98 | * A preferable way of detecting if your app is running in the context of an Android Auto
99 | * compatible car is by registering a BroadcastReceiver for the action
100 | * {@link CarHelper#ACTION_MEDIA_STATUS}. See a sample implementation in
101 | * {@link MusicService#onCreate()}.
102 | *
103 | * @param c Context to detect UI Mode.
104 | * @return true when device is running in car mode, false otherwise.
105 | */
106 | public static boolean isCarUiMode(Context c) {
107 | UiModeManager uiModeManager = (UiModeManager) c.getSystemService(Context.UI_MODE_SERVICE);
108 | if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
109 | LogHelper.d(TAG, "Running in Car mode");
110 | return true;
111 | } else {
112 | LogHelper.d(TAG, "Running on a non-Car mode");
113 | return false;
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/ui/MediaItemViewHolder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.ui;
17 |
18 | import android.app.Activity;
19 | import android.content.Context;
20 | import android.content.res.ColorStateList;
21 | import android.graphics.drawable.AnimationDrawable;
22 | import android.graphics.drawable.Drawable;
23 | import android.support.v4.content.ContextCompat;
24 | import android.support.v4.graphics.drawable.DrawableCompat;
25 | import android.support.v4.media.MediaDescriptionCompat;
26 | import android.view.LayoutInflater;
27 | import android.view.View;
28 | import android.view.ViewGroup;
29 | import android.widget.ImageView;
30 | import android.widget.TextView;
31 |
32 | import com.example.android.uamp.R;
33 |
34 | public class MediaItemViewHolder {
35 |
36 | static final int STATE_INVALID = -1;
37 | static final int STATE_NONE = 0;
38 | static final int STATE_PLAYABLE = 1;
39 | static final int STATE_PAUSED = 2;
40 | static final int STATE_PLAYING = 3;
41 |
42 | private static ColorStateList sColorStatePlaying;
43 | private static ColorStateList sColorStateNotPlaying;
44 |
45 | ImageView mImageView;
46 | TextView mTitleView;
47 | TextView mDescriptionView;
48 |
49 | static View setupView(Activity activity, View convertView, ViewGroup parent,
50 | MediaDescriptionCompat description, int state) {
51 |
52 | if (sColorStateNotPlaying == null || sColorStatePlaying == null) {
53 | initializeColorStateLists(activity);
54 | }
55 |
56 | MediaItemViewHolder holder;
57 |
58 | Integer cachedState = STATE_INVALID;
59 |
60 | if (convertView == null) {
61 | convertView = LayoutInflater.from(activity)
62 | .inflate(R.layout.media_list_item, parent, false);
63 | holder = new MediaItemViewHolder();
64 | holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq);
65 | holder.mTitleView = (TextView) convertView.findViewById(R.id.title);
66 | holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description);
67 | convertView.setTag(holder);
68 | } else {
69 | holder = (MediaItemViewHolder) convertView.getTag();
70 | cachedState = (Integer) convertView.getTag(R.id.tag_mediaitem_state_cache);
71 | }
72 |
73 | holder.mTitleView.setText(description.getTitle());
74 | holder.mDescriptionView.setText(description.getSubtitle());
75 |
76 | // If the state of convertView is different, we need to adapt the view to the
77 | // new state.
78 | if (cachedState == null || cachedState != state) {
79 | switch (state) {
80 | case STATE_PLAYABLE:
81 | Drawable pauseDrawable = ContextCompat.getDrawable(activity,
82 | R.drawable.ic_play_arrow_black_36dp);
83 | DrawableCompat.setTintList(pauseDrawable, sColorStateNotPlaying);
84 | holder.mImageView.setImageDrawable(pauseDrawable);
85 | holder.mImageView.setVisibility(View.VISIBLE);
86 | break;
87 | case STATE_PLAYING:
88 | AnimationDrawable animation = (AnimationDrawable)
89 | ContextCompat.getDrawable(activity, R.drawable.ic_equalizer_white_36dp);
90 | DrawableCompat.setTintList(animation, sColorStatePlaying);
91 | holder.mImageView.setImageDrawable(animation);
92 | holder.mImageView.setVisibility(View.VISIBLE);
93 | animation.start();
94 | break;
95 | case STATE_PAUSED:
96 | Drawable playDrawable = ContextCompat.getDrawable(activity,
97 | R.drawable.ic_equalizer1_white_36dp);
98 | DrawableCompat.setTintList(playDrawable, sColorStatePlaying);
99 | holder.mImageView.setImageDrawable(playDrawable);
100 | holder.mImageView.setVisibility(View.VISIBLE);
101 | break;
102 | default:
103 | holder.mImageView.setVisibility(View.GONE);
104 | }
105 | convertView.setTag(R.id.tag_mediaitem_state_cache, state);
106 | }
107 |
108 | return convertView;
109 | }
110 |
111 | static private void initializeColorStateLists(Context ctx) {
112 | sColorStateNotPlaying = ColorStateList.valueOf(ctx.getResources().getColor(
113 | R.color.media_item_icon_not_playing));
114 | sColorStatePlaying = ColorStateList.valueOf(ctx.getResources().getColor(
115 | R.color.media_item_icon_playing));
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/ui/tv/TvPlaybackActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.example.android.uamp.ui.tv;
17 |
18 | import android.content.ComponentName;
19 | import android.os.Bundle;
20 | import android.os.RemoteException;
21 | import android.support.annotation.NonNull;
22 | import android.support.v4.app.FragmentActivity;
23 | import android.support.v4.media.MediaBrowserCompat;
24 | import android.support.v4.media.MediaMetadataCompat;
25 | import android.support.v4.media.session.MediaControllerCompat;
26 | import android.support.v4.media.session.PlaybackStateCompat;
27 |
28 | import com.example.android.uamp.MusicService;
29 | import com.example.android.uamp.R;
30 | import com.example.android.uamp.utils.LogHelper;
31 |
32 | /**
33 | * Activity used to display details of the currently playing song, along with playback controls
34 | * and the playing queue.
35 | */
36 | public class TvPlaybackActivity extends FragmentActivity {
37 | private static final String TAG = LogHelper.makeLogTag(TvPlaybackActivity.class);
38 |
39 | private MediaBrowserCompat mMediaBrowser;
40 | private TvPlaybackFragment mPlaybackFragment;
41 |
42 | @Override
43 | public void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | LogHelper.d(TAG, "Activity onCreate");
46 |
47 | mMediaBrowser = new MediaBrowserCompat(this,
48 | new ComponentName(this, MusicService.class),
49 | mConnectionCallback, null);
50 |
51 | setContentView(R.layout.tv_playback_controls);
52 |
53 | mPlaybackFragment = (TvPlaybackFragment) getSupportFragmentManager()
54 | .findFragmentById(R.id.playback_controls_fragment);
55 | }
56 |
57 | @Override
58 | protected void onStart() {
59 | super.onStart();
60 | LogHelper.d(TAG, "Activity onStart");
61 | mMediaBrowser.connect();
62 | }
63 |
64 | @Override
65 | protected void onStop() {
66 | super.onStop();
67 | LogHelper.d(TAG, "Activity onStop");
68 | if (getSupportMediaController() != null) {
69 | getSupportMediaController().unregisterCallback(mMediaControllerCallback);
70 | }
71 | mMediaBrowser.disconnect();
72 |
73 | }
74 |
75 | private final MediaBrowserCompat.ConnectionCallback mConnectionCallback =
76 | new MediaBrowserCompat.ConnectionCallback() {
77 | @Override
78 | public void onConnected() {
79 | LogHelper.d(TAG, "onConnected");
80 | try {
81 | MediaControllerCompat mediaController = new MediaControllerCompat(
82 | TvPlaybackActivity.this, mMediaBrowser.getSessionToken());
83 | setSupportMediaController(mediaController);
84 | mediaController.registerCallback(mMediaControllerCallback);
85 |
86 | MediaMetadataCompat metadata = mediaController.getMetadata();
87 | if (metadata != null) {
88 | mPlaybackFragment.updateMetadata(metadata);
89 | mPlaybackFragment.updatePlaybackState(mediaController.getPlaybackState());
90 | }
91 | } catch (RemoteException e) {
92 | LogHelper.e(TAG, e, "could not connect media controller");
93 | }
94 | }
95 |
96 | @Override
97 | public void onConnectionFailed() {
98 | LogHelper.d(TAG, "onConnectionFailed");
99 | }
100 |
101 | @Override
102 | public void onConnectionSuspended() {
103 | LogHelper.d(TAG, "onConnectionSuspended");
104 | getSupportMediaController().unregisterCallback(mMediaControllerCallback);
105 | setSupportMediaController(null);
106 | }
107 | };
108 |
109 | /**
110 | * Receive callbacks from the MediaController. Here we update our state such as which queue
111 | * is being shown, the current title and description and the PlaybackState.
112 | */
113 | private final MediaControllerCompat.Callback mMediaControllerCallback =
114 | new MediaControllerCompat.Callback() {
115 | @Override
116 | public void onPlaybackStateChanged(@NonNull PlaybackStateCompat state) {
117 | LogHelper.d(TAG, "onPlaybackStateChanged, state=", state);
118 | if (mPlaybackFragment == null || state.getState() == PlaybackStateCompat.STATE_BUFFERING) {
119 | return;
120 | }
121 | mPlaybackFragment.updatePlaybackState(state);
122 | }
123 |
124 | @Override
125 | public void onMetadataChanged(MediaMetadataCompat metadata) {
126 | LogHelper.d(TAG, "onMetadataChanged, title=", metadata.getDescription().getTitle());
127 | if (mPlaybackFragment == null) {
128 | return;
129 | }
130 | mPlaybackFragment.updateMetadata(metadata);
131 | }
132 | };
133 | }
134 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/utils/MediaIDHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 com.example.android.uamp.utils;
18 |
19 | import android.support.annotation.NonNull;
20 |
21 | import java.util.Arrays;
22 |
23 | /**
24 | * Utility class to help on queue related tasks.
25 | */
26 | public class MediaIDHelper {
27 |
28 | // Media IDs used on browseable items of MediaBrowser
29 | public static final String MEDIA_ID_ROOT = "__ROOT__";
30 | public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
31 | public static final String MEDIA_ID_MUSICS_BY_SEARCH = "__BY_SEARCH__";
32 |
33 | private static final char CATEGORY_SEPARATOR = '/';
34 | private static final char LEAF_SEPARATOR = '|';
35 |
36 | /**
37 | * Create a String value that represents a playable or a browsable media.
38 | *
39 | * Encode the media browseable categories, if any, and the unique music ID, if any,
40 | * into a single String mediaID.
41 | *
42 | * MediaIDs are of the form /|, to make it easy
43 | * to find the category (like genre) that a music was selected from, so we
44 | * can correctly build the playing queue. This is specially useful when
45 | * one music can appear in more than one list, like "by genre -> genre_1"
46 | * and "by artist -> artist_1".
47 |
48 | * @param musicID Unique music ID for playable items, or null for browseable items.
49 | * @param categories hierarchy of categories representing this item's browsing parents
50 | * @return a hierarchy-aware media ID
51 | */
52 | public static String createMediaID(String musicID, String... categories) {
53 | StringBuilder sb = new StringBuilder();
54 | if (categories != null) {
55 | for (int i=0; i < categories.length; i++) {
56 | if (!isValidCategory(categories[i])) {
57 | throw new IllegalArgumentException("Invalid category: " + categories[0]);
58 | }
59 | sb.append(categories[i]);
60 | if (i < categories.length - 1) {
61 | sb.append(CATEGORY_SEPARATOR);
62 | }
63 | }
64 | }
65 | if (musicID != null) {
66 | sb.append(LEAF_SEPARATOR).append(musicID);
67 | }
68 | return sb.toString();
69 | }
70 |
71 | private static boolean isValidCategory(String category) {
72 | return category == null ||
73 | (
74 | category.indexOf(CATEGORY_SEPARATOR) < 0 &&
75 | category.indexOf(LEAF_SEPARATOR) < 0
76 | );
77 | }
78 |
79 | /**
80 | * Extracts unique musicID from the mediaID. mediaID is, by this sample's convention, a
81 | * concatenation of category (eg "by_genre"), categoryValue (eg "Classical") and unique
82 | * musicID. This is necessary so we know where the user selected the music from, when the music
83 | * exists in more than one music list, and thus we are able to correctly build the playing queue.
84 | *
85 | * @param mediaID that contains the musicID
86 | * @return musicID
87 | */
88 | public static String extractMusicIDFromMediaID(@NonNull String mediaID) {
89 | int pos = mediaID.indexOf(LEAF_SEPARATOR);
90 | if (pos >= 0) {
91 | return mediaID.substring(pos+1);
92 | }
93 | return null;
94 | }
95 |
96 | /**
97 | * Extracts category and categoryValue from the mediaID. mediaID is, by this sample's
98 | * convention, a concatenation of category (eg "by_genre"), categoryValue (eg "Classical") and
99 | * mediaID. This is necessary so we know where the user selected the music from, when the music
100 | * exists in more than one music list, and thus we are able to correctly build the playing queue.
101 | *
102 | * @param mediaID that contains a category and categoryValue.
103 | */
104 | public static @NonNull String[] getHierarchy(@NonNull String mediaID) {
105 | int pos = mediaID.indexOf(LEAF_SEPARATOR);
106 | if (pos >= 0) {
107 | mediaID = mediaID.substring(0, pos);
108 | }
109 | return mediaID.split(String.valueOf(CATEGORY_SEPARATOR));
110 | }
111 |
112 | public static String extractBrowseCategoryValueFromMediaID(@NonNull String mediaID) {
113 | String[] hierarchy = getHierarchy(mediaID);
114 | if (hierarchy.length == 2) {
115 | return hierarchy[1];
116 | }
117 | return null;
118 | }
119 |
120 | public static boolean isBrowseable(@NonNull String mediaID) {
121 | return mediaID.indexOf(LEAF_SEPARATOR) < 0;
122 | }
123 |
124 | public static String getParentMediaID(@NonNull String mediaID) {
125 | String[] hierarchy = getHierarchy(mediaID);
126 | if (!isBrowseable(mediaID)) {
127 | return createMediaID(null, hierarchy);
128 | }
129 | if (hierarchy.length <= 1) {
130 | return MEDIA_ID_ROOT;
131 | }
132 | String[] parentHierarchy = Arrays.copyOf(hierarchy, hierarchy.length-1);
133 | return createMediaID(null, parentHierarchy);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/AlbumArtCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 com.example.android.uamp;
18 |
19 | import android.graphics.Bitmap;
20 | import android.os.AsyncTask;
21 | import android.util.LruCache;
22 |
23 | import com.example.android.uamp.utils.BitmapHelper;
24 | import com.example.android.uamp.utils.LogHelper;
25 |
26 | import java.io.IOException;
27 |
28 | /**
29 | * Implements a basic cache of album arts, with async loading support.
30 | */
31 | public final class AlbumArtCache {
32 | private static final String TAG = LogHelper.makeLogTag(AlbumArtCache.class);
33 |
34 | private static final int MAX_ALBUM_ART_CACHE_SIZE = 12*1024*1024; // 12 MB
35 | private static final int MAX_ART_WIDTH = 800; // pixels
36 | private static final int MAX_ART_HEIGHT = 480; // pixels
37 |
38 | // Resolution reasonable for carrying around as an icon (generally in
39 | // MediaDescription.getIconBitmap). This should not be bigger than necessary, because
40 | // the MediaDescription object should be lightweight. If you set it too high and try to
41 | // serialize the MediaDescription, you may get FAILED BINDER TRANSACTION errors.
42 | private static final int MAX_ART_WIDTH_ICON = 128; // pixels
43 | private static final int MAX_ART_HEIGHT_ICON = 128; // pixels
44 |
45 | private static final int BIG_BITMAP_INDEX = 0;
46 | private static final int ICON_BITMAP_INDEX = 1;
47 |
48 | private final LruCache mCache;
49 |
50 | private static final AlbumArtCache sInstance = new AlbumArtCache();
51 |
52 | public static AlbumArtCache getInstance() {
53 | return sInstance;
54 | }
55 |
56 | private AlbumArtCache() {
57 | // Holds no more than MAX_ALBUM_ART_CACHE_SIZE bytes, bounded by maxmemory/4 and
58 | // Integer.MAX_VALUE:
59 | int maxSize = Math.min(MAX_ALBUM_ART_CACHE_SIZE,
60 | (int) (Math.min(Integer.MAX_VALUE, Runtime.getRuntime().maxMemory()/4)));
61 | mCache = new LruCache(maxSize) {
62 | @Override
63 | protected int sizeOf(String key, Bitmap[] value) {
64 | return value[BIG_BITMAP_INDEX].getByteCount()
65 | + value[ICON_BITMAP_INDEX].getByteCount();
66 | }
67 | };
68 | }
69 |
70 | public Bitmap getBigImage(String artUrl) {
71 | Bitmap[] result = mCache.get(artUrl);
72 | return result == null ? null : result[BIG_BITMAP_INDEX];
73 | }
74 |
75 | public Bitmap getIconImage(String artUrl) {
76 | Bitmap[] result = mCache.get(artUrl);
77 | return result == null ? null : result[ICON_BITMAP_INDEX];
78 | }
79 |
80 | public void fetch(final String artUrl, final FetchListener listener) {
81 | // WARNING: for the sake of simplicity, simultaneous multi-thread fetch requests
82 | // are not handled properly: they may cause redundant costly operations, like HTTP
83 | // requests and bitmap rescales. For production-level apps, we recommend you use
84 | // a proper image loading library, like Glide.
85 | Bitmap[] bitmap = mCache.get(artUrl);
86 | if (bitmap != null) {
87 | LogHelper.d(TAG, "getOrFetch: album art is in cache, using it", artUrl);
88 | listener.onFetched(artUrl, bitmap[BIG_BITMAP_INDEX], bitmap[ICON_BITMAP_INDEX]);
89 | return;
90 | }
91 | LogHelper.d(TAG, "getOrFetch: starting asynctask to fetch ", artUrl);
92 |
93 | new AsyncTask() {
94 | @Override
95 | protected Bitmap[] doInBackground(Void[] objects) {
96 | Bitmap[] bitmaps;
97 | try {
98 | Bitmap bitmap = BitmapHelper.fetchAndRescaleBitmap(artUrl,
99 | MAX_ART_WIDTH, MAX_ART_HEIGHT);
100 | Bitmap icon = BitmapHelper.scaleBitmap(bitmap,
101 | MAX_ART_WIDTH_ICON, MAX_ART_HEIGHT_ICON);
102 | bitmaps = new Bitmap[] {bitmap, icon};
103 | mCache.put(artUrl, bitmaps);
104 | } catch (IOException e) {
105 | return null;
106 | }
107 | LogHelper.d(TAG, "doInBackground: putting bitmap in cache. cache size=" +
108 | mCache.size());
109 | return bitmaps;
110 | }
111 |
112 | @Override
113 | protected void onPostExecute(Bitmap[] bitmaps) {
114 | if (bitmaps == null) {
115 | listener.onError(artUrl, new IllegalArgumentException("got null bitmaps"));
116 | } else {
117 | listener.onFetched(artUrl,
118 | bitmaps[BIG_BITMAP_INDEX], bitmaps[ICON_BITMAP_INDEX]);
119 | }
120 | }
121 | }.execute();
122 | }
123 |
124 | public static abstract class FetchListener {
125 | public abstract void onFetched(String artUrl, Bitmap bigImage, Bitmap iconImage);
126 | public void onError(String artUrl, Exception e) {
127 | LogHelper.e(TAG, e, "AlbumArtFetchListener: error while downloading " + artUrl);
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/mobile/src/main/java/com/example/android/uamp/model/RemoteJSONSource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 com.example.android.uamp.model;
18 |
19 | import android.support.v4.media.MediaMetadataCompat;
20 |
21 | import com.example.android.uamp.utils.LogHelper;
22 |
23 | import org.json.JSONArray;
24 | import org.json.JSONException;
25 | import org.json.JSONObject;
26 |
27 | import java.io.BufferedReader;
28 | import java.io.IOException;
29 | import java.io.InputStreamReader;
30 | import java.net.URL;
31 | import java.net.URLConnection;
32 | import java.util.ArrayList;
33 | import java.util.Iterator;
34 |
35 | /**
36 | * Utility class to get a list of MusicTrack's based on a server-side JSON
37 | * configuration.
38 | */
39 | public class RemoteJSONSource implements MusicProviderSource {
40 |
41 | private static final String TAG = LogHelper.makeLogTag(RemoteJSONSource.class);
42 |
43 | protected static final String CATALOG_URL =
44 | "http://storage.googleapis.com/automotive-media/music.json";
45 |
46 | private static final String JSON_MUSIC = "music";
47 | private static final String JSON_TITLE = "title";
48 | private static final String JSON_ALBUM = "album";
49 | private static final String JSON_ARTIST = "artist";
50 | private static final String JSON_GENRE = "genre";
51 | private static final String JSON_SOURCE = "source";
52 | private static final String JSON_IMAGE = "image";
53 | private static final String JSON_TRACK_NUMBER = "trackNumber";
54 | private static final String JSON_TOTAL_TRACK_COUNT = "totalTrackCount";
55 | private static final String JSON_DURATION = "duration";
56 |
57 | @Override
58 | public Iterator iterator() {
59 | try {
60 | int slashPos = CATALOG_URL.lastIndexOf('/');
61 | String path = CATALOG_URL.substring(0, slashPos + 1);
62 | JSONObject jsonObj = fetchJSONFromUrl(CATALOG_URL);
63 | ArrayList tracks = new ArrayList<>();
64 | if (jsonObj != null) {
65 | JSONArray jsonTracks = jsonObj.getJSONArray(JSON_MUSIC);
66 |
67 | if (jsonTracks != null) {
68 | for (int j = 0; j < jsonTracks.length(); j++) {
69 | tracks.add(buildFromJSON(jsonTracks.getJSONObject(j), path));
70 | }
71 | }
72 | }
73 | return tracks.iterator();
74 | } catch (JSONException e) {
75 | LogHelper.e(TAG, e, "Could not retrieve music list");
76 | throw new RuntimeException("Could not retrieve music list", e);
77 | }
78 | }
79 |
80 | private MediaMetadataCompat buildFromJSON(JSONObject json, String basePath) throws JSONException {
81 | String title = json.getString(JSON_TITLE);
82 | String album = json.getString(JSON_ALBUM);
83 | String artist = json.getString(JSON_ARTIST);
84 | String genre = json.getString(JSON_GENRE);
85 | String source = json.getString(JSON_SOURCE);
86 | String iconUrl = json.getString(JSON_IMAGE);
87 | int trackNumber = json.getInt(JSON_TRACK_NUMBER);
88 | int totalTrackCount = json.getInt(JSON_TOTAL_TRACK_COUNT);
89 | int duration = json.getInt(JSON_DURATION) * 1000; // ms
90 |
91 | LogHelper.d(TAG, "Found music track: ", json);
92 |
93 | // Media is stored relative to JSON file
94 | if (!source.startsWith("http")) {
95 | source = basePath + source;
96 | }
97 | if (!iconUrl.startsWith("http")) {
98 | iconUrl = basePath + iconUrl;
99 | }
100 | // Since we don't have a unique ID in the server, we fake one using the hashcode of
101 | // the music source. In a real world app, this could come from the server.
102 | String id = String.valueOf(source.hashCode());
103 |
104 | // Adding the music source to the MediaMetadata (and consequently using it in the
105 | // mediaSession.setMetadata) is not a good idea for a real world music app, because
106 | // the session metadata can be accessed by notification listeners. This is done in this
107 | // sample for convenience only.
108 | //noinspection ResourceType
109 | return new MediaMetadataCompat.Builder()
110 | .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
111 | .putString(MusicProviderSource.CUSTOM_METADATA_TRACK_SOURCE, source)
112 | .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, album)
113 | .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
114 | .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration)
115 | .putString(MediaMetadataCompat.METADATA_KEY_GENRE, genre)
116 | .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, iconUrl)
117 | .putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
118 | .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, trackNumber)
119 | .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, totalTrackCount)
120 | .build();
121 | }
122 |
123 | /**
124 | * Download a JSON file from a server, parse the content and return the JSON
125 | * object.
126 | *
127 | * @return result JSONObject containing the parsed representation.
128 | */
129 | private JSONObject fetchJSONFromUrl(String urlString) throws JSONException {
130 | BufferedReader reader = null;
131 | try {
132 | URLConnection urlConnection = new URL(urlString).openConnection();
133 | reader = new BufferedReader(new InputStreamReader(
134 | urlConnection.getInputStream(), "iso-8859-1"));
135 | StringBuilder sb = new StringBuilder();
136 | String line;
137 | while ((line = reader.readLine()) != null) {
138 | sb.append(line);
139 | }
140 | return new JSONObject(sb.toString());
141 | } catch (JSONException e) {
142 | throw e;
143 | } catch (Exception e) {
144 | LogHelper.e(TAG, "Failed to parse the json for media list", e);
145 | return null;
146 | } finally {
147 | if (reader != null) {
148 | try {
149 | reader.close();
150 | } catch (IOException e) {
151 | // ignore
152 | }
153 | }
154 | }
155 | }
156 | }
157 |
--------------------------------------------------------------------------------