From 3f3818efb761c07d9a5004bd44c040a5784c39c4 Mon Sep 17 00:00:00 2001 From: h4h13 Date: Wed, 22 Aug 2018 23:24:07 +0530 Subject: [PATCH] New UI --- .idea/RetroMusicPlayer.iml | 19 + .idea/assetWizardSettings.xml | 2 +- ...le__android_arch_core_common_1_1_0_jar.xml | 11 - ...le__android_arch_core_common_1_1_1_jar.xml | 11 + ...__android_arch_core_runtime_1_1_1_aar.xml} | 8 +- ...ndroid_arch_lifecycle_common_1_1_0_jar.xml | 11 - ...ndroid_arch_lifecycle_common_1_1_1_jar.xml | 11 + ...oid_arch_lifecycle_livedata_core_1_1_0.xml | 12 - ...arch_lifecycle_livedata_core_1_1_1_aar.xml | 12 + ...roid_arch_lifecycle_runtime_1_1_1_aar.xml} | 8 +- ...id_arch_lifecycle_viewmodel_1_1_1_aar.xml} | 8 +- ...om_afollestad_material_cab_0_1_12_aar.xml} | 6 +- ..._material_dialogs_commons_0_9_6_0_aar.xml} | 9 +- ...tad_material_dialogs_core_0_9_6_0_aar.xml} | 6 +- ...upport_animated_vector_drawable_27_1_1.xml | 12 - ...imated_vector_drawable_28_0_0_rc01_aar.xml | 10 + ...om_android_support_appcompat_v7_27_1_1.xml | 12 - ...d_support_appcompat_v7_28_0_0_rc01_aar.xml | 13 + ...rt_asynclayoutinflater_28_0_0_rc01_aar.xml | 10 + ...com_android_support_cardview_v7_27_1_1.xml | 12 - ...id_support_cardview_v7_28_0_0_rc01_aar.xml | 10 + ...id_support_collections_28_0_0_rc01_jar.xml | 9 + ...port_coordinatorlayout_28_0_0_rc01_aar.xml | 13 + ..._support_cursoradapter_28_0_0_rc01_aar.xml | 10 + ...id_support_customview_28_0_0_rc01_aar.xml} | 6 +- ...dle__com_android_support_design_27_1_1.xml | 12 - ...android_support_design_28_0_0_rc01_aar.xml | 13 + ...d_support_documentfile_28_0_0_rc01_aar.xml | 10 + ...d_support_drawerlayout_28_0_0_rc01_aar.xml | 13 + ...m_android_support_gridlayout_v7_27_1_1.xml | 12 - ..._support_gridlayout_v7_28_0_0_rc01_aar.xml | 10 + ...d_support_interpolator_28_0_0_rc01_aar.xml | 10 + ...android_support_loader_28_0_0_rc01_aar.xml | 10 + ..._localbroadcastmanager_28_0_0_rc01_aar.xml | 10 + ...roid_support_media2_28_0_0_alpha02_aar.xml | 13 + ...pport_mediarouter_v7_28_0_0_alpha5_aar.xml | 13 + ...om_android_support_multidex_1_0_3_aar.xml} | 6 +- ..._com_android_support_palette_v7_27_1_1.xml | 12 - ...oid_support_palette_v7_28_0_0_rc01_aar.xml | 10 + ..._android_support_preference_v14_27_1_1.xml | 12 - ...support_preference_v14_28_0_0_rc01_aar.xml | 10 + ...m_android_support_preference_v7_27_1_1.xml | 12 - ..._support_preference_v7_28_0_0_rc01_aar.xml | 10 + ..._android_support_print_28_0_0_rc01_aar.xml | 13 + ...android_support_recyclerview_v7_27_1_1.xml | 12 - ...upport_recyclerview_v7_28_0_0_rc01_aar.xml | 13 + ...port_slidingpanelayout_28_0_0_rc01_aar.xml | 10 + ...support_support_annotations_27_1_1_jar.xml | 11 - ...rt_support_annotations_28_0_0_rc01_jar.xml | 9 + ..._android_support_support_compat_27_1_1.xml | 12 - ...support_support_compat_28_0_0_rc01_aar.xml | 13 + ...android_support_support_core_ui_27_1_1.xml | 12 - ...upport_support_core_ui_28_0_0_rc01_aar.xml | 10 + ...roid_support_support_core_utils_27_1_1.xml | 12 - ...ort_support_core_utils_28_0_0_rc01_aar.xml | 10 + ...ndroid_support_support_fragment_27_1_1.xml | 12 - ...pport_support_fragment_28_0_0_rc01_aar.xml | 13 + ...id_support_support_media_compat_27_1_1.xml | 12 - ...t_support_media_compat_28_0_0_rc01_aar.xml | 13 + ..._com_android_support_support_v4_27_1_1.xml | 12 - ...support_support_vector_drawable_27_1_1.xml | 12 - ...upport_vector_drawable_28_0_0_rc01_aar.xml | 10 + ...ort_swiperefreshlayout_28_0_0_rc01_aar.xml | 13 + ..._com_android_support_transition_27_1_1.xml | 12 - ...oid_support_transition_28_0_0_rc01_aar.xml | 13 + ...rt_versionedparcelable_28_0_0_rc01_aar.xml | 10 + ...roid_support_viewpager_28_0_0_rc01_aar.xml | 10 + ...ub_AdrienPoupa_jaudiotagger_2_2_3_aar.xml} | 6 +- ...id_RecyclerView_FastScroll_1_0_16_kmod.xml | 10 - ...ecyclerView_FastScroll_1_0_16_kmod_aar.xml | 13 + ...ndroid_observablescrollview_1_6_0_aar.xml} | 6 +- ...roid_gms_play_services_base_15_0_1_aar.xml | 10 + ..._gms_play_services_basement_15_0_1_aar.xml | 10 + ...roid_gms_play_services_cast_16_0_1_aar.xml | 10 + ...lay_services_cast_framework_16_0_1_aar.xml | 10 + ...oid_gms_play_services_flags_15_0_1_aar.xml | 10 + ...oid_gms_play_services_tasks_15_0_1_aar.xml | 10 + ...dle__com_google_code_gson_gson_2_7_jar.xml | 13 - ...e__com_google_code_gson_gson_2_8_2_jar.xml | 11 + ...cyclerview_advrecyclerview_0_11_0_aar.xml} | 9 +- ...rton_butterknife_annotations_8_8_1_jar.xml | 13 - ...om_r0adkll_slidableactivity_2_0_6_aar.xml} | 6 +- ...hree_slidinguppanel_library_3_4_0_aar.xml} | 6 +- ...khttp3_logging_interceptor_3_11_0_jar.xml} | 6 +- ...com_squareup_okhttp3_okhttp_3_10_0_jar.xml | 11 - ...com_squareup_okhttp3_okhttp_3_11_0_jar.xml | 11 + ...up_retrofit2_adapter_rxjava2_2_3_0_jar.xml | 13 - ...up_retrofit2_adapter_rxjava2_2_4_0_jar.xml | 11 + ...eup_retrofit2_converter_gson_2_3_0_jar.xml | 13 - ...eup_retrofit2_converter_gson_2_4_0_jar.xml | 11 + ..._squareup_retrofit2_retrofit_2_3_0_jar.xml | 13 - ..._squareup_retrofit2_retrofit_2_4_0_jar.xml | 11 + ...reactivex_rxjava2_rxandroid_2_0_2_aar.xml} | 6 +- ...materialprogressbar_library_1_4_2_aar.xml} | 9 +- ...brains_kotlin_kotlin_runtime_1_0_6_jar.xml | 11 - ...tbrains_kotlin_kotlin_stdlib_1_0_6_jar.xml | 11 - ...dle__org_nanohttpd_nanohttpd_2_3_1_jar.xml | 11 + ...uk_co_chrisjenx_calligraphy_2_3_0_aar.xml} | 6 +- .idea/misc.xml | 7 +- .idea/modules.xml | 2 +- app/app.iml | 132 +- app/build.gradle | 40 +- app/src/main/AndroidManifest.xml | 9 + app/src/main/assets/retro-changelog.html | 2 +- .../name/monkey/retromusic/Constants.java | 1 + .../monkey/retromusic/RetroApplication.java | 9 +- .../monkey/retromusic/cast/CastHelper.java | 59 + .../retromusic/cast/CastOptionsProvider.java | 45 + .../cast/ExpandedControlsActivity.java | 28 + .../monkey/retromusic/cast/WebServer.java | 75 + .../retromusic/dialogs/DeleteSongsDialog.java | 15 +- .../retromusic/dialogs/HomeOptionDialog.java | 28 +- .../retromusic/dialogs/SleepTimerDialog.java | 8 +- .../retromusic/helper/MusicPlayerRemote.java | 7 +- .../misc/CustomMediaRouteActionProvider.java | 62 + .../misc/NavigationIconClickListener.java | 89 ++ .../ui/activities/AlbumDetailsActivity.java | 22 +- .../ui/activities/ArtistDetailActivity.java | 17 +- .../ui/activities/ErrorHandlerActivity.java | 2 +- .../ui/activities/GenreDetailsActivity.java | 2 - .../ui/activities/LyricsActivity.java | 539 +++---- .../ui/activities/MainActivity.java | 37 +- .../ui/activities/PlaylistDetailActivity.java | 1 - .../ui/activities/ProVersionActivity.java | 7 +- .../ui/activities/SearchActivity.java | 48 +- .../ui/activities/SettingsActivity.java | 9 +- .../ui/activities/base/AbsBaseActivity.java | 241 +-- .../ui/activities/base/AbsCastActivity.java | 159 ++ .../base/AbsMusicServiceActivity.java | 6 +- .../base/AbsSlidingMusicPanelActivity.java | 90 +- .../ui/activities/base/AbsThemeActivity.java | 357 ++--- .../tageditor/AlbumTagEditorActivity.java | 1 - .../tageditor/WriteTagsAsyncTask.java | 258 ++-- .../retromusic/ui/adapter/SearchAdapter.java | 10 +- .../ui/adapter/SongFileAdapter.java | 39 +- .../ui/adapter/album/AlbumAdapter.java | 1 - .../ui/adapter/playlist/AddToPlaylist.java | 86 +- .../song/OrderablePlaylistSongAdapter.java | 4 +- .../song/ShuffleButtonSongAdapter.java | 116 +- .../ui/adapter/song/SongAdapter.java | 2 +- .../ui/fragments/MiniPlayerFragment.java | 282 ++-- .../ui/fragments/NowPlayingScreen.java | 45 +- .../ui/fragments/PlayingQueueFragment.java | 232 +-- .../base/AbsLibraryPagerFragment.java | 1 - .../AbsLibraryPagerRecyclerViewFragment.java | 238 +-- .../base/AbsMainActivityFragment.java | 5 +- .../base/AbsMusicServiceFragment.java | 1 - .../ui/fragments/base/AbsPlayerFragment.java | 9 +- .../mainactivity/ArtistsFragment.java | 280 ++-- .../fragments/mainactivity/GenreFragment.java | 150 +- .../mainactivity/LibraryFragment.java | 147 +- .../mainactivity/PlaylistsFragment.java | 4 +- .../fragments/mainactivity/SongsFragment.java | 282 ++-- .../mainactivity/folders/FoldersFragment.java | 96 +- .../mainactivity/home/BannerHomeFragment.java | 119 +- .../mainactivity/home/HomeFragment.java | 70 +- .../player/PlayerAlbumCoverFragment.java | 231 +-- .../player/blur/BlurPlayerFragment.java | 522 +++---- .../player/cardblur/CardBlurFragment.java | 286 ++-- .../flat/FlatPlaybackControlsFragment.java | 2 - .../player/flat/FlatPlayerFragment.java | 259 ++-- .../hmm/HmmPlaybackControlsFragment.java | 6 +- .../material/MaterialControlsFragment.java | 9 +- .../SimplePlaybackControlsFragment.java | 543 +++---- .../settings/AbsSettingsFragment.java | 18 +- .../settings/NowPlayingSettingsFragment.java | 145 +- .../monkey/retromusic/util/AnimationUtil.java | 17 +- .../monkey/retromusic/util/DensityUtil.java | 2 +- .../name/monkey/retromusic/util/FileUtil.java | 404 ++--- .../monkey/retromusic/util/ImageUtil.java | 361 ++--- .../monkey/retromusic/util/LastFMUtil.java | 103 +- .../monkey/retromusic/util/MusicUtil.java | 687 ++++----- .../retromusic/util/PreferenceUtil.java | 1364 +++++++++-------- .../monkey/retromusic/util/RetroUtil.java | 2 - .../monkey/retromusic/util/TempUtils.java | 108 +- .../name/monkey/retromusic/util/ViewUtil.java | 76 +- .../util/color/ImageGradientColorizer.java | 114 +- .../color/MediaNotificationProcessor.java | 9 +- .../util/color/NotificationColorUtil.java | 191 ++- .../util/schedulers/SchedulerProvider.java | 2 +- .../views/BottomNavigationViewEx.java | 2 +- .../views/CustomMediaRouteButton.java | 55 + .../ThemeableMediaRouteActionProvider.java | 49 + .../main/res/animator/appbar_elevation.xml | 11 + .../res/drawable/bg_circular_top_corners.xml | 2 +- .../bottom_navigation_item_colors.xml | 5 + .../drawable/et_bg_circular_top_corners.xml | 1 + .../res/drawable/ic_bug_report_white_24dp.xml | 9 + .../ic_keyboard_backspace_black_24dp.xml | 6 +- .../main/res/drawable/ic_menu_white_24dp.xml | 6 +- .../activity_main_drawer_layout.xml | 14 - .../res/layout-land/activity_settings.xml | 18 +- .../res/layout-land/fragment_banner_home.xml | 127 +- .../main/res/layout-land/fragment_home.xml | 5 +- .../res/layout-land/fragment_material.xml | 77 + .../main/res/layout-land/retro_backdrop.xml | 63 + .../layout-sw600dp/fragment_banner_home.xml | 113 -- .../res/layout-sw600dp/retro_backdrop.xml | 52 + .../activity_album_tag_editor.xml | 0 .../fragment_banner_home.xml | 214 +-- .../res/layout-xlarge-land/fragment_home.xml | 1 + .../activity_about_content.xml | 0 .../activity_album_tag_editor.xml | 0 .../res/layout-xlarge/activity_settings.xml | 62 - .../activity_song_tag_editor.xml | 234 --- .../main/res/layout-xlarge/fragment_home.xml | 1 + .../res/layout-xlarge/fragment_library.xml | 92 -- app/src/main/res/layout/abs_playlists.xml | 33 + app/src/main/res/layout/activity_album.xml | 5 +- .../res/layout/activity_artist_details.xml | 5 +- app/src/main/res/layout/activity_donation.xml | 10 +- app/src/main/res/layout/activity_license.xml | 10 +- .../layout/activity_main_drawer_layout.xml | 3 +- .../res/layout/activity_playing_queue.xml | 10 +- .../res/layout/activity_playlist_detail.xml | 11 +- .../main/res/layout/activity_pro_version.xml | 51 +- app/src/main/res/layout/activity_search.xml | 111 +- app/src/main/res/layout/activity_settings.xml | 16 +- .../res/layout/activity_song_tag_editor.xml | 75 +- app/src/main/res/layout/card_social.xml | 35 +- .../main/res/layout/dialog_file_details.xml | 2 + .../main/res/layout/dialog_sleep_timer.xml | 1 + .../main/res/layout/fragment_banner_home.xml | 118 +- .../layout/fragment_cast_mini_controller.xml | 3 +- app/src/main/res/layout/fragment_folder.xml | 110 +- app/src/main/res/layout/fragment_home.xml | 66 +- app/src/main/res/layout/fragment_library.xml | 116 +- .../main/res/layout/fragment_mini_player.xml | 126 +- .../main/res/layout/home_section_content.xml | 10 +- app/src/main/res/layout/image.xml | 43 +- app/src/main/res/layout/retro_backdrop.xml | 47 + .../res/layout/sliding_music_panel_layout.xml | 40 +- .../main/res/layout/user_action_details.xml | 39 +- .../main/res/menu/bottom_navigation_main.xml | 9 +- app/src/main/res/menu/menu_album_detail.xml | 4 +- app/src/main/res/menu/menu_cast.xml | 2 +- app/src/main/res/menu/menu_main.xml | 6 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 +- app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 4425 -> 4430 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 1208 -> 1171 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 4425 -> 4430 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2807 -> 2797 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 752 -> 734 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2807 -> 2797 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 6440 -> 6406 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 1738 -> 1664 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 6440 -> 6406 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 10658 -> 10625 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 3077 -> 2989 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 10658 -> 10625 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 15934 -> 15851 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 4792 -> 4607 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 15934 -> 15851 bytes app/src/main/res/values-land/integers.xml | 4 + app/src/main/res/values/arrays.xml | 9 + app/src/main/res/values/cast_style.xml | 38 + app/src/main/res/values/dimens.xml | 12 +- app/src/main/res/values/integers.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 20 + app/src/main/res/values/styles_parents.xml | 14 +- app/src/main/res/xml/pref_advanced.xml | 94 +- appthemehelper/appthemehelper.iml | 74 +- appthemehelper/build.gradle | 14 +- .../common/prefs/ATESwitchPreference.java | 88 +- .../util/NavigationViewUtil.java | 68 + .../main/res/layout/ate_preference_switch.xml | 14 +- .../layout/ate_preference_switch_support.xml | 12 +- build.gradle | 3 +- 270 files changed, 7441 insertions(+), 6502 deletions(-) create mode 100644 .idea/RetroMusicPlayer.iml delete mode 100644 .idea/libraries/Gradle__android_arch_core_common_1_1_0_jar.xml create mode 100644 .idea/libraries/Gradle__android_arch_core_common_1_1_1_jar.xml rename .idea/libraries/{Gradle__android_arch_core_runtime_1_1_0.xml => Gradle__android_arch_core_runtime_1_1_1_aar.xml} (53%) delete mode 100644 .idea/libraries/Gradle__android_arch_lifecycle_common_1_1_0_jar.xml create mode 100644 .idea/libraries/Gradle__android_arch_lifecycle_common_1_1_1_jar.xml delete mode 100644 .idea/libraries/Gradle__android_arch_lifecycle_livedata_core_1_1_0.xml create mode 100644 .idea/libraries/Gradle__android_arch_lifecycle_livedata_core_1_1_1_aar.xml rename .idea/libraries/{Gradle__android_arch_lifecycle_runtime_1_1_0.xml => Gradle__android_arch_lifecycle_runtime_1_1_1_aar.xml} (52%) rename .idea/libraries/{Gradle__android_arch_lifecycle_viewmodel_1_1_0.xml => Gradle__android_arch_lifecycle_viewmodel_1_1_1_aar.xml} (52%) rename .idea/libraries/{Gradle__com_afollestad_material_cab_0_1_12.xml => Gradle__com_afollestad_material_cab_0_1_12_aar.xml} (73%) rename .idea/libraries/{Gradle__com_afollestad_material_dialogs_commons_0_9_6_0.xml => Gradle__com_afollestad_material_dialogs_commons_0_9_6_0_aar.xml} (62%) rename .idea/libraries/{Gradle__com_afollestad_material_dialogs_core_0_9_6_0.xml => Gradle__com_afollestad_material_dialogs_core_0_9_6_0_aar.xml} (69%) delete mode 100644 .idea/libraries/Gradle__com_android_support_animated_vector_drawable_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_animated_vector_drawable_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_appcompat_v7_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_appcompat_v7_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_asynclayoutinflater_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_cardview_v7_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_cardview_v7_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_collections_28_0_0_rc01_jar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_coordinatorlayout_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_cursoradapter_28_0_0_rc01_aar.xml rename .idea/libraries/{Gradle__com_github_jetradarmobile_android_snowfall_1_1_6.xml => Gradle__com_android_support_customview_28_0_0_rc01_aar.xml} (50%) delete mode 100644 .idea/libraries/Gradle__com_android_support_design_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_design_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_documentfile_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_drawerlayout_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_gridlayout_v7_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_gridlayout_v7_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_interpolator_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_loader_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_localbroadcastmanager_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_media2_28_0_0_alpha02_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_mediarouter_v7_28_0_0_alpha5_aar.xml rename .idea/libraries/{Gradle__com_android_support_multidex_1_0_3.xml => Gradle__com_android_support_multidex_1_0_3_aar.xml} (58%) delete mode 100644 .idea/libraries/Gradle__com_android_support_palette_v7_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_palette_v7_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_preference_v14_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_preference_v14_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_preference_v7_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_preference_v7_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_print_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_recyclerview_v7_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_recyclerview_v7_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_slidingpanelayout_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_support_annotations_27_1_1_jar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_support_annotations_28_0_0_rc01_jar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_support_compat_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_support_compat_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_support_core_ui_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_support_core_ui_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_support_core_utils_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_support_core_utils_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_support_fragment_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_support_fragment_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_support_media_compat_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_support_media_compat_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_support_v4_27_1_1.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_support_vector_drawable_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_support_vector_drawable_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_swiperefreshlayout_28_0_0_rc01_aar.xml delete mode 100644 .idea/libraries/Gradle__com_android_support_transition_27_1_1.xml create mode 100644 .idea/libraries/Gradle__com_android_support_transition_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_versionedparcelable_28_0_0_rc01_aar.xml create mode 100644 .idea/libraries/Gradle__com_android_support_viewpager_28_0_0_rc01_aar.xml rename .idea/libraries/{Gradle__com_github_AdrienPoupa_jaudiotagger_2_2_3.xml => Gradle__com_github_AdrienPoupa_jaudiotagger_2_2_3_aar.xml} (69%) delete mode 100644 .idea/libraries/Gradle__com_github_kabouzeid_RecyclerView_FastScroll_1_0_16_kmod.xml create mode 100644 .idea/libraries/Gradle__com_github_kabouzeid_RecyclerView_FastScroll_1_0_16_kmod_aar.xml rename .idea/libraries/{Gradle__com_github_ksoichiro_android_observablescrollview_1_6_0.xml => Gradle__com_github_ksoichiro_android_observablescrollview_1_6_0_aar.xml} (78%) create mode 100644 .idea/libraries/Gradle__com_google_android_gms_play_services_base_15_0_1_aar.xml create mode 100644 .idea/libraries/Gradle__com_google_android_gms_play_services_basement_15_0_1_aar.xml create mode 100644 .idea/libraries/Gradle__com_google_android_gms_play_services_cast_16_0_1_aar.xml create mode 100644 .idea/libraries/Gradle__com_google_android_gms_play_services_cast_framework_16_0_1_aar.xml create mode 100644 .idea/libraries/Gradle__com_google_android_gms_play_services_flags_15_0_1_aar.xml create mode 100644 .idea/libraries/Gradle__com_google_android_gms_play_services_tasks_15_0_1_aar.xml delete mode 100644 .idea/libraries/Gradle__com_google_code_gson_gson_2_7_jar.xml create mode 100644 .idea/libraries/Gradle__com_google_code_gson_gson_2_8_2_jar.xml rename .idea/libraries/{Gradle__com_h6ah4i_android_widget_advrecyclerview_advrecyclerview_0_11_0.xml => Gradle__com_h6ah4i_android_widget_advrecyclerview_advrecyclerview_0_11_0_aar.xml} (57%) delete mode 100644 .idea/libraries/Gradle__com_jakewharton_butterknife_annotations_8_8_1_jar.xml rename .idea/libraries/{Gradle__com_r0adkll_slidableactivity_2_0_6.xml => Gradle__com_r0adkll_slidableactivity_2_0_6_aar.xml} (65%) rename .idea/libraries/{Gradle__com_sothree_slidinguppanel_library_3_4_0.xml => Gradle__com_sothree_slidinguppanel_library_3_4_0_aar.xml} (70%) rename .idea/libraries/{Gradle__com_squareup_okhttp3_logging_interceptor_3_10_0_jar.xml => Gradle__com_squareup_okhttp3_logging_interceptor_3_11_0_jar.xml} (55%) delete mode 100644 .idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_10_0_jar.xml create mode 100644 .idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_11_0_jar.xml delete mode 100644 .idea/libraries/Gradle__com_squareup_retrofit2_adapter_rxjava2_2_3_0_jar.xml create mode 100644 .idea/libraries/Gradle__com_squareup_retrofit2_adapter_rxjava2_2_4_0_jar.xml delete mode 100644 .idea/libraries/Gradle__com_squareup_retrofit2_converter_gson_2_3_0_jar.xml create mode 100644 .idea/libraries/Gradle__com_squareup_retrofit2_converter_gson_2_4_0_jar.xml delete mode 100644 .idea/libraries/Gradle__com_squareup_retrofit2_retrofit_2_3_0_jar.xml create mode 100644 .idea/libraries/Gradle__com_squareup_retrofit2_retrofit_2_4_0_jar.xml rename .idea/libraries/{Gradle__io_reactivex_rxjava2_rxandroid_2_0_2.xml => Gradle__io_reactivex_rxjava2_rxandroid_2_0_2_aar.xml} (70%) rename .idea/libraries/{Gradle__me_zhanghai_android_materialprogressbar_library_1_4_2.xml => Gradle__me_zhanghai_android_materialprogressbar_library_1_4_2_aar.xml} (62%) delete mode 100644 .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_6_jar.xml delete mode 100644 .idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_6_jar.xml create mode 100644 .idea/libraries/Gradle__org_nanohttpd_nanohttpd_2_3_1_jar.xml rename .idea/libraries/{Gradle__uk_co_chrisjenx_calligraphy_2_3_0.xml => Gradle__uk_co_chrisjenx_calligraphy_2_3_0_aar.xml} (77%) create mode 100644 app/src/main/java/code/name/monkey/retromusic/cast/CastHelper.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/cast/CastOptionsProvider.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/cast/ExpandedControlsActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/cast/WebServer.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/misc/CustomMediaRouteActionProvider.java create mode 100755 app/src/main/java/code/name/monkey/retromusic/misc/NavigationIconClickListener.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/views/CustomMediaRouteButton.java create mode 100644 app/src/main/java/code/name/monkey/retromusic/views/ThemeableMediaRouteActionProvider.java create mode 100644 app/src/main/res/animator/appbar_elevation.xml create mode 100644 app/src/main/res/drawable/bottom_navigation_item_colors.xml create mode 100644 app/src/main/res/drawable/et_bg_circular_top_corners.xml create mode 100644 app/src/main/res/drawable/ic_bug_report_white_24dp.xml delete mode 100644 app/src/main/res/layout-land/activity_main_drawer_layout.xml create mode 100644 app/src/main/res/layout-land/fragment_material.xml create mode 100644 app/src/main/res/layout-land/retro_backdrop.xml delete mode 100644 app/src/main/res/layout-sw600dp/fragment_banner_home.xml create mode 100644 app/src/main/res/layout-sw600dp/retro_backdrop.xml rename app/src/main/res/{layout-sw600dp-land => layout-xlarge-land}/activity_album_tag_editor.xml (100%) rename app/src/main/res/{layout-sw600dp => layout-xlarge}/activity_about_content.xml (100%) rename app/src/main/res/{layout-sw600dp => layout-xlarge}/activity_album_tag_editor.xml (100%) delete mode 100644 app/src/main/res/layout-xlarge/activity_settings.xml delete mode 100644 app/src/main/res/layout-xlarge/activity_song_tag_editor.xml delete mode 100644 app/src/main/res/layout-xlarge/fragment_library.xml create mode 100644 app/src/main/res/layout/retro_backdrop.xml create mode 100644 app/src/main/res/values-land/integers.xml create mode 100644 app/src/main/res/values/cast_style.xml create mode 100644 appthemehelper/src/main/java/code/name/monkey/appthemehelper/util/NavigationViewUtil.java diff --git a/.idea/RetroMusicPlayer.iml b/.idea/RetroMusicPlayer.iml new file mode 100644 index 000000000..c57968224 --- /dev/null +++ b/.idea/RetroMusicPlayer.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml index 2a9c5e01a..6b96cf0c1 100644 --- a/.idea/assetWizardSettings.xml +++ b/.idea/assetWizardSettings.xml @@ -3,7 +3,7 @@ - - - - - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 9138ca8d4..bef72af79 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + diff --git a/app/app.iml b/app/app.iml index 2841e703f..2e4360fab 100644 --- a/app/app.iml +++ b/app/app.iml @@ -1,5 +1,5 @@ - + @@ -29,9 +29,9 @@ - + - + @@ -43,9 +43,9 @@ - + - + @@ -131,13 +131,11 @@ - - @@ -159,75 +157,97 @@ + - + - + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + - - - + + + + + + + + + + + + - - - - - - - + + + + + - - - + - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 923cfcfed..643562acb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,17 +2,17 @@ apply plugin: 'com.android.application' android { compileSdkVersion 28 - buildToolsVersion '28.0.1' + buildToolsVersion '28.0.2' defaultConfig { minSdkVersion 21 targetSdkVersion 28 - renderscriptTargetApi 28 //must match target sdk and build tools + renderscriptTargetApi 27 //must match target sdk and build tools vectorDrawables.useSupportLibrary = true applicationId "code.name.monkey.retromusic" - versionCode 210 - versionName '1.7.100' + versionCode 214 + versionName '1.7.200' multiDexEnabled true @@ -26,11 +26,12 @@ android { shrinkResources true zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - + resValue "string", "cast_app_id", "1234" } debug { applicationIdSuffix '.debug' versionNameSuffix ' DEBUG' + resValue "string", "cast_app_id", "BA9C3F5E" } } flavorDimensions "default" @@ -60,16 +61,17 @@ android { resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9' } - configurations.all { + /*configurations.all { resolutionStrategy.eachDependency { DependencyResolveDetails details -> def requested = details.requested if (requested.group == 'com.android.support') { - if (!requested.name.startsWith("multidex")) { - details.useVersion '27.1.1' + if (!(requested.name.startsWith("multidex") || requested.name.startsWith("mediarouter"))) { + details.useVersion $supportLibVersion } } } - } + }*/ + } def getProperties(String fileName) { @@ -90,10 +92,10 @@ static def getDate() { } ext { - supportLibVersion = "27.1.1" + supportLibVersion = "28.0.0-rc01" firebase = "11.8.0" retrofit = "2.3.0" - butterKnife = "8.8.1" + butterKnife = "9.0.0-SNAPSHOT" materialDialog = "0.9.6.0" } dependencies { @@ -108,9 +110,14 @@ dependencies { implementation "com.android.support:support-annotations:$supportLibVersion" implementation "com.android.support:preference-v7:$supportLibVersion" implementation "com.android.support:preference-v14:$supportLibVersion" - implementation "com.squareup.retrofit2:retrofit:$retrofit" - implementation "com.squareup.retrofit2:converter-gson:$retrofit" - implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit" + + //For casting + implementation "com.android.support:mediarouter-v7:28.0.0-alpha5" + implementation 'com.google.android.gms:play-services-cast-framework:16.0.1' + + implementation "com.squareup.retrofit2:retrofit:2.4.0" + implementation "com.squareup.retrofit2:converter-gson:2.4.0" + implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0" implementation "com.jakewharton:butterknife:$butterKnife" annotationProcessor "com.jakewharton:butterknife-compiler:$butterKnife" implementation "com.afollestad.material-dialogs:core:$materialDialog" @@ -119,8 +126,8 @@ dependencies { implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' implementation 'io.reactivex.rxjava2:rxjava:2.1.9' implementation 'com.github.bumptech.glide:glide:3.8.0' - implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0' - implementation 'com.squareup.okhttp3:okhttp:3.10.0' + implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' + implementation 'com.squareup.okhttp3:okhttp:3.11.0' implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0' implementation('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.11.0@aar') { transitive = true @@ -137,4 +144,5 @@ dependencies { implementation project(':appthemehelper') implementation 'com.sothree.slidinguppanel:library:3.4.0' implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3' + implementation 'org.nanohttpd:nanohttpd:2.3.1' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 26d2dfee5..9f97ae1a7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -124,6 +124,12 @@ + +

You can view the changelog dialog again at any time from the about section.

Version 1.7.100

  1. Fix: same theme follows entire app
  2. Added: Volume toggle for lock screen to support small screen
  3. Added: Volume toggle for lock screen to support small screen

*If you face any UI related issues you clear app data and cache, if its not working try to uninstall and install again.

\ No newline at end of file +

You can view the changelog dialog again at any time from the about section.

Version 1.7.200

  1. Fix: same theme follows entire app
  2. Fix: toggle home banner
  3. Fix: playlist rename
  4. Added: faq links about
  5. Added: volume toggle for lock screen to support small screen
  6. Added: on-click artist icon to artist details in album details screen
  7. Added: circle style for album and artist lists
  8. Improved: sleep timer

FAQ's

*If you face any UI related issues you clear app data and cache, if its not working try to uninstall and install again.

\ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.java b/app/src/main/java/code/name/monkey/retromusic/Constants.java index 1843bc2e6..529e3d80d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/Constants.java +++ b/app/src/main/java/code/name/monkey/retromusic/Constants.java @@ -38,4 +38,5 @@ public class Constants { public static final String APP_TELEGRAM_LINK = "https://t.me/retromusicapp/"; public static final String APP_TWITTER_LINK = "https://twitter.com/retromusicapp"; public static final String FAQ_LINK = "https://github.com/h4h13/RetroMusicPlayer/blob/master/FAQ.md"; + public static final int CAST_SERVER_PORT = 8080; } diff --git a/app/src/main/java/code/name/monkey/retromusic/RetroApplication.java b/app/src/main/java/code/name/monkey/retromusic/RetroApplication.java index c751f57c1..13cff8a82 100644 --- a/app/src/main/java/code/name/monkey/retromusic/RetroApplication.java +++ b/app/src/main/java/code/name/monkey/retromusic/RetroApplication.java @@ -1,16 +1,15 @@ package code.name.monkey.retromusic; -import android.content.Intent; import android.os.Build; import android.support.annotation.NonNull; import android.support.multidex.MultiDexApplication; import com.anjlab.android.iab.v3.BillingProcessor; import com.anjlab.android.iab.v3.TransactionDetails; +import com.bumptech.glide.Glide; import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager; -import code.name.monkey.retromusic.ui.activities.ErrorHandlerActivity; import uk.co.chrisjenx.calligraphy.CalligraphyConfig; public class RetroApplication extends MultiDexApplication { @@ -104,6 +103,12 @@ public class RetroApplication extends MultiDexApplication { //startActivity(intent); } + @Override + public void onLowMemory() { + super.onLowMemory(); + Glide.with(this).onLowMemory(); + } + @Override public void onTerminate() { super.onTerminate(); diff --git a/app/src/main/java/code/name/monkey/retromusic/cast/CastHelper.java b/app/src/main/java/code/name/monkey/retromusic/cast/CastHelper.java new file mode 100644 index 000000000..7607a410b --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/cast/CastHelper.java @@ -0,0 +1,59 @@ +package code.name.monkey.retromusic.cast; + +import android.net.Uri; + +import com.google.android.gms.cast.MediaInfo; +import com.google.android.gms.cast.MediaMetadata; +import com.google.android.gms.cast.framework.CastSession; +import com.google.android.gms.cast.framework.media.RemoteMediaClient; +import com.google.android.gms.common.images.WebImage; + +import java.net.MalformedURLException; +import java.net.URL; + +import code.name.monkey.retromusic.Constants; +import code.name.monkey.retromusic.model.Song; +import code.name.monkey.retromusic.util.RetroUtil; + +/** + * Created by naman on 2/12/17. + */ + +public class CastHelper { + + public static void startCasting(CastSession castSession, Song song) { + + String ipAddress = RetroUtil.getIPAddress(true); + URL baseUrl; + try { + baseUrl = new URL("http", ipAddress, Constants.CAST_SERVER_PORT, ""); + } catch (MalformedURLException e) { + e.printStackTrace(); + return; + } + + String songUrl = baseUrl.toString() + "/song?id=" + song.id; + String albumArtUrl = baseUrl.toString() + "/albumart?id=" + song.albumId; + + MediaMetadata musicMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MUSIC_TRACK); + + musicMetadata.putString(MediaMetadata.KEY_TITLE, song.title); + musicMetadata.putString(MediaMetadata.KEY_ARTIST, song.artistName); + musicMetadata.putString(MediaMetadata.KEY_ALBUM_TITLE, song.albumName); + musicMetadata.putInt(MediaMetadata.KEY_TRACK_NUMBER, song.trackNumber); + musicMetadata.addImage(new WebImage(Uri.parse(albumArtUrl))); + + try { + MediaInfo mediaInfo = new MediaInfo.Builder(songUrl) + .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) + .setContentType("audio/mpeg") + .setMetadata(musicMetadata) + .setStreamDuration(song.duration) + .build(); + RemoteMediaClient remoteMediaClient = castSession.getRemoteMediaClient(); + remoteMediaClient.load(mediaInfo, true, 0); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/cast/CastOptionsProvider.java b/app/src/main/java/code/name/monkey/retromusic/cast/CastOptionsProvider.java new file mode 100644 index 000000000..f84940fe0 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/cast/CastOptionsProvider.java @@ -0,0 +1,45 @@ +package code.name.monkey.retromusic.cast; + +import android.content.Context; + +import com.google.android.gms.cast.framework.CastOptions; +import com.google.android.gms.cast.framework.OptionsProvider; +import com.google.android.gms.cast.framework.SessionProvider; +import com.google.android.gms.cast.framework.media.CastMediaOptions; +import com.google.android.gms.cast.framework.media.MediaIntentReceiver; +import com.google.android.gms.cast.framework.media.NotificationOptions; + +import java.util.ArrayList; +import java.util.List; + +import code.name.monkey.retromusic.R; + +public class CastOptionsProvider implements OptionsProvider { + @Override + public CastOptions getCastOptions(Context context) { + List buttonActions = new ArrayList<>(); + buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK); + buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING); + int[] compatButtonActionsIndicies = new int[]{ 0, 1 }; + + NotificationOptions notificationOptions = new NotificationOptions.Builder() + .setActions(buttonActions, compatButtonActionsIndicies) + .setTargetActivityClassName(ExpandedControlsActivity.class.getName()) + .build(); + + CastMediaOptions mediaOptions = new CastMediaOptions.Builder() + .setNotificationOptions(notificationOptions) + .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName()) + .build(); + + return new CastOptions.Builder() + .setReceiverApplicationId(context.getString(R.string.cast_app_id)) + .setCastMediaOptions(mediaOptions) + .build(); + } + + @Override + public List getAdditionalSessionProviders(Context context) { + return null; + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/cast/ExpandedControlsActivity.java b/app/src/main/java/code/name/monkey/retromusic/cast/ExpandedControlsActivity.java new file mode 100644 index 000000000..3c9b39389 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/cast/ExpandedControlsActivity.java @@ -0,0 +1,28 @@ +package code.name.monkey.retromusic.cast; + +import android.os.Bundle; +import android.view.Menu; +import android.view.View; + +import com.google.android.gms.cast.framework.CastButtonFactory; +import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity; + +import code.name.monkey.retromusic.R; + + +public class ExpandedControlsActivity extends ExpandedControllerActivity { + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.menu_expanded_controller, menu); + CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item); + return true; + } + + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/cast/WebServer.java b/app/src/main/java/code/name/monkey/retromusic/cast/WebServer.java new file mode 100644 index 000000000..fd96ae8c8 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/cast/WebServer.java @@ -0,0 +1,75 @@ +package code.name.monkey.retromusic.cast; + +import android.content.Context; +import android.net.Uri; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Map; + +import code.name.monkey.retromusic.Constants; +import code.name.monkey.retromusic.util.RetroUtil; +import fi.iki.elonen.NanoHTTPD; + +public class WebServer extends NanoHTTPD { + + private Context context; + private Uri songUri, albumArtUri; + + public WebServer(Context context) { + super(Constants.CAST_SERVER_PORT); + this.context = context; + } + + @Override + public Response serve(String uri, Method method, + Map header, + Map parameters, + Map files) { + if (uri.contains("albumart")) { + //serve the picture + + String albumId = parameters.get("id"); + this.albumArtUri = RetroUtil.getAlbumArtUri(Long.parseLong(albumId)); + + if (albumArtUri != null) { + String mediasend = "image/jpg"; + InputStream fisAlbumArt = null; + try { + fisAlbumArt = context.getContentResolver().openInputStream(albumArtUri); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + Response.Status st = Response.Status.OK; + + //serve the song + return newChunkedResponse(st, mediasend, fisAlbumArt); + } + + } else if (uri.contains("song")) { + + String songId = parameters.get("id"); + this.songUri = RetroUtil.getSongUri(context, Long.parseLong(songId)); + + if (songUri != null) { + String mediasend = "audio/mp3"; + FileInputStream fisSong = null; + File song = new File(songUri.getPath()); + try { + fisSong = new FileInputStream(song); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + Response.Status st = Response.Status.OK; + + //serve the song + return newFixedLengthResponse(st, mediasend, fisSong, song.length()); + } + + } + return newFixedLengthResponse("Error"); + } + +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.java index 16a24fec3..8fcfe6171 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/DeleteSongsDialog.java @@ -21,11 +21,12 @@ import code.name.monkey.retromusic.views.RoundedBottomSheetDialogFragment; import java.util.ArrayList; public class DeleteSongsDialog extends RoundedBottomSheetDialogFragment { + @BindView(R.id.title) + TextView title; @BindView(R.id.action_delete) TextView delete; - @BindView(R.id.title) - TextView title; + @BindView(R.id.action_cancel) TextView cancel; @@ -63,28 +64,26 @@ public class DeleteSongsDialog extends RoundedBottomSheetDialogFragment { dismiss(); } + @SuppressWarnings("ConstantConditions") @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - delete.setTextColor(ThemeStore.textColorPrimary(getContext())); + title.setTextColor(ThemeStore.textColorPrimary(getContext())); + delete.setTextColor(ThemeStore.textColorPrimary(getContext())); cancel.setTextColor(ThemeStore.textColorPrimary(getContext())); //noinspection unchecked,ConstantConditions final ArrayList songs = getArguments().getParcelableArrayList("songs"); - int title; CharSequence content; if (songs != null) { if (songs.size() > 1) { - title = R.string.delete_songs_title; content = Html.fromHtml(getString(R.string.delete_x_songs, songs.size())); } else { - title = R.string.delete_song_title; content = Html.fromHtml(getString(R.string.delete_song_x, songs.get(0).title)); } - this.title.setText(title); - this.delete.setText(content); + this.title.setText(content); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/HomeOptionDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/HomeOptionDialog.java index db4d7bc96..2b235b34f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/HomeOptionDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/HomeOptionDialog.java @@ -14,7 +14,6 @@ import android.widget.TextView; import java.io.File; import java.util.Calendar; import java.util.List; -import java.util.Objects; import butterknife.BindView; import butterknife.BindViews; @@ -22,12 +21,8 @@ import butterknife.ButterKnife; import butterknife.OnClick; import butterknife.Unbinder; import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.common.views.ATEPrimaryTextView; -import code.name.monkey.appthemehelper.common.views.ATESecondaryTextView; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; -import code.name.monkey.retromusic.ui.activities.MainActivity; -import code.name.monkey.retromusic.ui.fragments.mainactivity.folders.FoldersFragment; import code.name.monkey.retromusic.util.Compressor; import code.name.monkey.retromusic.util.NavigationUtil; import code.name.monkey.retromusic.util.PreferenceUtil; @@ -45,20 +40,15 @@ import static code.name.monkey.retromusic.Constants.USER_PROFILE; public class HomeOptionDialog extends RoundedBottomSheetDialogFragment { private static final String TAG = "HomeOptionDialog"; + static ButterKnife.Setter textColor = (view, value, index) -> view.setTextColor(value.intValue()); Unbinder mUnbinder; - @BindView(R.id.user_image_bottom) CircularImageView userImageBottom; - @BindView(R.id.title_welcome) AppCompatTextView titleWelcome; - - @BindViews({R.id.tv_about, R.id.title_welcome, R.id.text, R.id.tv_buy_pro, R.id.tv_folder, R.id.tv_rate_app, + @BindViews({R.id.tv_about, R.id.title_welcome, R.id.text, R.id.tv_buy_pro, R.id.tv_rate_app, R.id.tv_settings, R.id.tv_sleep_timer}) List textViews; - - static ButterKnife.Setter textColor = (view, value, index) -> view.setTextColor(value.intValue()); - private CompositeDisposable disposable = new CompositeDisposable(); @Nullable @@ -67,8 +57,7 @@ public class HomeOptionDialog extends RoundedBottomSheetDialogFragment { @Nullable Bundle savedInstanceState) { View layout = inflater.inflate(R.layout.user_action_details, container, false); mUnbinder = ButterKnife.bind(this, layout); - layout.findViewById(R.id.action_buy_pro).setVisibility(RetroApplication.isProVersion() ? View - .GONE : View.VISIBLE); + layout.findViewById(R.id.action_buy_pro).setVisibility(RetroApplication.isProVersion() ? View.GONE : View.VISIBLE); ButterKnife.apply(textViews, textColor, ThemeStore.textColorPrimary(getContext())); return layout; } @@ -110,7 +99,7 @@ public class HomeOptionDialog extends RoundedBottomSheetDialogFragment { } @SuppressWarnings("ConstantConditions") - @OnClick({R.id.action_about, R.id.user_info_container, R.id.action_buy_pro, R.id.action_folder, + @OnClick({R.id.action_about, R.id.user_info_container, R.id.action_buy_pro, R.id.action_settings, R.id.action_sleep_timer, R.id.action_rate}) public void onViewClicked(View view) { switch (view.getId()) { @@ -120,14 +109,7 @@ public class HomeOptionDialog extends RoundedBottomSheetDialogFragment { case R.id.action_rate: NavigationUtil.goToPlayStore(getActivity()); break; - case R.id.action_folder: - MainActivity mainActivity = (MainActivity) getActivity(); - if (mainActivity == null) { - return; - } - mainActivity.setCurrentFragment(FoldersFragment.newInstance(getContext()), true, - FoldersFragment.TAG); - break; + case R.id.action_settings: NavigationUtil.goToSettings(getActivity()); break; diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java b/app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java index 489e2238d..21e5ec940 100755 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/SleepTimerDialog.java @@ -16,7 +16,6 @@ import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; @@ -44,6 +43,8 @@ public class SleepTimerDialog extends RoundedBottomSheetDialogFragment { TextView setButton; @BindView(R.id.action_cancel) TextView cancelButton; + @BindView(R.id.action_cancel_container) + View cancelButtonContainer; private int seekArcProgress; private TimerUpdater timerUpdater; @@ -76,6 +77,7 @@ public class SleepTimerDialog extends RoundedBottomSheetDialogFragment { clipDrawable.setColorFilter(dark, PorterDuff.Mode.SRC_IN); } + @SuppressWarnings("ConstantConditions") @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); @@ -84,8 +86,6 @@ public class SleepTimerDialog extends RoundedBottomSheetDialogFragment { seekArcProgress = PreferenceUtil.getInstance(getActivity()).getLastSleepTimerValue(); updateTimeDisplayTime(); seekArc.setProgress(seekArcProgress); - - //noinspection ConstantConditions setProgressBarColor(ThemeStore.accentColor(getContext())); seekArc.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @@ -161,6 +161,7 @@ public class SleepTimerDialog extends RoundedBottomSheetDialogFragment { private class TimerUpdater extends CountDownTimer { TimerUpdater() { + //noinspection ConstantConditions super(PreferenceUtil.getInstance(getActivity()).getNextSleepTimerElapsedRealTime() - SystemClock.elapsedRealtime(), 1000); } @@ -173,6 +174,7 @@ public class SleepTimerDialog extends RoundedBottomSheetDialogFragment { @Override public void onFinish() { cancelButton.setText(null); + cancelButtonContainer.setVisibility(View.GONE); //materialDialog.setActionButton(DialogAction.NEUTRAL, null); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.java b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.java index ccd21773b..77bfc4720 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.java +++ b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.java @@ -34,12 +34,11 @@ import io.reactivex.schedulers.Schedulers; public class MusicPlayerRemote { - public static final String TAG = MusicPlayerRemote.class.getSimpleName(); private static final WeakHashMap mConnectionMap = new WeakHashMap<>(); @Nullable public static MusicService musicService; - + public static ServiceToken bindToService(@NonNull final Context context, final ServiceConnection callback) { Activity realActivity = ((Activity) context).getParent(); @@ -444,6 +443,10 @@ public class MusicPlayerRemote { return musicService != null; } + @interface PlaybackLocation { + int REMOTE = 0; + int LOCAL = 1; + } public static final class ServiceBinder implements ServiceConnection { private final ServiceConnection mCallback; diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/CustomMediaRouteActionProvider.java b/app/src/main/java/code/name/monkey/retromusic/misc/CustomMediaRouteActionProvider.java new file mode 100644 index 000000000..b4ea251a9 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/misc/CustomMediaRouteActionProvider.java @@ -0,0 +1,62 @@ +package code.name.monkey.retromusic.misc; + +import android.app.Activity; +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.MediaRouteActionProvider; +import android.support.v7.app.MediaRouteButton; +import android.support.v7.media.MediaRouteSelector; +import android.util.Log; +import android.view.ViewGroup; + +import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.views.CustomMediaRouteButton; + +public class CustomMediaRouteActionProvider extends MediaRouteActionProvider { + private static final String TAG = "MediaRteActProvider"; + + private Activity activity; + + private MediaRouteSelector selector = MediaRouteSelector.EMPTY; + private CustomMediaRouteButton customMediaRouteButton; + + public CustomMediaRouteActionProvider(Context context) { + super(context); + } + + public void setActivity(@NonNull Activity activity) { + this.activity = activity; + if (customMediaRouteButton != null) { + customMediaRouteButton.setActivity(activity); + } + } + + @Nullable + @Override + public MediaRouteButton getMediaRouteButton() { + return customMediaRouteButton; + } + + /** + * Called when the media route button is being created. + */ + @SuppressWarnings("deprecation") + @Override + public CustomMediaRouteButton onCreateMediaRouteButton() { + if (customMediaRouteButton != null) { + Log.e(TAG, "onCreateMediaRouteButton: This ActionProvider is already associated " + + "with a menu item. Don't reuse MediaRouteActionProvider instances! " + + "Abandoning the old button..."); + } + + customMediaRouteButton = new CustomMediaRouteButton(getContext()); + customMediaRouteButton.setActivity(activity); + customMediaRouteButton.setAlpha(RetroApplication.isProVersion() ? 1.0f : 0.5f); + customMediaRouteButton.setRouteSelector(selector); + customMediaRouteButton.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.FILL_PARENT)); + return customMediaRouteButton; + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/NavigationIconClickListener.java b/app/src/main/java/code/name/monkey/retromusic/misc/NavigationIconClickListener.java new file mode 100755 index 000000000..d51a96328 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/misc/NavigationIconClickListener.java @@ -0,0 +1,89 @@ +package code.name.monkey.retromusic.misc; + +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; +import android.support.annotation.Nullable; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.animation.Interpolator; +import android.widget.ImageView; + +import butterknife.BindInt; +import code.name.monkey.appthemehelper.util.ATHUtil; +import code.name.monkey.retromusic.R; +import code.name.monkey.retromusic.util.RetroUtil; + +/** + * {@link View.OnClickListener} used to translate the product grid sheet downward on + * the Y-axis when the navigation icon in the toolbar is pressed. + */ +public class NavigationIconClickListener implements View.OnClickListener { + + private final AnimatorSet animatorSet = new AnimatorSet(); + @BindInt(R.integer.options_height) + int options; + private Context context; + private View sheet, menu; + private Interpolator interpolator; + private int height; + private boolean backdropShown = false; + private Drawable openIcon; + private Drawable closeIcon; + + public NavigationIconClickListener(Context context, View sheet, View menu, @Nullable Interpolator interpolator) { + this(context, sheet, menu, interpolator, null, null); + } + + public NavigationIconClickListener(Context context, View sheet, View menu, @Nullable Interpolator interpolator, + @Nullable Drawable openIcon, @Nullable Drawable closeIcon) { + this.context = context; + this.sheet = sheet; + this.menu = menu; + this.interpolator = interpolator; + this.openIcon = openIcon; + this.closeIcon = closeIcon; + + DisplayMetrics displayMetrics = new DisplayMetrics(); + ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + height = displayMetrics.heightPixels; + } + + @Override + public void onClick(View view) { + backdropShown = !backdropShown; + + // Cancel the existing animations + animatorSet.removeAllListeners(); + animatorSet.end(); + animatorSet.cancel(); + + if (!(view instanceof ImageView)) { + throw new IllegalArgumentException("updateIcon() must be called on an ImageView"); + } + updateIcon((ImageView) view); + final int translateY = (int) RetroUtil.convertDpToPixel(RetroUtil.isLandscape(view.getResources()) ? 3 * 48 : 5 * 48, view.getContext()); + + ObjectAnimator animator = ObjectAnimator.ofFloat(sheet, "translationY", backdropShown ? translateY : 0); + animator.setDuration(500); + if (interpolator != null) { + animator.setInterpolator(interpolator); + } + animatorSet.play(animator); + animator.start(); + } + + private void updateIcon(ImageView view) { + if (openIcon != null && closeIcon != null) { + view.setImageTintList(ColorStateList.valueOf(ATHUtil.resolveColor(context, R.attr.iconColor))); + if (backdropShown) { + view.setImageDrawable(closeIcon); + } else { + view.setImageDrawable(openIcon); + } + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.java index 46d1ca5ba..4af3e0a95 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/AlbumDetailsActivity.java @@ -70,30 +70,42 @@ public class AlbumDetailsActivity extends AbsSlidingMusicPanelActivity implement private static final int TAG_EDITOR_REQUEST = 2001; @BindView(R.id.image) ImageView image; + @BindView(R.id.recycler_view) RecyclerView recyclerView; + @BindView(R.id.title) TextView title; + @BindView(R.id.text) TextView text; + @BindView(R.id.song_title) AppCompatTextView songTitle; + @BindView(R.id.action_shuffle_all) FloatingActionButton shuffleButton; + @BindView(R.id.collapsing_toolbar) @Nullable CollapsingToolbarLayout collapsingToolbarLayout; + @BindView(R.id.app_bar) @Nullable AppBarLayout appBarLayout; + @BindView(R.id.content) View contentContainer; + @BindView(R.id.toolbar) Toolbar toolbar; + @BindView(R.id.more_recycler_view) RecyclerView moreRecyclerView; + @BindView(R.id.more_title) TextView moreTitle; + @BindView(R.id.artist_image) ImageView artistImage; @@ -114,7 +126,7 @@ public class AlbumDetailsActivity extends AbsSlidingMusicPanelActivity implement supportPostponeEnterTransition(); setupToolbarMarginHeight(); - setBottomBarVisibility(View.GONE); + setLightNavigationBar(true); setNavigationbarColorAuto(); @@ -132,9 +144,9 @@ public class AlbumDetailsActivity extends AbsSlidingMusicPanelActivity implement } toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); - setSupportActionBar(toolbar); //noinspection ConstantConditions getSupportActionBar().setTitle(null); + setSupportActionBar(toolbar); if (toolbar != null && !PreferenceUtil.getInstance(this).getFullScreenMode()) { ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) toolbar @@ -177,7 +189,9 @@ public class AlbumDetailsActivity extends AbsSlidingMusicPanelActivity implement artistPairs); break; case R.id.action_shuffle_all: - MusicPlayerRemote.openAndShuffleQueue(album.songs, true); + if (album.songs != null) { + MusicPlayerRemote.openAndShuffleQueue(album.songs, true); + } break; } } @@ -315,7 +329,7 @@ public class AlbumDetailsActivity extends AbsSlidingMusicPanelActivity implement getMenuInflater().inflate(R.menu.menu_album_detail, menu); MenuItem sortOrder = menu.findItem(R.id.action_sort_order); setUpSortOrderMenu(sortOrder.getSubMenu()); - return true; + return super.onCreateOptionsMenu(menu); } @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java index 6e3920693..4853e1aff 100755 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ArtistDetailActivity.java @@ -72,40 +72,55 @@ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implement @BindView(R.id.app_bar) @Nullable AppBarLayout appBarLayout; + @BindView(R.id.collapsing_toolbar) @Nullable CollapsingToolbarLayout collapsingToolbarLayout; @BindView(R.id.image) ImageView image; + @BindView(R.id.biography) TextView biographyTextView; + @BindView(R.id.recycler_view) RecyclerView recyclerView; + @BindView(R.id.album_recycler_view) RecyclerView albumRecyclerView; + @BindView(R.id.album_title) AppCompatTextView albumTitle; + @BindView(R.id.song_title) AppCompatTextView songTitle; + @BindView(R.id.biography_title) AppCompatTextView biographyTitle; + @BindView(R.id.title) TextView title; + @BindView(R.id.text) TextView text; + @BindView(R.id.action_shuffle_all) FloatingActionButton shuffleButton; + @BindView(R.id.gradient_background) @Nullable View background; + @BindView(R.id.image_container) @Nullable View imageContainer; + @BindView(R.id.content) View contentContainer; + @BindView(R.id.toolbar) Toolbar toolbar; + @Nullable private Spanned biography; private Artist artist; @@ -125,7 +140,7 @@ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implement setDrawUnderStatusBar(true); super.onCreate(bundle); ButterKnife.bind(this); - setBottomBarVisibility(View.GONE); + setNavigationbarColorAuto(); setLightNavigationBar(true); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java index fb5091bdd..ab89e96bd 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ErrorHandlerActivity.java @@ -1 +1 @@ -package code.name.monkey.retromusic.ui.activities; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import butterknife.OnClick; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; import code.name.monkey.retromusic.ui.activities.base.AbsBaseActivity; public class ErrorHandlerActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_error_handler); } @OnClick(R.id.clear_app_data) void clearAppDate(View view) { RetroApplication.deleteAppData(); } } \ No newline at end of file +package code.name.monkey.retromusic.ui.activities; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import butterknife.OnClick; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; public class ErrorHandlerActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_error_handler); } @OnClick(R.id.clear_app_data) void clearAppDate(View view) { RetroApplication.deleteAppData(); } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java index e246bf65a..888340a84 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/GenreDetailsActivity.java @@ -96,8 +96,6 @@ public class GenreDetailsActivity extends AbsSlidingMusicPanelActivity implement setTaskDescriptionColorAuto(); setLightNavigationBar(true); - setBottomBarVisibility(View.GONE); - RetroUtil.statusBarHeight(statusBar); genre = getIntent().getParcelableExtra(EXTRA_GENRE_ID); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.java index ad261d7b3..d3335770a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/LyricsActivity.java @@ -15,6 +15,18 @@ import android.view.WindowManager; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.TextView; + +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; +import com.bumptech.glide.Glide; + +import org.jaudiotagger.tag.FieldKey; + +import java.io.File; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.Map; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; @@ -33,307 +45,304 @@ import code.name.monkey.retromusic.util.MusicUtil; import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.RetroUtil; import code.name.monkey.retromusic.views.LyricView; -import com.afollestad.materialdialogs.DialogAction; -import com.afollestad.materialdialogs.MaterialDialog; -import com.bumptech.glide.Glide; import io.reactivex.disposables.CompositeDisposable; -import java.io.File; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.Map; -import org.jaudiotagger.tag.FieldKey; public class LyricsActivity extends AbsMusicServiceActivity implements - MusicProgressViewUpdateHelper.Callback { + MusicProgressViewUpdateHelper.Callback { - @BindView(R.id.title) - TextView songTitle; - @BindView(R.id.text) - TextView songText; - @BindView(R.id.lyrics_view) - LyricView lyricView; - @BindView(R.id.offline_lyrics) - TextView offlineLyrics; - @BindView(R.id.actions) - RadioGroup actionsLayout; - @BindView(R.id.gradient_background) - View background; + @BindView(R.id.title) + TextView songTitle; - private MusicProgressViewUpdateHelper updateHelper; - private AsyncTask updateLyricsAsyncTask; - private CompositeDisposable disposable; - private Song song; - private Lyrics lyrics; + @BindView(R.id.text) + TextView songText; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_lyrics); - ButterKnife.bind(this); + @BindView(R.id.lyrics_view) + LyricView lyricView; - setStatusbarColorAuto(); - setNavigationbarColorAuto(); - setTaskDescriptionColorAuto(); - setLightNavigationBar(true); + @BindView(R.id.offline_lyrics) + TextView offlineLyrics; - updateHelper = new MusicProgressViewUpdateHelper(this, 500, 1000); + @BindView(R.id.actions) + RadioGroup actionsLayout; - setupLyricsView(); - setupWakelock(); + @BindView(R.id.gradient_background) + View background; - actionsLayout.setOnCheckedChangeListener((group, checkedId) -> selectLyricsTye(checkedId)); + private MusicProgressViewUpdateHelper updateHelper; + private AsyncTask updateLyricsAsyncTask; + private CompositeDisposable disposable; + private Song song; + private Lyrics lyrics; - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_lyrics); + ButterKnife.bind(this); - private void selectLyricsTye(int group) { + setStatusbarColorAuto(); + setNavigationbarColorAuto(); + setTaskDescriptionColorAuto(); + setLightNavigationBar(true); - RadioButton radioButton = actionsLayout.findViewById(group); - radioButton.setBackgroundTintList(ColorStateList.valueOf(ThemeStore.accentColor(this))); - radioButton.setTextColor(ThemeStore.textColorPrimary(this)); + updateHelper = new MusicProgressViewUpdateHelper(this, 500, 1000); - offlineLyrics.setVisibility(View.GONE); - lyricView.setVisibility(View.GONE); + setupLyricsView(); + setupWakelock(); + loadLrcFile(); - switch (group) { - case R.id.synced_lyrics: - loadLRCLyrics(); - lyricView.setVisibility(View.VISIBLE); - break; - default: - case R.id.normal_lyrics: - loadSongLyrics(); - offlineLyrics.setVisibility(View.VISIBLE); - break; + actionsLayout.setOnCheckedChangeListener((group, checkedId) -> selectLyricsTye(checkedId)); + actionsLayout.check(PreferenceUtil.getInstance(this).getLastLyricsType()); } - } - private void loadLRCLyrics() { - if (LyricUtil.isLrcFileExist(song.title, song.artistName)) { - showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName)); + private void selectLyricsTye(int group) { + PreferenceUtil.getInstance(this).setLastLyricsType(group); + RadioButton radioButton = actionsLayout.findViewById(group); + radioButton.setBackgroundTintList(ColorStateList.valueOf(ThemeStore.accentColor(this))); + radioButton.setTextColor(ThemeStore.textColorPrimary(this)); + + offlineLyrics.setVisibility(View.GONE); + lyricView.setVisibility(View.GONE); + + switch (group) { + case R.id.synced_lyrics: + loadLRCLyrics(); + lyricView.setVisibility(View.VISIBLE); + break; + default: + case R.id.normal_lyrics: + loadSongLyrics(); + offlineLyrics.setVisibility(View.VISIBLE); + break; + } } - } - private void setupWakelock() { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - - private void setupLyricsView() { - disposable = new CompositeDisposable(); - - lyricView - .setOnPlayerClickListener((progress, content) -> MusicPlayerRemote.seekTo((int) progress)); - //lyricView.setHighLightTextColor(ThemeStore.accentColor(this)); - lyricView.setDefaultColor(ContextCompat.getColor(this, R.color.md_grey_400)); - //lyricView.setTouchable(false); - lyricView.setHintColor(Color.WHITE); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - loadLrcFile(); - } - - @Override - protected void onResume() { - super.onResume(); - updateHelper.start(); - } - - @Override - protected void onPause() { - super.onPause(); - updateHelper.stop(); - } - - @Override - public void onServiceConnected() { - super.onServiceConnected(); - loadLrcFile(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - disposable.clear(); - - if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask.isCancelled()) { - updateLyricsAsyncTask.cancel(true); + private void loadLRCLyrics() { + if (LyricUtil.isLrcFileExist(song.title, song.artistName)) { + showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName)); + } } - } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - onBackPressed(); + private void setupWakelock() { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } - return super.onOptionsItemSelected(item); - } - @Override - public void onUpdateProgressViews(int progress, int total) { - lyricView.setCurrentTimeMillis(progress); - } + private void setupLyricsView() { + disposable = new CompositeDisposable(); - private void loadLrcFile() { - song = MusicPlayerRemote.getCurrentSong(); - songTitle.setText(song.title); - songText.setText(song.artistName); - SongGlideRequest.Builder.from(Glide.with(this), song) - .checkIgnoreMediaStore(this) - .generatePalette(this) - .build() - .into(new RetroMusicColoredTarget(findViewById(R.id.image)) { - @Override - public void onColorReady(int color) { - if (PreferenceUtil.getInstance(LyricsActivity.this).getAdaptiveColor()) { - background.setBackgroundColor(color); + lyricView + .setOnPlayerClickListener((progress, content) -> MusicPlayerRemote.seekTo((int) progress)); + //lyricView.setHighLightTextColor(ThemeStore.accentColor(this)); + lyricView.setDefaultColor(ContextCompat.getColor(this, R.color.md_grey_400)); + //lyricView.setTouchable(false); + lyricView.setHintColor(Color.WHITE); + } + + @Override + public void onPlayingMetaChanged() { + super.onPlayingMetaChanged(); + loadLrcFile(); + } + + @Override + protected void onResume() { + super.onResume(); + updateHelper.start(); + } + + @Override + protected void onPause() { + super.onPause(); + updateHelper.stop(); + } + + @Override + public void onServiceConnected() { + super.onServiceConnected(); + loadLrcFile(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + disposable.clear(); + + if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask.isCancelled()) { + updateLyricsAsyncTask.cancel(true); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onUpdateProgressViews(int progress, int total) { + lyricView.setCurrentTimeMillis(progress); + } + + private void loadLrcFile() { + song = MusicPlayerRemote.getCurrentSong(); + songTitle.setText(song.title); + songText.setText(song.artistName); + SongGlideRequest.Builder.from(Glide.with(this), song) + .checkIgnoreMediaStore(this) + .generatePalette(this) + .build() + .into(new RetroMusicColoredTarget(findViewById(R.id.image)) { + @Override + public void onColorReady(int color) { + if (PreferenceUtil.getInstance(LyricsActivity.this).getAdaptiveColor()) { + background.setBackgroundColor(color); + } + } + }); + } + + private void showLyricsLocal(File file) { + if (file == null) { + lyricView.reset(); + } else { + lyricView.setLyricFile(file, "UTF-8"); + } + } + + @OnClick({R.id.edit_lyrics, R.id.back}) + public void onViewClicked(View view) { + switch (view.getId()) { + case R.id.back: + onBackPressed(); + break; + case R.id.edit_lyrics: + switch (actionsLayout.getCheckedRadioButtonId()) { + case R.id.synced_lyrics: + showSyncedLyrics(); + break; + case R.id.normal_lyrics: + showLyricsSaveDialog(); + break; + } + break; + } + } + + @SuppressLint("StaticFieldLeak") + private void loadSongLyrics() { + if (updateLyricsAsyncTask != null) { + updateLyricsAsyncTask.cancel(false); + } + final Song song = MusicPlayerRemote.getCurrentSong(); + updateLyricsAsyncTask = new AsyncTask() { + @Override + protected Lyrics doInBackground(Void... params) { + String data = MusicUtil.getLyrics(song); + if (TextUtils.isEmpty(data)) { + return null; + } + return Lyrics.parse(song, data); } - } - }); - selectLyricsTye(actionsLayout.getCheckedRadioButtonId()); - } - private void showLyricsLocal(File file) { - if (file == null) { - lyricView.reset(); - } else { - lyricView.setLyricFile(file, "UTF-8"); + @Override + protected void onPreExecute() { + super.onPreExecute(); + lyrics = null; + } + + @Override + protected void onPostExecute(Lyrics l) { + lyrics = l; + offlineLyrics.setVisibility(View.VISIBLE); + if (l == null) { + offlineLyrics.setText(R.string.no_lyrics_found); + return; + } + offlineLyrics.setText(l.data); + } + + @Override + protected void onCancelled(Lyrics s) { + onPostExecute(null); + } + }.execute(); } - } - @OnClick({R.id.edit_lyrics, R.id.back}) - public void onViewClicked(View view) { - switch (view.getId()) { - case R.id.back: - onBackPressed(); - break; - case R.id.edit_lyrics: - switch (actionsLayout.getCheckedRadioButtonId()) { - case R.id.synced_lyrics: - showSyncedLyrics(); - break; - case R.id.normal_lyrics: - showLyricsSaveDialog(); - break; + private void showSyncedLyrics() { + String content = ""; + try { + content = LyricUtil.getStringFromFile(song.title, song.artistName); + } catch (Exception e) { + e.printStackTrace(); } - break; + new MaterialDialog.Builder(this) + .title("Add lyrics") + .neutralText("Search") + .content("Add time frame lyrics") + .negativeText("Delete") + .onNegative((dialog, which) -> { + LyricUtil.deleteLrcFile(song.title, song.artistName); + loadLrcFile(); + }) + .onNeutral( + (dialog, which) -> RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchLrcUrl())) + .inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE) + .input("Paste lyrics here", content, (dialog, input) -> { + LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString()); + loadLrcFile(); + }).show(); } - } - @SuppressLint("StaticFieldLeak") - private void loadSongLyrics() { - if (updateLyricsAsyncTask != null) { - updateLyricsAsyncTask.cancel(false); + private String getGoogleSearchLrcUrl() { + String baseUrl = "http://www.google.com/search?"; + String query = song.title + "+" + song.artistName; + query = "q=" + query.replace(" ", "+") + " .lrc"; + baseUrl += query; + return baseUrl; } - final Song song = MusicPlayerRemote.getCurrentSong(); - updateLyricsAsyncTask = new AsyncTask() { - @Override - protected Lyrics doInBackground(Void... params) { - String data = MusicUtil.getLyrics(song); - if (TextUtils.isEmpty(data)) { - return null; + + private void showLyricsSaveDialog() { + String content = ""; + if (lyrics == null) { + content = ""; + } else { + content = lyrics.data; } - return Lyrics.parse(song, data); - } + new MaterialDialog.Builder(this) + .title("Add lyrics") + .neutralText("Search") + .onNeutral(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchUrl(song.title, song.artistName)); + } + }) + .inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE) + .input("Paste lyrics here", content, (dialog, input) -> { + Map fieldKeyValueMap = new EnumMap<>(FieldKey.class); + fieldKeyValueMap.put(FieldKey.LYRICS, input.toString()); - @Override - protected void onPreExecute() { - super.onPreExecute(); - lyrics = null; - } - - @Override - protected void onPostExecute(Lyrics l) { - lyrics = l; - offlineLyrics.setVisibility(View.VISIBLE); - if (l == null) { - offlineLyrics.setText(R.string.no_lyrics_found); - return; - } - offlineLyrics.setText(l.data); - } - - @Override - protected void onCancelled(Lyrics s) { - onPostExecute(null); - } - }.execute(); - } - - private void showSyncedLyrics() { - String content = ""; - try { - content = LyricUtil.getStringFromFile(song.title, song.artistName); - } catch (Exception e) { - e.printStackTrace(); + new WriteTagsAsyncTask(LyricsActivity.this) + .execute( + new WriteTagsAsyncTask.LoadingInfo(getSongPaths(song), fieldKeyValueMap, null)); + loadLrcFile(); + }) + .show(); } - new MaterialDialog.Builder(this) - .title("Add lyrics") - .neutralText("Search") - .content("Add time frame lyrics") - .negativeText("Delete") - .onNegative((dialog, which) -> { - LyricUtil.deleteLrcFile(song.title, song.artistName); - loadLrcFile(); - }) - .onNeutral( - (dialog, which) -> RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchLrcUrl())) - .inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE) - .input("Paste lyrics here", content, (dialog, input) -> { - LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString()); - loadLrcFile(); - }).show(); - } - private String getGoogleSearchLrcUrl() { - String baseUrl = "http://www.google.com/search?"; - String query = song.title + "+" + song.artistName; - query = "q=" + query.replace(" ", "+") + " .lrc"; - baseUrl += query; - return baseUrl; - } - - private void showLyricsSaveDialog() { - String content = ""; - if (lyrics == null) { - content = ""; - } else { - content = lyrics.data; + private ArrayList getSongPaths(Song song) { + ArrayList paths = new ArrayList<>(1); + paths.add(song.data); + return paths; } - new MaterialDialog.Builder(this) - .title("Add lyrics") - .neutralText("Search") - .onNeutral(new MaterialDialog.SingleButtonCallback() { - @Override - public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { - RetroUtil.openUrl(LyricsActivity.this, getGoogleSearchUrl(song.title, song.artistName)); - } - }) - .inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE) - .input("Paste lyrics here", content, (dialog, input) -> { - Map fieldKeyValueMap = new EnumMap<>(FieldKey.class); - fieldKeyValueMap.put(FieldKey.LYRICS, input.toString()); - new WriteTagsAsyncTask(LyricsActivity.this) - .execute( - new WriteTagsAsyncTask.LoadingInfo(getSongPaths(song), fieldKeyValueMap, null)); - loadLrcFile(); - }) - .show(); - } - - private ArrayList getSongPaths(Song song) { - ArrayList paths = new ArrayList<>(1); - paths.add(song.data); - return paths; - } - - private String getGoogleSearchUrl(String title, String text) { - String baseUrl = "http://www.google.com/search?"; - String query = title + "+" + text; - query = "q=" + query.replace(" ", "+") + " lyrics"; - baseUrl += query; - return baseUrl; - } + private String getGoogleSearchUrl(String title, String text) { + String baseUrl = "http://www.google.com/search?"; + String query = title + "+" + text; + query = "q=" + query.replace(" ", "+") + " lyrics"; + baseUrl += query; + return baseUrl; + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.java index 2b94f423e..645c002c1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/MainActivity.java @@ -44,6 +44,7 @@ import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.service.MusicService; import code.name.monkey.retromusic.ui.activities.base.AbsSlidingMusicPanelActivity; import code.name.monkey.retromusic.ui.fragments.mainactivity.LibraryFragment; +import code.name.monkey.retromusic.ui.fragments.mainactivity.folders.FoldersFragment; import code.name.monkey.retromusic.ui.fragments.mainactivity.home.BannerHomeFragment; import code.name.monkey.retromusic.ui.fragments.mainactivity.home.HomeFragment; import code.name.monkey.retromusic.util.PreferenceUtil; @@ -59,6 +60,10 @@ public class MainActivity extends AbsSlidingMusicPanelActivity implements private static final int APP_USER_INFO_REQUEST = 9003; private static final int REQUEST_CODE_THEME = 9002; + public static final int LIBRARY = 1; + public static final int FOLDERS = 3; + public static final int HOME = 0; + @Nullable MainActivityFragmentCallbacks currentFragment; @@ -83,7 +88,6 @@ public class MainActivity extends AbsSlidingMusicPanelActivity implements } }; - @Override protected View createContentView() { @SuppressLint("InflateParams") @@ -100,20 +104,21 @@ public class MainActivity extends AbsSlidingMusicPanelActivity implements ButterKnife.bind(this); - setBottomBarVisibility(View.VISIBLE); - drawerLayout.setOnApplyWindowInsetsListener((view, windowInsets) -> windowInsets.replaceSystemWindowInsets(0, 0, 0, 0)); if (savedInstanceState == null) { - setCurrentFragment(PreferenceUtil.getInstance(this).getLastPage()); + setCurrentFragment(PreferenceUtil.getInstance(this).getLastMusicChooser()); } else { restoreCurrentFragment(); } - getBottomNavigationView().setOnNavigationItemSelectedListener(this); + //getBottomNavigationView().setOnNavigationItemSelectedListener(this); checkShowChangelog(); + + } + private void checkShowChangelog() { try { PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); @@ -182,19 +187,18 @@ public class MainActivity extends AbsSlidingMusicPanelActivity implements return true; } - private void setCurrentFragment(int menuItem) { + public void setCurrentFragment(int menuItem) { + PreferenceUtil.getInstance(this).setLastMusicChooser(menuItem); switch (menuItem) { - case R.id.action_song: - case R.id.action_album: - case R.id.action_artist: - case R.id.action_playlist: - setCurrentFragment(LibraryFragment.newInstance(menuItem), false, LibraryFragment.TAG); - break; default: - case R.id.action_home: - setCurrentFragment(PreferenceUtil.getInstance(this).toggleHomeBanner() ? - BannerHomeFragment.newInstance() : HomeFragment.newInstance(), false, - HomeFragment.TAG); + case LIBRARY: + setCurrentFragment(LibraryFragment.newInstance(), false, LibraryFragment.TAG); + break; + case FOLDERS: + setCurrentFragment(FoldersFragment.newInstance(this), false, FoldersFragment.TAG); + break; + case HOME: + setCurrentFragment(PreferenceUtil.getInstance(this).toggleHomeBanner() ? HomeFragment.newInstance() : BannerHomeFragment.newInstance(), false, HomeFragment.TAG); break; } } @@ -269,7 +273,6 @@ public class MainActivity extends AbsSlidingMusicPanelActivity implements return id; } - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java index 1805bcdda..9d2a74879 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/PlaylistDetailActivity.java @@ -94,7 +94,6 @@ public class PlaylistDetailActivity extends AbsSlidingMusicPanelActivity impleme setTaskDescriptionColorAuto(); setLightNavigationBar(true); - setBottomBarVisibility(View.GONE); playlist = getIntent().getExtras().getParcelable(EXTRA_PLAYLIST); diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java index 322c3bc5a..c855f8d96 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/ProVersionActivity.java @@ -10,6 +10,7 @@ import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.MenuItem; import android.view.View; +import android.widget.TextView; import android.widget.Toast; import com.anjlab.android.iab.v3.BillingProcessor; @@ -43,12 +44,16 @@ public class ProVersionActivity extends AbsBaseActivity implements @BindView(R.id.purchase_button) View purchaseButton; - @BindView(R.id.app_bar) + @BindView(R.id.app_bar) AppBarLayout appBar; + @BindView(R.id.status_bar) View statusBar; + @BindView(R.id.title) + TextView title; + private BillingProcessor billingProcessor; private AsyncTask restorePurchaseAsyncTask; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SearchActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SearchActivity.java index be1d41ec8..72df9acc6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SearchActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SearchActivity.java @@ -6,16 +6,11 @@ import android.content.Intent; import android.os.Bundle; import android.speech.RecognizerIntent; import android.support.annotation.NonNull; -import android.support.design.widget.AppBarLayout; -import android.support.design.widget.CoordinatorLayout; -import android.support.transition.TransitionManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SearchView.OnQueryTextListener; -import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.TextWatcher; -import android.view.MenuItem; import android.view.View; import android.widget.EditText; import android.widget.TextView; @@ -29,7 +24,6 @@ import java.util.Locale; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.mvp.contract.SearchContract; import code.name.monkey.retromusic.mvp.presenter.SearchPresenter; @@ -51,24 +45,12 @@ public class SearchActivity extends AbsMusicServiceActivity implements OnQueryTe @BindView(R.id.recycler_view) RecyclerView recyclerView; - @BindView(R.id.toolbar) - Toolbar toolbar; - @BindView(android.R.id.empty) TextView empty; - @BindView(R.id.title) - TextView title; - @BindView(R.id.search_view) EditText searchView; - @BindView(R.id.root) - CoordinatorLayout container; - - @BindView(R.id.app_bar) - AppBarLayout appbar; - @BindView(R.id.status_bar) View statusBar; @@ -155,26 +137,12 @@ public class SearchActivity extends AbsMusicServiceActivity implements OnQueryTe } private void setUpToolBar() { - title.setTextColor(ThemeStore.textColorPrimary(this)); - int primaryColor = ThemeStore.primaryColor(this); - toolbar.setBackgroundColor(primaryColor); - toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); - appbar.setBackgroundColor(primaryColor); - setSupportActionBar(toolbar); setTitle(null); } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - onBackPressed(); - } - return super.onOptionsItemSelected(item); - } private void search(@NonNull String query) { this.query = query.trim(); - TransitionManager.beginDelayedTransition(toolbar); micIcon.setVisibility(query.length() > 0 ? View.GONE : View.VISIBLE); searchPresenter.search(query); } @@ -242,15 +210,21 @@ public class SearchActivity extends AbsMusicServiceActivity implements OnQueryTe } } - @OnClick(R.id.voice_search) - void searchImageView() { - startMicSearch(); + @OnClick({R.id.voice_search, R.id.back}) + void searchImageView(View view) { + switch (view.getId()) { + case R.id.voice_search: + startMicSearch(); + break; + case R.id.back: + finish(); + break; + } } private void startMicSearch() { Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); - intent - .putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); + intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault()); intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.speech_prompt)); try { diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java index 7e7fa9d65..39ae966e4 100755 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/SettingsActivity.java @@ -32,10 +32,13 @@ public class SettingsActivity extends AbsBaseActivity implements ColorChooserDia @BindView(R.id.toolbar) Toolbar toolbar; + @BindView(R.id.app_bar) AppBarLayout appBarLayout; + @BindView(R.id.title) TextView title; + @BindView(R.id.detail_content_frame) @Nullable FrameLayout detailsFrame; @@ -89,12 +92,12 @@ public class SettingsActivity extends AbsBaseActivity implements ColorChooserDia private void setupToolbar() { title.setTextColor(ThemeStore.textColorPrimary(this)); int primaryColor = ThemeStore.primaryColor(this); - appBarLayout.setBackgroundColor(primaryColor); toolbar.setBackgroundColor(primaryColor); - toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); - toolbar.setNavigationOnClickListener(v -> onBackPressed()); + appBarLayout.setBackgroundColor(primaryColor); setTitle(null); setSupportActionBar(toolbar); + toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); + toolbar.setNavigationOnClickListener(v -> onBackPressed()); } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java index 49ef88cf6..f8af76168 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsBaseActivity.java @@ -15,6 +15,7 @@ import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.view.KeyEvent; import android.view.View; + import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.retromusic.R; import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; @@ -22,136 +23,136 @@ import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; public abstract class AbsBaseActivity extends AbsThemeActivity { - public static final int PERMISSION_REQUEST = 100; - private boolean hadPermissions; - private String[] permissions; - private String permissionDeniedMessage; + public static final int PERMISSION_REQUEST = 100; + private boolean hadPermissions; + private String[] permissions; + private String permissionDeniedMessage; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - setVolumeControlStream(AudioManager.STREAM_MUSIC); + setVolumeControlStream(AudioManager.STREAM_MUSIC); - permissions = getPermissionsToRequest(); - hadPermissions = hasPermissions(); + permissions = getPermissionsToRequest(); + hadPermissions = hasPermissions(); - setPermissionDeniedMessage(null); - } - - @Override - protected void onPostCreate(@Nullable Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - if (!hasPermissions()) { - requestPermissions(); - } - } - - @Override - protected void onResume() { - super.onResume(); - final boolean hasPermissions = hasPermissions(); - if (hasPermissions != hadPermissions) { - hadPermissions = hasPermissions; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - onHasPermissionsChanged(hasPermissions); - } + setPermissionDeniedMessage(null); } - } - - protected void onHasPermissionsChanged(boolean hasPermissions) { - // implemented by sub classes - } - - @Override - public boolean dispatchKeyEvent(@NonNull KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_MENU && event.getAction() == KeyEvent.ACTION_UP) { - showOverflowMenu(); - return true; - } - return super.dispatchKeyEvent(event); - } - - protected void showOverflowMenu() { - - } - - @Override - protected void attachBaseContext(Context newBase) { - super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)); - } - - @Nullable - protected String[] getPermissionsToRequest() { - return null; - } - - protected View getSnackBarContainer() { - return getWindow().getDecorView(); - } - - private String getPermissionDeniedMessage() { - return permissionDeniedMessage == null ? getString(R.string.permissions_denied) - : permissionDeniedMessage; - } - - protected void setPermissionDeniedMessage(String message) { - permissionDeniedMessage = message; - } - - protected void requestPermissions() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) { - requestPermissions(permissions, PERMISSION_REQUEST); - } - } - - protected boolean hasPermissions() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) { - for (String permission : permissions) { - if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { - return false; + @Override + protected void onPostCreate(@Nullable Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + if (!hasPermissions()) { + requestPermissions(); } - } } - return true; - } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == PERMISSION_REQUEST) { - for (int grantResult : grantResults) { - if (grantResult != PackageManager.PERMISSION_GRANTED) { - if (ActivityCompat.shouldShowRequestPermissionRationale(AbsBaseActivity.this, - Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - //User has deny from permission dialog - Snackbar.make(getSnackBarContainer(), getPermissionDeniedMessage(), - Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.action_grant, view -> requestPermissions()) - .setActionTextColor(ThemeStore.accentColor(this)) - .show(); - } else { - // User has deny permission and checked never show permission dialog so you can redirect to Application settings page - Snackbar.make(getSnackBarContainer(), getPermissionDeniedMessage(), - Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.action_settings, view -> { - Intent intent = new Intent(); - intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - Uri uri = Uri.fromParts("package", AbsBaseActivity.this.getPackageName(), null); - intent.setData(uri); - startActivity(intent); - }) - .setActionTextColor(ThemeStore.accentColor(this)) - .show(); - } - return; + @Override + protected void onResume() { + super.onResume(); + final boolean hasPermissions = hasPermissions(); + if (hasPermissions != hadPermissions) { + hadPermissions = hasPermissions; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + onHasPermissionsChanged(hasPermissions); + } + } + + } + + protected void onHasPermissionsChanged(boolean hasPermissions) { + // implemented by sub classes + } + + @Override + public boolean dispatchKeyEvent(@NonNull KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_MENU && event.getAction() == KeyEvent.ACTION_UP) { + showOverflowMenu(); + return true; + } + return super.dispatchKeyEvent(event); + } + + protected void showOverflowMenu() { + + } + + @Override + protected void attachBaseContext(Context newBase) { + super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)); + } + + @Nullable + protected String[] getPermissionsToRequest() { + return null; + } + + protected View getSnackBarContainer() { + return getWindow().getDecorView(); + } + + private String getPermissionDeniedMessage() { + return permissionDeniedMessage == null ? getString(R.string.permissions_denied) + : permissionDeniedMessage; + } + + protected void setPermissionDeniedMessage(String message) { + permissionDeniedMessage = message; + } + + protected void requestPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) { + requestPermissions(permissions, PERMISSION_REQUEST); + } + } + + protected boolean hasPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) { + for (String permission : permissions) { + if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { + return false; + } + } + } + return true; + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == PERMISSION_REQUEST) { + for (int grantResult : grantResults) { + if (grantResult != PackageManager.PERMISSION_GRANTED) { + if (ActivityCompat.shouldShowRequestPermissionRationale(AbsBaseActivity.this, + Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + //User has deny from permission dialog + Snackbar.make(getSnackBarContainer(), getPermissionDeniedMessage(), + Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.action_grant, view -> requestPermissions()) + .setActionTextColor(ThemeStore.accentColor(this)) + .show(); + } else { + // User has deny permission and checked never show permission dialog so you can redirect to Application settings page + Snackbar.make(getSnackBarContainer(), getPermissionDeniedMessage(), + Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.action_settings, view -> { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", AbsBaseActivity.this.getPackageName(), null); + intent.setData(uri); + startActivity(intent); + }) + .setActionTextColor(ThemeStore.accentColor(this)) + .show(); + } + return; + } + } + hadPermissions = true; + onHasPermissionsChanged(true); } - } - hadPermissions = true; - onHasPermissionsChanged(true); } - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.java new file mode 100644 index 000000000..db8248415 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsCastActivity.java @@ -0,0 +1,159 @@ +package code.name.monkey.retromusic.ui.activities.base; + +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; + +import com.google.android.gms.cast.framework.CastButtonFactory; +import com.google.android.gms.cast.framework.CastContext; +import com.google.android.gms.cast.framework.CastSession; +import com.google.android.gms.cast.framework.SessionManagerListener; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; + +import java.io.IOException; + +import code.name.monkey.retromusic.R; +import code.name.monkey.retromusic.cast.WebServer; + +public abstract class AbsCastActivity extends AbsBaseActivity { + private static final String TAG = "AbsCastActivity"; + + public boolean playServicesAvailable = false; + + private CastContext castContext; + private SessionManagerListener sessionManagerListener; + private CastSession castSession; + private WebServer castServer; + + private void setupCastListener() { + sessionManagerListener = new SessionManagerListener() { + + @Override + public void onSessionEnded(CastSession session, int error) { + onApplicationDisconnected(); + Log.i(TAG, "onSessionEnded: "); + } + + @Override + public void onSessionResumed(CastSession session, boolean wasSuspended) { + onApplicationConnected(session); + Log.i(TAG, "onSessionResumed: "); + } + + @Override + public void onSessionResumeFailed(CastSession session, int error) { + onApplicationDisconnected(); + Log.i(TAG, "onSessionResumeFailed: "); + } + + @Override + public void onSessionStarted(CastSession session, String sessionId) { + onApplicationConnected(session); + Log.i(TAG, "onSessionStarted: "); + } + + @Override + public void onSessionStartFailed(CastSession session, int error) { + onApplicationDisconnected(); + Log.i(TAG, "onSessionStartFailed: "); + } + + @Override + public void onSessionStarting(CastSession session) { + } + + @Override + public void onSessionEnding(CastSession session) { + } + + @Override + public void onSessionResuming(CastSession session, String sessionId) { + } + + @Override + public void onSessionSuspended(CastSession session, int reason) { + } + + private void onApplicationConnected(CastSession castSession) { + AbsCastActivity.this.castSession = castSession; + castServer = new WebServer(getApplicationContext()); + try { + castServer.start(); + } catch (IOException e) { + e.printStackTrace(); + } + supportInvalidateOptionsMenu(); + showCastMiniController(); + } + + private void onApplicationDisconnected() { + if (castServer != null) { + castServer.stop(); + } + supportInvalidateOptionsMenu(); + hideCastMiniController(); + } + }; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + try { + playServicesAvailable = GoogleApiAvailability + .getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS; + } catch (Exception ignored) { + + } + + if (playServicesAvailable) + initCast(); + + + } + + @Override + protected void onResume() { + if (playServicesAvailable) { + castContext.getSessionManager().addSessionManagerListener(sessionManagerListener, CastSession.class); + } + super.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + if (playServicesAvailable) { + castContext.getSessionManager().removeSessionManagerListener( + sessionManagerListener, CastSession.class); + } + } + + private void initCast() { + setupCastListener(); + castContext = CastContext.getSharedInstance(this); + castSession = castContext.getSessionManager().getCurrentCastSession(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_cast, menu); + if (playServicesAvailable) { + CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, R.id.media_route_menu_item); + } + return true; + } + + public void showCastMiniController() { + //implement by overriding in activities + } + + public void hideCastMiniController() { + //implement by overriding in activities + } + + public CastSession getCastSession() { + return castSession; + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.java index 4d0f28a6e..97b50cf4b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsMusicServiceActivity.java @@ -15,9 +15,9 @@ import android.support.annotation.Nullable; import java.lang.ref.WeakReference; import java.util.ArrayList; -import code.name.monkey.retromusic.interfaces.MusicServiceEventListener; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.helper.MusicPlayerRemote; +import code.name.monkey.retromusic.interfaces.MusicServiceEventListener; import static code.name.monkey.retromusic.Constants.MEDIA_STORE_CHANGED; import static code.name.monkey.retromusic.Constants.META_CHANGED; @@ -27,7 +27,7 @@ import static code.name.monkey.retromusic.Constants.REPEAT_MODE_CHANGED; import static code.name.monkey.retromusic.Constants.SHUFFLE_MODE_CHANGED; -public abstract class AbsMusicServiceActivity extends AbsBaseActivity implements MusicServiceEventListener { +public abstract class AbsMusicServiceActivity extends AbsCastActivity implements MusicServiceEventListener { public static final String TAG = AbsMusicServiceActivity.class.getSimpleName(); private final ArrayList mMusicServiceEventListeners = new ArrayList<>(); @@ -52,6 +52,7 @@ public abstract class AbsMusicServiceActivity extends AbsBaseActivity implements }); setPermissionDeniedMessage(getString(R.string.permission_external_storage_denied)); + } @Override @@ -183,6 +184,7 @@ public abstract class AbsMusicServiceActivity extends AbsBaseActivity implements return new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; } + private static final class MusicStateReceiver extends BroadcastReceiver { private final WeakReference reference; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.java index a57045c52..183c5b8ff 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsSlidingMusicPanelActivity.java @@ -2,6 +2,7 @@ package code.name.monkey.retromusic.ui.activities.base; import android.animation.ValueAnimator; import android.annotation.SuppressLint; +import android.content.Intent; import android.os.Bundle; import android.support.annotation.ColorInt; import android.support.annotation.FloatRange; @@ -12,6 +13,8 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.animation.PathInterpolator; +import com.google.android.gms.cast.framework.CastSession; +import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity; import com.sothree.slidinguppanel.SlidingUpPanelLayout; import com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState; @@ -38,7 +41,6 @@ import code.name.monkey.retromusic.ui.fragments.player.plain.PlainPlayerFragment import code.name.monkey.retromusic.ui.fragments.player.simple.SimplePlayerFragment; import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.ViewUtil; -import code.name.monkey.retromusic.views.BottomNavigationViewEx; public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivity implements SlidingUpPanelLayout.PanelSlideListener, @@ -46,8 +48,6 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi public static final String TAG = AbsSlidingMusicPanelActivity.class.getSimpleName(); - @BindView(R.id.bottom_navigation) - BottomNavigationViewEx bottomNavigationView; @BindView(R.id.sliding_layout) SlidingUpPanelLayout slidingUpPanelLayout; @@ -60,14 +60,33 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi private MiniPlayerFragment miniPlayerFragment; private ValueAnimator navigationBarColorAnimator; + @Override + public void onPlayingMetaChanged() { + super.onPlayingMetaChanged(); + CastSession castSession = getCastSession(); + if (castSession == null) { + return; + } + //MusicPlayerRemote.pauseSong(); + //CastHelper.startCasting(castSession, MusicPlayerRemote.getCurrentSong()); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(createContentView()); ButterKnife.bind(this); choosFragmentForTheme(); + + findViewById(R.id.castMiniController).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(new Intent(AbsSlidingMusicPanelActivity.this, ExpandedControllerActivity.class)); + } + }); //noinspection ConstantConditions miniPlayerFragment.getView().setOnClickListener(v -> expandPanel()); + slidingUpPanelLayout.getViewTreeObserver() .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override @@ -85,7 +104,7 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi } }); - setupBottomView(); + //setupBottomView(); slidingUpPanelLayout.addPanelSlideListener(this); } @@ -144,16 +163,16 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi .findFragmentById(R.id.mini_player_fragment); } - private void setupBottomView() { - bottomNavigationView.setSelectedItemId(PreferenceUtil.getInstance(this).getLastPage()); - bottomNavigationView.setBackgroundColor(ThemeStore.primaryColor(this)); - bottomNavigationView.enableAnimation(false); - bottomNavigationView.enableItemShiftingMode(false); - bottomNavigationView.enableShiftingMode(false); - bottomNavigationView.setTextSize(10f); - bottomNavigationView.setTextVisibility(PreferenceUtil.getInstance(this).tabTitles()); - } - + /* private void setupBottomView() { + bottomNavigationView.setSelectedItemId(PreferenceUtil.getInstance(this).getLastPage()); + bottomNavigationView.setBackgroundColor(ThemeStore.primaryColor(this)); + bottomNavigationView.enableAnimation(false); + bottomNavigationView.enableItemShiftingMode(false); + bottomNavigationView.enableShiftingMode(false); + bottomNavigationView.setTextSize(10f); + bottomNavigationView.setTextVisibility(PreferenceUtil.getInstance(this).tabTitles()); + } + */ @Override protected void onResume() { super.onResume(); @@ -196,7 +215,7 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi @Override public void onPanelSlide(View panel, @FloatRange(from = 0, to = 1) float slideOffset) { - bottomNavigationView.setTranslationY(slideOffset * 400); + //bottomNavigationView.setTranslationY(slideOffset * 400); setMiniPlayerAlphaProgress(slideOffset); //findViewById(R.id.player_fragment_container).setAlpha(slideOffset); } @@ -317,35 +336,17 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi } public void hideBottomBar(final boolean hide) { - - int heightOfBar = - getResources().getDimensionPixelSize(R.dimen.mini_player_height); - int heightOfBarWithTabs = - getResources().getDimensionPixelSize(R.dimen.mini_player_height_expanded); - if (hide) { slidingUpPanelLayout.setPanelHeight(0); collapsePanel(); } else { - if (!MusicPlayerRemote.getPlayingQueue().isEmpty()) { - slidingUpPanelLayout.setPanelHeight(bottomNavigationView.getVisibility() == View.VISIBLE ? - heightOfBarWithTabs : heightOfBar); - } - } - } - - public void setBottomBarVisibility(int gone) { - if (bottomNavigationView != null) { - //TransitionManager.beginDelayedTransition(bottomNavigationView); - bottomNavigationView.setVisibility(gone); - hideBottomBar(false); + slidingUpPanelLayout.setPanelHeight(getResources().getDimensionPixelSize(R.dimen.mini_player_height)); } } protected View wrapSlidingMusicPanel(@LayoutRes int resId) { @SuppressLint("InflateParams") - View slidingMusicPanelLayout = getLayoutInflater() - .inflate(R.layout.sliding_music_panel_layout, null); + View slidingMusicPanelLayout = getLayoutInflater().inflate(R.layout.sliding_music_panel_layout, null); ViewGroup contentContainer = slidingMusicPanelLayout.findViewById(R.id.content_container); getLayoutInflater().inflate(resId, contentContainer); return slidingMusicPanelLayout; @@ -379,7 +380,7 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi navigationBarColorAnimator.addUpdateListener(animation -> { int playerFragmentColorDark = ColorUtil.darkenColor((Integer) animation.getAnimatedValue()); - bottomNavigationView.setBackgroundColor(playerFragmentColorDark); + //bottomNavigationView.setBackgroundColor(playerFragmentColorDark); miniPlayerFragment.setColor(playerFragmentColorDark); AbsSlidingMusicPanelActivity.super.setNavigationbarColor(playerFragmentColorDark); @@ -417,9 +418,6 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi return playerFragment; } - public BottomNavigationViewEx getBottomNavigationView() { - return bottomNavigationView; - } public SlidingUpPanelLayout.PanelState getPanelState() { return slidingUpPanelLayout == null ? null : slidingUpPanelLayout.getPanelState(); @@ -432,4 +430,18 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi public void expandPanel() { slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED); } + + @Override + public void hideCastMiniController() { + super.hideCastMiniController(); + hideBottomBar(false); + findViewById(R.id.castMiniController).setVisibility(View.GONE); + } + + @Override + public void showCastMiniController() { + super.showCastMiniController(); + hideBottomBar(true); + findViewById(R.id.castMiniController).setVisibility(View.VISIBLE); + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.java index 936317f39..c336e31d3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/base/AbsThemeActivity.java @@ -10,9 +10,10 @@ import android.support.v4.content.ContextCompat; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; + import code.name.monkey.appthemehelper.ATH; +import code.name.monkey.appthemehelper.ATHActivity; import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.common.ATHToolbarActivity; import code.name.monkey.appthemehelper.util.ATHUtil; import code.name.monkey.appthemehelper.util.ColorUtil; import code.name.monkey.appthemehelper.util.MaterialDialogsUtil; @@ -22,189 +23,189 @@ import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.RetroUtil; -public abstract class AbsThemeActivity extends ATHToolbarActivity implements Runnable { +public abstract class AbsThemeActivity extends ATHActivity implements Runnable { - private Handler handler = new Handler(); + private Handler handler = new Handler(); - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(PreferenceUtil.getInstance(this).getGeneralTheme()); - hideStatusBar(); - super.onCreate(savedInstanceState); - MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this); + @Override + protected void onCreate(Bundle savedInstanceState) { + setTheme(PreferenceUtil.getInstance(this).getGeneralTheme()); + hideStatusBar(); + super.onCreate(savedInstanceState); + MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this); - changeBackgroundShape(); - setImmersiveFullscreen(); - registerSystemUiVisibility(); - toggleScreenOn(); - } - - private void toggleScreenOn() { - if (PreferenceUtil.getInstance(this).isScreenOnEnabled()) { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } else { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus) { - hideStatusBar(); - handler.removeCallbacks(this); - handler.postDelayed(this, 300); - } else { - handler.removeCallbacks(this); - } - } - - public void hideStatusBar() { - hideStatusBar(PreferenceUtil.getInstance(this).getFullScreenMode()); - } - - private void hideStatusBar(boolean fullscreen) { - final View statusBar = getWindow().getDecorView().getRootView().findViewById(R.id.status_bar); - if (statusBar != null) { - statusBar.setVisibility(fullscreen ? View.GONE : View.VISIBLE); - } - } - - - private void changeBackgroundShape() { - Drawable background = PreferenceUtil.getInstance(this).isRoundCorners() ? - ContextCompat.getDrawable(this, R.drawable.round_window) - : ContextCompat.getDrawable(this, R.drawable.square_window); - background = TintHelper.createTintedDrawable(background, ThemeStore.primaryColor(this)); - getWindow().setBackgroundDrawable(background); - } - - protected void setDrawUnderStatusBar(boolean drawUnderStatusbar) { - if (VersionUtils.hasLollipop()) { - RetroUtil.setAllowDrawUnderStatusBar(getWindow()); - } else if (VersionUtils.hasKitKat()) { - RetroUtil.setStatusBarTranslucent(getWindow()); - } - } - - /** - * This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On - * Lollipop if no such view is found it will set the statusbar color using the native method. - * - * @param color the new statusbar color (will be shifted down on Lollipop and above) - */ - public void setStatusbarColor(int color) { - if (VersionUtils.hasKitKat()) { - final View statusBar = getWindow().getDecorView().getRootView().findViewById(R.id.status_bar); - if (statusBar != null) { - if (VersionUtils.hasLollipop()) { - statusBar.setBackgroundColor(ColorUtil.darkenColor(color)); - setLightStatusbarAuto(color); - } else { - statusBar.setBackgroundColor(color); - } - } else if (Build.VERSION.SDK_INT >= 21) { - getWindow().setStatusBarColor(ColorUtil.darkenColor(color)); - setLightStatusbarAuto(color); - } - } - } - - public void setStatusbarColorAuto() { - // we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat - setStatusbarColor(ThemeStore.primaryColor(this)); - } - - public void setTaskDescriptionColor(@ColorInt int color) { - ATH.setTaskDescriptionColor(this, color); - } - - public void setTaskDescriptionColorAuto() { - setTaskDescriptionColor(ThemeStore.primaryColor(this)); - } - - public void setNavigationbarColor(int color) { - if (ThemeStore.coloredNavigationBar(this)) { - ATH.setNavigationbarColor(this, color); - } else { - ATH.setNavigationbarColor(this, Color.BLACK); - } - } - - public void setNavigationbarColorAuto() { - setNavigationbarColor(ThemeStore.navigationBarColor(this)); - } - - public void setLightStatusbar(boolean enabled) { - ATH.setLightStatusbar(this, enabled); - } - - public void setLightStatusbarAuto(int bgColor) { - setLightStatusbar(ColorUtil.isColorLight(bgColor)); - } - - public void setLightNavigationBar(boolean enabled) { - if (!ATHUtil.isWindowBackgroundDark(this) && ThemeStore.coloredNavigationBar(this)) { - ATH.setLightNavigationbar(this, enabled); - } - } - - private void registerSystemUiVisibility() { - final View decorView = getWindow().getDecorView(); - decorView.setOnSystemUiVisibilityChangeListener(visibility -> { - if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { + changeBackgroundShape(); setImmersiveFullscreen(); - } - }); - } - - private void unregisterSystemUiVisibility() { - final View decorView = getWindow().getDecorView(); - decorView.setOnSystemUiVisibilityChangeListener(null); - } - - public void setImmersiveFullscreen() { - int flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | - View.SYSTEM_UI_FLAG_FULLSCREEN | - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - if (PreferenceUtil.getInstance(this).getFullScreenMode()) { - getWindow().getDecorView().setSystemUiVisibility(flags); + registerSystemUiVisibility(); + toggleScreenOn(); } - } - public void exitFullscreen() { - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } - - @Override - public void run() { - setImmersiveFullscreen(); - } - - @Override - protected void onStop() { - handler.removeCallbacks(this); - super.onStop(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - unregisterSystemUiVisibility(); - exitFullscreen(); - } - - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { - handler.removeCallbacks(this); - handler.postDelayed(this, 500); + private void toggleScreenOn() { + if (PreferenceUtil.getInstance(this).isScreenOnEnabled()) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } else { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } } - return super.onKeyDown(keyCode, event); - } + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) { + hideStatusBar(); + handler.removeCallbacks(this); + handler.postDelayed(this, 300); + } else { + handler.removeCallbacks(this); + } + } + + public void hideStatusBar() { + hideStatusBar(PreferenceUtil.getInstance(this).getFullScreenMode()); + } + + private void hideStatusBar(boolean fullscreen) { + final View statusBar = getWindow().getDecorView().getRootView().findViewById(R.id.status_bar); + if (statusBar != null) { + statusBar.setVisibility(fullscreen ? View.GONE : View.VISIBLE); + } + } + + + private void changeBackgroundShape() { + Drawable background = PreferenceUtil.getInstance(this).isRoundCorners() ? + ContextCompat.getDrawable(this, R.drawable.round_window) + : ContextCompat.getDrawable(this, R.drawable.square_window); + background = TintHelper.createTintedDrawable(background, ThemeStore.primaryColor(this)); + getWindow().setBackgroundDrawable(background); + } + + protected void setDrawUnderStatusBar(boolean drawUnderStatusbar) { + if (VersionUtils.hasLollipop()) { + RetroUtil.setAllowDrawUnderStatusBar(getWindow()); + } else if (VersionUtils.hasKitKat()) { + RetroUtil.setStatusBarTranslucent(getWindow()); + } + } + + /** + * This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On + * Lollipop if no such view is found it will set the statusbar color using the native method. + * + * @param color the new statusbar color (will be shifted down on Lollipop and above) + */ + public void setStatusbarColor(int color) { + if (VersionUtils.hasKitKat()) { + final View statusBar = getWindow().getDecorView().getRootView().findViewById(R.id.status_bar); + if (statusBar != null) { + if (VersionUtils.hasLollipop()) { + statusBar.setBackgroundColor(ColorUtil.darkenColor(color)); + setLightStatusbarAuto(color); + } else { + statusBar.setBackgroundColor(color); + } + } else if (Build.VERSION.SDK_INT >= 21) { + getWindow().setStatusBarColor(ColorUtil.darkenColor(color)); + setLightStatusbarAuto(color); + } + } + } + + public void setStatusbarColorAuto() { + // we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat + setStatusbarColor(ThemeStore.primaryColor(this)); + } + + public void setTaskDescriptionColor(@ColorInt int color) { + ATH.setTaskDescriptionColor(this, color); + } + + public void setTaskDescriptionColorAuto() { + setTaskDescriptionColor(ThemeStore.primaryColor(this)); + } + + public void setNavigationbarColor(int color) { + if (ThemeStore.coloredNavigationBar(this)) { + ATH.setNavigationbarColor(this, color); + } else { + ATH.setNavigationbarColor(this, Color.BLACK); + } + } + + public void setNavigationbarColorAuto() { + setNavigationbarColor(ThemeStore.navigationBarColor(this)); + } + + public void setLightStatusbar(boolean enabled) { + ATH.setLightStatusbar(this, enabled); + } + + public void setLightStatusbarAuto(int bgColor) { + setLightStatusbar(ColorUtil.isColorLight(bgColor)); + } + + public void setLightNavigationBar(boolean enabled) { + if (!ATHUtil.isWindowBackgroundDark(this) && ThemeStore.coloredNavigationBar(this)) { + ATH.setLightNavigationbar(this, enabled); + } + } + + private void registerSystemUiVisibility() { + final View decorView = getWindow().getDecorView(); + decorView.setOnSystemUiVisibilityChangeListener(visibility -> { + if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { + setImmersiveFullscreen(); + } + }); + } + + private void unregisterSystemUiVisibility() { + final View decorView = getWindow().getDecorView(); + decorView.setOnSystemUiVisibilityChangeListener(null); + } + + public void setImmersiveFullscreen() { + int flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + if (PreferenceUtil.getInstance(this).getFullScreenMode()) { + getWindow().getDecorView().setSystemUiVisibility(flags); + } + } + + public void exitFullscreen() { + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + + @Override + public void run() { + setImmersiveFullscreen(); + } + + @Override + protected void onStop() { + handler.removeCallbacks(this); + super.onStop(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + unregisterSystemUiVisibility(); + exitFullscreen(); + } + + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { + handler.removeCallbacks(this); + handler.postDelayed(this, 500); + } + return super.onKeyDown(keyCode, event); + + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/AlbumTagEditorActivity.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/AlbumTagEditorActivity.java index a83a17f47..c74f4f013 100755 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/AlbumTagEditorActivity.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/AlbumTagEditorActivity.java @@ -32,7 +32,6 @@ import java.util.Map; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; -import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.util.ATHUtil; import code.name.monkey.appthemehelper.util.ColorUtil; import code.name.monkey.retromusic.R; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/WriteTagsAsyncTask.java b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/WriteTagsAsyncTask.java index 32c546889..0c53ae7b1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/WriteTagsAsyncTask.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/activities/tageditor/WriteTagsAsyncTask.java @@ -7,16 +7,9 @@ import android.graphics.Bitmap; import android.media.MediaScannerConnection; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.misc.DialogAsyncTask; -import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener; -import code.name.monkey.retromusic.util.MusicUtil; + import com.afollestad.materialdialogs.MaterialDialog; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Collection; -import java.util.Map; + import org.jaudiotagger.audio.AudioFile; import org.jaudiotagger.audio.AudioFileIO; import org.jaudiotagger.audio.exceptions.CannotReadException; @@ -29,135 +22,146 @@ import org.jaudiotagger.tag.TagException; import org.jaudiotagger.tag.images.Artwork; import org.jaudiotagger.tag.images.ArtworkFactory; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +import code.name.monkey.retromusic.R; +import code.name.monkey.retromusic.misc.DialogAsyncTask; +import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener; +import code.name.monkey.retromusic.util.MusicUtil; + public class WriteTagsAsyncTask extends - DialogAsyncTask { + DialogAsyncTask { - private Context applicationContext; + private Context applicationContext; - public WriteTagsAsyncTask(Context context) { - super(context); - applicationContext = context; - } - - @Override - protected String[] doInBackground(LoadingInfo... params) { - try { - LoadingInfo info = params[0]; - - Artwork artwork = null; - File albumArtFile = null; - if (info.artworkInfo != null && info.artworkInfo.artwork != null) { - try { - albumArtFile = MusicUtil.createAlbumArtFile().getCanonicalFile(); - info.artworkInfo.artwork - .compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile)); - artwork = ArtworkFactory.createArtworkFromFile(albumArtFile); - } catch (IOException e) { - e.printStackTrace(); - } - } - - int counter = 0; - boolean wroteArtwork = false; - boolean deletedArtwork = false; - for (String filePath : info.filePaths) { - publishProgress(++counter, info.filePaths.size()); - try { - AudioFile audioFile = AudioFileIO.read(new File(filePath)); - Tag tag = audioFile.getTagOrCreateAndSetDefault(); - - if (info.fieldKeyValueMap != null) { - for (Map.Entry entry : info.fieldKeyValueMap.entrySet()) { - try { - tag.setField(entry.getKey(), entry.getValue()); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - if (info.artworkInfo != null) { - if (info.artworkInfo.artwork == null) { - tag.deleteArtworkField(); - deletedArtwork = true; - } else if (artwork != null) { - tag.deleteArtworkField(); - tag.setField(artwork); - wroteArtwork = true; - } - } - - audioFile.commit(); - } catch (@NonNull CannotReadException | IOException | CannotWriteException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { - e.printStackTrace(); - } - } - - Context context = getContext(); - if (context != null) { - if (wroteArtwork) { - MusicUtil.insertAlbumArt(context, info.artworkInfo.albumId, albumArtFile.getPath()); - } else if (deletedArtwork) { - MusicUtil.deleteAlbumArt(context, info.artworkInfo.albumId); - } - } - - return info.filePaths.toArray(new String[info.filePaths.size()]); - } catch (Exception e) { - e.printStackTrace(); - return null; + public WriteTagsAsyncTask(Context context) { + super(context); + applicationContext = context; } - } - @Override - protected void onPostExecute(String[] toBeScanned) { - super.onPostExecute(toBeScanned); - scan(toBeScanned); - } + @Override + protected String[] doInBackground(LoadingInfo... params) { + try { + LoadingInfo info = params[0]; - @Override - protected void onCancelled(String[] toBeScanned) { - super.onCancelled(toBeScanned); - scan(toBeScanned); - } + Artwork artwork = null; + File albumArtFile = null; + if (info.artworkInfo != null && info.artworkInfo.artwork != null) { + try { + albumArtFile = MusicUtil.createAlbumArtFile().getCanonicalFile(); + info.artworkInfo.artwork + .compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile)); + artwork = ArtworkFactory.createArtworkFromFile(albumArtFile); + } catch (IOException e) { + e.printStackTrace(); + } + } - private void scan(String[] toBeScanned) { - Context context = getContext(); - MediaScannerConnection.scanFile(applicationContext, toBeScanned, null, - context instanceof Activity ? new UpdateToastMediaScannerCompletionListener( - (Activity) context, toBeScanned) : null); - } + int counter = 0; + boolean wroteArtwork = false; + boolean deletedArtwork = false; + for (String filePath : info.filePaths) { + publishProgress(++counter, info.filePaths.size()); + try { + AudioFile audioFile = AudioFileIO.read(new File(filePath)); + Tag tag = audioFile.getTagOrCreateAndSetDefault(); - @Override - protected Dialog createDialog(@NonNull Context context) { - return new MaterialDialog.Builder(context) - .title(R.string.saving_changes) - .cancelable(false) - .progress(false, 0) - .build(); - } + if (info.fieldKeyValueMap != null) { + for (Map.Entry entry : info.fieldKeyValueMap.entrySet()) { + try { + tag.setField(entry.getKey(), entry.getValue()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } - @Override - protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) { - super.onProgressUpdate(dialog, values); - ((MaterialDialog) dialog).setMaxProgress(values[1]); - ((MaterialDialog) dialog).setProgress(values[0]); - } + if (info.artworkInfo != null) { + if (info.artworkInfo.artwork == null) { + tag.deleteArtworkField(); + deletedArtwork = true; + } else if (artwork != null) { + tag.deleteArtworkField(); + tag.setField(artwork); + wroteArtwork = true; + } + } - public static class LoadingInfo { + audioFile.commit(); + } catch (@NonNull CannotReadException | IOException | CannotWriteException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { + e.printStackTrace(); + } + } - final Collection filePaths; - @Nullable - final Map fieldKeyValueMap; - @Nullable - private AbsTagEditorActivity.ArtworkInfo artworkInfo; + Context context = getContext(); + if (context != null) { + if (wroteArtwork) { + MusicUtil.insertAlbumArt(context, info.artworkInfo.albumId, albumArtFile.getPath()); + } else if (deletedArtwork) { + MusicUtil.deleteAlbumArt(context, info.artworkInfo.albumId); + } + } - public LoadingInfo(Collection filePaths, - @Nullable Map fieldKeyValueMap, - @Nullable AbsTagEditorActivity.ArtworkInfo artworkInfo) { - this.filePaths = filePaths; - this.fieldKeyValueMap = fieldKeyValueMap; - this.artworkInfo = artworkInfo; + return info.filePaths.toArray(new String[info.filePaths.size()]); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + protected void onPostExecute(String[] toBeScanned) { + super.onPostExecute(toBeScanned); + scan(toBeScanned); + } + + @Override + protected void onCancelled(String[] toBeScanned) { + super.onCancelled(toBeScanned); + scan(toBeScanned); + } + + private void scan(String[] toBeScanned) { + Context context = getContext(); + MediaScannerConnection.scanFile(applicationContext, toBeScanned, null, + context instanceof Activity ? new UpdateToastMediaScannerCompletionListener( + (Activity) context, toBeScanned) : null); + } + + @Override + protected Dialog createDialog(@NonNull Context context) { + return new MaterialDialog.Builder(context) + .title(R.string.saving_changes) + .cancelable(false) + .progress(false, 0) + .build(); + } + + @Override + protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) { + super.onProgressUpdate(dialog, values); + ((MaterialDialog) dialog).setMaxProgress(values[1]); + ((MaterialDialog) dialog).setProgress(values[0]); + } + + public static class LoadingInfo { + + final Collection filePaths; + @Nullable + final Map fieldKeyValueMap; + @Nullable + private AbsTagEditorActivity.ArtworkInfo artworkInfo; + + public LoadingInfo(Collection filePaths, + @Nullable Map fieldKeyValueMap, + @Nullable AbsTagEditorActivity.ArtworkInfo artworkInfo) { + this.filePaths = filePaths; + this.fieldKeyValueMap = fieldKeyValueMap; + this.artworkInfo = artworkInfo; + } } - } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.java index 75706a95f..6d4317a73 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/SearchAdapter.java @@ -9,19 +9,19 @@ import android.view.View; import android.view.ViewGroup; import com.bumptech.glide.Glide; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.retromusic.model.Album; -import code.name.monkey.retromusic.model.Artist; -import code.name.monkey.retromusic.model.Song; import java.util.ArrayList; import java.util.List; +import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.glide.ArtistGlideRequest; import code.name.monkey.retromusic.glide.SongGlideRequest; import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.helper.menu.SongMenuHelper; +import code.name.monkey.retromusic.model.Album; +import code.name.monkey.retromusic.model.Artist; +import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; import code.name.monkey.retromusic.util.MusicUtil; import code.name.monkey.retromusic.util.NavigationUtil; @@ -57,7 +57,7 @@ public class SearchAdapter extends RecyclerView.Adapter dataSet; private final int itemLayoutRes; @Nullable private final Callbacks callbacks; + private List dataSet; public SongFileAdapter(@NonNull AppCompatActivity activity, @NonNull List songFiles, @LayoutRes int itemLayoutRes, @Nullable Callbacks callback, @Nullable CabHolder cabHolder) { super(activity, cabHolder, R.menu.menu_media_selection); @@ -49,6 +49,13 @@ public class SongFileAdapter extends AbsMultiSelectAdapter files); + } + public class ViewHolder extends MediaEntryViewHolder { public ViewHolder(View itemView) { @@ -203,12 +212,4 @@ public class SongFileAdapter extends AbsMultiSelectAdapter= 0 && position < dataSet.size(); } } - - public interface Callbacks { - void onFileSelected(File file); - - void onFileMenuClicked(File file, View view); - - void onMultipleItemAction(MenuItem item, ArrayList files); - } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.java index e0c08a7d9..c161a9f00 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/album/AlbumAdapter.java @@ -1,6 +1,5 @@ package code.name.monkey.retromusic.ui.adapter.album; -import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.java index 957280880..718bff8f7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/playlist/AddToPlaylist.java @@ -7,64 +7,66 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + +import java.util.ArrayList; + import code.name.monkey.retromusic.model.Playlist; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder; import code.name.monkey.retromusic.ui.adapter.playlist.AddToPlaylist.ViewHolder; import code.name.monkey.retromusic.util.PlaylistsUtil; -import java.util.ArrayList; /** * @author Hemanth S (h4h13). */ public class AddToPlaylist extends RecyclerView.Adapter { - private Activity activity; - private ArrayList playlists; - private int itemLayoutRes; - private ArrayList songs; - private Dialog dialog; + private Activity activity; + private ArrayList playlists; + private int itemLayoutRes; + private ArrayList songs; + private Dialog dialog; - public AddToPlaylist(Activity activity, - ArrayList playlists, int itemLayoutRes, - ArrayList songs, Dialog dialog) { - this.activity = activity; - this.playlists = playlists; - this.itemLayoutRes = itemLayoutRes; - this.songs = songs; - this.dialog = dialog; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - Playlist playlist = playlists.get(position); - if (holder.title != null) { - holder.title.setText(playlist.name); + public AddToPlaylist(Activity activity, + ArrayList playlists, int itemLayoutRes, + ArrayList songs, Dialog dialog) { + this.activity = activity; + this.playlists = playlists; + this.itemLayoutRes = itemLayoutRes; + this.songs = songs; + this.dialog = dialog; } - } - @Override - public int getItemCount() { - return playlists.size(); - } - - public class ViewHolder extends MediaEntryViewHolder { - - public ViewHolder(View itemView) { - super(itemView); + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)); } @Override - public void onClick(View v) { - super.onClick(v); - PlaylistsUtil.addToPlaylist(activity, songs, playlists.get(getAdapterPosition()).id, true); - dialog.dismiss(); + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Playlist playlist = playlists.get(position); + if (holder.title != null) { + holder.title.setText(playlist.name); + } + } + + @Override + public int getItemCount() { + return playlists.size(); + } + + public class ViewHolder extends MediaEntryViewHolder { + + public ViewHolder(View itemView) { + super(itemView); + } + + @Override + public void onClick(View v) { + super.onClick(v); + PlaylistsUtil.addToPlaylist(activity, songs, playlists.get(getAdapterPosition()).id, true); + dialog.dismiss(); + } } - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.java index cadfc52f1..fc77ffebc 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/OrderablePlaylistSongAdapter.java @@ -11,8 +11,6 @@ import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter; import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder; import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange; import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags; -import code.name.monkey.retromusic.model.PlaylistSong; -import code.name.monkey.retromusic.model.Song; import java.util.ArrayList; import java.util.List; @@ -20,6 +18,8 @@ import java.util.List; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.dialogs.RemoveFromPlaylistDialog; import code.name.monkey.retromusic.interfaces.CabHolder; +import code.name.monkey.retromusic.model.PlaylistSong; +import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.util.ViewUtil; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.java index b2fd96c4b..ec53d5056 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/ShuffleButtonSongAdapter.java @@ -5,74 +5,76 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.View; + +import java.util.ArrayList; + import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.interfaces.CabHolder; import code.name.monkey.retromusic.model.Song; -import java.util.ArrayList; public class ShuffleButtonSongAdapter extends AbsOffsetSongAdapter { - public ShuffleButtonSongAdapter(AppCompatActivity activity, ArrayList dataSet, - @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { - super(activity, dataSet, itemLayoutRes, usePalette, cabHolder); - } - - @Override - protected SongAdapter.ViewHolder createViewHolder(View view) { - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull final SongAdapter.ViewHolder holder, int position) { - if (holder.getItemViewType() == OFFSET_ITEM) { - int accentColor = ThemeStore.accentColor(activity); - if (holder.title != null) { - holder.title.setText(activity.getResources().getString(R.string.action_shuffle_all)); - holder.title.setTextColor(accentColor); - /*((GradientTextView) holder.title).setLinearGradient(ThemeStore.accentColor(activity), - PhonographColorUtil.getMatColor(activity, "A400"), GradientTextView.LG_VERTICAL); - */ - } - if (holder.text != null) { - holder.text.setVisibility(View.GONE); - } - if (holder.menu != null) { - holder.menu.setVisibility(View.GONE); - } - if (holder.image != null) { - final int padding = - activity.getResources().getDimensionPixelSize(R.dimen.default_item_margin) / 2; - holder.image.setPadding(padding, padding, padding, padding); - holder.image.setColorFilter(accentColor); - holder.image.setImageResource(R.drawable.ic_shuffle_white_24dp); - } - if (holder.separator != null) { - holder.separator.setVisibility(View.VISIBLE); - } - if (holder.shortSeparator != null) { - holder.shortSeparator.setVisibility(View.GONE); - } - } else { - super.onBindViewHolder(holder, position - 1); - } - } - - public class ViewHolder extends AbsOffsetSongAdapter.ViewHolder { - - public ViewHolder(@NonNull View itemView) { - super(itemView); + public ShuffleButtonSongAdapter(AppCompatActivity activity, ArrayList dataSet, + @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { + super(activity, dataSet, itemLayoutRes, usePalette, cabHolder); } @Override - public void onClick(View v) { - if (getItemViewType() == OFFSET_ITEM) { - MusicPlayerRemote.openAndShuffleQueue(dataSet, true); - return; - } - super.onClick(v); + protected SongAdapter.ViewHolder createViewHolder(View view) { + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull final SongAdapter.ViewHolder holder, int position) { + if (holder.getItemViewType() == OFFSET_ITEM) { + int accentColor = ThemeStore.accentColor(activity); + if (holder.title != null) { + holder.title.setText(activity.getResources().getString(R.string.action_shuffle_all)); + holder.title.setTextColor(accentColor); + /*((GradientTextView) holder.title).setLinearGradient(ThemeStore.accentColor(activity), + PhonographColorUtil.getMatColor(activity, "A400"), GradientTextView.LG_VERTICAL); + */ + } + if (holder.text != null) { + holder.text.setVisibility(View.GONE); + } + if (holder.menu != null) { + holder.menu.setVisibility(View.GONE); + } + if (holder.image != null) { + final int padding = + activity.getResources().getDimensionPixelSize(R.dimen.default_item_margin) / 2; + holder.image.setPadding(padding, padding, padding, padding); + holder.image.setColorFilter(accentColor); + holder.image.setImageResource(R.drawable.ic_shuffle_white_24dp); + } + if (holder.separator != null) { + holder.separator.setVisibility(View.VISIBLE); + } + if (holder.shortSeparator != null) { + holder.shortSeparator.setVisibility(View.GONE); + } + } else { + super.onBindViewHolder(holder, position - 1); + } + } + + public class ViewHolder extends AbsOffsetSongAdapter.ViewHolder { + + public ViewHolder(@NonNull View itemView) { + super(itemView); + } + + @Override + public void onClick(View v) { + if (getItemViewType() == OFFSET_ITEM) { + MusicPlayerRemote.openAndShuffleQueue(dataSet, true); + return; + } + super.onClick(v); + } } - } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.java b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.java index 99fadd579..374174de9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/adapter/song/SongAdapter.java @@ -90,7 +90,7 @@ public class SongAdapter extends AbsMultiSelectAdapter Math.abs(velocityY)) { - if (velocityX < 0) { - MusicPlayerRemote.playNextSong(); - return true; - } else if (velocityX > 0) { - MusicPlayerRemote.playPreviousSong(); - return true; - } - } - return false; - } - }); - } - - @SuppressLint("ClickableViewAccessibility") @Override - public boolean onTouch(View v, MotionEvent event) { - return flingPlayBackController.onTouchEvent(event); + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View layout = inflater.inflate(R.layout.fragment_mini_player, container, false); + unbinder = ButterKnife.bind(this, layout); + return layout; + } + + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + //noinspection ConstantConditions + view.setBackgroundColor(ThemeStore.primaryColor(getContext())); + view.setOnTouchListener(new FlingPlayBackController(getActivity())); + setUpMiniPlayer(); + + next.setVisibility(PreferenceUtil.getInstance(getContext()).isExtraMiniExtraControls() ? View.VISIBLE : View.GONE); + previous.setVisibility(PreferenceUtil.getInstance(getContext()).isExtraMiniExtraControls() ? View.VISIBLE : View.GONE); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + + @SuppressWarnings({"ConstantConditions"}) + private void setUpMiniPlayer() { + setUpPlayPauseButton(); + progressBar.setProgressTintList(ColorStateList.valueOf(ThemeStore.accentColor(getActivity()))); + } + + private void setUpPlayPauseButton() { + //noinspection ConstantConditions + miniPlayerPlayPauseDrawable = new PlayPauseDrawable(getActivity()); + miniPlayerPlayPauseButton.setImageDrawable(miniPlayerPlayPauseDrawable); + miniPlayerPlayPauseButton.setColorFilter(ATHUtil.resolveColor(getActivity(), + R.attr.iconColor, + ThemeStore.textColorSecondary(getActivity())), PorterDuff.Mode.SRC_IN); + miniPlayerPlayPauseButton.setOnClickListener(new PlayPauseButtonOnClickHandler()); + } + + private void updateSongTitle() { + miniPlayerTitle.setText(MusicPlayerRemote.getCurrentSong().title); + } + + @Override + public void onServiceConnected() { + updateSongTitle(); + updatePlayPauseDrawableState(false); + } + + @Override + public void onPlayingMetaChanged() { + updateSongTitle(); + } + + @Override + public void onPlayStateChanged() { + updatePlayPauseDrawableState(true); + } + + @Override + public void onUpdateProgressViews(int progress, int total) { + progressBar.setMax(total); + ObjectAnimator animator = ObjectAnimator.ofInt(progressBar, "progress", progress); + animator.setDuration(1000); + animator.setInterpolator(new DecelerateInterpolator()); + animator.start(); + } + + @Override + public void onResume() { + super.onResume(); + progressViewUpdateHelper.start(); + } + + @Override + public void onPause() { + super.onPause(); + progressViewUpdateHelper.stop(); + } + + protected void updatePlayPauseDrawableState(boolean animate) { + if (MusicPlayerRemote.isPlaying()) { + miniPlayerPlayPauseDrawable.setPause(animate); + } else { + miniPlayerPlayPauseDrawable.setPlay(animate); + } + } + + public void setColor(int playerFragmentColor) { + //noinspection ConstantConditions + getView().setBackgroundColor(playerFragmentColor); + } + + public static class FlingPlayBackController implements View.OnTouchListener { + + GestureDetector flingPlayBackController; + + public FlingPlayBackController(Context context) { + flingPlayBackController = new GestureDetector(context, + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + if (Math.abs(velocityX) > Math.abs(velocityY)) { + if (velocityX < 0) { + MusicPlayerRemote.playNextSong(); + return true; + } else if (velocityX > 0) { + MusicPlayerRemote.playPreviousSong(); + return true; + } + } + return false; + } + }); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent event) { + return flingPlayBackController.onTouchEvent(event); + } } - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java index 233437f21..d32d9d0f1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/NowPlayingScreen.java @@ -2,32 +2,33 @@ package code.name.monkey.retromusic.ui.fragments; import android.support.annotation.DrawableRes; import android.support.annotation.StringRes; + import code.name.monkey.retromusic.R; public enum NowPlayingScreen { - NORMAL(R.string.normal, R.drawable.np_normal, 0), - FLAT(R.string.flat, R.drawable.np_flat, 1), - FULL(R.string.full, R.drawable.np_full, 2), - PLAIN(R.string.plain, R.drawable.np_plain, 3), - BLUR(R.string.blur, R.drawable.np_blur, 4), - COLOR(R.string.color, R.drawable.np_color, 5), - CARD(R.string.card, R.drawable.np_card, 6), - TINY(R.string.tiny, R.drawable.np_tiny, 7), - SIMPLE(R.string.simple, R.drawable.np_simple, 8), - BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9), - ADAPTIVE(R.string.adaptive, R.drawable.np_adaptive, 10), - MATERIAL(R.string.material, R.drawable.np_material, 11); + NORMAL(R.string.normal, R.drawable.np_normal, 0), + FLAT(R.string.flat, R.drawable.np_flat, 1), + FULL(R.string.full, R.drawable.np_full, 2), + PLAIN(R.string.plain, R.drawable.np_plain, 3), + BLUR(R.string.blur, R.drawable.np_blur, 4), + COLOR(R.string.color, R.drawable.np_color, 5), + CARD(R.string.card, R.drawable.np_card, 6), + TINY(R.string.tiny, R.drawable.np_tiny, 7), + SIMPLE(R.string.simple, R.drawable.np_simple, 8), + BLUR_CARD(R.string.blur_card, R.drawable.np_blur_card, 9), + ADAPTIVE(R.string.adaptive, R.drawable.np_adaptive, 10), + MATERIAL(R.string.material, R.drawable.np_material, 11); - @StringRes - public final int titleRes; - @DrawableRes - public final int drawableResId; - public final int id; + @StringRes + public final int titleRes; + @DrawableRes + public final int drawableResId; + public final int id; - NowPlayingScreen(@StringRes int titleRes, @DrawableRes int drawableResId, int id) { - this.titleRes = titleRes; - this.drawableResId = drawableResId; - this.id = id; - } + NowPlayingScreen(@StringRes int titleRes, @DrawableRes int drawableResId, int id) { + this.titleRes = titleRes; + this.drawableResId = drawableResId; + this.id = id; + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java index 6dd59ae50..33719764b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/PlayingQueueFragment.java @@ -9,6 +9,12 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + +import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; +import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; +import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; +import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; @@ -16,128 +22,124 @@ import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.ui.adapter.song.PlayingQueueAdapter; import code.name.monkey.retromusic.ui.fragments.base.AbsMusicServiceFragment; -import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; -import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; public class PlayingQueueFragment extends AbsMusicServiceFragment { - @BindView(R.id.recycler_view) - RecyclerView mRecyclerView; - Unbinder unbinder; - private RecyclerView.Adapter mWrappedAdapter; - private RecyclerViewDragDropManager mRecyclerViewDragDropManager; - private PlayingQueueAdapter mPlayingQueueAdapter; - private LinearLayoutManager mLayoutManager; + @BindView(R.id.recycler_view) + RecyclerView mRecyclerView; + Unbinder unbinder; + private RecyclerView.Adapter mWrappedAdapter; + private RecyclerViewDragDropManager mRecyclerViewDragDropManager; + private PlayingQueueAdapter mPlayingQueueAdapter; + private LinearLayoutManager mLayoutManager; - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_main_activity_recycler_view, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpRecyclerView(); - } - - private void setUpRecyclerView() { - mRecyclerViewDragDropManager = new RecyclerViewDragDropManager(); - final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); - - mPlayingQueueAdapter = new PlayingQueueAdapter( - (AppCompatActivity) getActivity(), - MusicPlayerRemote.getPlayingQueue(), - MusicPlayerRemote.getPosition(), - R.layout.item_list, - false, - null); - mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(mPlayingQueueAdapter); - - mLayoutManager = new LinearLayoutManager(getContext()); - - mRecyclerView.setLayoutManager(mLayoutManager); - mRecyclerView.setAdapter(mWrappedAdapter); - mRecyclerView.setItemAnimator(animator); - mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView); - mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); - } - - @Override - public void onQueueChanged() { - updateQueue(); - updateCurrentSong(); - } - - @Override - public void onMediaStoreChanged() { - updateQueue(); - updateCurrentSong(); - } - - @SuppressWarnings("ConstantConditions") - private void updateCurrentSong() { - } - - @Override - public void onPlayingMetaChanged() { - //updateCurrentSong(); - //updateIsFavorite(); - updateQueuePosition(); - //updateLyrics(); - } - - private void updateQueuePosition() { - mPlayingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition()); - // if (slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { - resetToCurrentPosition(); - //} - } - - private void updateQueue() { - mPlayingQueueAdapter - .swapDataSet(MusicPlayerRemote.getPlayingQueue(), MusicPlayerRemote.getPosition()); - resetToCurrentPosition(); - } - - private void resetToCurrentPosition() { - mRecyclerView.stopScroll(); - mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); - } - - @Override - public void onPause() { - if (mRecyclerViewDragDropManager != null) { - mRecyclerViewDragDropManager.cancelDrag(); - } - super.onPause(); - } - - @Override - public void onDestroyView() { - if (mRecyclerViewDragDropManager != null) { - mRecyclerViewDragDropManager.release(); - mRecyclerViewDragDropManager = null; + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_main_activity_recycler_view, container, false); + unbinder = ButterKnife.bind(this, view); + return view; } - if (mRecyclerView != null) { - mRecyclerView.setItemAnimator(null); - mRecyclerView.setAdapter(null); - mRecyclerView = null; + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + setUpRecyclerView(); } - if (mWrappedAdapter != null) { - WrapperAdapterUtils.releaseAll(mWrappedAdapter); - mWrappedAdapter = null; + private void setUpRecyclerView() { + mRecyclerViewDragDropManager = new RecyclerViewDragDropManager(); + final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); + + mPlayingQueueAdapter = new PlayingQueueAdapter( + (AppCompatActivity) getActivity(), + MusicPlayerRemote.getPlayingQueue(), + MusicPlayerRemote.getPosition(), + R.layout.item_list, + false, + null); + mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(mPlayingQueueAdapter); + + mLayoutManager = new LinearLayoutManager(getContext()); + + mRecyclerView.setLayoutManager(mLayoutManager); + mRecyclerView.setAdapter(mWrappedAdapter); + mRecyclerView.setItemAnimator(animator); + mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView); + mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); + } + + @Override + public void onQueueChanged() { + updateQueue(); + updateCurrentSong(); + } + + @Override + public void onMediaStoreChanged() { + updateQueue(); + updateCurrentSong(); + } + + @SuppressWarnings("ConstantConditions") + private void updateCurrentSong() { + } + + @Override + public void onPlayingMetaChanged() { + //updateCurrentSong(); + //updateIsFavorite(); + updateQueuePosition(); + //updateLyrics(); + } + + private void updateQueuePosition() { + mPlayingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition()); + // if (slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { + resetToCurrentPosition(); + //} + } + + private void updateQueue() { + mPlayingQueueAdapter + .swapDataSet(MusicPlayerRemote.getPlayingQueue(), MusicPlayerRemote.getPosition()); + resetToCurrentPosition(); + } + + private void resetToCurrentPosition() { + mRecyclerView.stopScroll(); + mLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); + } + + @Override + public void onPause() { + if (mRecyclerViewDragDropManager != null) { + mRecyclerViewDragDropManager.cancelDrag(); + } + super.onPause(); + } + + @Override + public void onDestroyView() { + if (mRecyclerViewDragDropManager != null) { + mRecyclerViewDragDropManager.release(); + mRecyclerViewDragDropManager = null; + } + + if (mRecyclerView != null) { + mRecyclerView.setItemAnimator(null); + mRecyclerView.setAdapter(null); + mRecyclerView = null; + } + + if (mWrappedAdapter != null) { + WrapperAdapterUtils.releaseAll(mWrappedAdapter); + mWrappedAdapter = null; + } + mPlayingQueueAdapter = null; + mLayoutManager = null; + super.onDestroyView(); + unbinder.unbind(); } - mPlayingQueueAdapter = null; - mLayoutManager = null; - super.onDestroyView(); - unbinder.unbind(); - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.java index 5871de851..724a78fe4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerFragment.java @@ -1,7 +1,6 @@ package code.name.monkey.retromusic.ui.fragments.base; import android.os.Bundle; -import android.support.v4.app.LoaderManager; import code.name.monkey.retromusic.ui.fragments.mainactivity.LibraryFragment; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.java index f9af465fb..b0eea8b5c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsLibraryPagerRecyclerViewFragment.java @@ -12,6 +12,9 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + +import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; @@ -19,143 +22,148 @@ import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.util.ViewUtil; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; public abstract class AbsLibraryPagerRecyclerViewFragment extends - AbsLibraryPagerFragment implements OnOffsetChangedListener { + AbsLibraryPagerFragment implements OnOffsetChangedListener { - public static final String TAG = AbsLibraryPagerRecyclerViewFragment.class.getSimpleName(); - @BindView(R.id.container) - ViewGroup container; - @BindView(R.id.recycler_view) - RecyclerView recyclerView; - @BindView(android.R.id.empty) - TextView empty; + public static final String TAG = AbsLibraryPagerRecyclerViewFragment.class.getSimpleName(); + @BindView(R.id.container) + ViewGroup container; + @BindView(R.id.recycler_view) + RecyclerView recyclerView; + @BindView(android.R.id.empty) + TextView empty; - private Unbinder unbinder; - private A adapter; - private LM layoutManager; + private Unbinder unbinder; + private A adapter; + private LM layoutManager; - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(getLayoutRes(), container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - getLibraryFragment().addOnAppBarOffsetChangedListener(this); - initLayoutManager(); - initAdapter(); - setUpRecyclerView(); - } - - private void setUpRecyclerView() { - if (recyclerView instanceof FastScrollRecyclerView) { - //noinspection ConstantConditions - ViewUtil.setUpFastScrollRecyclerViewColor(getActivity(), - ((FastScrollRecyclerView) recyclerView), ThemeStore.accentColor(getActivity())); + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(getLayoutRes(), container, false); + unbinder = ButterKnife.bind(this, view); + return view; } - recyclerView.setLayoutManager(layoutManager); - recyclerView.setAdapter(adapter); - } - @Override - public void onQueueChanged() { - super.onQueueChanged(); - checkForPadding(); - } + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + getLibraryFragment().addOnAppBarOffsetChangedListener(this); + initLayoutManager(); + initAdapter(); + setUpRecyclerView(); + } - @Override - public void onServiceConnected() { - super.onServiceConnected(); - checkForPadding(); - } + private void setUpRecyclerView() { + if (recyclerView instanceof FastScrollRecyclerView) { + //noinspection ConstantConditions + ViewUtil.setUpFastScrollRecyclerViewColor(getActivity(), + ((FastScrollRecyclerView) recyclerView), ThemeStore.accentColor(getActivity())); + } + recyclerView.setLayoutManager(layoutManager); + recyclerView.setAdapter(adapter); + recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + getLibraryFragment().addAppbarLayoutElevation(recyclerView.canScrollVertically(RecyclerView.NO_POSITION) ? 6f : 0f); + } + }); + } - private void checkForPadding() { - int height = (MusicPlayerRemote.getPlayingQueue().isEmpty() ? getResources() - .getDimensionPixelSize(R.dimen.mini_player_height) : 0); - recyclerView.setPadding(0, 0, 0, height); - } - - protected void invalidateLayoutManager() { - initLayoutManager(); - recyclerView.setLayoutManager(layoutManager); - } - - protected void invalidateAdapter() { - initAdapter(); - checkIsEmpty(); - recyclerView.setAdapter(adapter); - } - - private void initAdapter() { - adapter = createAdapter(); - adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { - @Override - public void onChanged() { - super.onChanged(); - checkIsEmpty(); + @Override + public void onQueueChanged() { + super.onQueueChanged(); checkForPadding(); - } - }); - } + } - private void initLayoutManager() { - layoutManager = createLayoutManager(); - } + @Override + public void onServiceConnected() { + super.onServiceConnected(); + checkForPadding(); + } - protected A getAdapter() { - return adapter; - } + private void checkForPadding() { + int height = (MusicPlayerRemote.getPlayingQueue().isEmpty() ? getResources() + .getDimensionPixelSize(R.dimen.mini_player_height) : 0); + recyclerView.setPadding(0, 0, 0, height); + } - protected LM getLayoutManager() { - return layoutManager; - } + protected void invalidateLayoutManager() { + initLayoutManager(); + recyclerView.setLayoutManager(layoutManager); + } - protected RecyclerView getRecyclerView() { - return recyclerView; - } + protected void invalidateAdapter() { + initAdapter(); + checkIsEmpty(); + recyclerView.setAdapter(adapter); + } - public ViewGroup getContainer() { - return container; - } + private void initAdapter() { + adapter = createAdapter(); + adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + super.onChanged(); + checkIsEmpty(); + checkForPadding(); + } + }); + } - @Override - public void onOffsetChanged(AppBarLayout appBarLayout, int i) { - container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), - container.getPaddingRight(), getLibraryFragment().getTotalAppBarScrollingRange() + i); - } + private void initLayoutManager() { + layoutManager = createLayoutManager(); + } - private void checkIsEmpty() { - empty.setText(getEmptyMessage()); - empty.setVisibility(adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); - } + protected A getAdapter() { + return adapter; + } - @StringRes - protected int getEmptyMessage() { - return R.string.empty; - } + protected LM getLayoutManager() { + return layoutManager; + } - @LayoutRes - protected int getLayoutRes() { - return R.layout.fragment_main_activity_recycler_view; - } + protected RecyclerView getRecyclerView() { + return recyclerView; + } - protected abstract LM createLayoutManager(); + public ViewGroup getContainer() { + return container; + } - @NonNull - protected abstract A createAdapter(); + @Override + public void onOffsetChanged(AppBarLayout appBarLayout, int i) { + container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), + container.getPaddingRight(), getLibraryFragment().getTotalAppBarScrollingRange() + i); + } - @Override - public void onDestroyView() { - super.onDestroyView(); - getLibraryFragment().removeOnAppBarOffsetChangedListener(this); - unbinder.unbind(); - } + private void checkIsEmpty() { + empty.setText(getEmptyMessage()); + empty.setVisibility(adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + } + @StringRes + protected int getEmptyMessage() { + return R.string.empty; + } + + @LayoutRes + protected int getLayoutRes() { + return R.layout.fragment_main_activity_recycler_view; + } + + protected abstract LM createLayoutManager(); + + @NonNull + protected abstract A createAdapter(); + + @Override + public void onDestroyView() { + super.onDestroyView(); + getLibraryFragment().removeOnAppBarOffsetChangedListener(this); + unbinder.unbind(); + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.java index 3b49c0864..9c1462fa7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMainActivityFragment.java @@ -21,7 +21,6 @@ public abstract class AbsMainActivityFragment extends AbsMusicServiceFragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setHasOptionsMenu(true); - getMainActivity().setNavigationbarColorAuto(); getMainActivity().setLightNavigationBar(true); getMainActivity().setTaskDescriptionColorAuto(); @@ -39,7 +38,7 @@ public abstract class AbsMainActivityFragment extends AbsMusicServiceFragment { statusBar.setBackgroundColor(color); getMainActivity().setLightStatusbarAuto(color); } else { - statusBar.setBackgroundColor(ColorUtil.darkenColor(color)); + statusBar.setBackgroundColor(color); } } } @@ -48,6 +47,6 @@ public abstract class AbsMainActivityFragment extends AbsMusicServiceFragment { public void setStatusbarColorAuto(View view) { // we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat //noinspection ConstantConditions - setStatusbarColor(view, ThemeStore.primaryColor(getContext())); + setStatusbarColor(view, ColorUtil.darkenColor(ThemeStore.primaryColor(getContext()))); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.java index 5d8ee787c..b9bca32fa 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsMusicServiceFragment.java @@ -7,7 +7,6 @@ import android.support.v4.app.Fragment; import android.view.View; import code.name.monkey.retromusic.interfaces.MusicServiceEventListener; - import code.name.monkey.retromusic.ui.activities.base.AbsMusicServiceActivity; /** diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.java index cd694d765..01a744eaa 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/base/AbsPlayerFragment.java @@ -34,23 +34,20 @@ import code.name.monkey.retromusic.util.NavigationUtil; import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.RetroUtil; -public abstract class AbsPlayerFragment extends AbsMusicServiceFragment implements - Toolbar.OnMenuItemClickListener, PaletteColorHolder { - +public abstract class AbsPlayerFragment extends AbsMusicServiceFragment implements Toolbar.OnMenuItemClickListener, PaletteColorHolder { public static final String TAG = AbsPlayerFragment.class.getSimpleName(); private boolean isToolbarShown = true; private Callbacks callbacks; private AsyncTask updateIsFavoriteTask; + @Override public void onAttach(Context context) { super.onAttach(context); try { callbacks = (Callbacks) context; } catch (ClassCastException e) { - throw new RuntimeException( - context.getClass().getSimpleName() + " must implement " + Callbacks.class - .getSimpleName()); + throw new RuntimeException(context.getClass().getSimpleName() + " must implement " + Callbacks.class.getSimpleName()); } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.java index 86554410d..7e56ce9b1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/ArtistsFragment.java @@ -4,6 +4,9 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.GridLayoutManager; + +import java.util.ArrayList; + import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.model.Artist; import code.name.monkey.retromusic.mvp.contract.ArtistContract; @@ -11,161 +14,160 @@ import code.name.monkey.retromusic.mvp.presenter.ArtistPresenter; import code.name.monkey.retromusic.ui.adapter.artist.ArtistAdapter; import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment; import code.name.monkey.retromusic.util.PreferenceUtil; -import java.util.ArrayList; public class ArtistsFragment extends - AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements - ArtistContract.ArtistView { + AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements + ArtistContract.ArtistView { - public static final String TAG = ArtistsFragment.class.getSimpleName(); - private ArtistPresenter presenter; + public static final String TAG = ArtistsFragment.class.getSimpleName(); + private ArtistPresenter presenter; - public static ArtistsFragment newInstance() { + public static ArtistsFragment newInstance() { - Bundle args = new Bundle(); + Bundle args = new Bundle(); - ArtistsFragment fragment = new ArtistsFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - presenter = new ArtistPresenter(this); - } - - @NonNull - @Override - protected GridLayoutManager createLayoutManager() { - return new GridLayoutManager(getActivity(), getGridSize()); - } - - @NonNull - @Override - protected ArtistAdapter createAdapter() { - int itemLayoutRes = getItemLayoutRes(); - notifyLayoutResChanged(itemLayoutRes); - if (itemLayoutRes != R.layout.item_list) { - itemLayoutRes = PreferenceUtil.getInstance(getContext()).getArtistGridStyle(getContext()); + ArtistsFragment fragment = new ArtistsFragment(); + fragment.setArguments(args); + return fragment; } - ArrayList dataSet = - getAdapter() == null ? new ArrayList<>() : getAdapter().getDataSet(); - return new ArtistAdapter(getLibraryFragment().getMainActivity(), dataSet, itemLayoutRes, - loadUsePalette(), getLibraryFragment()); - } - @Override - protected int getEmptyMessage() { - return R.string.no_artists; - } - - @Override - public void onMediaStoreChanged() { - presenter.loadArtists(); - } - - @Override - protected int loadGridSize() { - return PreferenceUtil.getInstance(getActivity()).getArtistGridSize(getActivity()); - } - - @Override - protected void saveGridSize(int gridSize) { - PreferenceUtil.getInstance(getActivity()).setArtistGridSize(gridSize); - } - - @Override - protected int loadGridSizeLand() { - return PreferenceUtil.getInstance(getActivity()).getArtistGridSizeLand(getActivity()); - } - - @Override - protected void saveGridSizeLand(int gridSize) { - PreferenceUtil.getInstance(getActivity()).setArtistGridSizeLand(gridSize); - } - - @Override - protected void saveUsePalette(boolean usePalette) { - PreferenceUtil.getInstance(getActivity()).setArtistColoredFooters(usePalette); - } - - @Override - public boolean loadUsePalette() { - return PreferenceUtil.getInstance(getActivity()).artistColoredFooters(); - } - - @Override - protected void setUsePalette(boolean usePalette) { - getAdapter().usePalette(usePalette); - } - - @Override - protected void setGridSize(int gridSize) { - getLayoutManager().setSpanCount(gridSize); - getAdapter().notifyDataSetChanged(); - } - - - @Override - protected String loadSortOrder() { - return PreferenceUtil.getInstance(getActivity()).getArtistSortOrder(); - } - - @Override - protected void saveSortOrder(String sortOrder) { - PreferenceUtil.getInstance(getActivity()).setArtistSortOrder(sortOrder); - } - - @Override - protected void setSortOrder(String sortOrder) { - presenter.loadArtists(); - } - - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (menuVisible) { - getLibraryFragment().setTitle( - PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library - : R.string.artists); + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + presenter = new ArtistPresenter(this); } - } - @Override - public void onResume() { - super.onResume(); - getLibraryFragment().setTitle( - PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library : R.string.artists); - if (getAdapter().getDataSet().isEmpty()) { - presenter.subscribe(); + @NonNull + @Override + protected GridLayoutManager createLayoutManager() { + return new GridLayoutManager(getActivity(), getGridSize()); } - } - @Override - public void onDestroy() { - super.onDestroy(); - presenter.unsubscribe(); - } + @NonNull + @Override + protected ArtistAdapter createAdapter() { + int itemLayoutRes = getItemLayoutRes(); + notifyLayoutResChanged(itemLayoutRes); + if (itemLayoutRes != R.layout.item_list) { + itemLayoutRes = PreferenceUtil.getInstance(getContext()).getArtistGridStyle(getContext()); + } + ArrayList dataSet = + getAdapter() == null ? new ArrayList<>() : getAdapter().getDataSet(); + return new ArtistAdapter(getLibraryFragment().getMainActivity(), dataSet, itemLayoutRes, + loadUsePalette(), getLibraryFragment()); + } - @Override - public void loading() { - } + @Override + protected int getEmptyMessage() { + return R.string.no_artists; + } - @Override - public void showEmptyView() { - getAdapter().swapDataSet(new ArrayList<>()); - } + @Override + public void onMediaStoreChanged() { + presenter.loadArtists(); + } - @Override - public void completed() { + @Override + protected int loadGridSize() { + return PreferenceUtil.getInstance(getActivity()).getArtistGridSize(getActivity()); + } - } + @Override + protected void saveGridSize(int gridSize) { + PreferenceUtil.getInstance(getActivity()).setArtistGridSize(gridSize); + } - @Override - public void showData(ArrayList artists) { - getAdapter().swapDataSet(artists); - } + @Override + protected int loadGridSizeLand() { + return PreferenceUtil.getInstance(getActivity()).getArtistGridSizeLand(getActivity()); + } + + @Override + protected void saveGridSizeLand(int gridSize) { + PreferenceUtil.getInstance(getActivity()).setArtistGridSizeLand(gridSize); + } + + @Override + protected void saveUsePalette(boolean usePalette) { + PreferenceUtil.getInstance(getActivity()).setArtistColoredFooters(usePalette); + } + + @Override + public boolean loadUsePalette() { + return PreferenceUtil.getInstance(getActivity()).artistColoredFooters(); + } + + @Override + protected void setUsePalette(boolean usePalette) { + getAdapter().usePalette(usePalette); + } + + @Override + protected void setGridSize(int gridSize) { + getLayoutManager().setSpanCount(gridSize); + getAdapter().notifyDataSetChanged(); + } + + + @Override + protected String loadSortOrder() { + return PreferenceUtil.getInstance(getActivity()).getArtistSortOrder(); + } + + @Override + protected void saveSortOrder(String sortOrder) { + PreferenceUtil.getInstance(getActivity()).setArtistSortOrder(sortOrder); + } + + @Override + protected void setSortOrder(String sortOrder) { + presenter.loadArtists(); + } + + + @Override + public void setMenuVisibility(boolean menuVisible) { + super.setMenuVisibility(menuVisible); + if (menuVisible) { + getLibraryFragment().setTitle( + PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library + : R.string.artists); + } + } + + @Override + public void onResume() { + super.onResume(); + getLibraryFragment().setTitle( + PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library : R.string.artists); + if (getAdapter().getDataSet().isEmpty()) { + presenter.subscribe(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + presenter.unsubscribe(); + } + + @Override + public void loading() { + } + + @Override + public void showEmptyView() { + getAdapter().swapDataSet(new ArrayList<>()); + } + + @Override + public void completed() { + + } + + @Override + public void showData(ArrayList artists) { + getAdapter().swapDataSet(artists); + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.java index 5f973bf1b..957728431 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/GenreFragment.java @@ -6,6 +6,9 @@ import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.view.Menu; import android.view.MenuInflater; + +import java.util.ArrayList; + import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.model.Genre; import code.name.monkey.retromusic.mvp.contract.GenreContract; @@ -13,100 +16,95 @@ import code.name.monkey.retromusic.mvp.presenter.GenrePresenter; import code.name.monkey.retromusic.ui.adapter.GenreAdapter; import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewFragment; import code.name.monkey.retromusic.util.PreferenceUtil; -import java.util.ArrayList; public class GenreFragment extends - AbsLibraryPagerRecyclerViewFragment implements - GenreContract.GenreView { + AbsLibraryPagerRecyclerViewFragment implements + GenreContract.GenreView { - private GenrePresenter mPresenter; + private GenrePresenter mPresenter; - public static GenreFragment newInstance() { - Bundle args = new Bundle(); - GenreFragment fragment = new GenreFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - mPresenter = new GenrePresenter(this); - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (menuVisible) { - getLibraryFragment().setTitle( - PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library - : R.string.genres); + public static GenreFragment newInstance() { + Bundle args = new Bundle(); + GenreFragment fragment = new GenreFragment(); + fragment.setArguments(args); + return fragment; } - } - @Override - public void onResume() { - super.onResume(); - getLibraryFragment().setTitle( - PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library : R.string.genres); - if (getAdapter().getDataSet().isEmpty()) { - mPresenter.subscribe(); + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + mPresenter = new GenrePresenter(this); + } + + @Override + public void setMenuVisibility(boolean menuVisible) { + super.setMenuVisibility(menuVisible); + if (menuVisible) { + getLibraryFragment().setTitle(PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library : R.string.genres); + } + } + + @Override + public void onResume() { + super.onResume(); + getLibraryFragment().setTitle(PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library : R.string.genres); + if (getAdapter().getDataSet().isEmpty()) { + mPresenter.subscribe(); + } } - } - @Override - public void onDestroy() { - super.onDestroy(); - mPresenter.unsubscribe(); - } + @Override + public void onDestroy() { + super.onDestroy(); + mPresenter.unsubscribe(); + } - @NonNull - @Override - protected LinearLayoutManager createLayoutManager() { - return new LinearLayoutManager(getActivity()); - } + @NonNull + @Override + protected LinearLayoutManager createLayoutManager() { + return new LinearLayoutManager(getActivity()); + } - @NonNull - @Override - protected GenreAdapter createAdapter() { - ArrayList dataSet = - getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); - return new GenreAdapter(getLibraryFragment().getMainActivity(), dataSet, R.layout.item_list); - } + @NonNull + @Override + protected GenreAdapter createAdapter() { + ArrayList dataSet = getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); + return new GenreAdapter(getLibraryFragment().getMainActivity(), dataSet, R.layout.item_list); + } - @Override - public void loading() { + @Override + public void loading() { - } + } - @Override - public void showData(ArrayList songs) { - getAdapter().swapDataSet(songs); - } + @Override + public void showData(ArrayList songs) { + getAdapter().swapDataSet(songs); + } - @Override - public void showEmptyView() { - getAdapter().swapDataSet(new ArrayList()); - } + @Override + public void showEmptyView() { + getAdapter().swapDataSet(new ArrayList()); + } - @Override - public void completed() { + @Override + public void completed() { - } + } - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - menu.removeItem(R.id.action_sort_order); - menu.removeItem(R.id.action_grid_size); - menu.removeItem(R.id.action_new_playlist); - } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + menu.removeItem(R.id.action_sort_order); + menu.removeItem(R.id.action_grid_size); + menu.removeItem(R.id.action_new_playlist); + } - @Override - protected int getEmptyMessage() { - return R.string.no_genres; - } + @Override + protected int getEmptyMessage() { + return R.string.no_genres; + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.java index 3dae3b5cd..e66d3791b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/LibraryFragment.java @@ -1,12 +1,15 @@ package code.name.monkey.retromusic.ui.fragments.mainactivity; import android.app.Activity; +import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.design.widget.AppBarLayout; +import android.support.design.widget.BottomNavigationView; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; @@ -18,10 +21,12 @@ import android.view.MenuItem; import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.TextView; import com.afollestad.materialcab.MaterialCab; +import butterknife.BindDrawable; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; @@ -29,6 +34,9 @@ import butterknife.Unbinder; import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.common.ATHToolbarActivity; import code.name.monkey.appthemehelper.util.ATHUtil; +import code.name.monkey.appthemehelper.util.ColorUtil; +import code.name.monkey.appthemehelper.util.MaterialValueHelper; +import code.name.monkey.appthemehelper.util.NavigationViewUtil; import code.name.monkey.appthemehelper.util.TintHelper; import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; import code.name.monkey.retromusic.R; @@ -39,13 +47,15 @@ import code.name.monkey.retromusic.helper.SortOrder; import code.name.monkey.retromusic.interfaces.CabHolder; import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks; import code.name.monkey.retromusic.loaders.SongLoader; +import code.name.monkey.retromusic.misc.NavigationIconClickListener; +import code.name.monkey.retromusic.ui.activities.MainActivity; import code.name.monkey.retromusic.ui.activities.SettingsActivity; import code.name.monkey.retromusic.ui.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment; import code.name.monkey.retromusic.ui.fragments.base.AbsMainActivityFragment; import code.name.monkey.retromusic.util.NavigationUtil; +import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.RetroColorUtil; import code.name.monkey.retromusic.util.RetroUtil; -import code.name.monkey.retromusic.views.IconImageView; public class LibraryFragment extends AbsMainActivityFragment implements CabHolder, MainActivityFragmentCallbacks { @@ -62,9 +72,24 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde @BindView(R.id.title) TextView title; - @BindView(R.id.search) - IconImageView search; + @BindView(R.id.action_library) + TextView actionLibrary; + @BindView(R.id.bottom_navigation) + BottomNavigationView bottomNavigationView; + + @BindView(R.id.fragment_container) + View contentContainer; + + @BindView(R.id.coordinator_layout) + View coordinatorLayout; + + @BindView(R.id.menu_container) + View menuContainer; + @BindDrawable(R.drawable.ic_menu_white_24dp) + Drawable menu; + @BindDrawable(R.drawable.ic_close_white_24dp) + Drawable close; private Unbinder unBinder; private MaterialCab cab; private FragmentManager fragmentManager; @@ -77,6 +102,28 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde return fragment; } + public static Fragment newInstance() { + return new LibraryFragment(); + } + + private void setupBottomView() { + Context context = getContext(); + if (context != null) { + bottomNavigationView.setSelectedItemId(PreferenceUtil.getInstance(context).getLastPage()); + bottomNavigationView.setBackgroundColor(ThemeStore.primaryColor(context)); + bottomNavigationView.setOnNavigationItemSelectedListener(this::onOptionsItemSelected); + int iconColor = ATHUtil.resolveColor(context, R.attr.iconColor); + int accentColor = ThemeStore.accentColor(context); + NavigationViewUtil.setItemIconColors(bottomNavigationView, iconColor, accentColor); + NavigationViewUtil.setItemTextColors(bottomNavigationView, iconColor, accentColor); + + if (PreferenceUtil.getInstance(getContext()).tabTitles()) { + bottomNavigationView.setItemTextAppearanceActive(R.style.HideTabTitleTextAppearance); + bottomNavigationView.setItemTextAppearanceInactive(R.style.HideTabTitleTextAppearance); + } + } + } + public void setTitle(@StringRes int name) { title.setText(getString(name)); } @@ -108,45 +155,43 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setStatusbarColorAuto(view); - getMainActivity().setBottomBarVisibility(View.VISIBLE); + setupBottomView(); setupToolbar(); - inflateFragment(); - } - private void inflateFragment() { - if (getArguments() == null) { + if (savedInstanceState == null) { selectedFragment(SongsFragment.newInstance()); - return; - } - switch (getArguments().getInt(CURRENT_TAB_ID)) { - default: - case R.id.action_song: - selectedFragment(SongsFragment.newInstance()); - break; - case R.id.action_album: - selectedFragment(AlbumsFragment.newInstance()); - break; - case R.id.action_artist: - selectedFragment(ArtistsFragment.newInstance()); - break; - case R.id.action_playlist: - selectedFragment(PlaylistsFragment.newInstance()); - break; } } @SuppressWarnings("ConstantConditions") private void setupToolbar() { + int accentColor = ThemeStore.accentColor(getContext()); title.setTextColor(ThemeStore.textColorPrimary(getContext())); - TintHelper.setTintAuto(search, ThemeStore.textColorSecondary(getContext()), false); + actionLibrary.setTextColor(MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(accentColor))); + actionLibrary.setBackgroundResource(R.drawable.et_bg_circular_top_corners); + TintHelper.setTintAuto(actionLibrary, ThemeStore.accentColor(getContext()), true); int primaryColor = ThemeStore.primaryColor(getContext()); - appbar.setBackgroundColor(primaryColor); - toolbar.setBackgroundColor(primaryColor); + int darkPrimaryColor = ColorUtil.darkenColor(primaryColor); + + TintHelper.setTintAuto(contentContainer, primaryColor, true); + + toolbar.setBackgroundColor(darkPrimaryColor); + toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp); + appbar.setBackgroundColor(darkPrimaryColor); appbar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> getMainActivity().setLightStatusbar(!ATHUtil.isWindowBackgroundDark(getContext()))); getMainActivity().setTitle(null); getMainActivity().setSupportActionBar(toolbar); + toolbar.setNavigationOnClickListener(new NavigationIconClickListener( + getContext(), + contentContainer, + menuContainer, + new AccelerateDecelerateInterpolator(), + menu, + close + )); + coordinatorLayout.setBackgroundColor(ColorUtil.darkenColor(primaryColor)); } public Fragment getCurrentFragment() { @@ -196,10 +241,6 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde return cab; } - @OnClick(R.id.search) - void search() { - NavigationUtil.goToSearch(getActivity()); - } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { @@ -270,7 +311,8 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde } private boolean handleSortOrderMenuItem( - @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment, @NonNull MenuItem item) { + @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment + fragment, @NonNull MenuItem item) { String sortOrder = null; if (fragment instanceof AlbumsFragment) { switch (item.getItemId()) { @@ -354,6 +396,21 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde } int id = item.getItemId(); switch (id) { + case R.id.action_song: + selectedFragment(SongsFragment.newInstance()); + return true; + case R.id.action_album: + selectedFragment(AlbumsFragment.newInstance()); + return true; + case R.id.action_artist: + selectedFragment(ArtistsFragment.newInstance()); + return true; + case R.id.action_playlist: + selectedFragment(PlaylistsFragment.newInstance()); + return true; + case R.id.action_genre: + selectedFragment(GenreFragment.newInstance()); + return true; case R.id.action_new_playlist: CreatePlaylistDialog.create().show(getChildFragmentManager(), "CREATE_PLAYLIST"); return true; @@ -432,7 +489,8 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde private boolean handleGridSizeMenuItem( - @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment, @NonNull MenuItem item) { + @NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment + fragment, @NonNull MenuItem item) { int gridSize = 0; switch (item.getItemId()) { case R.id.action_grid_size_1: @@ -468,4 +526,27 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde } return false; } + + @OnClick({R.id.action_home, R.id.action_settings, R.id.action_folders}) + void startUserInfo(View view) { + Activity activity = getActivity(); + if (activity != null) { + switch (view.getId()) { + case R.id.action_settings: + NavigationUtil.goToSettings(activity); + break; + case R.id.action_home: + getMainActivity().setCurrentFragment(MainActivity.HOME); + break; + case R.id.action_folders: + getMainActivity().setCurrentFragment(MainActivity.FOLDERS); + break; + } + } + } + + public void addAppbarLayoutElevation(float v) { + //TransitionManager.beginDelayedTransition(appbar); + //appbar.setElevation(v); + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.java index 2d220d984..daa7e3225 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/PlaylistsFragment.java @@ -54,14 +54,14 @@ public class PlaylistsFragment extends AbsLibraryPagerRecyclerViewFragment implements - SongContract.SongView { + AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements + SongContract.SongView { - private SongPresenter presenter; + private SongPresenter presenter; - public SongsFragment() { - // Required empty public constructor - } - - public static SongsFragment newInstance() { - Bundle args = new Bundle(); - SongsFragment fragment = new SongsFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - presenter = new SongPresenter(this); - } - - @NonNull - @Override - protected GridLayoutManager createLayoutManager() { - return new GridLayoutManager(getActivity(), getGridSize()); - } - - @Override - protected int getEmptyMessage() { - return R.string.no_songs; - } - - @NonNull - @Override - protected SongAdapter createAdapter() { - int itemLayoutRes = getItemLayoutRes(); - notifyLayoutResChanged(itemLayoutRes); - boolean usePalette = loadUsePalette(); - ArrayList dataSet = - getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); - - if (getGridSize() <= getMaxGridSizeForList()) { - return new ShuffleButtonSongAdapter(getLibraryFragment().getMainActivity(), dataSet, - itemLayoutRes, usePalette, getLibraryFragment()); + public SongsFragment() { + // Required empty public constructor } - return new SongAdapter(getLibraryFragment().getMainActivity(), dataSet, itemLayoutRes, - usePalette, getLibraryFragment()); - } - @Override - public void onMediaStoreChanged() { - presenter.loadSongs(); - } - - @Override - protected int loadGridSize() { - return PreferenceUtil.getInstance(getActivity()).getSongGridSize(getActivity()); - } - - @Override - protected void saveGridSize(int gridSize) { - PreferenceUtil.getInstance(getActivity()).setSongGridSize(gridSize); - } - - @Override - protected int loadGridSizeLand() { - return PreferenceUtil.getInstance(getActivity()).getSongGridSizeLand(getActivity()); - } - - @Override - protected void saveGridSizeLand(int gridSize) { - PreferenceUtil.getInstance(getActivity()).setSongGridSizeLand(gridSize); - } - - @Override - public void saveUsePalette(boolean usePalette) { - PreferenceUtil.getInstance(getActivity()).setSongColoredFooters(usePalette); - } - - @Override - public boolean loadUsePalette() { - return PreferenceUtil.getInstance(getActivity()).songColoredFooters(); - } - - @Override - public void setUsePalette(boolean usePalette) { - getAdapter().usePalette(usePalette); - } - - @Override - protected void setGridSize(int gridSize) { - getLayoutManager().setSpanCount(gridSize); - getAdapter().notifyDataSetChanged(); - } - - @Override - public void onResume() { - super.onResume(); - getLibraryFragment().setTitle( - PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library : R.string.songs); - if (getAdapter().getDataSet().isEmpty()) { - presenter.subscribe(); + public static SongsFragment newInstance() { + Bundle args = new Bundle(); + SongsFragment fragment = new SongsFragment(); + fragment.setArguments(args); + return fragment; } - } - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (menuVisible) { - getLibraryFragment().setTitle( - PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library - : R.string.songs); + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + presenter = new SongPresenter(this); } - } - @Override - public void onDestroy() { - presenter.unsubscribe(); - super.onDestroy(); - } + @NonNull + @Override + protected GridLayoutManager createLayoutManager() { + return new GridLayoutManager(getActivity(), getGridSize()); + } - @Override - public void loading() { + @Override + protected int getEmptyMessage() { + return R.string.no_songs; + } - } + @NonNull + @Override + protected SongAdapter createAdapter() { + int itemLayoutRes = getItemLayoutRes(); + notifyLayoutResChanged(itemLayoutRes); + boolean usePalette = loadUsePalette(); + ArrayList dataSet = + getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); - @Override - public void showData(ArrayList songs) { - getAdapter().swapDataSet(songs); - } + if (getGridSize() <= getMaxGridSizeForList()) { + return new ShuffleButtonSongAdapter(getLibraryFragment().getMainActivity(), dataSet, + itemLayoutRes, usePalette, getLibraryFragment()); + } + return new SongAdapter(getLibraryFragment().getMainActivity(), dataSet, itemLayoutRes, + usePalette, getLibraryFragment()); + } - @Override - public void showEmptyView() { - getAdapter().swapDataSet(new ArrayList()); - } + @Override + public void onMediaStoreChanged() { + presenter.loadSongs(); + } - @Override - public void completed() { + @Override + protected int loadGridSize() { + return PreferenceUtil.getInstance(getActivity()).getSongGridSize(getActivity()); + } - } + @Override + protected void saveGridSize(int gridSize) { + PreferenceUtil.getInstance(getActivity()).setSongGridSize(gridSize); + } - @Override - protected String loadSortOrder() { - return PreferenceUtil.getInstance(getActivity()).getSongSortOrder(); - } + @Override + protected int loadGridSizeLand() { + return PreferenceUtil.getInstance(getActivity()).getSongGridSizeLand(getActivity()); + } - @Override - protected void saveSortOrder(String sortOrder) { - PreferenceUtil.getInstance(getActivity()).setSongSortOrder(sortOrder); - } + @Override + protected void saveGridSizeLand(int gridSize) { + PreferenceUtil.getInstance(getActivity()).setSongGridSizeLand(gridSize); + } - @Override - protected void setSortOrder(String sortOrder) { - presenter.loadSongs(); - } + @Override + public void saveUsePalette(boolean usePalette) { + PreferenceUtil.getInstance(getActivity()).setSongColoredFooters(usePalette); + } + + @Override + public boolean loadUsePalette() { + return PreferenceUtil.getInstance(getActivity()).songColoredFooters(); + } + + @Override + public void setUsePalette(boolean usePalette) { + getAdapter().usePalette(usePalette); + } + + @Override + protected void setGridSize(int gridSize) { + getLayoutManager().setSpanCount(gridSize); + getAdapter().notifyDataSetChanged(); + } + + @Override + public void onResume() { + super.onResume(); + getLibraryFragment().setTitle( + PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library : R.string.songs); + if (getAdapter().getDataSet().isEmpty()) { + presenter.subscribe(); + } + } + + @Override + public void setMenuVisibility(boolean menuVisible) { + super.setMenuVisibility(menuVisible); + if (menuVisible) { + getLibraryFragment().setTitle( + PreferenceUtil.getInstance(getContext()).tabTitles() ? R.string.library + : R.string.songs); + } + } + + @Override + public void onDestroy() { + presenter.unsubscribe(); + super.onDestroy(); + } + + @Override + public void loading() { + + } + + @Override + public void showData(ArrayList songs) { + getAdapter().swapDataSet(songs); + } + + @Override + public void showEmptyView() { + getAdapter().swapDataSet(new ArrayList()); + } + + @Override + public void completed() { + + } + + @Override + protected String loadSortOrder() { + return PreferenceUtil.getInstance(getActivity()).getSongSortOrder(); + } + + @Override + protected void saveSortOrder(String sortOrder) { + PreferenceUtil.getInstance(getActivity()).setSongSortOrder(sortOrder); + } + + @Override + protected void setSortOrder(String sortOrder) { + presenter.loadSongs(); + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java index 2528069f9..79f2d64f9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/folders/FoldersFragment.java @@ -1,14 +1,15 @@ package code.name.monkey.retromusic.ui.fragments.mainactivity.folders; +import android.app.Activity; import android.app.Dialog; import android.content.Context; +import android.graphics.drawable.Drawable; import android.media.MediaScannerConnection; import android.os.Bundle; import android.os.Environment; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.AppBarLayout; -import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.Snackbar; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; @@ -22,6 +23,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; import android.webkit.MimeTypeMap; import android.widget.PopupMenu; import android.widget.TextView; @@ -41,13 +43,16 @@ import java.util.Comparator; import java.util.LinkedList; import java.util.List; +import butterknife.BindDrawable; import butterknife.BindView; import butterknife.ButterKnife; +import butterknife.OnClick; import butterknife.Unbinder; import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.common.ATHToolbarActivity; import code.name.monkey.appthemehelper.util.ATHUtil; import code.name.monkey.appthemehelper.util.ColorUtil; +import code.name.monkey.appthemehelper.util.MaterialValueHelper; import code.name.monkey.appthemehelper.util.TintHelper; import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; import code.name.monkey.retromusic.R; @@ -58,12 +63,15 @@ import code.name.monkey.retromusic.interfaces.CabHolder; import code.name.monkey.retromusic.interfaces.LoaderIds; import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks; import code.name.monkey.retromusic.misc.DialogAsyncTask; +import code.name.monkey.retromusic.misc.NavigationIconClickListener; import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener; import code.name.monkey.retromusic.misc.WrappedAsyncTaskLoader; import code.name.monkey.retromusic.model.Song; +import code.name.monkey.retromusic.ui.activities.MainActivity; import code.name.monkey.retromusic.ui.adapter.SongFileAdapter; import code.name.monkey.retromusic.ui.fragments.base.AbsMainActivityFragment; import code.name.monkey.retromusic.util.FileUtil; +import code.name.monkey.retromusic.util.NavigationUtil; import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.RetroColorUtil; import code.name.monkey.retromusic.util.ViewUtil; @@ -85,7 +93,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements protected static final String CRUMBS = "crumbs"; private static final int LOADER_ID = LoaderIds.FOLDERS_FRAGMENT; @BindView(R.id.coordinator_layout) - CoordinatorLayout coordinatorLayout; + View coordinatorLayout; @BindView(R.id.container) View container; @@ -102,13 +110,25 @@ public class FoldersFragment extends AbsMainActivityFragment implements @BindView(R.id.bread_crumbs) BreadCrumbLayout breadCrumbs; - @BindView(R.id.appbar) + @BindView(R.id.app_bar) AppBarLayout appbar; @BindView(R.id.recycler_view) FastScrollRecyclerView recyclerView; - Comparator fileComparator = (lhs, rhs) -> { + @BindView(R.id.action_folders) + TextView actionFolders; + + @BindView(R.id.menu_container) + View menuContainer; + + @BindDrawable(R.drawable.ic_menu_white_24dp) + Drawable menu; + + @BindDrawable(R.drawable.ic_close_white_24dp) + Drawable close; + + private Comparator fileComparator = (lhs, rhs) -> { if (lhs.isDirectory() && !rhs.isDirectory()) { return -1; } else if (!lhs.isDirectory() && rhs.isDirectory()) { @@ -118,7 +138,6 @@ public class FoldersFragment extends AbsMainActivityFragment implements (rhs.getName()); } }; - private Unbinder unbinder; private SongFileAdapter adapter; private MaterialCab cab; @@ -138,7 +157,6 @@ public class FoldersFragment extends AbsMainActivityFragment implements return frag; } - public static File getDefaultStartDirectory() { File musicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC); File startFolder; @@ -184,6 +202,25 @@ public class FoldersFragment extends AbsMainActivityFragment implements } } + @OnClick({R.id.action_library, R.id.action_settings, R.id.action_home}) + void startUserInfo(View view) { + Activity activity = getActivity(); + if (activity != null) { + switch (view.getId()) { + case R.id.action_home: + getMainActivity().setCurrentFragment(MainActivity.HOME); + break; + case R.id.action_library: + getMainActivity().setCurrentFragment(MainActivity.LIBRARY); + break; + case R.id.action_settings: + NavigationUtil.goToSettings(activity); + break; + + } + } + } + @Nullable private BreadCrumbLayout.Crumb getActiveCrumb() { return breadCrumbs != null && breadCrumbs.size() > 0 ? breadCrumbs @@ -221,10 +258,8 @@ public class FoldersFragment extends AbsMainActivityFragment implements public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { setStatusbarColorAuto(view); getMainActivity().getSlidingUpPanelLayout().setShadowHeight(0); - getMainActivity().setBottomBarVisibility(View.GONE); setUpAppbarColor(); - setUpToolbar(); setUpBreadCrumbs(); setUpRecyclerView(); setUpAdapter(); @@ -232,26 +267,39 @@ public class FoldersFragment extends AbsMainActivityFragment implements } private void setUpAppbarColor() { + int accentColor = ThemeStore.accentColor(getContext()); + title.setTextColor(ThemeStore.textColorPrimary(getContext())); + actionFolders.setTextColor(MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(accentColor))); + actionFolders.setBackgroundResource(R.drawable.et_bg_circular_top_corners); + TintHelper.setTintAuto(actionFolders, ThemeStore.accentColor(getContext()), true); + + //noinspection ConstantConditions - int primaryColor = ThemeStore.primaryColor(getActivity()); + int primaryColor = ThemeStore.primaryColor(getContext()); + int darkPrimaryColor = ColorUtil.darkenColor(primaryColor); + + toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp); + //noinspection ConstantConditions + getActivity().setTitle(null); + getMainActivity().setSupportActionBar(toolbar); + toolbar.setNavigationOnClickListener(new NavigationIconClickListener( + getContext(), + container, + menuContainer, + new AccelerateDecelerateInterpolator(), + menu, + close + )); TintHelper.setTintAuto(container, primaryColor, true); - appbar.setBackgroundColor(primaryColor); - toolbar.setBackgroundColor(primaryColor); - //breadCrumbs.setBackgroundColor(primaryColor); + appbar.setBackgroundColor(darkPrimaryColor); + toolbar.setBackgroundColor(darkPrimaryColor); + coordinatorLayout.setBackgroundColor(darkPrimaryColor); + breadCrumbs.setActivatedContentColor(ToolbarContentTintHelper.toolbarTitleColor(getActivity(), ColorUtil.darkenColor(primaryColor))); breadCrumbs.setDeactivatedContentColor(ToolbarContentTintHelper.toolbarSubtitleColor(getActivity(), ColorUtil.darkenColor(primaryColor))); appbar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> getMainActivity().setLightStatusbar(!ATHUtil.isWindowBackgroundDark(getContext()))); } - private void setUpToolbar() { - //noinspection ConstantConditions - title.setTextColor(ThemeStore.textColorPrimary(getContext())); - toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp); - //noinspection ConstantConditions - getActivity().setTitle(R.string.folders); - getMainActivity().setSupportActionBar(toolbar); - } - private void setUpBreadCrumbs() { breadCrumbs.setCallback(this); } @@ -387,7 +435,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements String.format(getString(R.string.not_listed_in_media_store), file1.getName())), Snackbar.LENGTH_LONG) .setAction(R.string.action_scan, - v -> new ListPathsAsyncTask(getActivity(), paths -> scanPaths(paths)) + v -> new ListPathsAsyncTask(getActivity(), this::scanPaths) .execute(new ListPathsAsyncTask.LoadingInfo(finalFile, AUDIO_FILE_FILTER))) .setActionTextColor(ThemeStore.accentColor(getActivity())) .show(); @@ -441,7 +489,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements Toast.LENGTH_SHORT).show(); return true; case R.id.action_scan: - new ListPathsAsyncTask(getActivity(), paths -> scanPaths(paths)) + new ListPathsAsyncTask(getActivity(), this::scanPaths) .execute(new ListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER)); return true; } @@ -468,7 +516,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements getFileComparator())); return true; case R.id.action_scan: - new ListPathsAsyncTask(getActivity(), paths -> scanPaths(paths)) + new ListPathsAsyncTask(getActivity(), this::scanPaths) .execute(new ListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER)); return true; } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.java index 2c02a35bf..852306a64 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/BannerHomeFragment.java @@ -2,6 +2,7 @@ package code.name.monkey.retromusic.ui.fragments.mainactivity.home; import android.app.Activity; import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -16,6 +17,11 @@ import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AnticipateInterpolator; +import android.view.animation.AnticipateOvershootInterpolator; +import android.view.animation.BounceInterpolator; +import android.view.animation.LinearInterpolator; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -23,18 +29,22 @@ import android.widget.TextView; import java.io.File; import java.util.ArrayList; +import butterknife.BindDrawable; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import butterknife.Unbinder; import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.util.ATHUtil; +import code.name.monkey.appthemehelper.util.ColorUtil; +import code.name.monkey.appthemehelper.util.MaterialValueHelper; import code.name.monkey.appthemehelper.util.TintHelper; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.dialogs.HomeOptionDialog; import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks; import code.name.monkey.retromusic.loaders.SongLoader; +import code.name.monkey.retromusic.misc.NavigationIconClickListener; import code.name.monkey.retromusic.model.Album; import code.name.monkey.retromusic.model.Artist; import code.name.monkey.retromusic.model.Genre; @@ -44,6 +54,7 @@ import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist; import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist; import code.name.monkey.retromusic.mvp.contract.HomeContract; import code.name.monkey.retromusic.mvp.presenter.HomePresenter; +import code.name.monkey.retromusic.ui.activities.MainActivity; import code.name.monkey.retromusic.ui.adapter.GenreAdapter; import code.name.monkey.retromusic.ui.adapter.album.AlbumFullWithAdapter; import code.name.monkey.retromusic.ui.adapter.artist.ArtistAdapter; @@ -59,45 +70,70 @@ import io.reactivex.schedulers.Schedulers; import static code.name.monkey.retromusic.Constants.USER_PROFILE; -public class BannerHomeFragment extends AbsMainActivityFragment implements - MainActivityFragmentCallbacks, - HomeContract.HomeView { +public class BannerHomeFragment extends AbsMainActivityFragment implements MainActivityFragmentCallbacks, HomeContract.HomeView { private static final String TAG = "HomeFragment"; Unbinder unbinder; - @BindView(R.id.home_toolbar) + + @BindView(R.id.toolbar) Toolbar toolbar; + @BindView(R.id.appbar) AppBarLayout appbar; + + @BindView(R.id.menu_container) + View menuContainer; + @BindView(R.id.user_image) CircularImageView userImage; + @BindView(R.id.recycler_view) RecyclerView recentArtistRV; + @BindView(R.id.recent_album) RecyclerView recentAlbumRV; + @BindView(R.id.top_artist) RecyclerView topArtistRV; + @BindView(R.id.top_album) MetalRecyclerViewPager topAlbumRV; + @BindView(R.id.recent_artist_container) View recentArtistContainer; + @BindView(R.id.recent_albums_container) View recentAlbumsContainer; + @BindView(R.id.top_artist_container) View topArtistContainer; + @BindView(R.id.top_albums_container) View topAlbumContainer; + @BindView(R.id.genres) RecyclerView genresRecyclerView; + @BindView(R.id.genre_container) LinearLayout genreContainer; - @BindView(R.id.container) - View container; + + @BindView(R.id.content_container) + View contentContainer; + + @BindView(R.id.coordinator_layout) + View coordinatorLayout; + @BindView(R.id.title) TextView title; - @BindView(R.id.search) - ImageView search; + @BindView(R.id.action_home) + TextView actionHome; + + @BindDrawable(R.drawable.ic_menu_white_24dp) + Drawable menu; + + @BindDrawable(R.drawable.ic_close_white_24dp) + Drawable close; private HomePresenter homePresenter; private CompositeDisposable disposable; @@ -109,7 +145,6 @@ public class BannerHomeFragment extends AbsMainActivityFragment implements return fragment; } - private void loadImageFromStorage(ImageView imageView) { //noinspection ConstantConditions disposable.add(new Compressor(getContext()) @@ -119,7 +154,7 @@ public class BannerHomeFragment extends AbsMainActivityFragment implements .setCompressFormat(Bitmap.CompressFormat.WEBP) .compressToBitmapAsFlowable( new File(PreferenceUtil.getInstance(getContext()).getProfileImage(), USER_PROFILE)) - .subscribeOn(Schedulers.io()) + .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(imageView::setImageBitmap, throwable -> imageView.setImageDrawable(ContextCompat @@ -147,9 +182,7 @@ public class BannerHomeFragment extends AbsMainActivityFragment implements public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setStatusbarColorAuto(view); - getMainActivity().getSlidingUpPanelLayout().setShadowHeight(8); - getMainActivity().setBottomBarVisibility(View.VISIBLE); - + setTextColor(); setupToolbar(); loadImageFromStorage(userImage); @@ -159,19 +192,34 @@ public class BannerHomeFragment extends AbsMainActivityFragment implements @SuppressWarnings("ConstantConditions") private void setupToolbar() { - //noinspection ConstantConditions int primaryColor = ThemeStore.primaryColor(getContext()); - appbar.setBackgroundColor(primaryColor); - toolbar.setBackgroundColor(primaryColor); + int darkPrimaryColor = ColorUtil.darkenColor(primaryColor); + + toolbar.setBackgroundColor(darkPrimaryColor); + toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp); + appbar.setBackgroundColor(darkPrimaryColor); appbar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> getMainActivity().setLightStatusbar(!ATHUtil.isWindowBackgroundDark(getContext()))); - - getActivity().setTitle(R.string.app_name); + getMainActivity().setTitle(null); getMainActivity().setSupportActionBar(toolbar); + toolbar.setNavigationOnClickListener(new NavigationIconClickListener( + getContext(), + contentContainer, + menuContainer, + new AccelerateDecelerateInterpolator(), + menu, + close + )); + coordinatorLayout.setBackgroundColor(ColorUtil.darkenColor(primaryColor)); + TintHelper.setTintAuto(contentContainer, primaryColor, true); + } + private void setTextColor() { + int accentColor = ThemeStore.accentColor(getContext()); title.setTextColor(ThemeStore.textColorPrimary(getContext())); - TintHelper.setTintAuto(search, ThemeStore.textColorPrimary(getContext()), false); - TintHelper.setTintAuto(container, primaryColor, true); + actionHome.setTextColor(MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(accentColor))); + actionHome.setBackgroundResource(R.drawable.et_bg_circular_top_corners); + TintHelper.setTintAuto(actionHome, accentColor, true); } @Override @@ -227,7 +275,7 @@ public class BannerHomeFragment extends AbsMainActivityFragment implements private void checkPadding() { int height = getResources().getDimensionPixelSize(R.dimen.mini_player_height); - container.setPadding(0, 0, 0, MusicPlayerRemote.getPlayingQueue().isEmpty() ? height * 2 : 0); + contentContainer.setPadding(0, 0, 0, MusicPlayerRemote.getPlayingQueue().isEmpty() ? height * 2 : 0); } @Override @@ -293,15 +341,14 @@ public class BannerHomeFragment extends AbsMainActivityFragment implements } - @OnClick({R.id.last_added, R.id.top_played, R.id.action_shuffle, R.id.history, - R.id.user_image, R.id.search}) + @OnClick({R.id.last_added, R.id.top_played, R.id.search, R.id.action_shuffle, R.id.history, + R.id.user_image, R.id.action_library, R.id.action_settings, R.id.action_folders}) void startUserInfo(View view) { Activity activity = getActivity(); if (activity != null) { switch (view.getId()) { case R.id.action_shuffle: - MusicPlayerRemote - .openAndShuffleQueue(SongLoader.getAllSongs(activity).blockingFirst(), true); + MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(activity).blockingFirst(), true); break; case R.id.last_added: NavigationUtil.goToPlaylistNew(activity, new LastAddedPlaylist(activity)); @@ -312,20 +359,22 @@ public class BannerHomeFragment extends AbsMainActivityFragment implements case R.id.history: NavigationUtil.goToPlaylistNew(activity, new HistoryPlaylist(activity)); break; - case R.id.search: - NavigationUtil.goToSearch(activity); - break; case R.id.user_image: new HomeOptionDialog().show(getFragmentManager(), TAG); break; + case R.id.action_folders: + getMainActivity().setCurrentFragment(MainActivity.FOLDERS); + break; + case R.id.action_library: + getMainActivity().setCurrentFragment(MainActivity.LIBRARY); + break; + case R.id.action_settings: + NavigationUtil.goToSettings(activity); + break; + case R.id.search: + NavigationUtil.goToSearch(activity); + break; } } } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - homePresenter.loadRecentArtists(); - homePresenter.loadRecentAlbums(); - } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/HomeFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/HomeFragment.java index 721a625e5..a709a2548 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/HomeFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/mainactivity/home/HomeFragment.java @@ -2,6 +2,7 @@ package code.name.monkey.retromusic.ui.fragments.mainactivity.home; import android.app.Activity; import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -17,6 +18,8 @@ import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AnticipateOvershootInterpolator; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -29,6 +32,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Random; +import butterknife.BindDrawable; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; @@ -42,6 +46,7 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks; import code.name.monkey.retromusic.loaders.SongLoader; import code.name.monkey.retromusic.misc.AppBarStateChangeListener; +import code.name.monkey.retromusic.misc.NavigationIconClickListener; import code.name.monkey.retromusic.model.Album; import code.name.monkey.retromusic.model.Artist; import code.name.monkey.retromusic.model.Genre; @@ -75,41 +80,66 @@ public class HomeFragment extends AbsMainActivityFragment implements MainActivit Unbinder unbinder; @BindView(R.id.home_toolbar) Toolbar toolbar; - @BindView(R.id.appbar) + + @BindView(R.id.menu_container) + View menuContainer; + + @BindView(R.id.coordinator_layout) + View coordinatorLayout; + + @BindView(R.id.app_bar) AppBarLayout appbar; + @BindView(R.id.image) ImageView imageView; + @BindView(R.id.user_image) CircularImageView userImage; + @BindView(R.id.collapsing_toolbar) CollapsingToolbarLayout toolbarLayout; + @BindView(R.id.recycler_view) RecyclerView recentArtistRV; + @BindView(R.id.recent_album) RecyclerView recentAlbumRV; + @BindView(R.id.top_artist) RecyclerView topArtistRV; + @BindView(R.id.top_album) MetalRecyclerViewPager topAlbumRV; + @BindView(R.id.recent_artist_container) View recentArtistContainer; + @BindView(R.id.recent_albums_container) View recentAlbumsContainer; + @BindView(R.id.top_artist_container) View topArtistContainer; + @BindView(R.id.top_albums_container) View topAlbumContainer; + @BindView(R.id.genres) RecyclerView genresRecyclerView; + @BindView(R.id.genre_container) LinearLayout genreContainer; - @BindView(R.id.container) - View container; + + @BindView(R.id.content_container) + View contentContainer; + @BindView(R.id.title) TextView title; - @BindView(R.id.search) - ImageView search; + @BindDrawable(R.drawable.ic_menu_white_24dp) + Drawable menu; + + @BindDrawable(R.drawable.ic_close_white_24dp) + Drawable close; private HomePresenter homePresenter; private CompositeDisposable disposable; @@ -206,7 +236,6 @@ public class HomeFragment extends AbsMainActivityFragment implements MainActivit super.onViewCreated(view, savedInstanceState); getMainActivity().getSlidingUpPanelLayout().setShadowHeight(8); - getMainActivity().setBottomBarVisibility(View.VISIBLE); setupToolbar(); loadImageFromStorage(userImage); @@ -219,8 +248,7 @@ public class HomeFragment extends AbsMainActivityFragment implements MainActivit @SuppressWarnings("ConstantConditions") private void setupToolbar() { if (!PreferenceUtil.getInstance(getContext()).getFullScreenMode()) { - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) toolbar - .getLayoutParams(); + ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams(); params.topMargin = RetroUtil.getStatusBarHeight(getContext()); toolbar.setLayoutParams(params); } @@ -241,20 +269,27 @@ public class HomeFragment extends AbsMainActivityFragment implements MainActivit color = ContextCompat.getColor(getContext(), R.color.md_white_1000); break; } - TintHelper.setTintAuto(search, color, false); title.setTextColor(color); } }); int primaryColor = ThemeStore.primaryColor(getContext()); - TintHelper.setTintAuto(container, primaryColor, true); + TintHelper.setTintAuto(coordinatorLayout, primaryColor, true); toolbarLayout.setStatusBarScrimColor(primaryColor); toolbarLayout.setContentScrimColor(primaryColor); - toolbar.setTitle(R.string.home); + getActivity().setTitle(null); getMainActivity().setSupportActionBar(toolbar); - + toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp); + toolbar.setNavigationOnClickListener(new NavigationIconClickListener( + getContext(), + contentContainer, + menuContainer, + new AccelerateDecelerateInterpolator(), + menu, + close + )); } @Override @@ -310,7 +345,7 @@ public class HomeFragment extends AbsMainActivityFragment implements MainActivit private void checkPadding() { int height = getResources().getDimensionPixelSize(R.dimen.mini_player_height); - container.setPadding(0, 0, 0, MusicPlayerRemote.getPlayingQueue().isEmpty() ? height * 2 : 0); + contentContainer.setPadding(0, 0, 0, MusicPlayerRemote.getPlayingQueue().isEmpty() ? height * 2 : 0); } @Override @@ -377,14 +412,13 @@ public class HomeFragment extends AbsMainActivityFragment implements MainActivit @OnClick({R.id.last_added, R.id.top_played, R.id.action_shuffle, R.id.history, - R.id.user_image, R.id.search}) + R.id.user_image}) void startUserInfo(View view) { Activity activity = getActivity(); if (activity != null) { switch (view.getId()) { case R.id.action_shuffle: - MusicPlayerRemote - .openAndShuffleQueue(SongLoader.getAllSongs(activity).blockingFirst(), true); + MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(activity).blockingFirst(), true); break; case R.id.last_added: NavigationUtil.goToPlaylistNew(activity, new LastAddedPlaylist(activity)); @@ -395,9 +429,7 @@ public class HomeFragment extends AbsMainActivityFragment implements MainActivit case R.id.history: NavigationUtil.goToPlaylistNew(activity, new HistoryPlaylist(activity)); break; - case R.id.search: - NavigationUtil.goToSearch(activity); - break; + case R.id.user_image: new HomeOptionDialog().show(getFragmentManager(), TAG); break; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.java index e5ff394f4..4e8754c90 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/PlayerAlbumCoverFragment.java @@ -6,6 +6,7 @@ import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; @@ -21,134 +22,134 @@ import code.name.monkey.retromusic.util.PreferenceUtil; public class PlayerAlbumCoverFragment extends AbsMusicServiceFragment implements - ViewPager.OnPageChangeListener { + ViewPager.OnPageChangeListener { - public static final String TAG = PlayerAlbumCoverFragment.class.getSimpleName(); - public static final long VISIBILITY_ANIM_DURATION = 300; - @BindView(R.id.player_album_cover_viewpager) - ViewPager viewPager; - private Unbinder unbinder; - private Callbacks callbacks; - private int currentPosition; - private AlbumCoverPagerAdapter.AlbumCoverFragment.ColorReceiver colorReceiver = - new AlbumCoverPagerAdapter.AlbumCoverFragment.ColorReceiver() { - @Override - public void onColorReady(int color, int requestCode) { - if (currentPosition == requestCode) { - notifyColorChange(color); - } + public static final String TAG = PlayerAlbumCoverFragment.class.getSimpleName(); + public static final long VISIBILITY_ANIM_DURATION = 300; + @BindView(R.id.player_album_cover_viewpager) + ViewPager viewPager; + private Unbinder unbinder; + private Callbacks callbacks; + private int currentPosition; + private AlbumCoverPagerAdapter.AlbumCoverFragment.ColorReceiver colorReceiver = + new AlbumCoverPagerAdapter.AlbumCoverFragment.ColorReceiver() { + @Override + public void onColorReady(int color, int requestCode) { + if (currentPosition == requestCode) { + notifyColorChange(color); + } + } + }; + + public void removeSlideEffect() { + ParallaxPagerTransformer transformer = new ParallaxPagerTransformer(R.id.player_image); + transformer.setSpeed(0.3f); + viewPager.setPageTransformer(true, transformer); + + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_player_album_cover, container, false); + unbinder = ButterKnife.bind(this, view); + return view; + } + + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + viewPager.addOnPageChangeListener(this); + + //noinspection ConstantConditions + if (PreferenceUtil.getInstance(getContext()).carouselEffect() && !( + (PreferenceUtil.getInstance(getContext()).getNowPlayingScreen() == NowPlayingScreen.FULL) || + (PreferenceUtil.getInstance(getContext()).getNowPlayingScreen() + == NowPlayingScreen.FLAT))) { + viewPager.setClipToPadding(false); + viewPager.setPadding(96, 0, 96, 0); + viewPager.setPageMargin(18); + + viewPager.setPageTransformer(false, new CustPagerTransformer(getContext())); + } else { + viewPager.setPageTransformer(true, new NormalPageTransformer()); } - }; - - public void removeSlideEffect() { - ParallaxPagerTransformer transformer = new ParallaxPagerTransformer(R.id.player_image); - transformer.setSpeed(0.3f); - viewPager.setPageTransformer(true, transformer); - - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_player_album_cover, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - viewPager.addOnPageChangeListener(this); - - //noinspection ConstantConditions - if (PreferenceUtil.getInstance(getContext()).carouselEffect() && !( - (PreferenceUtil.getInstance(getContext()).getNowPlayingScreen() == NowPlayingScreen.FULL) || - (PreferenceUtil.getInstance(getContext()).getNowPlayingScreen() - == NowPlayingScreen.FLAT))) { - viewPager.setClipToPadding(false); - viewPager.setPadding(96, 0, 96, 0); - viewPager.setPageMargin(18); - - viewPager.setPageTransformer(false, new CustPagerTransformer(getContext())); - } else { - viewPager.setPageTransformer(true, new NormalPageTransformer()); } - } - @Override - public void onDestroyView() { - super.onDestroyView(); - viewPager.removeOnPageChangeListener(this); - unbinder.unbind(); - } - - @Override - public void onServiceConnected() { - updatePlayingQueue(); - } - - @Override - public void onPlayingMetaChanged() { - viewPager.setCurrentItem(MusicPlayerRemote.getPosition()); - } - - @Override - public void onQueueChanged() { - updatePlayingQueue(); - } - - private void updatePlayingQueue() { - viewPager.setAdapter( - new AlbumCoverPagerAdapter(getFragmentManager(), MusicPlayerRemote.getPlayingQueue())); - //noinspection ConstantConditions - viewPager.getAdapter().notifyDataSetChanged(); - viewPager.setCurrentItem(MusicPlayerRemote.getPosition()); - onPageSelected(MusicPlayerRemote.getPosition()); - - } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - - } - - @Override - public void onPageSelected(int position) { - currentPosition = position; - if (viewPager.getAdapter() != null) { - ((AlbumCoverPagerAdapter) viewPager.getAdapter()).receiveColor(colorReceiver, position); + @Override + public void onDestroyView() { + super.onDestroyView(); + viewPager.removeOnPageChangeListener(this); + unbinder.unbind(); } - if (position != MusicPlayerRemote.getPosition()) { - MusicPlayerRemote.playSongAt(position); + + @Override + public void onServiceConnected() { + updatePlayingQueue(); } - } - @Override - public void onPageScrollStateChanged(int state) { - - } - - - private void notifyColorChange(int color) { - if (callbacks != null) { - callbacks.onColorChanged(color); + @Override + public void onPlayingMetaChanged() { + viewPager.setCurrentItem(MusicPlayerRemote.getPosition()); } - } - public void setCallbacks(Callbacks listener) { - callbacks = listener; - } + @Override + public void onQueueChanged() { + updatePlayingQueue(); + } - public void removeEffect() { - viewPager.setPageTransformer(false, null); - } + private void updatePlayingQueue() { + viewPager.setAdapter( + new AlbumCoverPagerAdapter(getFragmentManager(), MusicPlayerRemote.getPlayingQueue())); + //noinspection ConstantConditions + viewPager.getAdapter().notifyDataSetChanged(); + viewPager.setCurrentItem(MusicPlayerRemote.getPosition()); + onPageSelected(MusicPlayerRemote.getPosition()); + + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + currentPosition = position; + if (viewPager.getAdapter() != null) { + ((AlbumCoverPagerAdapter) viewPager.getAdapter()).receiveColor(colorReceiver, position); + } + if (position != MusicPlayerRemote.getPosition()) { + MusicPlayerRemote.playSongAt(position); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + + } - public interface Callbacks { + private void notifyColorChange(int color) { + if (callbacks != null) { + callbacks.onColorChanged(color); + } + } - void onColorChanged(int color); + public void setCallbacks(Callbacks listener) { + callbacks = listener; + } - void onFavoriteToggled(); + public void removeEffect() { + viewPager.setPageTransformer(false, null); + } - } + + public interface Callbacks { + + void onColorChanged(int color); + + void onFavoriteToggled(); + + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.java index 19e153566..3162f87ab 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/blur/BlurPlayerFragment.java @@ -16,6 +16,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; +import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; +import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; +import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; @@ -31,287 +38,282 @@ import code.name.monkey.retromusic.ui.adapter.song.PlayingQueueAdapter; import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment; -import com.bumptech.glide.Glide; -import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; -import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; -import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; /** * @author Hemanth S (h4h13). */ public class BlurPlayerFragment extends AbsPlayerFragment implements - PlayerAlbumCoverFragment.Callbacks { + PlayerAlbumCoverFragment.Callbacks { - @BindView(R.id.player_toolbar) - Toolbar toolbar; - @BindView(R.id.toolbar_container) - View toolbarContainer; - @BindView(R.id.gradient_background) - ImageView colorBackground; - @BindView(R.id.status_bar) - View statusBar; - @Nullable - @BindView(R.id.recycler_view) - RecyclerView recyclerView; - @Nullable - @BindView(R.id.title) - TextView title; + @BindView(R.id.player_toolbar) + Toolbar toolbar; + @BindView(R.id.toolbar_container) + View toolbarContainer; + @BindView(R.id.gradient_background) + ImageView colorBackground; + @BindView(R.id.status_bar) + View statusBar; + @Nullable + @BindView(R.id.recycler_view) + RecyclerView recyclerView; + @Nullable + @BindView(R.id.title) + TextView title; - private int lastColor; - private BlurPlaybackControlsFragment playbackControlsFragment; - private Unbinder unbinder; + private int lastColor; + private BlurPlaybackControlsFragment playbackControlsFragment; + private Unbinder unbinder; - private RecyclerView.Adapter wrappedAdapter; - private RecyclerViewDragDropManager recyclerViewDragDropManager; - private PlayingQueueAdapter playingQueueAdapter; - private LinearLayoutManager layoutManager; + private RecyclerView.Adapter wrappedAdapter; + private RecyclerViewDragDropManager recyclerViewDragDropManager; + private PlayingQueueAdapter playingQueueAdapter; + private LinearLayoutManager layoutManager; - public static PlayerFragment newInstance() { - Bundle args = new Bundle(); - PlayerFragment fragment = new PlayerFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return Color.WHITE; - } - - @Override - public void onColorChanged(int color) { - playbackControlsFragment.setDark(color); - lastColor = color; - getCallbacks().onPaletteColorChanged(); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - - if (title != null && playingQueueAdapter != null) { - if (ColorUtil.isColorLight(color)) { - title.setTextColor(Color.BLACK); - playingQueueAdapter.usePalette(false); - } else { - title.setTextColor(Color.WHITE); - playingQueueAdapter.usePalette(true); - } - } - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); - } - } - - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - if (recyclerViewDragDropManager != null) { - recyclerViewDragDropManager.release(); - recyclerViewDragDropManager = null; + public static PlayerFragment newInstance() { + Bundle args = new Bundle(); + PlayerFragment fragment = new PlayerFragment(); + fragment.setArguments(args); + return fragment; } - if (recyclerView != null) { - recyclerView.setItemAnimator(null); - recyclerView.setAdapter(null); - recyclerView = null; + @Override + @ColorInt + public int getPaletteColor() { + return lastColor; } - if (wrappedAdapter != null) { - WrapperAdapterUtils.releaseAll(wrappedAdapter); - wrappedAdapter = null; - } - playingQueueAdapter = null; - layoutManager = null; - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_blur, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - toggleStatusBar(statusBar); - - setUpSubFragments(); - setUpPlayerToolbar(); - } - - private void setUpSubFragments() { - playbackControlsFragment = (BlurPlaybackControlsFragment) getChildFragmentManager() - .findFragmentById(R.id.playback_controls_fragment); - - PlayerAlbumCoverFragment playerAlbumCoverFragment = - (PlayerAlbumCoverFragment) getChildFragmentManager() - .findFragmentById(R.id.player_album_cover_fragment); - playerAlbumCoverFragment.setCallbacks(this); - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - //noinspection ConstantConditions - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - } - - private void updateBlur() { - Activity activity = getActivity(); - if (activity == null) { - return; + @Override + public void onShow() { + playbackControlsFragment.show(); } - int blurAmount = PreferenceManager.getDefaultSharedPreferences(getContext()) - .getInt("new_blur_amount", 25); + @Override + public void onHide() { + playbackControlsFragment.hide(); + onBackPressed(); + } - colorBackground.clearColorFilter(); + @Override + public boolean onBackPressed() { + return false; + } - SongGlideRequest.Builder.from(Glide.with(activity), MusicPlayerRemote.getCurrentSong()) - .checkIgnoreMediaStore(activity) - .generatePalette(activity) - .build() - .override(320, 480) - .transform(new BlurTransformation.Builder(getActivity()).blurRadius(blurAmount).build()) - .into(new RetroMusicColoredTarget(colorBackground) { - @Override - public void onColorReady(int color) { - if (color == getDefaultFooterColor()) { - colorBackground.setColorFilter(color); + @Override + public Toolbar getToolbar() { + return toolbar; + } + + @Override + public int toolbarIconColor() { + return Color.WHITE; + } + + @Override + public void onColorChanged(int color) { + playbackControlsFragment.setDark(color); + lastColor = color; + getCallbacks().onPaletteColorChanged(); + + ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); + + if (title != null && playingQueueAdapter != null) { + if (ColorUtil.isColorLight(color)) { + title.setTextColor(Color.BLACK); + playingQueueAdapter.usePalette(false); + } else { + title.setTextColor(Color.WHITE); + playingQueueAdapter.usePalette(true); } - } - }); - } - - @Override - public void onServiceConnected() { - updateIsFavorite(); - updateBlur(); - setUpRecyclerView(); - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - updateBlur(); - updateQueuePosition(); - } - - private void setUpRecyclerView() { - if (recyclerView != null) { - recyclerViewDragDropManager = new RecyclerViewDragDropManager(); - final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); - - playingQueueAdapter = new PlayingQueueAdapter( - (AppCompatActivity) getActivity(), - MusicPlayerRemote.getPlayingQueue(), - MusicPlayerRemote.getPosition(), - R.layout.item_song, - false, - null); - wrappedAdapter = recyclerViewDragDropManager.createWrappedAdapter(playingQueueAdapter); - - layoutManager = new LinearLayoutManager(getContext()); - - recyclerView.setLayoutManager(layoutManager); - recyclerView.setAdapter(wrappedAdapter); - recyclerView.setItemAnimator(animator); - recyclerViewDragDropManager.attachRecyclerView(recyclerView); - layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); - } - } - - @Override - public void onQueueChanged() { - updateQueue(); - updateCurrentSong(); - } - - @Override - public void onMediaStoreChanged() { - updateQueue(); - updateCurrentSong(); - } - - @SuppressWarnings("ConstantConditions") - private void updateCurrentSong() { - } - - private void updateQueuePosition() { - if (playingQueueAdapter != null) { - playingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition()); - // if (slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { - resetToCurrentPosition(); - //} - } - } - - private void updateQueue() { - if (playingQueueAdapter != null) { - playingQueueAdapter - .swapDataSet(MusicPlayerRemote.getPlayingQueue(), MusicPlayerRemote.getPosition()); - resetToCurrentPosition(); - } - } - - private void resetToCurrentPosition() { - if (recyclerView != null) { - recyclerView.stopScroll(); - layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); + } } - } - - @Override - public void onPause() { - if (recyclerViewDragDropManager != null) { - recyclerViewDragDropManager.cancelDrag(); + @Override + protected void toggleFavorite(Song song) { + super.toggleFavorite(song); + if (song.id == MusicPlayerRemote.getCurrentSong().id) { + updateIsFavorite(); + } + } + + @Override + public void onFavoriteToggled() { + toggleFavorite(MusicPlayerRemote.getCurrentSong()); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (recyclerViewDragDropManager != null) { + recyclerViewDragDropManager.release(); + recyclerViewDragDropManager = null; + } + + if (recyclerView != null) { + recyclerView.setItemAnimator(null); + recyclerView.setAdapter(null); + recyclerView = null; + } + + if (wrappedAdapter != null) { + WrapperAdapterUtils.releaseAll(wrappedAdapter); + wrappedAdapter = null; + } + playingQueueAdapter = null; + layoutManager = null; + super.onDestroyView(); + unbinder.unbind(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_blur, container, false); + unbinder = ButterKnife.bind(this, view); + return view; + } + + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + toggleStatusBar(statusBar); + + setUpSubFragments(); + setUpPlayerToolbar(); + } + + private void setUpSubFragments() { + playbackControlsFragment = (BlurPlaybackControlsFragment) getChildFragmentManager() + .findFragmentById(R.id.playback_controls_fragment); + + PlayerAlbumCoverFragment playerAlbumCoverFragment = + (PlayerAlbumCoverFragment) getChildFragmentManager() + .findFragmentById(R.id.player_album_cover_fragment); + playerAlbumCoverFragment.setCallbacks(this); + } + + private void setUpPlayerToolbar() { + toolbar.inflateMenu(R.menu.menu_player); + //noinspection ConstantConditions + toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); + toolbar.setOnMenuItemClickListener(this); + + ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); + } + + private void updateBlur() { + Activity activity = getActivity(); + if (activity == null) { + return; + } + + int blurAmount = PreferenceManager.getDefaultSharedPreferences(getContext()) + .getInt("new_blur_amount", 25); + + colorBackground.clearColorFilter(); + + SongGlideRequest.Builder.from(Glide.with(activity), MusicPlayerRemote.getCurrentSong()) + .checkIgnoreMediaStore(activity) + .generatePalette(activity) + .build() + .override(320, 480) + .transform(new BlurTransformation.Builder(getActivity()).blurRadius(blurAmount).build()) + .into(new RetroMusicColoredTarget(colorBackground) { + @Override + public void onColorReady(int color) { + if (color == getDefaultFooterColor()) { + colorBackground.setColorFilter(color); + } + } + }); + } + + @Override + public void onServiceConnected() { + updateIsFavorite(); + updateBlur(); + setUpRecyclerView(); + } + + @Override + public void onPlayingMetaChanged() { + updateIsFavorite(); + updateBlur(); + updateQueuePosition(); + } + + private void setUpRecyclerView() { + if (recyclerView != null) { + recyclerViewDragDropManager = new RecyclerViewDragDropManager(); + final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); + + playingQueueAdapter = new PlayingQueueAdapter( + (AppCompatActivity) getActivity(), + MusicPlayerRemote.getPlayingQueue(), + MusicPlayerRemote.getPosition(), + R.layout.item_song, + false, + null); + wrappedAdapter = recyclerViewDragDropManager.createWrappedAdapter(playingQueueAdapter); + + layoutManager = new LinearLayoutManager(getContext()); + + recyclerView.setLayoutManager(layoutManager); + recyclerView.setAdapter(wrappedAdapter); + recyclerView.setItemAnimator(animator); + recyclerViewDragDropManager.attachRecyclerView(recyclerView); + layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); + } + } + + @Override + public void onQueueChanged() { + updateQueue(); + updateCurrentSong(); + } + + @Override + public void onMediaStoreChanged() { + updateQueue(); + updateCurrentSong(); + } + + @SuppressWarnings("ConstantConditions") + private void updateCurrentSong() { + } + + private void updateQueuePosition() { + if (playingQueueAdapter != null) { + playingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition()); + // if (slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { + resetToCurrentPosition(); + //} + } + } + + private void updateQueue() { + if (playingQueueAdapter != null) { + playingQueueAdapter + .swapDataSet(MusicPlayerRemote.getPlayingQueue(), MusicPlayerRemote.getPosition()); + resetToCurrentPosition(); + } + } + + private void resetToCurrentPosition() { + if (recyclerView != null) { + recyclerView.stopScroll(); + layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); + } + + } + + @Override + public void onPause() { + if (recyclerViewDragDropManager != null) { + recyclerViewDragDropManager.cancelDrag(); + } + super.onPause(); } - super.onPause(); - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.java index f813c7434..794be029a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/cardblur/CardBlurFragment.java @@ -12,6 +12,9 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; + +import com.bumptech.glide.Glide; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; @@ -25,167 +28,166 @@ import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.ui.fragments.base.AbsPlayerFragment; import code.name.monkey.retromusic.ui.fragments.player.PlayerAlbumCoverFragment; import code.name.monkey.retromusic.ui.fragments.player.normal.PlayerFragment; -import com.bumptech.glide.Glide; public class CardBlurFragment extends AbsPlayerFragment implements - PlayerAlbumCoverFragment.Callbacks { + PlayerAlbumCoverFragment.Callbacks { - @BindView(R.id.player_toolbar) - Toolbar toolbar; - @BindView(R.id.status_bar) - View statusBar; - @BindView(R.id.gradient_background) - ImageView colorBackground; + @BindView(R.id.player_toolbar) + Toolbar toolbar; + @BindView(R.id.status_bar) + View statusBar; + @BindView(R.id.gradient_background) + ImageView colorBackground; - private int lastColor; - private CardBlurPlaybackControlsFragment playbackControlsFragment; - private Unbinder unbinder; + private int lastColor; + private CardBlurPlaybackControlsFragment playbackControlsFragment; + private Unbinder unbinder; - public static PlayerFragment newInstance() { - Bundle args = new Bundle(); - PlayerFragment fragment = new PlayerFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - @ColorInt - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - playbackControlsFragment.show(); - } - - @Override - public void onHide() { - playbackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public void onResume() { - super.onResume(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - return Color.WHITE; - } - - @Override - public void onColorChanged(int color) { - playbackControlsFragment.setDark(color); - lastColor = color; - getCallbacks().onPaletteColorChanged(); - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - } - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); + public static PlayerFragment newInstance() { + Bundle args = new Bundle(); + PlayerFragment fragment = new PlayerFragment(); + fragment.setArguments(args); + return fragment; } - } - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } + @Override + @ColorInt + public int getPaletteColor() { + return lastColor; + } - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } + @Override + public void onShow() { + playbackControlsFragment.show(); + } - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_card_blur_player, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } + @Override + public void onHide() { + playbackControlsFragment.hide(); + onBackPressed(); + } - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - toggleStatusBar(statusBar); + @Override + public void onResume() { + super.onResume(); + } - setUpSubFragments(); - setUpPlayerToolbar(); - } + @Override + public boolean onBackPressed() { + return false; + } - private void setUpSubFragments() { - playbackControlsFragment = (CardBlurPlaybackControlsFragment) getChildFragmentManager() - .findFragmentById(R.id.playback_controls_fragment); + @Override + public Toolbar getToolbar() { + return toolbar; + } - PlayerAlbumCoverFragment playerAlbumCoverFragment = - (PlayerAlbumCoverFragment) getChildFragmentManager() - .findFragmentById(R.id.player_album_cover_fragment); - playerAlbumCoverFragment.setCallbacks(this); - playerAlbumCoverFragment.removeEffect(); - } + @Override + public int toolbarIconColor() { + return Color.WHITE; + } - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); + @Override + public void onColorChanged(int color) { + playbackControlsFragment.setDark(color); + lastColor = color; + getCallbacks().onPaletteColorChanged(); + ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); + } + + @Override + protected void toggleFavorite(Song song) { + super.toggleFavorite(song); + if (song.id == MusicPlayerRemote.getCurrentSong().id) { + updateIsFavorite(); + } + } + + @Override + public void onFavoriteToggled() { + toggleFavorite(MusicPlayerRemote.getCurrentSong()); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_card_blur_player, container, false); + unbinder = ButterKnife.bind(this, view); + return view; + } + + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + toggleStatusBar(statusBar); + + setUpSubFragments(); + setUpPlayerToolbar(); + } + + private void setUpSubFragments() { + playbackControlsFragment = (CardBlurPlaybackControlsFragment) getChildFragmentManager() + .findFragmentById(R.id.playback_controls_fragment); + + PlayerAlbumCoverFragment playerAlbumCoverFragment = + (PlayerAlbumCoverFragment) getChildFragmentManager() + .findFragmentById(R.id.player_album_cover_fragment); + playerAlbumCoverFragment.setCallbacks(this); + playerAlbumCoverFragment.removeEffect(); + } + + private void setUpPlayerToolbar() { + toolbar.inflateMenu(R.menu.menu_player); + toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); + toolbar.setOnMenuItemClickListener(this); /* for (int i = 0; i < toolbar.getMenu().size(); i++) { MenuItem menuItem = toolbar.getMenu().getItem(i); menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); }*/ - ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); - } - - @Override - public void onServiceConnected() { - updateIsFavorite(); - updateBlur(); - } - - @Override - public void onPlayingMetaChanged() { - updateIsFavorite(); - updateBlur(); - } - - private void updateBlur() { - Activity activity = getActivity(); - if (activity == null) { - return; + ToolbarContentTintHelper.colorizeToolbar(toolbar, Color.WHITE, getActivity()); } - int blurAmount = PreferenceManager.getDefaultSharedPreferences(getContext()) - .getInt("new_blur_amount", 25); - colorBackground.clearColorFilter(); - SongGlideRequest.Builder.from(Glide.with(activity), MusicPlayerRemote.getCurrentSong()) - .checkIgnoreMediaStore(activity) - .generatePalette(activity) - .build() - .transform(new BlurTransformation.Builder(getActivity()).blurRadius(blurAmount).build()) - .into(new RetroMusicColoredTarget(colorBackground) { - @Override - public void onColorReady(int color) { - if (color == getDefaultFooterColor()) { - colorBackground.setColorFilter(color); - } - } - }); - } + @Override + public void onServiceConnected() { + updateIsFavorite(); + updateBlur(); + } + + @Override + public void onPlayingMetaChanged() { + updateIsFavorite(); + updateBlur(); + } + + private void updateBlur() { + Activity activity = getActivity(); + if (activity == null) { + return; + } + int blurAmount = PreferenceManager.getDefaultSharedPreferences(getContext()) + .getInt("new_blur_amount", 25); + + colorBackground.clearColorFilter(); + SongGlideRequest.Builder.from(Glide.with(activity), MusicPlayerRemote.getCurrentSong()) + .checkIgnoreMediaStore(activity) + .generatePalette(activity) + .build() + .transform(new BlurTransformation.Builder(getActivity()).blurRadius(blurAmount).build()) + .into(new RetroMusicColoredTarget(colorBackground) { + @Override + public void onColorReady(int color) { + if (color == getDefaultFooterColor()) { + colorBackground.setColorFilter(color); + } + } + }); + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.java index 4c7b20fb7..075844c83 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlaybackControlsFragment.java @@ -2,8 +2,6 @@ package code.name.monkey.retromusic.ui.fragments.player.flat; import android.animation.ObjectAnimator; import android.graphics.PorterDuff; -import android.graphics.drawable.ClipDrawable; -import android.graphics.drawable.LayerDrawable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.java index 256e3f381..6e4e7f55f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/flat/FlatPlayerFragment.java @@ -11,6 +11,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; @@ -28,147 +29,147 @@ import code.name.monkey.retromusic.util.ViewUtil; import code.name.monkey.retromusic.views.DrawableGradient; public class FlatPlayerFragment extends AbsPlayerFragment implements - PlayerAlbumCoverFragment.Callbacks { + PlayerAlbumCoverFragment.Callbacks { - @BindView(R.id.player_toolbar) - Toolbar toolbar; - @BindView(R.id.gradient_background) - View colorBackground; - @BindView(R.id.toolbar_container) - FrameLayout toolbarContainer; - @BindView(R.id.status_bar) - View statusBar; + @BindView(R.id.player_toolbar) + Toolbar toolbar; + @BindView(R.id.gradient_background) + View colorBackground; + @BindView(R.id.toolbar_container) + FrameLayout toolbarContainer; + @BindView(R.id.status_bar) + View statusBar; - private Unbinder unbinder; - private ValueAnimator valueAnimator; - private FlatPlaybackControlsFragment flatPlaybackControlsFragment; - private int lastColor; + private Unbinder unbinder; + private ValueAnimator valueAnimator; + private FlatPlaybackControlsFragment flatPlaybackControlsFragment; + private int lastColor; - private void setUpSubFragments() { - flatPlaybackControlsFragment = (FlatPlaybackControlsFragment) - getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); + private void setUpSubFragments() { + flatPlaybackControlsFragment = (FlatPlaybackControlsFragment) + getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); - PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) - getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment); - playerAlbumCoverFragment.setCallbacks(this); - } - - private void setUpPlayerToolbar() { - toolbar.inflateMenu(R.menu.menu_player); - toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); - toolbar.setOnMenuItemClickListener(this); - - ToolbarContentTintHelper.colorizeToolbar(toolbar, ATHUtil.resolveColor(getContext(), - R.attr.iconColor), getActivity()); - } - - private void colorize(int i) { - if (valueAnimator != null) { - valueAnimator.cancel(); + PlayerAlbumCoverFragment playerAlbumCoverFragment = (PlayerAlbumCoverFragment) + getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment); + playerAlbumCoverFragment.setCallbacks(this); } - valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), android.R.color.transparent, i); - valueAnimator.addUpdateListener(animation -> { - GradientDrawable drawable = new DrawableGradient(GradientDrawable.Orientation.TOP_BOTTOM, - new int[]{(int) animation.getAnimatedValue(), android.R.color.transparent}, 0); - if (colorBackground != null) { - colorBackground.setBackground(drawable); - } - }); - valueAnimator.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME).start(); - } + private void setUpPlayerToolbar() { + toolbar.inflateMenu(R.menu.menu_player); + toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed()); + toolbar.setOnMenuItemClickListener(this); - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_flat_player, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - toggleStatusBar(statusBar); - - setUpPlayerToolbar(); - setUpSubFragments(); - - } - - @Override - public int getPaletteColor() { - return lastColor; - } - - @Override - public void onShow() { - flatPlaybackControlsFragment.show(); - } - - @Override - public void onHide() { - flatPlaybackControlsFragment.hide(); - onBackPressed(); - } - - @Override - public boolean onBackPressed() { - return false; - } - - @Override - public Toolbar getToolbar() { - return toolbar; - } - - @Override - public int toolbarIconColor() { - boolean isLight = ColorUtil.isColorLight(lastColor); - return PreferenceUtil.getInstance(getContext()).getAdaptiveColor() ? - MaterialValueHelper.getPrimaryTextColor(getContext(), isLight) : - ATHUtil.resolveColor(getContext(), R.attr.iconColor); - } - - @Override - public void onColorChanged(int color) { - lastColor = color; - flatPlaybackControlsFragment.setDark(color); - getCallbacks().onPaletteColorChanged(); - - boolean isLight = ColorUtil.isColorLight(color); - - //TransitionManager.beginDelayedTransition(mToolbar); - int iconColor = PreferenceUtil.getInstance(getContext()).getAdaptiveColor() ? - MaterialValueHelper.getPrimaryTextColor(getContext(), isLight) : - ATHUtil.resolveColor(getContext(), R.attr.iconColor); - ToolbarContentTintHelper.colorizeToolbar(toolbar, iconColor, getActivity()); - if (PreferenceUtil.getInstance(getContext()).getAdaptiveColor()) { - colorize(color); + ToolbarContentTintHelper.colorizeToolbar(toolbar, ATHUtil.resolveColor(getContext(), + R.attr.iconColor), getActivity()); } - } + private void colorize(int i) { + if (valueAnimator != null) { + valueAnimator.cancel(); + } - @Override - public void onFavoriteToggled() { - toggleFavorite(MusicPlayerRemote.getCurrentSong()); - } - - - @Override - protected void toggleFavorite(Song song) { - super.toggleFavorite(song); - if (song.id == MusicPlayerRemote.getCurrentSong().id) { - updateIsFavorite(); + valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), android.R.color.transparent, i); + valueAnimator.addUpdateListener(animation -> { + GradientDrawable drawable = new DrawableGradient(GradientDrawable.Orientation.TOP_BOTTOM, + new int[]{(int) animation.getAnimatedValue(), android.R.color.transparent}, 0); + if (colorBackground != null) { + colorBackground.setBackground(drawable); + } + }); + valueAnimator.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME).start(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_flat_player, container, false); + unbinder = ButterKnife.bind(this, view); + return view; + } + + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + toggleStatusBar(statusBar); + + setUpPlayerToolbar(); + setUpSubFragments(); + + } + + @Override + public int getPaletteColor() { + return lastColor; + } + + @Override + public void onShow() { + flatPlaybackControlsFragment.show(); + } + + @Override + public void onHide() { + flatPlaybackControlsFragment.hide(); + onBackPressed(); + } + + @Override + public boolean onBackPressed() { + return false; + } + + @Override + public Toolbar getToolbar() { + return toolbar; + } + + @Override + public int toolbarIconColor() { + boolean isLight = ColorUtil.isColorLight(lastColor); + return PreferenceUtil.getInstance(getContext()).getAdaptiveColor() ? + MaterialValueHelper.getPrimaryTextColor(getContext(), isLight) : + ATHUtil.resolveColor(getContext(), R.attr.iconColor); + } + + @Override + public void onColorChanged(int color) { + lastColor = color; + flatPlaybackControlsFragment.setDark(color); + getCallbacks().onPaletteColorChanged(); + + boolean isLight = ColorUtil.isColorLight(color); + + //TransitionManager.beginDelayedTransition(mToolbar); + int iconColor = PreferenceUtil.getInstance(getContext()).getAdaptiveColor() ? + MaterialValueHelper.getPrimaryTextColor(getContext(), isLight) : + ATHUtil.resolveColor(getContext(), R.attr.iconColor); + ToolbarContentTintHelper.colorizeToolbar(toolbar, iconColor, getActivity()); + if (PreferenceUtil.getInstance(getContext()).getAdaptiveColor()) { + colorize(color); + } } - } - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } + @Override + public void onFavoriteToggled() { + toggleFavorite(MusicPlayerRemote.getCurrentSong()); + } + + + @Override + protected void toggleFavorite(Song song) { + super.toggleFavorite(song); + if (song.id == MusicPlayerRemote.getCurrentSong().id) { + updateIsFavorite(); + } + } + + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlaybackControlsFragment.java index 3bb3cff3e..5af0059cf 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlaybackControlsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/hmm/HmmPlaybackControlsFragment.java @@ -9,13 +9,11 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; - import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; +import code.name.monkey.appthemehelper.util.ColorUtil; +import code.name.monkey.appthemehelper.util.MaterialValueHelper; import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.service.MusicService; diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialControlsFragment.java index a2425edb7..e48825641 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialControlsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/material/MaterialControlsFragment.java @@ -79,7 +79,7 @@ public class MaterialControlsFragment extends AbsPlayerControlsFragment { } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); unbinder = ButterKnife.bind(this, view); setUpMusicControllers(); @@ -155,9 +155,14 @@ public class MaterialControlsFragment extends AbsPlayerControlsFragment { lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(getActivity(), false); } - updatePlayPauseColor(); updateRepeatState(); updateShuffleState(); + + if (PreferenceUtil.getInstance(getContext()).getAdaptiveColor()) { + lastPlaybackControlsColor = dark; + } + + updatePlayPauseColor(); updatePrevNextColor(); } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlaybackControlsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlaybackControlsFragment.java index 6ca30bb66..8571f64d4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlaybackControlsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/player/simple/SimplePlaybackControlsFragment.java @@ -11,6 +11,7 @@ import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.ImageButton; import android.widget.TextView; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; @@ -36,287 +37,287 @@ import code.name.monkey.retromusic.views.PlayPauseDrawable; public class SimplePlaybackControlsFragment extends AbsPlayerControlsFragment { - @BindView(R.id.player_play_pause_button) - ImageButton playPauseFab; - @BindView(R.id.player_prev_button) - ImageButton prevButton; - @BindView(R.id.player_next_button) - ImageButton nextButton; - @BindView(R.id.player_repeat_button) - ImageButton repeatButton; - @BindView(R.id.player_shuffle_button) - ImageButton shuffleButton; - @BindView(R.id.player_song_current_progress) - TextView songCurrentProgress; - @BindView(R.id.volume_fragment_container) - View volumeContainer; - @BindView(R.id.title) - TextView title; - @BindView(R.id.text) - TextView text; - private Unbinder unbinder; - private PlayPauseDrawable playerFabPlayPauseDrawable; - private int lastPlaybackControlsColor; - private int lastDisabledPlaybackControlsColor; - private MusicProgressViewUpdateHelper progressViewUpdateHelper; + @BindView(R.id.player_play_pause_button) + ImageButton playPauseFab; + @BindView(R.id.player_prev_button) + ImageButton prevButton; + @BindView(R.id.player_next_button) + ImageButton nextButton; + @BindView(R.id.player_repeat_button) + ImageButton repeatButton; + @BindView(R.id.player_shuffle_button) + ImageButton shuffleButton; + @BindView(R.id.player_song_current_progress) + TextView songCurrentProgress; + @BindView(R.id.volume_fragment_container) + View volumeContainer; + @BindView(R.id.title) + TextView title; + @BindView(R.id.text) + TextView text; + private Unbinder unbinder; + private PlayPauseDrawable playerFabPlayPauseDrawable; + private int lastPlaybackControlsColor; + private int lastDisabledPlaybackControlsColor; + private MusicProgressViewUpdateHelper progressViewUpdateHelper; - @Override - public void onPlayStateChanged() { - updatePlayPauseDrawableState(true); - } - - @Override - public void onRepeatModeChanged() { - updateRepeatState(); - } - - @Override - public void onShuffleModeChanged() { - updateShuffleState(); - } - - @Override - public void onServiceConnected() { - updatePlayPauseDrawableState(false); - updateRepeatState(); - updateShuffleState(); - updateSong(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unbinder.unbind(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_simple_controls_fragment, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } - - @Override - public void onResume() { - super.onResume(); - progressViewUpdateHelper.start(); - } - - @Override - public void onPause() { - super.onPause(); - progressViewUpdateHelper.stop(); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpMusicControllers(); - volumeContainer.setVisibility( - PreferenceUtil.getInstance(getContext()).getVolumeToggle() ? View.VISIBLE : View.GONE); - } - - private void setUpMusicControllers() { - setUpPlayPauseFab(); - setUpPrevNext(); - setUpRepeatButton(); - setUpShuffleButton(); - setUpProgressSlider(); - } - - private void setUpPrevNext() { - updatePrevNextColor(); - nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); - prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); - } - - private void updatePrevNextColor() { - nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - } - - private void setUpShuffleButton() { - shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); - } - - @Override - protected void updateShuffleState() { - switch (MusicPlayerRemote.getShuffleMode()) { - case MusicService.SHUFFLE_MODE_SHUFFLE: - shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - default: - shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void setUpRepeatButton() { - repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); - } - - @Override - protected void updateRepeatState() { - switch (MusicPlayerRemote.getRepeatMode()) { - case MusicService.REPEAT_MODE_NONE: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - case MusicService.REPEAT_MODE_THIS: - repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); - repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); - break; - } - } - - private void updateSong() { - Song song = MusicPlayerRemote.getCurrentSong(); - title.setText(song.title); - text.setText(song.artistName); - } - - @Override - public void onPlayingMetaChanged() { - super.onPlayingMetaChanged(); - updateSong(); - - } - - @Override - protected void setUpProgressSlider() { - - } - - @Override - protected void show() { - playPauseFab.animate() - .scaleX(1f) - .scaleY(1f) - .rotation(360f) - .setInterpolator(new DecelerateInterpolator()) - .start(); - } - - @Override - protected void hide() { - if (playPauseFab != null) { - playPauseFab.setScaleX(0f); - playPauseFab.setScaleY(0f); - playPauseFab.setRotation(0f); - } - } - - - public void showBouceAnimation() { - playPauseFab.clearAnimation(); - - playPauseFab.setScaleX(0.9f); - playPauseFab.setScaleY(0.9f); - playPauseFab.setVisibility(View.VISIBLE); - playPauseFab.setPivotX(playPauseFab.getWidth() / 2); - playPauseFab.setPivotY(playPauseFab.getHeight() / 2); - - playPauseFab.animate() - .setDuration(200) - .setInterpolator(new DecelerateInterpolator()) - .scaleX(1.1f) - .scaleY(1.1f) - .withEndAction(() -> playPauseFab.animate() - .setDuration(200) - .setInterpolator(new AccelerateInterpolator()) - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .start()) - .start(); - } - - @OnClick(R.id.player_play_pause_button) - void showAnimation() { - if (MusicPlayerRemote.isPlaying()) { - MusicPlayerRemote.pauseSong(); - } else { - MusicPlayerRemote.resumePlaying(); - } - showBouceAnimation(); - } - - @Override - public void onUpdateProgressViews(int progress, int total) { - songCurrentProgress - .setText(String.format("%s / %s", MusicUtil.getReadableDurationString(progress), - MusicUtil.getReadableDurationString(total))); - } - - @Override - public void setDark(int dark) { - int color = ATHUtil.resolveColor(getActivity(), android.R.attr.colorBackground); - if (ColorUtil.isColorLight(color)) { - lastPlaybackControlsColor = MaterialValueHelper - .getSecondaryTextColor(getActivity(), true); - lastDisabledPlaybackControlsColor = MaterialValueHelper - .getSecondaryDisabledTextColor(getActivity(), true); - } else { - lastPlaybackControlsColor = MaterialValueHelper - .getPrimaryTextColor(getActivity(), false); - lastDisabledPlaybackControlsColor = MaterialValueHelper - .getPrimaryDisabledTextColor(getActivity(), false); + @Override + public void onPlayStateChanged() { + updatePlayPauseDrawableState(true); } - if (PreferenceUtil.getInstance(getContext()).getAdaptiveColor()) { - TintHelper.setTintAuto(playPauseFab, - MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(dark)), - false); - TintHelper.setTintAuto(playPauseFab, dark, true); - text.setTextColor(dark); - } else { - int accentColor = ThemeStore.accentColor(getContext()); - text.setTextColor(accentColor); - TintHelper.setTintAuto(playPauseFab, - MaterialValueHelper - .getPrimaryTextColor(getContext(), ColorUtil.isColorLight(accentColor)), - false); - TintHelper.setTintAuto(playPauseFab, accentColor, true); + @Override + public void onRepeatModeChanged() { + updateRepeatState(); } - updateRepeatState(); - updateShuffleState(); - updatePrevNextColor(); - } + @Override + public void onShuffleModeChanged() { + updateShuffleState(); + } - private void setUpPlayPauseFab() { - playerFabPlayPauseDrawable = new PlayPauseDrawable(getActivity()); + @Override + public void onServiceConnected() { + updatePlayPauseDrawableState(false); + updateRepeatState(); + updateShuffleState(); + updateSong(); + } - playPauseFab.setImageDrawable( - playerFabPlayPauseDrawable); // Note: set the drawable AFTER TintHelper.setTintAuto() was called - //playPauseFab.setColorFilter(MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(fabColor)), PorterDuff.Mode.SRC_IN); - //playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); - playPauseFab.post(() -> { - if (playPauseFab != null) { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_simple_controls_fragment, container, false); + unbinder = ButterKnife.bind(this, view); + return view; + } + + @Override + public void onResume() { + super.onResume(); + progressViewUpdateHelper.start(); + } + + @Override + public void onPause() { + super.onPause(); + progressViewUpdateHelper.stop(); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + setUpMusicControllers(); + volumeContainer.setVisibility( + PreferenceUtil.getInstance(getContext()).getVolumeToggle() ? View.VISIBLE : View.GONE); + } + + private void setUpMusicControllers() { + setUpPlayPauseFab(); + setUpPrevNext(); + setUpRepeatButton(); + setUpShuffleButton(); + setUpProgressSlider(); + } + + private void setUpPrevNext() { + updatePrevNextColor(); + nextButton.setOnClickListener(v -> MusicPlayerRemote.playNextSong()); + prevButton.setOnClickListener(v -> MusicPlayerRemote.back()); + } + + private void updatePrevNextColor() { + nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); + prevButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); + } + + private void setUpShuffleButton() { + shuffleButton.setOnClickListener(v -> MusicPlayerRemote.toggleShuffleMode()); + } + + @Override + protected void updateShuffleState() { + switch (MusicPlayerRemote.getShuffleMode()) { + case MusicService.SHUFFLE_MODE_SHUFFLE: + shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); + break; + default: + shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); + break; + } + } + + private void setUpRepeatButton() { + repeatButton.setOnClickListener(v -> MusicPlayerRemote.cycleRepeatMode()); + } + + @Override + protected void updateRepeatState() { + switch (MusicPlayerRemote.getRepeatMode()) { + case MusicService.REPEAT_MODE_NONE: + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); + repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN); + break; + case MusicService.REPEAT_MODE_ALL: + repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp); + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); + break; + case MusicService.REPEAT_MODE_THIS: + repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp); + repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN); + break; + } + } + + private void updateSong() { + Song song = MusicPlayerRemote.getCurrentSong(); + title.setText(song.title); + text.setText(song.artistName); + } + + @Override + public void onPlayingMetaChanged() { + super.onPlayingMetaChanged(); + updateSong(); + + } + + @Override + protected void setUpProgressSlider() { + + } + + @Override + protected void show() { + playPauseFab.animate() + .scaleX(1f) + .scaleY(1f) + .rotation(360f) + .setInterpolator(new DecelerateInterpolator()) + .start(); + } + + @Override + protected void hide() { + if (playPauseFab != null) { + playPauseFab.setScaleX(0f); + playPauseFab.setScaleY(0f); + playPauseFab.setRotation(0f); + } + } + + + public void showBouceAnimation() { + playPauseFab.clearAnimation(); + + playPauseFab.setScaleX(0.9f); + playPauseFab.setScaleY(0.9f); + playPauseFab.setVisibility(View.VISIBLE); playPauseFab.setPivotX(playPauseFab.getWidth() / 2); playPauseFab.setPivotY(playPauseFab.getHeight() / 2); - } - }); - } - protected void updatePlayPauseDrawableState(boolean animate) { - if (MusicPlayerRemote.isPlaying()) { - playerFabPlayPauseDrawable.setPause(animate); - } else { - playerFabPlayPauseDrawable.setPlay(animate); + playPauseFab.animate() + .setDuration(200) + .setInterpolator(new DecelerateInterpolator()) + .scaleX(1.1f) + .scaleY(1.1f) + .withEndAction(() -> playPauseFab.animate() + .setDuration(200) + .setInterpolator(new AccelerateInterpolator()) + .scaleX(1f) + .scaleY(1f) + .alpha(1f) + .start()) + .start(); + } + + @OnClick(R.id.player_play_pause_button) + void showAnimation() { + if (MusicPlayerRemote.isPlaying()) { + MusicPlayerRemote.pauseSong(); + } else { + MusicPlayerRemote.resumePlaying(); + } + showBouceAnimation(); + } + + @Override + public void onUpdateProgressViews(int progress, int total) { + songCurrentProgress + .setText(String.format("%s / %s", MusicUtil.getReadableDurationString(progress), + MusicUtil.getReadableDurationString(total))); + } + + @Override + public void setDark(int dark) { + int color = ATHUtil.resolveColor(getActivity(), android.R.attr.colorBackground); + if (ColorUtil.isColorLight(color)) { + lastPlaybackControlsColor = MaterialValueHelper + .getSecondaryTextColor(getActivity(), true); + lastDisabledPlaybackControlsColor = MaterialValueHelper + .getSecondaryDisabledTextColor(getActivity(), true); + } else { + lastPlaybackControlsColor = MaterialValueHelper + .getPrimaryTextColor(getActivity(), false); + lastDisabledPlaybackControlsColor = MaterialValueHelper + .getPrimaryDisabledTextColor(getActivity(), false); + } + + if (PreferenceUtil.getInstance(getContext()).getAdaptiveColor()) { + TintHelper.setTintAuto(playPauseFab, + MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(dark)), + false); + TintHelper.setTintAuto(playPauseFab, dark, true); + text.setTextColor(dark); + } else { + int accentColor = ThemeStore.accentColor(getContext()); + text.setTextColor(accentColor); + TintHelper.setTintAuto(playPauseFab, + MaterialValueHelper + .getPrimaryTextColor(getContext(), ColorUtil.isColorLight(accentColor)), + false); + TintHelper.setTintAuto(playPauseFab, accentColor, true); + } + + updateRepeatState(); + updateShuffleState(); + updatePrevNextColor(); + } + + private void setUpPlayPauseFab() { + playerFabPlayPauseDrawable = new PlayPauseDrawable(getActivity()); + + playPauseFab.setImageDrawable( + playerFabPlayPauseDrawable); // Note: set the drawable AFTER TintHelper.setTintAuto() was called + //playPauseFab.setColorFilter(MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(fabColor)), PorterDuff.Mode.SRC_IN); + //playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler()); + playPauseFab.post(() -> { + if (playPauseFab != null) { + playPauseFab.setPivotX(playPauseFab.getWidth() / 2); + playPauseFab.setPivotY(playPauseFab.getHeight() / 2); + } + }); + } + + protected void updatePlayPauseDrawableState(boolean animate) { + if (MusicPlayerRemote.isPlaying()) { + playerFabPlayPauseDrawable.setPause(animate); + } else { + playerFabPlayPauseDrawable.setPlay(animate); + } } - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/AbsSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/AbsSettingsFragment.java index d004c321a..6f8814e82 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/AbsSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/AbsSettingsFragment.java @@ -9,7 +9,6 @@ import android.support.v4.app.DialogFragment; import android.support.v7.preference.ListPreference; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceManager; -import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.Toast; @@ -19,8 +18,6 @@ import code.name.monkey.retromusic.preferences.BlacklistPreference; import code.name.monkey.retromusic.preferences.BlacklistPreferenceDialog; import code.name.monkey.retromusic.preferences.NowPlayingScreenPreference; import code.name.monkey.retromusic.preferences.NowPlayingScreenPreferenceDialog; -import code.name.monkey.retromusic.ui.activities.SettingsActivity; -import code.name.monkey.retromusic.util.DensityUtil; import code.name.monkey.retromusic.util.NavigationUtil; /** @@ -56,22 +53,11 @@ public abstract class AbsSettingsFragment extends ATEPreferenceFragmentCompat { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - setDividerHeight(0); setDivider(new ColorDrawable(Color.TRANSPARENT)); - - //noinspection ConstantConditions - getListView().setPadding(DensityUtil.dip2px(getContext(), 0), 0, 0, 0); - getListView().addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - super.onScrolled(recyclerView, dx, dy); - if (getActivity() != null) { - ((SettingsActivity) getActivity()).addAppbarLayoutElevation(recyclerView.canScrollVertically(RecyclerView.NO_POSITION) ? 8f : 0f); - } - } - }); getListView().setBackgroundColor(ThemeStore.primaryColor(getContext())); getListView().setOverScrollMode(View.OVER_SCROLL_NEVER); + getListView().setPadding(0, 0, 0, 0); + getListView().setPaddingRelative(0, 0, 0, 0); invalidateSettings(); } diff --git a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java index eab599dec..647222788 100644 --- a/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java +++ b/app/src/main/java/code/name/monkey/retromusic/ui/fragments/settings/NowPlayingSettingsFragment.java @@ -5,6 +5,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.preference.TwoStatePreference; import android.view.View; + import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; import code.name.monkey.retromusic.util.PreferenceUtil; @@ -14,79 +15,79 @@ import code.name.monkey.retromusic.util.PreferenceUtil; */ public class NowPlayingSettingsFragment extends AbsSettingsFragment implements - SharedPreferences.OnSharedPreferenceChangeListener { + SharedPreferences.OnSharedPreferenceChangeListener { - @SuppressWarnings("ConstantConditions") - @Override - public void invalidateSettings() { - updateNowPlayingScreenSummary(); - - final TwoStatePreference cornerWindow = (TwoStatePreference) findPreference("corner_window"); - cornerWindow.setOnPreferenceChangeListener((preference, newValue) -> { - if ((Boolean) newValue && !RetroApplication.isProVersion()) { - showProToastAndNavigate(getActivity().getString(R.string.pref_title_round_corners)); - return false; - } - getActivity().recreate(); - return true; - }); - final TwoStatePreference carouselEffect = (TwoStatePreference) findPreference( - "carousel_effect"); - carouselEffect.setOnPreferenceChangeListener((preference, newValue) -> { - if ((Boolean) newValue && !RetroApplication.isProVersion()) { - showProToastAndNavigate( - getActivity().getString(R.string.pref_title_toggle_carousel_effect)); - return false; - } - return true; - }); - - final TwoStatePreference toggleFullScreen = (TwoStatePreference) findPreference( - "toggle_full_screen"); - toggleFullScreen.setOnPreferenceChangeListener((preference, newValue) -> { - getActivity().recreate(); - return true; - }); - } - - @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - addPreferencesFromResource(R.xml.pref_now_playing_screen); - addPreferencesFromResource(R.xml.pref_ui); - addPreferencesFromResource(R.xml.pref_window); - addPreferencesFromResource(R.xml.pref_lockscreen); - } - - private void updateNowPlayingScreenSummary() { - //noinspection ConstantConditions - findPreference("now_playing_screen_id") - .setSummary(PreferenceUtil.getInstance(getActivity()).getNowPlayingScreen().titleRes); - } - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - //noinspection ConstantConditions - PreferenceUtil.getInstance(getContext()).registerOnSharedPreferenceChangedListener(this); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - //noinspection ConstantConditions - PreferenceUtil.getInstance(getContext()).unregisterOnSharedPreferenceChangedListener(this); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - switch (key) { - case PreferenceUtil.NOW_PLAYING_SCREEN_ID: + @SuppressWarnings("ConstantConditions") + @Override + public void invalidateSettings() { updateNowPlayingScreenSummary(); - break; - case PreferenceUtil.CIRCULAR_ALBUM_ART: - case PreferenceUtil.CAROUSEL_EFFECT: - invalidateSettings(); - break; + + final TwoStatePreference cornerWindow = (TwoStatePreference) findPreference("corner_window"); + cornerWindow.setOnPreferenceChangeListener((preference, newValue) -> { + if ((Boolean) newValue && !RetroApplication.isProVersion()) { + showProToastAndNavigate(getActivity().getString(R.string.pref_title_round_corners)); + return false; + } + getActivity().recreate(); + return true; + }); + final TwoStatePreference carouselEffect = (TwoStatePreference) findPreference( + "carousel_effect"); + carouselEffect.setOnPreferenceChangeListener((preference, newValue) -> { + if ((Boolean) newValue && !RetroApplication.isProVersion()) { + showProToastAndNavigate( + getActivity().getString(R.string.pref_title_toggle_carousel_effect)); + return false; + } + return true; + }); + + final TwoStatePreference toggleFullScreen = (TwoStatePreference) findPreference( + "toggle_full_screen"); + toggleFullScreen.setOnPreferenceChangeListener((preference, newValue) -> { + getActivity().recreate(); + return true; + }); + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.pref_now_playing_screen); + addPreferencesFromResource(R.xml.pref_ui); + addPreferencesFromResource(R.xml.pref_window); + addPreferencesFromResource(R.xml.pref_lockscreen); + } + + private void updateNowPlayingScreenSummary() { + //noinspection ConstantConditions + findPreference("now_playing_screen_id") + .setSummary(PreferenceUtil.getInstance(getActivity()).getNowPlayingScreen().titleRes); + } + + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + //noinspection ConstantConditions + PreferenceUtil.getInstance(getContext()).registerOnSharedPreferenceChangedListener(this); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + //noinspection ConstantConditions + PreferenceUtil.getInstance(getContext()).unregisterOnSharedPreferenceChangedListener(this); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + switch (key) { + case PreferenceUtil.NOW_PLAYING_SCREEN_ID: + updateNowPlayingScreenSummary(); + break; + case PreferenceUtil.CIRCULAR_ALBUM_ART: + case PreferenceUtil.CAROUSEL_EFFECT: + invalidateSettings(); + break; + } } - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/AnimationUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/AnimationUtil.java index 27a8caa84..7a7297be5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/AnimationUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/AnimationUtil.java @@ -4,6 +4,7 @@ import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; + import code.name.monkey.retromusic.R; /** @@ -11,13 +12,13 @@ import code.name.monkey.retromusic.R; */ public class AnimationUtil { - public static void runLayoutAnimation(final RecyclerView recyclerView) { - final Context context = recyclerView.getContext(); - final LayoutAnimationController controller = - AnimationUtils.loadLayoutAnimation(context, R.anim.layout_animation_slide_from_bottom); + public static void runLayoutAnimation(final RecyclerView recyclerView) { + final Context context = recyclerView.getContext(); + final LayoutAnimationController controller = + AnimationUtils.loadLayoutAnimation(context, R.anim.layout_animation_slide_from_bottom); - recyclerView.setLayoutAnimation(controller); - recyclerView.getAdapter().notifyDataSetChanged(); - recyclerView.scheduleLayoutAnimation(); - } + recyclerView.setLayoutAnimation(controller); + recyclerView.getAdapter().notifyDataSetChanged(); + recyclerView.scheduleLayoutAnimation(); + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/DensityUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/DensityUtil.java index 78aedd215..8ca9d6315 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/DensityUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/DensityUtil.java @@ -39,7 +39,7 @@ public class DensityUtil { * Converts sp to px * * @param context Context - * @param sp the value in sp + * @param sp the value in sp * @return int */ public static int dip2sp(Context context, float sp) { diff --git a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java index 4ac97d3cd..9dea5b9a8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java @@ -7,10 +7,7 @@ import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.webkit.MimeTypeMap; -import code.name.monkey.retromusic.loaders.SongLoader; -import code.name.monkey.retromusic.loaders.SortedCursor; -import code.name.monkey.retromusic.model.Song; -import io.reactivex.Observable; + import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; @@ -25,227 +22,232 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import code.name.monkey.retromusic.loaders.SongLoader; +import code.name.monkey.retromusic.loaders.SortedCursor; +import code.name.monkey.retromusic.model.Song; +import io.reactivex.Observable; + public final class FileUtil { - private FileUtil() { - } - - public static byte[] readBytes(InputStream stream) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[4096]; - int count; - while ((count = stream.read(buffer)) != -1) { - baos.write(buffer, 0, count); - } - stream.close(); - return baos.toByteArray(); - } - - @NonNull - public static Observable> matchFilesWithMediaStore(@NonNull Context context, - @Nullable List files) { - return SongLoader.getSongs(makeSongCursor(context, files)); - } - - public static String safeGetCanonicalPath(File file) { - try { - return file.getCanonicalPath(); - } catch (IOException e) { - e.printStackTrace(); - return file.getAbsolutePath(); - } - } - - @Nullable - public static SortedCursor makeSongCursor(@NonNull final Context context, - @Nullable final List files) { - String selection = null; - String[] paths = null; - - if (files != null) { - paths = toPathArray(files); - - if (files.size() > 0 - && files.size() < 999) { // 999 is the max amount Androids SQL implementation can handle. - selection = - MediaStore.Audio.AudioColumns.DATA + " IN (" + makePlaceholders(files.size()) + ")"; - } + private FileUtil() { } - Cursor songCursor = SongLoader - .makeSongCursor(context, selection, selection == null ? null : paths); - - return songCursor == null ? null - : new SortedCursor(songCursor, paths, MediaStore.Audio.AudioColumns.DATA); - } - - private static String makePlaceholders(int len) { - StringBuilder sb = new StringBuilder(len * 2 - 1); - sb.append("?"); - for (int i = 1; i < len; i++) { - sb.append(",?"); + public static byte[] readBytes(InputStream stream) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int count; + while ((count = stream.read(buffer)) != -1) { + baos.write(buffer, 0, count); + } + stream.close(); + return baos.toByteArray(); } - return sb.toString(); - } - @Nullable - private static String[] toPathArray(@Nullable List files) { - if (files != null) { - String[] paths = new String[files.size()]; - for (int i = 0; i < files.size(); i++) { + @NonNull + public static Observable> matchFilesWithMediaStore(@NonNull Context context, + @Nullable List files) { + return SongLoader.getSongs(makeSongCursor(context, files)); + } + + public static String safeGetCanonicalPath(File file) { + try { + return file.getCanonicalPath(); + } catch (IOException e) { + e.printStackTrace(); + return file.getAbsolutePath(); + } + } + + @Nullable + public static SortedCursor makeSongCursor(@NonNull final Context context, + @Nullable final List files) { + String selection = null; + String[] paths = null; + + if (files != null) { + paths = toPathArray(files); + + if (files.size() > 0 + && files.size() < 999) { // 999 is the max amount Androids SQL implementation can handle. + selection = + MediaStore.Audio.AudioColumns.DATA + " IN (" + makePlaceholders(files.size()) + ")"; + } + } + + Cursor songCursor = SongLoader + .makeSongCursor(context, selection, selection == null ? null : paths); + + return songCursor == null ? null + : new SortedCursor(songCursor, paths, MediaStore.Audio.AudioColumns.DATA); + } + + private static String makePlaceholders(int len) { + StringBuilder sb = new StringBuilder(len * 2 - 1); + sb.append("?"); + for (int i = 1; i < len; i++) { + sb.append(",?"); + } + return sb.toString(); + } + + @Nullable + private static String[] toPathArray(@Nullable List files) { + if (files != null) { + String[] paths = new String[files.size()]; + for (int i = 0; i < files.size(); i++) { /*try { paths[i] = files.get(i).getCanonicalPath(); // canonical path is important here because we want to compare the path with the media store entry later } catch (IOException e) { e.printStackTrace(); paths[i] = files.get(i).getPath(); }*/ - paths[i] = safeGetCanonicalPath(files.get(i)); - } - return paths; - } - return null; - } - - @NonNull - public static List listFiles(@NonNull File directory, @Nullable FileFilter fileFilter) { - List fileList = new LinkedList<>(); - File[] found = directory.listFiles(fileFilter); - if (found != null) { - Collections.addAll(fileList, found); - } - return fileList; - } - - @NonNull - public static List listFilesDeep(@NonNull File directory, @Nullable FileFilter fileFilter) { - List files = new LinkedList<>(); - internalListFilesDeep(files, directory, fileFilter); - return files; - } - - @NonNull - public static List listFilesDeep(@NonNull Collection files, - @Nullable FileFilter fileFilter) { - List resFiles = new LinkedList<>(); - for (File file : files) { - if (file.isDirectory()) { - internalListFilesDeep(resFiles, file, fileFilter); - } else if (fileFilter == null || fileFilter.accept(file)) { - resFiles.add(file); - } - } - return resFiles; - } - - private static void internalListFilesDeep(@NonNull Collection files, - @NonNull File directory, @Nullable FileFilter fileFilter) { - File[] found = directory.listFiles(fileFilter); - - if (found != null) { - for (File file : found) { - if (file.isDirectory()) { - internalListFilesDeep(files, file, fileFilter); - } else { - files.add(file); + paths[i] = safeGetCanonicalPath(files.get(i)); + } + return paths; } - } + return null; } - } - public static boolean fileIsMimeType(File file, String mimeType, MimeTypeMap mimeTypeMap) { - if (mimeType == null || mimeType.equals("*/*")) { - return true; - } else { - // get the file mime type - String filename = file.toURI().toString(); - int dotPos = filename.lastIndexOf('.'); - if (dotPos == -1) { - return false; - } - String fileExtension = filename.substring(dotPos + 1).toLowerCase(); - String fileType = mimeTypeMap.getMimeTypeFromExtension(fileExtension); - if (fileType == null) { - return false; - } - // check the 'type/subtype' pattern - if (fileType.equals(mimeType)) { - return true; - } - // check the 'type/*' pattern - int mimeTypeDelimiter = mimeType.lastIndexOf('/'); - if (mimeTypeDelimiter == -1) { - return false; - } - String mimeTypeMainType = mimeType.substring(0, mimeTypeDelimiter); - String mimeTypeSubtype = mimeType.substring(mimeTypeDelimiter + 1); - if (!mimeTypeSubtype.equals("*")) { - return false; - } - int fileTypeDelimiter = fileType.lastIndexOf('/'); - if (fileTypeDelimiter == -1) { - return false; - } - String fileTypeMainType = fileType.substring(0, fileTypeDelimiter); - if (fileTypeMainType.equals(mimeTypeMainType)) { - return true; - } - return fileTypeMainType.equals(mimeTypeMainType); + @NonNull + public static List listFiles(@NonNull File directory, @Nullable FileFilter fileFilter) { + List fileList = new LinkedList<>(); + File[] found = directory.listFiles(fileFilter); + if (found != null) { + Collections.addAll(fileList, found); + } + return fileList; } - } - public static String stripExtension(String str) { - if (str == null) { - return null; + @NonNull + public static List listFilesDeep(@NonNull File directory, @Nullable FileFilter fileFilter) { + List files = new LinkedList<>(); + internalListFilesDeep(files, directory, fileFilter); + return files; } - int pos = str.lastIndexOf('.'); - if (pos == -1) { - return str; + + @NonNull + public static List listFilesDeep(@NonNull Collection files, + @Nullable FileFilter fileFilter) { + List resFiles = new LinkedList<>(); + for (File file : files) { + if (file.isDirectory()) { + internalListFilesDeep(resFiles, file, fileFilter); + } else if (fileFilter == null || fileFilter.accept(file)) { + resFiles.add(file); + } + } + return resFiles; } - return str.substring(0, pos); - } - public static String readFromStream(InputStream is) throws Exception { - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - StringBuilder sb = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - if (sb.length() > 0) { - sb.append("\n"); - } - sb.append(line); + private static void internalListFilesDeep(@NonNull Collection files, + @NonNull File directory, @Nullable FileFilter fileFilter) { + File[] found = directory.listFiles(fileFilter); + + if (found != null) { + for (File file : found) { + if (file.isDirectory()) { + internalListFilesDeep(files, file, fileFilter); + } else { + files.add(file); + } + } + } } - reader.close(); - return sb.toString(); - } - public static String read(File file) throws Exception { - FileInputStream fin = new FileInputStream(file); - String ret = readFromStream(fin); - fin.close(); - return ret; - } - - public static boolean isExternalMemoryAvailable() { - Boolean isSDPresent = Environment.getExternalStorageState() - .equals(android.os.Environment.MEDIA_MOUNTED); - Boolean isSDSupportedDevice = Environment.isExternalStorageRemovable(); - - if (isSDSupportedDevice && isSDPresent) { - // yes SD-card is present - return true; - } else { - return false; - // Sorry + public static boolean fileIsMimeType(File file, String mimeType, MimeTypeMap mimeTypeMap) { + if (mimeType == null || mimeType.equals("*/*")) { + return true; + } else { + // get the file mime type + String filename = file.toURI().toString(); + int dotPos = filename.lastIndexOf('.'); + if (dotPos == -1) { + return false; + } + String fileExtension = filename.substring(dotPos + 1).toLowerCase(); + String fileType = mimeTypeMap.getMimeTypeFromExtension(fileExtension); + if (fileType == null) { + return false; + } + // check the 'type/subtype' pattern + if (fileType.equals(mimeType)) { + return true; + } + // check the 'type/*' pattern + int mimeTypeDelimiter = mimeType.lastIndexOf('/'); + if (mimeTypeDelimiter == -1) { + return false; + } + String mimeTypeMainType = mimeType.substring(0, mimeTypeDelimiter); + String mimeTypeSubtype = mimeType.substring(mimeTypeDelimiter + 1); + if (!mimeTypeSubtype.equals("*")) { + return false; + } + int fileTypeDelimiter = fileType.lastIndexOf('/'); + if (fileTypeDelimiter == -1) { + return false; + } + String fileTypeMainType = fileType.substring(0, fileTypeDelimiter); + if (fileTypeMainType.equals(mimeTypeMainType)) { + return true; + } + return fileTypeMainType.equals(mimeTypeMainType); + } } - } - public static File safeGetCanonicalFile(File file) { - try { - return file.getCanonicalFile(); - } catch (IOException e) { - e.printStackTrace(); - return file.getAbsoluteFile(); + public static String stripExtension(String str) { + if (str == null) { + return null; + } + int pos = str.lastIndexOf('.'); + if (pos == -1) { + return str; + } + return str.substring(0, pos); + } + + public static String readFromStream(InputStream is) throws Exception { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + if (sb.length() > 0) { + sb.append("\n"); + } + sb.append(line); + } + reader.close(); + return sb.toString(); + } + + public static String read(File file) throws Exception { + FileInputStream fin = new FileInputStream(file); + String ret = readFromStream(fin); + fin.close(); + return ret; + } + + public static boolean isExternalMemoryAvailable() { + Boolean isSDPresent = Environment.getExternalStorageState() + .equals(android.os.Environment.MEDIA_MOUNTED); + Boolean isSDSupportedDevice = Environment.isExternalStorageRemovable(); + + if (isSDSupportedDevice && isSDPresent) { + // yes SD-card is present + return true; + } else { + return false; + // Sorry + } + } + + public static File safeGetCanonicalFile(File file) { + try { + return file.getCanonicalFile(); + } catch (IOException e) { + e.printStackTrace(); + return file.getAbsoluteFile(); + } } - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/ImageUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/ImageUtil.java index bb0a8f047..275331516 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/ImageUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/ImageUtil.java @@ -9,6 +9,7 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.media.ExifInterface; import android.support.annotation.NonNull; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -19,204 +20,204 @@ import java.io.IOException; */ public class ImageUtil { - private static final int TOLERANCE = 20; - // Alpha amount for which values below are considered transparent. - private static final int ALPHA_TOLERANCE = 50; - private static int[] mTempBuffer; + private static final int TOLERANCE = 20; + // Alpha amount for which values below are considered transparent. + private static final int ALPHA_TOLERANCE = 50; + private static int[] mTempBuffer; - private ImageUtil() { + private ImageUtil() { - } - - public static boolean isGrayscale(Bitmap bitmap) { - final int height = bitmap.getHeight(); - final int width = bitmap.getWidth(); - int size = height * width; - ensureBufferSize(size); - bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height); - for (int i = 0; i < size; i++) { - if (!isGrayscale(mTempBuffer[i])) { - return false; - } - } - return true; - } - - /** - * Makes sure that {@code mTempBuffer} has at least length {@code size}. - */ - private static void ensureBufferSize(int size) { - if (mTempBuffer == null || mTempBuffer.length < size) { - mTempBuffer = new int[size]; - } - } - - public static Bitmap setBitmapColor(Bitmap bitmap, int color) { - Bitmap result = Bitmap - .createBitmap(bitmap, 0, 0, bitmap.getWidth() - 1, bitmap.getHeight() - 1); - Paint paint = new Paint(); - paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)); - - Canvas canvas = new Canvas(result); - canvas.drawBitmap(result, 0, 0, paint); - - return result; - } - - public static boolean isGrayscale(int color) { - int alpha = 0xFF & (color >> 24); - if (alpha < ALPHA_TOLERANCE) { - return true; - } - int r = 0xFF & (color >> 16); - int g = 0xFF & (color >> 8); - int b = 0xFF & color; - return Math.abs(r - g) < TOLERANCE - && Math.abs(r - b) < TOLERANCE - && Math.abs(g - b) < TOLERANCE; - } // Amount (max is 255) that two channels can differ before the color is no longer "gray". - - public static Bitmap resizeBitmap(@NonNull Bitmap src, int maxForSmallerSize) { - int width = src.getWidth(); - int height = src.getHeight(); - - final int dstWidth; - final int dstHeight; - - if (width < height) { - if (maxForSmallerSize >= width) { - return src; - } - float ratio = (float) height / width; - dstWidth = maxForSmallerSize; - dstHeight = Math.round(maxForSmallerSize * ratio); - } else { - if (maxForSmallerSize >= height) { - return src; - } - float ratio = (float) width / height; - dstWidth = Math.round(maxForSmallerSize * ratio); - dstHeight = maxForSmallerSize; } - return Bitmap.createScaledBitmap(src, dstWidth, dstHeight, false); - } - - public static int calculateInSampleSize(int width, int height, int reqWidth) { - // setting reqWidth matching to desired 1:1 ratio and screen-size - if (width < height) { - reqWidth = (height / width) * reqWidth; - } else { - reqWidth = (width / height) * reqWidth; + public static boolean isGrayscale(Bitmap bitmap) { + final int height = bitmap.getHeight(); + final int width = bitmap.getWidth(); + int size = height * width; + ensureBufferSize(size); + bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height); + for (int i = 0; i < size; i++) { + if (!isGrayscale(mTempBuffer[i])) { + return false; + } + } + return true; } - int inSampleSize = 1; - - if (height > reqWidth || width > reqWidth) { - final int halfHeight = height / 2; - final int halfWidth = width / 2; - - // Calculate the largest inSampleSize value that is a power of 2 and keeps both - // height and width larger than the requested height and width. - while ((halfHeight / inSampleSize) > reqWidth - && (halfWidth / inSampleSize) > reqWidth) { - inSampleSize *= 2; - } + /** + * Makes sure that {@code mTempBuffer} has at least length {@code size}. + */ + private static void ensureBufferSize(int size) { + if (mTempBuffer == null || mTempBuffer.length < size) { + mTempBuffer = new int[size]; + } } - return inSampleSize; - } + public static Bitmap setBitmapColor(Bitmap bitmap, int color) { + Bitmap result = Bitmap + .createBitmap(bitmap, 0, 0, bitmap.getWidth() - 1, bitmap.getHeight() - 1); + Paint paint = new Paint(); + paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)); - static File compressImage(File imageFile, int reqWidth, int reqHeight, - Bitmap.CompressFormat compressFormat, int quality, String destinationPath) - throws IOException { - FileOutputStream fileOutputStream = null; - File file = new File(destinationPath).getParentFile(); - if (!file.exists()) { - file.mkdirs(); - } - try { - fileOutputStream = new FileOutputStream(destinationPath); - // write the compressed bitmap at the destination specified by destinationPath. - decodeSampledBitmapFromFile(imageFile, reqWidth, reqHeight) - .compress(compressFormat, quality, fileOutputStream); - } finally { - if (fileOutputStream != null) { - fileOutputStream.flush(); - fileOutputStream.close(); - } + Canvas canvas = new Canvas(result); + canvas.drawBitmap(result, 0, 0, paint); + + return result; } - return new File(destinationPath); - } + public static boolean isGrayscale(int color) { + int alpha = 0xFF & (color >> 24); + if (alpha < ALPHA_TOLERANCE) { + return true; + } + int r = 0xFF & (color >> 16); + int g = 0xFF & (color >> 8); + int b = 0xFF & color; + return Math.abs(r - g) < TOLERANCE + && Math.abs(r - b) < TOLERANCE + && Math.abs(g - b) < TOLERANCE; + } // Amount (max is 255) that two channels can differ before the color is no longer "gray". - static Bitmap decodeSampledBitmapFromFile(File imageFile, int reqWidth, int reqHeight) - throws IOException { - // First decode with inJustDecodeBounds=true to check dimensions - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options); + public static Bitmap resizeBitmap(@NonNull Bitmap src, int maxForSmallerSize) { + int width = src.getWidth(); + int height = src.getHeight(); - // Calculate inSampleSize - options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + final int dstWidth; + final int dstHeight; - // Decode bitmap with inSampleSize set - options.inJustDecodeBounds = false; + if (width < height) { + if (maxForSmallerSize >= width) { + return src; + } + float ratio = (float) height / width; + dstWidth = maxForSmallerSize; + dstHeight = Math.round(maxForSmallerSize * ratio); + } else { + if (maxForSmallerSize >= height) { + return src; + } + float ratio = (float) width / height; + dstWidth = Math.round(maxForSmallerSize * ratio); + dstHeight = maxForSmallerSize; + } - Bitmap scaledBitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options); - - //check the rotation of the image and display it properly - ExifInterface exif; - exif = new ExifInterface(imageFile.getAbsolutePath()); - int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); - Matrix matrix = new Matrix(); - if (orientation == 6) { - matrix.postRotate(90); - } else if (orientation == 3) { - matrix.postRotate(180); - } else if (orientation == 8) { - matrix.postRotate(270); - } - scaledBitmap = Bitmap - .createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, - true); - return scaledBitmap; - } - - private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, - int reqHeight) { - // Raw height and width of image - final int height = options.outHeight; - final int width = options.outWidth; - int inSampleSize = 1; - - if (height > reqHeight || width > reqWidth) { - - final int halfHeight = height / 2; - final int halfWidth = width / 2; - - // Calculate the largest inSampleSize value that is a power of 2 and keeps both - // height and width larger than the requested height and width. - while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { - inSampleSize *= 2; - } + return Bitmap.createScaledBitmap(src, dstWidth, dstHeight, false); } - return inSampleSize; - } + public static int calculateInSampleSize(int width, int height, int reqWidth) { + // setting reqWidth matching to desired 1:1 ratio and screen-size + if (width < height) { + reqWidth = (height / width) * reqWidth; + } else { + reqWidth = (width / height) * reqWidth; + } - public static Bitmap getResizedBitmap(Bitmap image, int maxSize) { - int width = image.getWidth(); - int height = image.getHeight(); + int inSampleSize = 1; - float bitmapRatio = (float) width / (float) height; - if (bitmapRatio > 1) { - width = maxSize; - height = (int) (width / bitmapRatio); - } else { - height = maxSize; - width = (int) (height * bitmapRatio); + if (height > reqWidth || width > reqWidth) { + final int halfHeight = height / 2; + final int halfWidth = width / 2; + + // Calculate the largest inSampleSize value that is a power of 2 and keeps both + // height and width larger than the requested height and width. + while ((halfHeight / inSampleSize) > reqWidth + && (halfWidth / inSampleSize) > reqWidth) { + inSampleSize *= 2; + } + } + + return inSampleSize; + } + + static File compressImage(File imageFile, int reqWidth, int reqHeight, + Bitmap.CompressFormat compressFormat, int quality, String destinationPath) + throws IOException { + FileOutputStream fileOutputStream = null; + File file = new File(destinationPath).getParentFile(); + if (!file.exists()) { + file.mkdirs(); + } + try { + fileOutputStream = new FileOutputStream(destinationPath); + // write the compressed bitmap at the destination specified by destinationPath. + decodeSampledBitmapFromFile(imageFile, reqWidth, reqHeight) + .compress(compressFormat, quality, fileOutputStream); + } finally { + if (fileOutputStream != null) { + fileOutputStream.flush(); + fileOutputStream.close(); + } + } + + return new File(destinationPath); + } + + static Bitmap decodeSampledBitmapFromFile(File imageFile, int reqWidth, int reqHeight) + throws IOException { + // First decode with inJustDecodeBounds=true to check dimensions + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options); + + // Calculate inSampleSize + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + + // Decode bitmap with inSampleSize set + options.inJustDecodeBounds = false; + + Bitmap scaledBitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options); + + //check the rotation of the image and display it properly + ExifInterface exif; + exif = new ExifInterface(imageFile.getAbsolutePath()); + int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); + Matrix matrix = new Matrix(); + if (orientation == 6) { + matrix.postRotate(90); + } else if (orientation == 3) { + matrix.postRotate(180); + } else if (orientation == 8) { + matrix.postRotate(270); + } + scaledBitmap = Bitmap + .createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, + true); + return scaledBitmap; + } + + private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, + int reqHeight) { + // Raw height and width of image + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + + if (height > reqHeight || width > reqWidth) { + + final int halfHeight = height / 2; + final int halfWidth = width / 2; + + // Calculate the largest inSampleSize value that is a power of 2 and keeps both + // height and width larger than the requested height and width. + while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2; + } + } + + return inSampleSize; + } + + public static Bitmap getResizedBitmap(Bitmap image, int maxSize) { + int width = image.getWidth(); + int height = image.getHeight(); + + float bitmapRatio = (float) width / (float) height; + if (bitmapRatio > 1) { + width = maxSize; + height = (int) (width / bitmapRatio); + } else { + height = maxSize; + width = (int) (height * bitmapRatio); + } + return Bitmap.createScaledBitmap(image, width, height, true); } - return Bitmap.createScaledBitmap(image, width, height, true); - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/LastFMUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/LastFMUtil.java index d7d6fe1af..36bd1e4b2 100755 --- a/app/src/main/java/code/name/monkey/retromusic/util/LastFMUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/LastFMUtil.java @@ -1,69 +1,70 @@ package code.name.monkey.retromusic.util; -import code.name.monkey.retromusic.rest.model.LastFmAlbum.Album.Image; -import code.name.monkey.retromusic.rest.model.LastFmArtist; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import code.name.monkey.retromusic.rest.model.LastFmAlbum.Album.Image; +import code.name.monkey.retromusic.rest.model.LastFmArtist; + public class LastFMUtil { - public static String getLargestAlbumImageUrl(List list) { - Map hashMap = new HashMap(); - for (Image image : list) { - Object obj = null; - String size = image.getSize(); - if (size == null) { - obj = ImageSize.UNKNOWN; - } else { - try { - obj = ImageSize.valueOf(size.toUpperCase(Locale.ENGLISH)); - } catch (IllegalArgumentException ignored) { + public static String getLargestAlbumImageUrl(List list) { + Map hashMap = new HashMap(); + for (Image image : list) { + Object obj = null; + String size = image.getSize(); + if (size == null) { + obj = ImageSize.UNKNOWN; + } else { + try { + obj = ImageSize.valueOf(size.toUpperCase(Locale.ENGLISH)); + } catch (IllegalArgumentException ignored) { + } + } + if (obj != null) { + hashMap.put(obj, image.getText()); + } } - } - if (obj != null) { - hashMap.put(obj, image.getText()); - } + return getLargestImageUrl(hashMap); } - return getLargestImageUrl(hashMap); - } - public static String getLargestArtistImageUrl(List list) { - Map hashMap = new HashMap(); - for (LastFmArtist.Artist.Image image : list) { - Object obj = null; - String size = image.getSize(); - if (size == null) { - obj = ImageSize.UNKNOWN; - } else { - try { - obj = ImageSize.valueOf(size.toUpperCase(Locale.ENGLISH)); - } catch (IllegalArgumentException ignored) { + public static String getLargestArtistImageUrl(List list) { + Map hashMap = new HashMap(); + for (LastFmArtist.Artist.Image image : list) { + Object obj = null; + String size = image.getSize(); + if (size == null) { + obj = ImageSize.UNKNOWN; + } else { + try { + obj = ImageSize.valueOf(size.toUpperCase(Locale.ENGLISH)); + } catch (IllegalArgumentException ignored) { + } + } + if (obj != null) { + hashMap.put(obj, image.getText()); + } } - } - if (obj != null) { - hashMap.put(obj, image.getText()); - } + return getLargestImageUrl(hashMap); } - return getLargestImageUrl(hashMap); - } - private static String getLargestImageUrl(Map map) { - return map.containsKey(ImageSize.MEGA) ? map.get(ImageSize.MEGA) - : map.containsKey(ImageSize.EXTRALARGE) ? map.get(ImageSize.EXTRALARGE) - : map.containsKey(ImageSize.LARGE) ? map.get(ImageSize.LARGE) + private static String getLargestImageUrl(Map map) { + return map.containsKey(ImageSize.MEGA) ? map.get(ImageSize.MEGA) + : map.containsKey(ImageSize.EXTRALARGE) ? map.get(ImageSize.EXTRALARGE) + : map.containsKey(ImageSize.LARGE) ? map.get(ImageSize.LARGE) : map.containsKey(ImageSize.MEDIUM) ? map.get(ImageSize.MEDIUM) - : map.containsKey(ImageSize.SMALL) ? map.get(ImageSize.SMALL) - : map.containsKey(ImageSize.UNKNOWN) ? map.get(ImageSize.UNKNOWN) : null; - } + : map.containsKey(ImageSize.SMALL) ? map.get(ImageSize.SMALL) + : map.containsKey(ImageSize.UNKNOWN) ? map.get(ImageSize.UNKNOWN) : null; + } - private enum ImageSize { - SMALL, - MEDIUM, - LARGE, - EXTRALARGE, - MEGA, - UNKNOWN - } + private enum ImageSize { + SMALL, + MEDIUM, + LARGE, + EXTRALARGE, + MEGA, + UNKNOWN + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java index b14d63a5c..a7945a0b0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.java @@ -18,6 +18,17 @@ import android.support.v4.content.FileProvider; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; + +import org.jaudiotagger.audio.AudioFileIO; +import org.jaudiotagger.tag.FieldKey; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.helper.MusicPlayerRemote; import code.name.monkey.retromusic.loaders.PlaylistLoader; @@ -27,384 +38,376 @@ import code.name.monkey.retromusic.model.Playlist; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics; import io.reactivex.Observable; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.regex.Pattern; -import org.jaudiotagger.audio.AudioFileIO; -import org.jaudiotagger.tag.FieldKey; public class MusicUtil { - public static final String TAG = MusicUtil.class.getSimpleName(); - private static Playlist playlist; + public static final String TAG = MusicUtil.class.getSimpleName(); + private static Playlist playlist; - public static Uri getMediaStoreAlbumCoverUri(int albumId) { - final Uri sArtworkUri = Uri - .parse("content://media/external/audio/albumart"); + public static Uri getMediaStoreAlbumCoverUri(int albumId) { + final Uri sArtworkUri = Uri + .parse("content://media/external/audio/albumart"); - return ContentUris.withAppendedId(sArtworkUri, albumId); - } - - public static Uri getSongFileUri(int songId) { - return ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songId); - } - - @NonNull - public static Intent createShareSongFileIntent(@NonNull final Song song, Context context) { - try { - - return new Intent() - .setAction(Intent.ACTION_SEND) - .putExtra(Intent.EXTRA_STREAM, - FileProvider.getUriForFile(context, - context.getApplicationContext().getPackageName(), - new File(song.data))) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .setType("audio/*"); - } catch (IllegalArgumentException e) { - // TODO the path is most likely not like /storage/emulated/0/... but something like /storage/28C7-75B0/... - e.printStackTrace(); - Toast.makeText(context, "Could not share this file, I'm aware of the issue.", - Toast.LENGTH_SHORT).show(); - return new Intent(); - } - } - - public static void setRingtone(@NonNull final Context context, final int id) { - final ContentResolver resolver = context.getContentResolver(); - final Uri uri = getSongFileUri(id); - try { - final ContentValues values = new ContentValues(2); - values.put(MediaStore.Audio.AudioColumns.IS_RINGTONE, "1"); - values.put(MediaStore.Audio.AudioColumns.IS_ALARM, "1"); - resolver.update(uri, values, null, null); - } catch (@NonNull final UnsupportedOperationException ignored) { - return; + return ContentUris.withAppendedId(sArtworkUri, albumId); } - try { - Cursor cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, - new String[]{MediaStore.MediaColumns.TITLE}, - BaseColumns._ID + "=?", - new String[]{String.valueOf(id)}, - null); - try { - if (cursor != null && cursor.getCount() == 1) { - cursor.moveToFirst(); - Settings.System.putString(resolver, Settings.System.RINGTONE, uri.toString()); - final String message = context - .getString(R.string.x_has_been_set_as_ringtone, cursor.getString(0)); - Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + public static Uri getSongFileUri(int songId) { + return ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songId); + } + + @NonNull + public static Intent createShareSongFileIntent(@NonNull final Song song, Context context) { + try { + + return new Intent() + .setAction(Intent.ACTION_SEND) + .putExtra(Intent.EXTRA_STREAM, + FileProvider.getUriForFile(context, + context.getApplicationContext().getPackageName(), + new File(song.data))) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + .setType("audio/*"); + } catch (IllegalArgumentException e) { + // TODO the path is most likely not like /storage/emulated/0/... but something like /storage/28C7-75B0/... + e.printStackTrace(); + Toast.makeText(context, "Could not share this file, I'm aware of the issue.", + Toast.LENGTH_SHORT).show(); + return new Intent(); } - } finally { - if (cursor != null) { - cursor.close(); - } - } - } catch (SecurityException ignored) { - } - } - - @NonNull - public static String getArtistInfoString(@NonNull final Context context, - @NonNull final Artist artist) { - int albumCount = artist.getAlbumCount(); - int songCount = artist.getSongCount(); - String albumString = albumCount == 1 ? context.getResources().getString(R.string.album) - : context.getResources().getString(R.string.albums); - String songString = songCount == 1 ? context.getResources().getString(R.string.song) - : context.getResources().getString(R.string.songs); - return albumCount + " " + albumString + " • " + songCount + " " + songString; - } - - @NonNull - public static String getArtistInfoStringSmall(@NonNull final Context context, - @NonNull final Artist artist) { - int songCount = artist.getSongCount(); - String songString = songCount == 1 ? context.getResources().getString(R.string.song) - : context.getResources().getString(R.string.songs); - return songCount + " " + songString; - } - - @NonNull - public static String getPlaylistInfoString(@NonNull final Context context, - @NonNull List songs) { - final int songCount = songs.size(); - final String songString = songCount == 1 ? context.getResources().getString(R.string.song) - : context.getResources().getString(R.string.songs); - - long duration = 0; - for (int i = 0; i < songs.size(); i++) { - duration += songs.get(i).duration; } - return songCount + " " + songString + " • " + MusicUtil.getReadableDurationString(duration); - } - - public static String getReadableDurationString(long songDurationMillis) { - long minutes = (songDurationMillis / 1000) / 60; - long seconds = (songDurationMillis / 1000) % 60; - if (minutes < 60) { - return String.format(Locale.getDefault(), "%01d:%02d", minutes, seconds); - } else { - long hours = minutes / 60; - minutes = minutes % 60; - return String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, seconds); - } - } - - //iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3. - //this method converts those values to normal tracknumbers - public static int getFixedTrackNumber(int trackNumberToFix) { - return trackNumberToFix % 1000; - } - - public static void insertAlbumArt(@NonNull Context context, int albumId, String path) { - ContentResolver contentResolver = context.getContentResolver(); - - Uri artworkUri = Uri.parse("content://media/external/audio/albumart"); - contentResolver.delete(ContentUris.withAppendedId(artworkUri, albumId), null, null); - - ContentValues values = new ContentValues(); - values.put("album_id", albumId); - values.put("_data", path); - - contentResolver.insert(artworkUri, values); - } - - @NonNull - public static File createAlbumArtFile() { - return new File(createAlbumArtDir(), String.valueOf(System.currentTimeMillis())); - } - - @NonNull - @SuppressWarnings("ResultOfMethodCallIgnored") - private static File createAlbumArtDir() { - File albumArtDir = new File(Environment.getExternalStorageDirectory(), "/albumthumbs/"); - if (!albumArtDir.exists()) { - albumArtDir.mkdirs(); - try { - new File(albumArtDir, ".nomedia").createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return albumArtDir; - } - - public static void deleteTracks(@NonNull final Activity activity, - @NonNull final List songs) { - final String[] projection = new String[]{ - BaseColumns._ID, MediaStore.MediaColumns.DATA - }; - final StringBuilder selection = new StringBuilder(); - selection.append(BaseColumns._ID + " IN ("); - for (int i = 0; i < songs.size(); i++) { - selection.append(songs.get(i).id); - if (i < songs.size() - 1) { - selection.append(","); - } - } - selection.append(")"); - - try { - final Cursor cursor = activity.getContentResolver().query( - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(), - null, null); - if (cursor != null) { - // Step 1: Remove selected tracks from the current playlist, as well - // as from the album art cache - cursor.moveToFirst(); - while (!cursor.isAfterLast()) { - final int id = cursor.getInt(0); - Song song = SongLoader.getSong(activity, id).blockingFirst(); - MusicPlayerRemote.removeFromQueue(song); - cursor.moveToNext(); + public static void setRingtone(@NonNull final Context context, final int id) { + final ContentResolver resolver = context.getContentResolver(); + final Uri uri = getSongFileUri(id); + try { + final ContentValues values = new ContentValues(2); + values.put(MediaStore.Audio.AudioColumns.IS_RINGTONE, "1"); + values.put(MediaStore.Audio.AudioColumns.IS_ALARM, "1"); + resolver.update(uri, values, null, null); + } catch (@NonNull final UnsupportedOperationException ignored) { + return; } - // Step 2: Remove selected tracks from the database - activity.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, - selection.toString(), null); - - // Step 3: Remove files from card - cursor.moveToFirst(); - while (!cursor.isAfterLast()) { - final String name = cursor.getString(1); - try { // File.delete can throw a security exception - final File f = new File(name); - if (!f.delete()) { - // I'm not sure if we'd ever get here (deletion would - // have to fail, but no exception thrown) - Log.e("MusicUtils", "Failed to delete file " + name); - } - cursor.moveToNext(); - } catch (@NonNull final SecurityException ex) { - cursor.moveToNext(); - } catch (NullPointerException e) { - Log.e("MusicUtils", "Failed to find file " + name); - } - } - cursor.close(); - } - activity.getContentResolver().notifyChange(Uri.parse("content://media"), null); - Toast.makeText(activity, activity.getString(R.string.deleted_x_songs, songs.size()), - Toast.LENGTH_SHORT).show(); - } catch (SecurityException ignored) { - } - } - - public static void deleteAlbumArt(@NonNull Context context, int albumId) { - ContentResolver contentResolver = context.getContentResolver(); - Uri localUri = Uri.parse("content://media/external/audio/albumart"); - contentResolver.delete(ContentUris.withAppendedId(localUri, albumId), null, null); - } - - - @Nullable - public static String getLyrics(Song song) { - String lyrics = null; - - File file = new File(song.data); - - try { - lyrics = AudioFileIO.read(file).getTagOrCreateDefault().getFirst(FieldKey.LYRICS); - } catch (Exception e) { - e.printStackTrace(); - } - - if (lyrics == null || lyrics.trim().isEmpty() || !AbsSynchronizedLyrics - .isSynchronized(lyrics)) { - File dir = file.getAbsoluteFile().getParentFile(); - - if (dir != null && dir.exists() && dir.isDirectory()) { - String format = ".*%s.*\\.(lrc|txt)"; - String filename = Pattern.quote(FileUtil.stripExtension(file.getName())); - String songtitle = Pattern.quote(song.title); - - final ArrayList patterns = new ArrayList<>(); - patterns.add(Pattern.compile(String.format(format, filename), - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); - patterns.add(Pattern.compile(String.format(format, songtitle), - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); - - File[] files = dir.listFiles(f -> { - for (Pattern pattern : patterns) { - if (pattern.matcher(f.getName()).matches()) { - return true; - } - } - return false; - }); - - if (files != null && files.length > 0) { - for (File f : files) { + try { + Cursor cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + new String[]{MediaStore.MediaColumns.TITLE}, + BaseColumns._ID + "=?", + new String[]{String.valueOf(id)}, + null); try { - String newLyrics = FileUtil.read(f); - if (newLyrics != null && !newLyrics.trim().isEmpty()) { - if (AbsSynchronizedLyrics.isSynchronized(newLyrics)) { - return newLyrics; + if (cursor != null && cursor.getCount() == 1) { + cursor.moveToFirst(); + Settings.System.putString(resolver, Settings.System.RINGTONE, uri.toString()); + final String message = context + .getString(R.string.x_has_been_set_as_ringtone, cursor.getString(0)); + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + } + } finally { + if (cursor != null) { + cursor.close(); } - lyrics = newLyrics; - } - } catch (Exception e) { - e.printStackTrace(); } - } + } catch (SecurityException ignored) { } - } } - return lyrics; - } - - public static void toggleFavorite(@NonNull final Context context, @NonNull final Song song) { - if (isFavorite(context, song)) { - PlaylistsUtil - .removeFromPlaylist(context, song, getFavoritesPlaylist(context).blockingFirst().id); - } else { - PlaylistsUtil - .addToPlaylist(context, song, getOrCreateFavoritesPlaylist(context).blockingFirst().id, - false); + @NonNull + public static String getArtistInfoString(@NonNull final Context context, + @NonNull final Artist artist) { + int albumCount = artist.getAlbumCount(); + int songCount = artist.getSongCount(); + String albumString = albumCount == 1 ? context.getResources().getString(R.string.album) + : context.getResources().getString(R.string.albums); + String songString = songCount == 1 ? context.getResources().getString(R.string.song) + : context.getResources().getString(R.string.songs); + return albumCount + " " + albumString + " • " + songCount + " " + songString; } - } - public static boolean isFavoritePlaylist(@NonNull final Context context, - @NonNull final Playlist playlist) { - return playlist.name != null && playlist.name.equals(context.getString(R.string.favorites)); - } + @NonNull + public static String getArtistInfoStringSmall(@NonNull final Context context, + @NonNull final Artist artist) { + int songCount = artist.getSongCount(); + String songString = songCount == 1 ? context.getResources().getString(R.string.song) + : context.getResources().getString(R.string.songs); + return songCount + " " + songString; + } - private static Observable getFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.getPlaylist(context, context.getString(R.string.favorites)); - } + @NonNull + public static String getPlaylistInfoString(@NonNull final Context context, + @NonNull List songs) { + final int songCount = songs.size(); + final String songString = songCount == 1 ? context.getResources().getString(R.string.song) + : context.getResources().getString(R.string.songs); - private static Observable getOrCreateFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.getPlaylist(context, - PlaylistsUtil.createPlaylist(context, context.getString(R.string.favorites))); - } + long duration = 0; + for (int i = 0; i < songs.size(); i++) { + duration += songs.get(i).duration; + } - public static boolean isFavorite(@NonNull final Context context, @NonNull final Song song) { + return songCount + " " + songString + " • " + MusicUtil.getReadableDurationString(duration); + } + + public static String getReadableDurationString(long songDurationMillis) { + long minutes = (songDurationMillis / 1000) / 60; + long seconds = (songDurationMillis / 1000) % 60; + if (minutes < 60) { + return String.format(Locale.getDefault(), "%01d:%02d", minutes, seconds); + } else { + long hours = minutes / 60; + minutes = minutes % 60; + return String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, seconds); + } + } + + //iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3. + //this method converts those values to normal tracknumbers + public static int getFixedTrackNumber(int trackNumberToFix) { + return trackNumberToFix % 1000; + } + + public static void insertAlbumArt(@NonNull Context context, int albumId, String path) { + ContentResolver contentResolver = context.getContentResolver(); + + Uri artworkUri = Uri.parse("content://media/external/audio/albumart"); + contentResolver.delete(ContentUris.withAppendedId(artworkUri, albumId), null, null); + + ContentValues values = new ContentValues(); + values.put("album_id", albumId); + values.put("_data", path); + + contentResolver.insert(artworkUri, values); + } + + @NonNull + public static File createAlbumArtFile() { + return new File(createAlbumArtDir(), String.valueOf(System.currentTimeMillis())); + } + + @NonNull + @SuppressWarnings("ResultOfMethodCallIgnored") + private static File createAlbumArtDir() { + File albumArtDir = new File(Environment.getExternalStorageDirectory(), "/albumthumbs/"); + if (!albumArtDir.exists()) { + albumArtDir.mkdirs(); + try { + new File(albumArtDir, ".nomedia").createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return albumArtDir; + } + + public static void deleteTracks(@NonNull final Activity activity, + @NonNull final List songs) { + final String[] projection = new String[]{ + BaseColumns._ID, MediaStore.MediaColumns.DATA + }; + final StringBuilder selection = new StringBuilder(); + selection.append(BaseColumns._ID + " IN ("); + for (int i = 0; i < songs.size(); i++) { + selection.append(songs.get(i).id); + if (i < songs.size() - 1) { + selection.append(","); + } + } + selection.append(")"); + + try { + final Cursor cursor = activity.getContentResolver().query( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(), + null, null); + if (cursor != null) { + // Step 1: Remove selected tracks from the current playlist, as well + // as from the album art cache + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + final int id = cursor.getInt(0); + Song song = SongLoader.getSong(activity, id).blockingFirst(); + MusicPlayerRemote.removeFromQueue(song); + cursor.moveToNext(); + } + + // Step 2: Remove selected tracks from the database + activity.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + selection.toString(), null); + + // Step 3: Remove files from card + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + final String name = cursor.getString(1); + try { // File.delete can throw a security exception + final File f = new File(name); + if (!f.delete()) { + // I'm not sure if we'd ever get here (deletion would + // have to fail, but no exception thrown) + Log.e("MusicUtils", "Failed to delete file " + name); + } + cursor.moveToNext(); + } catch (@NonNull final SecurityException ex) { + cursor.moveToNext(); + } catch (NullPointerException e) { + Log.e("MusicUtils", "Failed to find file " + name); + } + } + cursor.close(); + } + activity.getContentResolver().notifyChange(Uri.parse("content://media"), null); + Toast.makeText(activity, activity.getString(R.string.deleted_x_songs, songs.size()), + Toast.LENGTH_SHORT).show(); + } catch (SecurityException ignored) { + } + } + + public static void deleteAlbumArt(@NonNull Context context, int albumId) { + ContentResolver contentResolver = context.getContentResolver(); + Uri localUri = Uri.parse("content://media/external/audio/albumart"); + contentResolver.delete(ContentUris.withAppendedId(localUri, albumId), null, null); + } + + + @Nullable + public static String getLyrics(Song song) { + String lyrics = null; + + File file = new File(song.data); + + try { + lyrics = AudioFileIO.read(file).getTagOrCreateDefault().getFirst(FieldKey.LYRICS); + } catch (Exception e) { + e.printStackTrace(); + } + + if (lyrics == null || lyrics.trim().isEmpty() || !AbsSynchronizedLyrics + .isSynchronized(lyrics)) { + File dir = file.getAbsoluteFile().getParentFile(); + + if (dir != null && dir.exists() && dir.isDirectory()) { + String format = ".*%s.*\\.(lrc|txt)"; + String filename = Pattern.quote(FileUtil.stripExtension(file.getName())); + String songtitle = Pattern.quote(song.title); + + final ArrayList patterns = new ArrayList<>(); + patterns.add(Pattern.compile(String.format(format, filename), + Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); + patterns.add(Pattern.compile(String.format(format, songtitle), + Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); + + File[] files = dir.listFiles(f -> { + for (Pattern pattern : patterns) { + if (pattern.matcher(f.getName()).matches()) { + return true; + } + } + return false; + }); + + if (files != null && files.length > 0) { + for (File f : files) { + try { + String newLyrics = FileUtil.read(f); + if (newLyrics != null && !newLyrics.trim().isEmpty()) { + if (AbsSynchronizedLyrics.isSynchronized(newLyrics)) { + return newLyrics; + } + lyrics = newLyrics; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + + return lyrics; + } + + public static void toggleFavorite(@NonNull final Context context, @NonNull final Song song) { + if (isFavorite(context, song)) { + PlaylistsUtil + .removeFromPlaylist(context, song, getFavoritesPlaylist(context).blockingFirst().id); + } else { + PlaylistsUtil + .addToPlaylist(context, song, getOrCreateFavoritesPlaylist(context).blockingFirst().id, + false); + } + } + + public static boolean isFavoritePlaylist(@NonNull final Context context, + @NonNull final Playlist playlist) { + return playlist.name != null && playlist.name.equals(context.getString(R.string.favorites)); + } + + private static Observable getFavoritesPlaylist(@NonNull final Context context) { + return PlaylistLoader.getPlaylist(context, context.getString(R.string.favorites)); + } + + private static Observable getOrCreateFavoritesPlaylist(@NonNull final Context context) { + return PlaylistLoader.getPlaylist(context, + PlaylistsUtil.createPlaylist(context, context.getString(R.string.favorites))); + } + + public static boolean isFavorite(@NonNull final Context context, @NonNull final Song song) { /*return Observable.create(e -> getFavoritesPlaylist(context).subscribe(playlist1 -> { boolean isBoolean = PlaylistsUtil.doPlaylistContains(context, playlist1.id, song.id); e.onNext(isBoolean); e.onComplete(); }));*/ - //getFavoritesPlaylist(context).blockingFirst().id.subscribe(MusicUtil::setPlaylist); - //return PlaylistsUtil.doPlaylistContains(context, getFavoritesPlaylist(context).blockingFirst().id, song.id); - return PlaylistsUtil - .doPlaylistContains(context, getFavoritesPlaylist(context).blockingFirst().id, song.id); - } - - public static boolean isArtistNameUnknown(@Nullable String artistName) { - if (TextUtils.isEmpty(artistName)) { - return false; + //getFavoritesPlaylist(context).blockingFirst().id.subscribe(MusicUtil::setPlaylist); + //return PlaylistsUtil.doPlaylistContains(context, getFavoritesPlaylist(context).blockingFirst().id, song.id); + return PlaylistsUtil + .doPlaylistContains(context, getFavoritesPlaylist(context).blockingFirst().id, song.id); } - if (artistName.equals(Artist.UNKNOWN_ARTIST_DISPLAY_NAME)) { - return true; - } - artistName = artistName.trim().toLowerCase(); - return artistName.equals("unknown") || artistName.equals(""); - } - @NonNull - public static String getSectionName(@Nullable String musicMediaTitle) { - if (TextUtils.isEmpty(musicMediaTitle)) { - return ""; + public static boolean isArtistNameUnknown(@Nullable String artistName) { + if (TextUtils.isEmpty(artistName)) { + return false; + } + if (artistName.equals(Artist.UNKNOWN_ARTIST_DISPLAY_NAME)) { + return true; + } + artistName = artistName.trim().toLowerCase(); + return artistName.equals("unknown") || artistName.equals(""); } - musicMediaTitle = musicMediaTitle.trim().toLowerCase(); - if (musicMediaTitle.startsWith("the ")) { - musicMediaTitle = musicMediaTitle.substring(4); - } else if (musicMediaTitle.startsWith("a ")) { - musicMediaTitle = musicMediaTitle.substring(2); + + @NonNull + public static String getSectionName(@Nullable String musicMediaTitle) { + if (TextUtils.isEmpty(musicMediaTitle)) { + return ""; + } + musicMediaTitle = musicMediaTitle.trim().toLowerCase(); + if (musicMediaTitle.startsWith("the ")) { + musicMediaTitle = musicMediaTitle.substring(4); + } else if (musicMediaTitle.startsWith("a ")) { + musicMediaTitle = musicMediaTitle.substring(2); + } + if (musicMediaTitle.isEmpty()) { + return ""; + } + return String.valueOf(musicMediaTitle.charAt(0)).toUpperCase(); } - if (musicMediaTitle.isEmpty()) { - return ""; + + public static Playlist getPlaylist() { + return playlist; } - return String.valueOf(musicMediaTitle.charAt(0)).toUpperCase(); - } - public static Playlist getPlaylist() { - return playlist; - } - - public static void setPlaylist(Playlist playlist) { - MusicUtil.playlist = playlist; - } - - public static long getTotalDuration(@NonNull final Context context, @NonNull List songs) { - long duration = 0; - for (int i = 0; i < songs.size(); i++) { - duration += songs.get(i).duration; + public static void setPlaylist(Playlist playlist) { + MusicUtil.playlist = playlist; } - return duration; - } - @NonNull - public static String getYearString(int year) { - return year > 0 ? String.valueOf(year) : "-"; - } + public static long getTotalDuration(@NonNull final Context context, @NonNull List songs) { + long duration = 0; + for (int i = 0; i < songs.size(); i++) { + duration += songs.get(i).duration; + } + return duration; + } + + @NonNull + public static String getYearString(int year) { + return year > 0 ? String.valueOf(year) : "-"; + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java index e3c4743aa..71db2d536 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.java @@ -9,699 +9,719 @@ import android.preference.PreferenceManager; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.annotation.StyleRes; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; + +import java.io.File; +import java.lang.reflect.Type; +import java.util.ArrayList; + import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.RetroApplication; import code.name.monkey.retromusic.helper.SortOrder; import code.name.monkey.retromusic.model.CategoryInfo; import code.name.monkey.retromusic.ui.fragments.NowPlayingScreen; import code.name.monkey.retromusic.ui.fragments.mainactivity.folders.FoldersFragment; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import com.google.gson.reflect.TypeToken; -import java.io.File; -import java.lang.reflect.Type; -import java.util.ArrayList; public final class PreferenceUtil { - public static final String KEEP_SCREEN_ON = "keep_screen_on"; - public static final String TOGGLE_HOME_BANNER = "toggle_home_banner"; - public static final String NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"; - public static final String CAROUSEL_EFFECT = "carousel_effect"; - public static final String COLORED_NOTIFICATION = "colored_notification"; - public static final String CLASSIC_NOTIFICATION = "classic_notification"; - public static final String GAPLESS_PLAYBACK = "gapless_playback"; - public static final String ALBUM_ART_ON_LOCKSCREEN = "album_art_on_lockscreen"; - public static final String BLURRED_ALBUM_ART = "blurred_album_art"; - public static final String TOGGLE_HEADSET = "toggle_headset"; - public static final String DOMINANT_COLOR = "dominant_color"; - public static final String GENERAL_THEME = "general_theme"; - public static final String CIRCULAR_ALBUM_ART = "circular_album_art"; - public static final String USER_NAME = "user_name"; - public static final String TOGGLE_FULL_SCREEN = "toggle_full_screen"; - public static final String TOGGLE_VOLUME = "toggle_volume"; - public static final String TOGGLE_TAB_TITLES = "toggle_tab_titles"; - public static final String ROUND_CORNERS = "corner_window"; - public static final String TOGGLE_GENRE = "toggle_genre"; - public static final String PROFILE_IMAGE_PATH = "profile_image_path"; - public static final String BANNER_IMAGE_PATH = "banner_image_path"; - public static final String ADAPTIVE_COLOR_APP = "adaptive_color_app"; - public static final String TOGGLE_SEPARATE_LINE = "toggle_separate_line"; - public static final String ALBUM_GRID_STYLE = "album_grid_style"; - public static final String ARTIST_GRID_STYLE = "artist_grid_style"; - private static final String GENRE_SORT_ORDER = "genre_sort_order"; - private static final String LIBRARY_CATEGORIES = "library_categories"; - private static final String LAST_PAGE = "last_start_page"; - private static final String LAST_MUSIC_CHOOSER = "last_music_chooser"; - private static final String DEFAULT_START_PAGE = "default_start_page"; - private static final String INITIALIZED_BLACKLIST = "initialized_blacklist"; - private static final String ARTIST_SORT_ORDER = "artist_sort_order"; - private static final String ARTIST_SONG_SORT_ORDER = "artist_song_sort_order"; - private static final String ARTIST_ALBUM_SORT_ORDER = "artist_album_sort_order"; - private static final String ALBUM_SORT_ORDER = "album_sort_order"; - private static final String ALBUM_SONG_SORT_ORDER = "album_song_sort_order"; - private static final String SONG_SORT_ORDER = "song_sort_order"; - private static final String ALBUM_GRID_SIZE = "album_grid_size"; - private static final String ALBUM_GRID_SIZE_LAND = "album_grid_size_land"; - private static final String SONG_GRID_SIZE = "song_grid_size"; - private static final String SONG_GRID_SIZE_LAND = "song_grid_size_land"; - private static final String ARTIST_GRID_SIZE = "artist_grid_size"; - private static final String ARTIST_GRID_SIZE_LAND = "artist_grid_size_land"; - private static final String ALBUM_COLORED_FOOTERS = "album_colored_footers"; - private static final String SONG_COLORED_FOOTERS = "song_colored_footers"; - private static final String ARTIST_COLORED_FOOTERS = "artist_colored_footers"; - private static final String ALBUM_ARTIST_COLORED_FOOTERS = "album_artist_colored_footers"; - private static final String COLORED_APP_SHORTCUTS = "colored_app_shortcuts"; - private static final String AUDIO_DUCKING = "audio_ducking"; - private static final String LAST_ADDED_CUTOFF = "last_added_interval"; - private static final String LAST_SLEEP_TIMER_VALUE = "last_sleep_timer_value"; - private static final String NEXT_SLEEP_TIMER_ELAPSED_REALTIME = "next_sleep_timer_elapsed_real_time"; - private static final String IGNORE_MEDIA_STORE_ARTWORK = "ignore_media_store_artwork"; - private static final String LAST_CHANGELOG_VERSION = "last_changelog_version"; - private static final String INTRO_SHOWN = "intro_shown"; - private static final String AUTO_DOWNLOAD_IMAGES_POLICY = "auto_download_images_policy"; - private static final String START_DIRECTORY = "start_directory"; - private static final String SYNCHRONIZED_LYRICS_SHOW = "synchronized_lyrics_show"; - private static final String LOCK_SCREEN = "lock_screen"; - private static final String ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"; - private static final String ARTIST_DETAIL_SONG_SORT_ORDER = "artist_detail_song_sort_order"; - private static final String LYRICS_OPTIONS = "lyrics_options"; - private static final String SAF_SDCARD_URI = "saf_sdcard_uri"; - private static final String DOCUMENT_TREE_URI = "document_tree_uri"; - private static final String CHOOSE_EQUALIZER = "choose_equalizer"; - private static final String TOGGLE_SHUFFLE = "toggle_shuffle"; - private static final String SONG_GRID_STYLE = "song_grid_style"; - private static final String TOGGLE_ANIMATIONS = "toggle_animations"; - private static final String TAG = "PreferenceUtil"; - private static PreferenceUtil sInstance; - private final SharedPreferences mPreferences; + public static final String KEEP_SCREEN_ON = "keep_screen_on"; + public static final String TOGGLE_HOME_BANNER = "toggle_home_banner"; + public static final String NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"; + public static final String CAROUSEL_EFFECT = "carousel_effect"; + public static final String COLORED_NOTIFICATION = "colored_notification"; + public static final String CLASSIC_NOTIFICATION = "classic_notification"; + public static final String GAPLESS_PLAYBACK = "gapless_playback"; + public static final String ALBUM_ART_ON_LOCKSCREEN = "album_art_on_lockscreen"; + public static final String BLURRED_ALBUM_ART = "blurred_album_art"; + public static final String TOGGLE_HEADSET = "toggle_headset"; + public static final String DOMINANT_COLOR = "dominant_color"; + public static final String GENERAL_THEME = "general_theme"; + public static final String CIRCULAR_ALBUM_ART = "circular_album_art"; + public static final String USER_NAME = "user_name"; + public static final String TOGGLE_FULL_SCREEN = "toggle_full_screen"; + public static final String TOGGLE_VOLUME = "toggle_volume"; + public static final String TOGGLE_TAB_TITLES = "toggle_tab_titles"; + public static final String ROUND_CORNERS = "corner_window"; + public static final String TOGGLE_GENRE = "toggle_genre"; + public static final String PROFILE_IMAGE_PATH = "profile_image_path"; + public static final String BANNER_IMAGE_PATH = "banner_image_path"; + public static final String ADAPTIVE_COLOR_APP = "adaptive_color_app"; + public static final String TOGGLE_SEPARATE_LINE = "toggle_separate_line"; + public static final String ALBUM_GRID_STYLE = "album_grid_style"; + public static final String ARTIST_GRID_STYLE = "artist_grid_style"; + public static final String TOGGLE_ADD_CONTROLS = "toggle_add_controls"; + private static final String GENRE_SORT_ORDER = "genre_sort_order"; + private static final String LIBRARY_CATEGORIES = "library_categories"; + private static final String LAST_PAGE = "last_start_page"; + private static final String LAST_MUSIC_CHOOSER = "last_music_chooser"; + private static final String DEFAULT_START_PAGE = "default_start_page"; + private static final String INITIALIZED_BLACKLIST = "initialized_blacklist"; + private static final String ARTIST_SORT_ORDER = "artist_sort_order"; + private static final String ARTIST_SONG_SORT_ORDER = "artist_song_sort_order"; + private static final String ARTIST_ALBUM_SORT_ORDER = "artist_album_sort_order"; + private static final String ALBUM_SORT_ORDER = "album_sort_order"; + private static final String ALBUM_SONG_SORT_ORDER = "album_song_sort_order"; + private static final String SONG_SORT_ORDER = "song_sort_order"; + private static final String ALBUM_GRID_SIZE = "album_grid_size"; + private static final String ALBUM_GRID_SIZE_LAND = "album_grid_size_land"; + private static final String SONG_GRID_SIZE = "song_grid_size"; + private static final String SONG_GRID_SIZE_LAND = "song_grid_size_land"; + private static final String ARTIST_GRID_SIZE = "artist_grid_size"; + private static final String ARTIST_GRID_SIZE_LAND = "artist_grid_size_land"; + private static final String ALBUM_COLORED_FOOTERS = "album_colored_footers"; + private static final String SONG_COLORED_FOOTERS = "song_colored_footers"; + private static final String ARTIST_COLORED_FOOTERS = "artist_colored_footers"; + private static final String ALBUM_ARTIST_COLORED_FOOTERS = "album_artist_colored_footers"; + private static final String COLORED_APP_SHORTCUTS = "colored_app_shortcuts"; + private static final String AUDIO_DUCKING = "audio_ducking"; + private static final String LAST_ADDED_CUTOFF = "last_added_interval"; + private static final String LAST_SLEEP_TIMER_VALUE = "last_sleep_timer_value"; + private static final String NEXT_SLEEP_TIMER_ELAPSED_REALTIME = "next_sleep_timer_elapsed_real_time"; + private static final String IGNORE_MEDIA_STORE_ARTWORK = "ignore_media_store_artwork"; + private static final String LAST_CHANGELOG_VERSION = "last_changelog_version"; + private static final String INTRO_SHOWN = "intro_shown"; + private static final String AUTO_DOWNLOAD_IMAGES_POLICY = "auto_download_images_policy"; + private static final String START_DIRECTORY = "start_directory"; + private static final String SYNCHRONIZED_LYRICS_SHOW = "synchronized_lyrics_show"; + private static final String LOCK_SCREEN = "lock_screen"; + private static final String ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"; + private static final String ARTIST_DETAIL_SONG_SORT_ORDER = "artist_detail_song_sort_order"; + private static final String LYRICS_OPTIONS = "lyrics_options"; + private static final String SAF_SDCARD_URI = "saf_sdcard_uri"; + private static final String DOCUMENT_TREE_URI = "document_tree_uri"; + private static final String CHOOSE_EQUALIZER = "choose_equalizer"; + private static final String TOGGLE_SHUFFLE = "toggle_shuffle"; + private static final String SONG_GRID_STYLE = "song_grid_style"; + private static final String TOGGLE_ANIMATIONS = "toggle_animations"; + private static final String TAG = "PreferenceUtil"; + private static final String LAST_KNOWN_LYRICS_TYPE = "LAST_KNOWN_LYRICS_TYPE"; + private static PreferenceUtil sInstance; + private final SharedPreferences mPreferences; - private PreferenceUtil(@NonNull final Context context) { - mPreferences = PreferenceManager.getDefaultSharedPreferences(context); - } - - public static PreferenceUtil getInstance(@NonNull final Context context) { - if (sInstance == null) { - sInstance = new PreferenceUtil(context.getApplicationContext()); - } - return sInstance; - } - - @StyleRes - public static int getThemeResFromPrefValue(String themePrefValue) { - switch (themePrefValue) { - case "dark": - return R.style.Theme_RetroMusic; - case "color": - return R.style.Theme_RetroMusic_Color; - case "acolor": - return R.style.Theme_RetroMusic_Black; - case "black": - return R.style.Theme_RetroMusic_Black; - case "light": - default: - return R.style.Theme_RetroMusic_Light; - } - } - - public int getAlbumDetailsPageStyle() { - - TypedArray typedArray = RetroApplication.getInstance(). - getResources().obtainTypedArray(R.array.pref_album_detail_style_values); - int layout = typedArray.getResourceId(mPreferences.getInt("album_detail_style", 0), -1); - typedArray.recycle(); - return layout; - } - - public final String getArtistSortOrder() { - return mPreferences.getString(ARTIST_SORT_ORDER, SortOrder.ArtistSortOrder.ARTIST_A_Z); - } - - public void setArtistSortOrder(final String sortOrder) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putString(ARTIST_SORT_ORDER, sortOrder); - editor.apply(); - } - - public final String getArtistSongSortOrder() { - return mPreferences.getString(ARTIST_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.SONG_A_Z); - } - - public final boolean toggleHomeBanner() { - return mPreferences.getBoolean(TOGGLE_HOME_BANNER, false); - } - - public final String getArtistAlbumSortOrder() { - return mPreferences - .getString(ARTIST_ALBUM_SORT_ORDER, SortOrder.ArtistAlbumSortOrder.ALBUM_YEAR); - } - - public final String getAlbumSortOrder() { - return mPreferences.getString(ALBUM_SORT_ORDER, SortOrder.AlbumSortOrder.ALBUM_A_Z); - } - - public void setAlbumSortOrder(final String sortOrder) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putString(ALBUM_SORT_ORDER, sortOrder); - editor.apply(); - } - - public final String getAlbumSongSortOrder() { - return mPreferences - .getString(ALBUM_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST); - } - - public final String getSongSortOrder() { - return mPreferences.getString(SONG_SORT_ORDER, SortOrder.SongSortOrder.SONG_A_Z); - } - - public void setSongSortOrder(final String sortOrder) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putString(SONG_SORT_ORDER, sortOrder); - editor.commit(); - } - - public final String getGenreSortOrder() { - return mPreferences.getString(GENRE_SORT_ORDER, SortOrder.GenreSortOrder.GENRE_A_Z); - } - - public boolean isScreenOnEnabled() { - return mPreferences.getBoolean(KEEP_SCREEN_ON, false); - } - - public void setInitializedBlacklist() { - final Editor editor = mPreferences.edit(); - editor.putBoolean(INITIALIZED_BLACKLIST, true); - editor.apply(); - } - - public final boolean initializedBlacklist() { - return mPreferences.getBoolean(INITIALIZED_BLACKLIST, false); - } - - - public boolean circularAlbumArt() { - return mPreferences.getBoolean(CIRCULAR_ALBUM_ART, false); - } - - public boolean carouselEffect() { - return mPreferences.getBoolean(CAROUSEL_EFFECT, false); - } - - public ArrayList getLibraryCategoryInfos() { - String data = mPreferences.getString(LIBRARY_CATEGORIES, null); - if (data != null) { - Gson gson = new Gson(); - Type collectionType = new TypeToken>() { - }.getType(); - - try { - return gson.fromJson(data, collectionType); - } catch (JsonSyntaxException e) { - e.printStackTrace(); - } + private PreferenceUtil(@NonNull final Context context) { + mPreferences = PreferenceManager.getDefaultSharedPreferences(context); } - return getDefaultLibraryCategoryInfos(); - } - - public void setLibraryCategoryInfos(ArrayList categories) { - Gson gson = new Gson(); - Type collectionType = new TypeToken>() { - }.getType(); - - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putString(LIBRARY_CATEGORIES, gson.toJson(categories, collectionType)); - editor.apply(); - } - - public ArrayList getDefaultLibraryCategoryInfos() { - ArrayList defaultCategoryInfos = new ArrayList<>(5); - defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.SONGS, true)); - defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.ALBUMS, true)); - defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.ARTISTS, true)); - defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.GENRES, true)); - defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.PLAYLISTS, true)); - return defaultCategoryInfos; - } - - public boolean isRoundCorners() { - return mPreferences.getBoolean(ROUND_CORNERS, false); - } - - public void registerOnSharedPreferenceChangedListener( - SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener) { - mPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); - } - - public void unregisterOnSharedPreferenceChangedListener( - SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener) { - mPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); - } - - public final int getDefaultStartPage() { - return Integer.parseInt(mPreferences.getString(DEFAULT_START_PAGE, "-1")); - } - - - public final int getLastPage() { - return mPreferences.getInt(LAST_PAGE, 0); - } - - public void setLastPage(final int value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(LAST_PAGE, value); - editor.apply(); - } - - public final int getLastMusicChooser() { - return mPreferences.getInt(LAST_MUSIC_CHOOSER, 0); - } - - public void setLastMusicChooser(final int value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(LAST_MUSIC_CHOOSER, value); - editor.apply(); - } - - public final boolean coloredNotification() { - return mPreferences.getBoolean(COLORED_NOTIFICATION, true); - } - - public final boolean classicNotification() { - return mPreferences.getBoolean(CLASSIC_NOTIFICATION, false); - } - - public void setClassicNotification(final boolean value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putBoolean(CLASSIC_NOTIFICATION, value); - editor.apply(); - } - - public final NowPlayingScreen getNowPlayingScreen() { - int id = mPreferences.getInt(NOW_PLAYING_SCREEN_ID, 0); - for (NowPlayingScreen nowPlayingScreen : NowPlayingScreen.values()) { - if (nowPlayingScreen.id == id) { - return nowPlayingScreen; - } - } - return NowPlayingScreen.NORMAL; - } - - @SuppressLint("CommitPrefEdits") - public void setNowPlayingScreen(NowPlayingScreen nowPlayingScreen) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(NOW_PLAYING_SCREEN_ID, nowPlayingScreen.id); - editor.apply(); - } - - public void setColoredAppShortcuts(final boolean value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putBoolean(COLORED_APP_SHORTCUTS, value); - editor.apply(); - } - - public final boolean coloredAppShortcuts() { - return mPreferences.getBoolean(COLORED_APP_SHORTCUTS, true); - } - - public final boolean gaplessPlayback() { - return mPreferences.getBoolean(GAPLESS_PLAYBACK, false); - } - - public final boolean audioDucking() { - return mPreferences.getBoolean(AUDIO_DUCKING, true); - } - - public final boolean albumArtOnLockscreen() { - return mPreferences.getBoolean(ALBUM_ART_ON_LOCKSCREEN, true); - } - - public final boolean blurredAlbumArt() { - return mPreferences.getBoolean(BLURRED_ALBUM_ART, false); - } - - public final boolean ignoreMediaStoreArtwork() { - return mPreferences.getBoolean(IGNORE_MEDIA_STORE_ARTWORK, false); - } - - - public int getLastSleepTimerValue() { - return mPreferences.getInt(LAST_SLEEP_TIMER_VALUE, 30); - } - - public void setLastSleepTimerValue(final int value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(LAST_SLEEP_TIMER_VALUE, value); - editor.apply(); - } - - public long getNextSleepTimerElapsedRealTime() { - return mPreferences.getLong(NEXT_SLEEP_TIMER_ELAPSED_REALTIME, -1); - } - - public void setNextSleepTimerElapsedRealtime(final long value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putLong(NEXT_SLEEP_TIMER_ELAPSED_REALTIME, value); - editor.apply(); - } - - public final int getAlbumGridSize(Context context) { - return mPreferences - .getInt(ALBUM_GRID_SIZE, context.getResources().getInteger(R.integer.default_grid_columns)); - } - - public void setSongGridSize(final int gridSize) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(SONG_GRID_SIZE, gridSize); - editor.apply(); - } - - public final int getSongGridSize(Context context) { - return mPreferences - .getInt(SONG_GRID_SIZE, context.getResources().getInteger(R.integer.default_list_columns)); - } - - public void setArtistGridSize(final int gridSize) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(ARTIST_GRID_SIZE, gridSize); - editor.apply(); - } - - public final int getArtistGridSize(Context context) { - return mPreferences.getInt(ARTIST_GRID_SIZE, - context.getResources().getInteger(R.integer.default_list_artist_columns)); - } - - public void setAlbumGridSizeLand(final int gridSize) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(ALBUM_GRID_SIZE_LAND, gridSize); - editor.apply(); - } - - public final int getAlbumGridSizeLand(Context context) { - return mPreferences.getInt(ALBUM_GRID_SIZE_LAND, - context.getResources().getInteger(R.integer.default_grid_columns_land)); - } - - public void setSongGridSizeLand(final int gridSize) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(SONG_GRID_SIZE_LAND, gridSize); - editor.apply(); - } - - public final int getSongGridSizeLand(Context context) { - return mPreferences.getInt(SONG_GRID_SIZE_LAND, - context.getResources().getInteger(R.integer.default_list_columns_land)); - } - - public void setArtistGridSizeLand(final int gridSize) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(ARTIST_GRID_SIZE_LAND, gridSize); - editor.apply(); - } - - public final int getArtistGridSizeLand(Context context) { - return mPreferences.getInt(ARTIST_GRID_SIZE_LAND, - context.getResources().getInteger(R.integer.default_list_artist_columns_land)); - } - - public void setAlbumGridSize(final int gridSize) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt(ALBUM_GRID_SIZE, gridSize); - editor.apply(); - } - - public void setAlbumColoredFooters(final boolean value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putBoolean(ALBUM_COLORED_FOOTERS, value); - editor.apply(); - } - - public final boolean albumColoredFooters() { - return mPreferences.getBoolean(ALBUM_COLORED_FOOTERS, false); - } - - public void setAlbumArtistColoredFooters(final boolean value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putBoolean(ALBUM_ARTIST_COLORED_FOOTERS, value); - editor.apply(); - } - - public final boolean albumArtistColoredFooters() { - return mPreferences.getBoolean(ALBUM_ARTIST_COLORED_FOOTERS, true); - } - - public void setSongColoredFooters(final boolean value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putBoolean(SONG_COLORED_FOOTERS, value); - editor.apply(); - } - - public final boolean songColoredFooters() { - return mPreferences.getBoolean(SONG_COLORED_FOOTERS, false); - } - - public void setArtistColoredFooters(final boolean value) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putBoolean(ARTIST_COLORED_FOOTERS, value); - editor.apply(); - } - - public final boolean artistColoredFooters() { - return mPreferences.getBoolean(ARTIST_COLORED_FOOTERS, true); - } - - public void setLastChangeLogVersion(int version) { - mPreferences.edit().putInt(LAST_CHANGELOG_VERSION, version).apply(); - } - - public final int getLastChangelogVersion() { - return mPreferences.getInt(LAST_CHANGELOG_VERSION, -1); - } - - @SuppressLint("CommitPrefEdits") - public void setIntroShown() { - // don't use apply here - mPreferences.edit().putBoolean(INTRO_SHOWN, true).commit(); - } - - public final File getStartDirectory() { - return new File(mPreferences - .getString(START_DIRECTORY, FoldersFragment.getDefaultStartDirectory().getPath())); - } - - public void setStartDirectory(File file) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putString(START_DIRECTORY, FileUtil.safeGetCanonicalPath(file)); - editor.apply(); - } - - public final boolean introShown() { - return mPreferences.getBoolean(INTRO_SHOWN, false); - } - - public final String autoDownloadImagesPolicy() { - return mPreferences.getString(AUTO_DOWNLOAD_IMAGES_POLICY, "only_wifi"); - } - - public final boolean synchronizedLyricsShow() { - return mPreferences.getBoolean(SYNCHRONIZED_LYRICS_SHOW, true); - } - - public int getGeneralTheme() { - return getThemeResFromPrefValue(mPreferences.getString(GENERAL_THEME, "light")); - } - - public void setGeneralTheme(String theme) { - final SharedPreferences.Editor editor = mPreferences.edit(); - editor.putString(GENERAL_THEME, theme); - editor.apply(); - } - - public long getLastAddedCutoff() { - final CalendarUtil calendarUtil = new CalendarUtil(); - long interval; - - switch (mPreferences.getString(LAST_ADDED_CUTOFF, "")) { - case "today": - interval = calendarUtil.getElapsedToday(); - break; - case "this_week": - interval = calendarUtil.getElapsedWeek(); - break; - case "past_three_months": - interval = calendarUtil.getElapsedMonths(3); - break; - case "this_year": - interval = calendarUtil.getElapsedYear(); - break; - case "this_month": - default: - interval = calendarUtil.getElapsedMonth(); - break; + public static PreferenceUtil getInstance(@NonNull final Context context) { + if (sInstance == null) { + sInstance = new PreferenceUtil(context.getApplicationContext()); + } + return sInstance; } - return (System.currentTimeMillis() - interval) / 1000; - } - - public boolean getAdaptiveColor() { - return mPreferences.getBoolean(ADAPTIVE_COLOR_APP, false); - } - - public boolean getLockScreen() { - return mPreferences.getBoolean(LOCK_SCREEN, false); - } - - public String getUserName() { - return mPreferences.getString(USER_NAME, "User"); - } - - public void setUserName(String name) { - mPreferences.edit().putString(USER_NAME, name).apply(); - } - - public boolean getFullScreenMode() { - return mPreferences.getBoolean(TOGGLE_FULL_SCREEN, false); - } - - public void setFullScreenMode(int newValue) { - mPreferences.edit().putInt(TOGGLE_FULL_SCREEN, newValue).apply(); - } - - public String lyricsOptions() { - return mPreferences.getString(LYRICS_OPTIONS, "offline"); - } - - public void saveProfileImage(String profileImagePath) { - mPreferences.edit().putString(PROFILE_IMAGE_PATH, profileImagePath) - .apply(); - - } - - public String getProfileImage() { - return mPreferences.getString(PROFILE_IMAGE_PATH, ""); - } - - public String getBannerImage() { - return mPreferences.getString(BANNER_IMAGE_PATH, ""); - } - - public void setBannerImagePath(String bannerImagePath) { - mPreferences.edit().putString(BANNER_IMAGE_PATH, bannerImagePath) - .apply(); - } - - public String getAlbumDetailSongSortOrder() { - return mPreferences - .getString(ALBUM_DETAIL_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST); - } - - public void setAlbumDetailSongSortOrder(String sortOrder) { - Editor edit = this.mPreferences.edit(); - edit.putString(ALBUM_DETAIL_SONG_SORT_ORDER, sortOrder); - edit.apply(); - } - - public String getArtistDetailSongSortOrder() { - return mPreferences - .getString(ARTIST_DETAIL_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.SONG_A_Z); - } - - public void setArtistDetailSongSortOrder(String sortOrder) { - Editor edit = this.mPreferences.edit(); - edit.putString(ARTIST_DETAIL_SONG_SORT_ORDER, sortOrder); - edit.apply(); - } - - public boolean getVolumeToggle() { - return mPreferences.getBoolean(TOGGLE_VOLUME, false); - } - - public int getLyricsOptions() { - return mPreferences.getInt(LYRICS_OPTIONS, 1); - } - - public void setLyricsOptions(int i) { - mPreferences.edit().putInt(LYRICS_OPTIONS, i).apply(); - } - - public boolean getHeadsetPlugged() { - return mPreferences.getBoolean(TOGGLE_HEADSET, false); - } - - public boolean tabTitles() { - return mPreferences.getBoolean(TOGGLE_TAB_TITLES, true); - } - - public boolean isDominantColor() { - return mPreferences.getBoolean(DOMINANT_COLOR, false); - } - - public boolean isGenreShown() { - return mPreferences.getBoolean(TOGGLE_GENRE, false); - } - - public String getSelectedEqualizer() { - return mPreferences.getString(CHOOSE_EQUALIZER, "system"); - } - - public boolean isShuffleModeOn() { - return mPreferences.getBoolean(TOGGLE_SHUFFLE, false); - } - - public void resetCarouselEffect() { - mPreferences.edit().putBoolean(CAROUSEL_EFFECT, false).apply(); - } - - public void resetCircularAlbumArt() { - mPreferences.edit().putBoolean(CIRCULAR_ALBUM_ART, false).apply(); - } - - @LayoutRes - public int getAlbumGridStyle(Context context) { - int pos = Integer.parseInt(mPreferences.getString(ALBUM_GRID_STYLE, "0")); - TypedArray typedArray = context.getResources().obtainTypedArray(R.array.pref_grid_style_layout); - int layoutRes = typedArray.getResourceId(pos, -1); - typedArray.recycle(); - if (layoutRes == -1) { - return R.layout.item_card; + @StyleRes + public static int getThemeResFromPrefValue(String themePrefValue) { + switch (themePrefValue) { + case "dark": + return R.style.Theme_RetroMusic; + case "color": + return R.style.Theme_RetroMusic_Color; + case "acolor": + return R.style.Theme_RetroMusic_Black; + case "black": + return R.style.Theme_RetroMusic_Black; + case "light": + default: + return R.style.Theme_RetroMusic_Light; + } } - return layoutRes; - } - public void setAlbumGridStyle(int type) { - mPreferences.edit().putInt(ALBUM_GRID_STYLE, type).apply(); - } + public int getAlbumDetailsPageStyle() { - public int getArtistGridStyle(Context context) { - int pos = Integer.parseInt(mPreferences.getString(ARTIST_GRID_STYLE, "0")); - TypedArray typedArray = context.getResources().obtainTypedArray(R.array.pref_grid_style_layout); - int layoutRes = typedArray.getResourceId(pos, -1); - typedArray.recycle(); - if (layoutRes == -1) { - return R.layout.item_card; + TypedArray typedArray = RetroApplication.getInstance(). + getResources().obtainTypedArray(R.array.pref_album_detail_style_values); + int layout = typedArray.getResourceId(mPreferences.getInt("album_detail_style", 0), -1); + typedArray.recycle(); + return layout; } - return layoutRes; - } - public void setArtistGridStyle(int viewAs) { - mPreferences.edit().putInt(ARTIST_GRID_STYLE, viewAs).apply(); - } + public final String getArtistSortOrder() { + return mPreferences.getString(ARTIST_SORT_ORDER, SortOrder.ArtistSortOrder.ARTIST_A_Z); + } - public boolean toggleSeparateLine() { - return mPreferences.getBoolean(TOGGLE_SEPARATE_LINE, false); - } + public void setArtistSortOrder(final String sortOrder) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(ARTIST_SORT_ORDER, sortOrder); + editor.apply(); + } - public int getSongGridStyle() { - return mPreferences.getInt(SONG_GRID_STYLE, R.layout.item_list); - } + public final String getArtistSongSortOrder() { + return mPreferences.getString(ARTIST_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.SONG_A_Z); + } + + public final boolean toggleHomeBanner() { + return mPreferences.getBoolean(TOGGLE_HOME_BANNER, false); + } + + public final String getArtistAlbumSortOrder() { + return mPreferences + .getString(ARTIST_ALBUM_SORT_ORDER, SortOrder.ArtistAlbumSortOrder.ALBUM_YEAR); + } + + public final String getAlbumSortOrder() { + return mPreferences.getString(ALBUM_SORT_ORDER, SortOrder.AlbumSortOrder.ALBUM_A_Z); + } + + public void setAlbumSortOrder(final String sortOrder) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(ALBUM_SORT_ORDER, sortOrder); + editor.apply(); + } + + public final String getAlbumSongSortOrder() { + return mPreferences + .getString(ALBUM_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST); + } + + public final String getSongSortOrder() { + return mPreferences.getString(SONG_SORT_ORDER, SortOrder.SongSortOrder.SONG_A_Z); + } + + public void setSongSortOrder(final String sortOrder) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(SONG_SORT_ORDER, sortOrder); + editor.apply(); + } + + public final String getGenreSortOrder() { + return mPreferences.getString(GENRE_SORT_ORDER, SortOrder.GenreSortOrder.GENRE_A_Z); + } + + public boolean isScreenOnEnabled() { + return mPreferences.getBoolean(KEEP_SCREEN_ON, false); + } + + public void setInitializedBlacklist() { + final Editor editor = mPreferences.edit(); + editor.putBoolean(INITIALIZED_BLACKLIST, true); + editor.apply(); + } + + public final boolean initializedBlacklist() { + return mPreferences.getBoolean(INITIALIZED_BLACKLIST, false); + } + + public boolean isExtraMiniExtraControls() { + return mPreferences.getBoolean(TOGGLE_ADD_CONTROLS, false); + } + + public boolean circularAlbumArt() { + return mPreferences.getBoolean(CIRCULAR_ALBUM_ART, false); + } + + public boolean carouselEffect() { + return mPreferences.getBoolean(CAROUSEL_EFFECT, false); + } + + public ArrayList getLibraryCategoryInfos() { + String data = mPreferences.getString(LIBRARY_CATEGORIES, null); + if (data != null) { + Gson gson = new Gson(); + Type collectionType = new TypeToken>() { + }.getType(); + + try { + return gson.fromJson(data, collectionType); + } catch (JsonSyntaxException e) { + e.printStackTrace(); + } + } + + return getDefaultLibraryCategoryInfos(); + } + + public void setLibraryCategoryInfos(ArrayList categories) { + Gson gson = new Gson(); + Type collectionType = new TypeToken>() { + }.getType(); + + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(LIBRARY_CATEGORIES, gson.toJson(categories, collectionType)); + editor.apply(); + } + + public ArrayList getDefaultLibraryCategoryInfos() { + ArrayList defaultCategoryInfos = new ArrayList<>(5); + defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.SONGS, true)); + defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.ALBUMS, true)); + defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.ARTISTS, true)); + defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.GENRES, true)); + defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.PLAYLISTS, true)); + return defaultCategoryInfos; + } + + public boolean isRoundCorners() { + return mPreferences.getBoolean(ROUND_CORNERS, false); + } + + public void registerOnSharedPreferenceChangedListener( + SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener) { + mPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); + } + + public void unregisterOnSharedPreferenceChangedListener( + SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener) { + mPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); + } + + public final int getDefaultStartPage() { + return Integer.parseInt(mPreferences.getString(DEFAULT_START_PAGE, "-1")); + } + + + public final int getLastPage() { + return mPreferences.getInt(LAST_PAGE, 0); + } + + public void setLastPage(final int value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(LAST_PAGE, value); + editor.apply(); + } + + public int getLastLyricsType() { + return mPreferences.getInt(LAST_KNOWN_LYRICS_TYPE, R.id.normal_lyrics); + } + + public void setLastLyricsType(int group) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(LAST_KNOWN_LYRICS_TYPE, group); + editor.apply(); + } + + public final int getLastMusicChooser() { + return mPreferences.getInt(LAST_MUSIC_CHOOSER, 0); + } + + public void setLastMusicChooser(final int value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(LAST_MUSIC_CHOOSER, value); + editor.apply(); + } + + public final boolean coloredNotification() { + return mPreferences.getBoolean(COLORED_NOTIFICATION, true); + } + + public final boolean classicNotification() { + return mPreferences.getBoolean(CLASSIC_NOTIFICATION, false); + } + + public void setClassicNotification(final boolean value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putBoolean(CLASSIC_NOTIFICATION, value); + editor.apply(); + } + + public final NowPlayingScreen getNowPlayingScreen() { + int id = mPreferences.getInt(NOW_PLAYING_SCREEN_ID, 0); + for (NowPlayingScreen nowPlayingScreen : NowPlayingScreen.values()) { + if (nowPlayingScreen.id == id) { + return nowPlayingScreen; + } + } + return NowPlayingScreen.NORMAL; + } + + @SuppressLint("CommitPrefEdits") + public void setNowPlayingScreen(NowPlayingScreen nowPlayingScreen) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(NOW_PLAYING_SCREEN_ID, nowPlayingScreen.id); + editor.apply(); + } + + public void setColoredAppShortcuts(final boolean value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putBoolean(COLORED_APP_SHORTCUTS, value); + editor.apply(); + } + + public final boolean coloredAppShortcuts() { + return mPreferences.getBoolean(COLORED_APP_SHORTCUTS, true); + } + + public final boolean gaplessPlayback() { + return mPreferences.getBoolean(GAPLESS_PLAYBACK, false); + } + + public final boolean audioDucking() { + return mPreferences.getBoolean(AUDIO_DUCKING, true); + } + + public final boolean albumArtOnLockscreen() { + return mPreferences.getBoolean(ALBUM_ART_ON_LOCKSCREEN, true); + } + + public final boolean blurredAlbumArt() { + return mPreferences.getBoolean(BLURRED_ALBUM_ART, false); + } + + public final boolean ignoreMediaStoreArtwork() { + return mPreferences.getBoolean(IGNORE_MEDIA_STORE_ARTWORK, false); + } + + + public int getLastSleepTimerValue() { + return mPreferences.getInt(LAST_SLEEP_TIMER_VALUE, 30); + } + + public void setLastSleepTimerValue(final int value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(LAST_SLEEP_TIMER_VALUE, value); + editor.apply(); + } + + public long getNextSleepTimerElapsedRealTime() { + return mPreferences.getLong(NEXT_SLEEP_TIMER_ELAPSED_REALTIME, -1); + } + + public void setNextSleepTimerElapsedRealtime(final long value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putLong(NEXT_SLEEP_TIMER_ELAPSED_REALTIME, value); + editor.apply(); + } + + public final int getAlbumGridSize(Context context) { + return mPreferences + .getInt(ALBUM_GRID_SIZE, context.getResources().getInteger(R.integer.default_grid_columns)); + } + + public void setSongGridSize(final int gridSize) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(SONG_GRID_SIZE, gridSize); + editor.apply(); + } + + public final int getSongGridSize(Context context) { + return mPreferences + .getInt(SONG_GRID_SIZE, context.getResources().getInteger(R.integer.default_list_columns)); + } + + public void setArtistGridSize(final int gridSize) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(ARTIST_GRID_SIZE, gridSize); + editor.apply(); + } + + public final int getArtistGridSize(Context context) { + return mPreferences.getInt(ARTIST_GRID_SIZE, + context.getResources().getInteger(R.integer.default_list_artist_columns)); + } + + public void setAlbumGridSizeLand(final int gridSize) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(ALBUM_GRID_SIZE_LAND, gridSize); + editor.apply(); + } + + public final int getAlbumGridSizeLand(Context context) { + return mPreferences.getInt(ALBUM_GRID_SIZE_LAND, + context.getResources().getInteger(R.integer.default_grid_columns_land)); + } + + public void setSongGridSizeLand(final int gridSize) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(SONG_GRID_SIZE_LAND, gridSize); + editor.apply(); + } + + public final int getSongGridSizeLand(Context context) { + return mPreferences.getInt(SONG_GRID_SIZE_LAND, + context.getResources().getInteger(R.integer.default_list_columns_land)); + } + + public void setArtistGridSizeLand(final int gridSize) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(ARTIST_GRID_SIZE_LAND, gridSize); + editor.apply(); + } + + public final int getArtistGridSizeLand(Context context) { + return mPreferences.getInt(ARTIST_GRID_SIZE_LAND, + context.getResources().getInteger(R.integer.default_list_artist_columns_land)); + } + + public void setAlbumGridSize(final int gridSize) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt(ALBUM_GRID_SIZE, gridSize); + editor.apply(); + } + + public void setAlbumColoredFooters(final boolean value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putBoolean(ALBUM_COLORED_FOOTERS, value); + editor.apply(); + } + + public final boolean albumColoredFooters() { + return mPreferences.getBoolean(ALBUM_COLORED_FOOTERS, false); + } + + public void setAlbumArtistColoredFooters(final boolean value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putBoolean(ALBUM_ARTIST_COLORED_FOOTERS, value); + editor.apply(); + } + + public final boolean albumArtistColoredFooters() { + return mPreferences.getBoolean(ALBUM_ARTIST_COLORED_FOOTERS, true); + } + + public void setSongColoredFooters(final boolean value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putBoolean(SONG_COLORED_FOOTERS, value); + editor.apply(); + } + + public final boolean songColoredFooters() { + return mPreferences.getBoolean(SONG_COLORED_FOOTERS, false); + } + + public void setArtistColoredFooters(final boolean value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putBoolean(ARTIST_COLORED_FOOTERS, value); + editor.apply(); + } + + public final boolean artistColoredFooters() { + return mPreferences.getBoolean(ARTIST_COLORED_FOOTERS, true); + } + + public void setLastChangeLogVersion(int version) { + mPreferences.edit().putInt(LAST_CHANGELOG_VERSION, version).apply(); + } + + public final int getLastChangelogVersion() { + return mPreferences.getInt(LAST_CHANGELOG_VERSION, -1); + } + + @SuppressLint("CommitPrefEdits") + public void setIntroShown() { + // don't use apply here + mPreferences.edit().putBoolean(INTRO_SHOWN, true).commit(); + } + + public final File getStartDirectory() { + return new File(mPreferences + .getString(START_DIRECTORY, FoldersFragment.getDefaultStartDirectory().getPath())); + } + + public void setStartDirectory(File file) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(START_DIRECTORY, FileUtil.safeGetCanonicalPath(file)); + editor.apply(); + } + + public final boolean introShown() { + return mPreferences.getBoolean(INTRO_SHOWN, false); + } + + public final String autoDownloadImagesPolicy() { + return mPreferences.getString(AUTO_DOWNLOAD_IMAGES_POLICY, "only_wifi"); + } + + public final boolean synchronizedLyricsShow() { + return mPreferences.getBoolean(SYNCHRONIZED_LYRICS_SHOW, true); + } + + public int getGeneralTheme() { + return getThemeResFromPrefValue(mPreferences.getString(GENERAL_THEME, "light")); + } + + public void setGeneralTheme(String theme) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(GENERAL_THEME, theme); + editor.apply(); + } + + public long getLastAddedCutoff() { + final CalendarUtil calendarUtil = new CalendarUtil(); + long interval; + + switch (mPreferences.getString(LAST_ADDED_CUTOFF, "")) { + case "today": + interval = calendarUtil.getElapsedToday(); + break; + case "this_week": + interval = calendarUtil.getElapsedWeek(); + break; + case "past_three_months": + interval = calendarUtil.getElapsedMonths(3); + break; + case "this_year": + interval = calendarUtil.getElapsedYear(); + break; + case "this_month": + default: + interval = calendarUtil.getElapsedMonth(); + break; + } + + return (System.currentTimeMillis() - interval) / 1000; + } + + public boolean getAdaptiveColor() { + return mPreferences.getBoolean(ADAPTIVE_COLOR_APP, false); + } + + public boolean getLockScreen() { + return mPreferences.getBoolean(LOCK_SCREEN, false); + } + + public String getUserName() { + return mPreferences.getString(USER_NAME, "User"); + } + + public void setUserName(String name) { + mPreferences.edit().putString(USER_NAME, name).apply(); + } + + public boolean getFullScreenMode() { + return mPreferences.getBoolean(TOGGLE_FULL_SCREEN, false); + } + + public void setFullScreenMode(int newValue) { + mPreferences.edit().putInt(TOGGLE_FULL_SCREEN, newValue).apply(); + } + + public String lyricsOptions() { + return mPreferences.getString(LYRICS_OPTIONS, "offline"); + } + + public void saveProfileImage(String profileImagePath) { + mPreferences.edit().putString(PROFILE_IMAGE_PATH, profileImagePath) + .apply(); + + } + + public String getProfileImage() { + return mPreferences.getString(PROFILE_IMAGE_PATH, ""); + } + + public String getBannerImage() { + return mPreferences.getString(BANNER_IMAGE_PATH, ""); + } + + public void setBannerImagePath(String bannerImagePath) { + mPreferences.edit().putString(BANNER_IMAGE_PATH, bannerImagePath) + .apply(); + } + + public String getAlbumDetailSongSortOrder() { + return mPreferences + .getString(ALBUM_DETAIL_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST); + } + + public void setAlbumDetailSongSortOrder(String sortOrder) { + Editor edit = this.mPreferences.edit(); + edit.putString(ALBUM_DETAIL_SONG_SORT_ORDER, sortOrder); + edit.apply(); + } + + public String getArtistDetailSongSortOrder() { + return mPreferences + .getString(ARTIST_DETAIL_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.SONG_A_Z); + } + + public void setArtistDetailSongSortOrder(String sortOrder) { + Editor edit = this.mPreferences.edit(); + edit.putString(ARTIST_DETAIL_SONG_SORT_ORDER, sortOrder); + edit.apply(); + } + + public boolean getVolumeToggle() { + return mPreferences.getBoolean(TOGGLE_VOLUME, false); + } + + public int getLyricsOptions() { + return mPreferences.getInt(LYRICS_OPTIONS, 1); + } + + public void setLyricsOptions(int i) { + mPreferences.edit().putInt(LYRICS_OPTIONS, i).apply(); + } + + public boolean getHeadsetPlugged() { + return mPreferences.getBoolean(TOGGLE_HEADSET, false); + } + + public boolean tabTitles() { + return mPreferences.getBoolean(TOGGLE_TAB_TITLES, true); + } + + public boolean isDominantColor() { + return mPreferences.getBoolean(DOMINANT_COLOR, false); + } + + public boolean isGenreShown() { + return mPreferences.getBoolean(TOGGLE_GENRE, false); + } + + public String getSelectedEqualizer() { + return mPreferences.getString(CHOOSE_EQUALIZER, "system"); + } + + public boolean isShuffleModeOn() { + return mPreferences.getBoolean(TOGGLE_SHUFFLE, false); + } + + public void resetCarouselEffect() { + mPreferences.edit().putBoolean(CAROUSEL_EFFECT, false).apply(); + } + + public void resetCircularAlbumArt() { + mPreferences.edit().putBoolean(CIRCULAR_ALBUM_ART, false).apply(); + } + + @LayoutRes + public int getAlbumGridStyle(Context context) { + int pos = Integer.parseInt(mPreferences.getString(ALBUM_GRID_STYLE, "0")); + TypedArray typedArray = context.getResources().obtainTypedArray(R.array.pref_grid_style_layout); + int layoutRes = typedArray.getResourceId(pos, -1); + typedArray.recycle(); + if (layoutRes == -1) { + return R.layout.item_card; + } + return layoutRes; + } + + public void setAlbumGridStyle(int type) { + mPreferences.edit().putInt(ALBUM_GRID_STYLE, type).apply(); + } + + public int getArtistGridStyle(Context context) { + int pos = Integer.parseInt(mPreferences.getString(ARTIST_GRID_STYLE, "0")); + TypedArray typedArray = context.getResources().obtainTypedArray(R.array.pref_grid_style_layout); + int layoutRes = typedArray.getResourceId(pos, -1); + typedArray.recycle(); + if (layoutRes == -1) { + return R.layout.item_card; + } + return layoutRes; + } + + public void setArtistGridStyle(int viewAs) { + mPreferences.edit().putInt(ARTIST_GRID_STYLE, viewAs).apply(); + } + + public boolean toggleSeparateLine() { + return mPreferences.getBoolean(TOGGLE_SEPARATE_LINE, false); + } + + public int getSongGridStyle() { + return mPreferences.getInt(SONG_GRID_STYLE, R.layout.item_list); + } + + public void setSongGridStyle(int viewAs) { + mPreferences.edit().putInt(SONG_GRID_STYLE, viewAs).apply(); + } + + public boolean enableAnimations() { + return mPreferences.getBoolean(TOGGLE_ANIMATIONS, false); + } - public void setSongGridStyle(int viewAs) { - mPreferences.edit().putInt(SONG_GRID_STYLE, viewAs).apply(); - } - public boolean enableAnimations() { - return mPreferences.getBoolean(TOGGLE_ANIMATIONS, false); - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java index 614b2ab3d..308764c3c 100755 --- a/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java @@ -25,7 +25,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.graphics.drawable.VectorDrawableCompat; import android.support.v4.content.ContextCompat; -import android.support.v7.app.AppCompatActivity; import android.util.DisplayMetrics; import android.view.Display; import android.view.View; @@ -34,7 +33,6 @@ import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; -import android.widget.RelativeLayout; import java.lang.reflect.Method; import java.net.InetAddress; diff --git a/app/src/main/java/code/name/monkey/retromusic/util/TempUtils.java b/app/src/main/java/code/name/monkey/retromusic/util/TempUtils.java index 9d2b86aa2..ce10eed44 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/TempUtils.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/TempUtils.java @@ -5,64 +5,64 @@ package code.name.monkey.retromusic.util; */ public class TempUtils { - // Enums - public static final int TEMPO_STROLL = 0; - public static final int TEMPO_WALK = 1; - public static final int TEMPO_LIGHT_JOG = 2; - public static final int TEMPO_JOG = 3; - public static final int TEMPO_RUN = 4; - public static final int TEMPO_SPRINT = 5; - public static final int TEMPO_UNKNOWN = 6; + // Enums + public static final int TEMPO_STROLL = 0; + public static final int TEMPO_WALK = 1; + public static final int TEMPO_LIGHT_JOG = 2; + public static final int TEMPO_JOG = 3; + public static final int TEMPO_RUN = 4; + public static final int TEMPO_SPRINT = 5; + public static final int TEMPO_UNKNOWN = 6; - // take BPM as an int - public static int getTempoFromBPM(int bpm) { + // take BPM as an int + public static int getTempoFromBPM(int bpm) { - // STROLL less than 60 - if (bpm < 60) { - return TEMPO_STROLL; + // STROLL less than 60 + if (bpm < 60) { + return TEMPO_STROLL; + } + + // WALK between 60 and 70, or between 120 and 140 + else if (bpm < 70 || bpm >= 120 && bpm < 140) { + return TEMPO_WALK; + } + + // LIGHT_JOG between 70 and 80, or between 140 and 160 + else if (bpm < 80 || bpm >= 140 && bpm < 160) { + return TEMPO_LIGHT_JOG; + } + + // JOG between 80 and 90, or between 160 and 180 + else if (bpm < 90 || bpm >= 160 && bpm < 180) { + return TEMPO_JOG; + } + + // RUN between 90 and 100, or between 180 and 200 + else if (bpm < 100 || bpm >= 180 && bpm < 200) { + return TEMPO_RUN; + } + + // SPRINT between 100 and 120 + else if (bpm < 120) { + return TEMPO_SPRINT; + } + + // UNKNOWN + else { + return TEMPO_UNKNOWN; + } } - // WALK between 60 and 70, or between 120 and 140 - else if (bpm < 70 || bpm >= 120 && bpm < 140) { - return TEMPO_WALK; - } + // take BPM as a string + public static int getTempoFromBPM(String bpm) { + // cast to an int from string + try { + // convert the string to an int + return getTempoFromBPM(Integer.parseInt(bpm.trim())); + } catch (NumberFormatException nfe) { - // LIGHT_JOG between 70 and 80, or between 140 and 160 - else if (bpm < 80 || bpm >= 140 && bpm < 160) { - return TEMPO_LIGHT_JOG; + // + return TEMPO_UNKNOWN; + } } - - // JOG between 80 and 90, or between 160 and 180 - else if (bpm < 90 || bpm >= 160 && bpm < 180) { - return TEMPO_JOG; - } - - // RUN between 90 and 100, or between 180 and 200 - else if (bpm < 100 || bpm >= 180 && bpm < 200) { - return TEMPO_RUN; - } - - // SPRINT between 100 and 120 - else if (bpm < 120) { - return TEMPO_SPRINT; - } - - // UNKNOWN - else { - return TEMPO_UNKNOWN; - } - } - - // take BPM as a string - public static int getTempoFromBPM(String bpm) { - // cast to an int from string - try { - // convert the string to an int - return getTempoFromBPM(Integer.parseInt(bpm.trim())); - } catch (NumberFormatException nfe) { - - // - return TEMPO_UNKNOWN; - } - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/ViewUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/ViewUtil.java index c20887cb8..912242ebe 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/ViewUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/ViewUtil.java @@ -7,52 +7,54 @@ import android.support.v4.view.ViewCompat; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup; + +import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; + import code.name.monkey.appthemehelper.util.ColorUtil; import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; public class ViewUtil { - public final static int RETRO_MUSIC_ANIM_TIME = 1000; + public final static int RETRO_MUSIC_ANIM_TIME = 1000; - public static void setStatusBarHeight(final Context context, View statusBar) { - ViewGroup.LayoutParams lp = statusBar.getLayoutParams(); - lp.height = getStatusBarHeight(context); - statusBar.requestLayout(); - } - - public static int getStatusBarHeight(final Context context) { - int result = 0; - int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); - if (resourceId > 0) { - result = context.getResources().getDimensionPixelSize(resourceId); + public static void setStatusBarHeight(final Context context, View statusBar) { + ViewGroup.LayoutParams lp = statusBar.getLayoutParams(); + lp.height = getStatusBarHeight(context); + statusBar.requestLayout(); } - return result; - } - public static boolean hitTest(View v, int x, int y) { - final int tx = (int) (ViewCompat.getTranslationX(v) + 0.5f); - final int ty = (int) (ViewCompat.getTranslationY(v) + 0.5f); - final int left = v.getLeft() + tx; - final int right = v.getRight() + tx; - final int top = v.getTop() + ty; - final int bottom = v.getBottom() + ty; + public static int getStatusBarHeight(final Context context) { + int result = 0; + int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = context.getResources().getDimensionPixelSize(resourceId); + } + return result; + } - return (x >= left) && (x <= right) && (y >= top) && (y <= bottom); - } + public static boolean hitTest(View v, int x, int y) { + final int tx = (int) (ViewCompat.getTranslationX(v) + 0.5f); + final int ty = (int) (ViewCompat.getTranslationY(v) + 0.5f); + final int left = v.getLeft() + tx; + final int right = v.getRight() + tx; + final int top = v.getTop() + ty; + final int bottom = v.getBottom() + ty; - public static void setUpFastScrollRecyclerViewColor(Context context, - FastScrollRecyclerView recyclerView, int accentColor) { - recyclerView.setPopupBgColor(accentColor); - recyclerView.setPopupTextColor( - MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(accentColor))); - recyclerView.setThumbColor(accentColor); - recyclerView.setTrackColor(Color.TRANSPARENT); - //recyclerView.setTrackColor(ColorUtil.withAlpha(ATHUtil.resolveColor(context, R.attr.colorControlNormal), 0.12f)); - } + return (x >= left) && (x <= right) && (y >= top) && (y <= bottom); + } - public static float convertDpToPixel(float dp, Resources resources) { - DisplayMetrics metrics = resources.getDisplayMetrics(); - return dp * metrics.density; - } + public static void setUpFastScrollRecyclerViewColor(Context context, + FastScrollRecyclerView recyclerView, int accentColor) { + recyclerView.setPopupBgColor(accentColor); + recyclerView.setPopupTextColor( + MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(accentColor))); + recyclerView.setThumbColor(accentColor); + recyclerView.setTrackColor(Color.TRANSPARENT); + //recyclerView.setTrackColor(ColorUtil.withAlpha(ATHUtil.resolveColor(context, R.attr.colorControlNormal), 0.12f)); + } + + public static float convertDpToPixel(float dp, Resources resources) { + DisplayMetrics metrics = resources.getDisplayMetrics(); + return dp * metrics.density; + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/color/ImageGradientColorizer.java b/app/src/main/java/code/name/monkey/retromusic/util/color/ImageGradientColorizer.java index 87b883320..8431c34df 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/color/ImageGradientColorizer.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/color/ImageGradientColorizer.java @@ -32,62 +32,62 @@ import android.graphics.drawable.Drawable; */ public class ImageGradientColorizer { - public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) { - int width = drawable.getIntrinsicWidth(); - int height = drawable.getIntrinsicHeight(); - int size = Math.min(width, height); - int widthInset = (width - size) / 2; - int heightInset = (height - size) / 2; - drawable = drawable.mutate(); - drawable.setBounds(-widthInset, -heightInset, width - widthInset, height - heightInset); - Bitmap newBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(newBitmap); - // Values to calculate the luminance of a color - float lr = 0.2126f; - float lg = 0.7152f; - float lb = 0.0722f; - // Extract the red, green, blue components of the color extraction color in - // float and int form - int tri = Color.red(backgroundColor); - int tgi = Color.green(backgroundColor); - int tbi = Color.blue(backgroundColor); - float tr = tri / 255f; - float tg = tgi / 255f; - float tb = tbi / 255f; - // Calculate the luminance of the color extraction color - float cLum = (tr * lr + tg * lg + tb * lb) * 255; - ColorMatrix m = new ColorMatrix(new float[]{ - lr, lg, lb, 0, tri - cLum, - lr, lg, lb, 0, tgi - cLum, - lr, lg, lb, 0, tbi - cLum, - 0, 0, 0, 1, 0, - }); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - LinearGradient linearGradient = new LinearGradient(0, 0, size, 0, - new int[]{0, Color.argb(0.5f, 1, 1, 1), Color.BLACK}, - new float[]{0.0f, 0.4f, 1.0f}, Shader.TileMode.CLAMP); - paint.setShader(linearGradient); - Bitmap fadeIn = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - Canvas fadeInCanvas = new Canvas(fadeIn); - drawable.clearColorFilter(); - drawable.draw(fadeInCanvas); - if (isRtl) { - // Let's flip the gradient - fadeInCanvas.translate(size, 0); - fadeInCanvas.scale(-1, 1); + public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) { + int width = drawable.getIntrinsicWidth(); + int height = drawable.getIntrinsicHeight(); + int size = Math.min(width, height); + int widthInset = (width - size) / 2; + int heightInset = (height - size) / 2; + drawable = drawable.mutate(); + drawable.setBounds(-widthInset, -heightInset, width - widthInset, height - heightInset); + Bitmap newBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newBitmap); + // Values to calculate the luminance of a color + float lr = 0.2126f; + float lg = 0.7152f; + float lb = 0.0722f; + // Extract the red, green, blue components of the color extraction color in + // float and int form + int tri = Color.red(backgroundColor); + int tgi = Color.green(backgroundColor); + int tbi = Color.blue(backgroundColor); + float tr = tri / 255f; + float tg = tgi / 255f; + float tb = tbi / 255f; + // Calculate the luminance of the color extraction color + float cLum = (tr * lr + tg * lg + tb * lb) * 255; + ColorMatrix m = new ColorMatrix(new float[]{ + lr, lg, lb, 0, tri - cLum, + lr, lg, lb, 0, tgi - cLum, + lr, lg, lb, 0, tbi - cLum, + 0, 0, 0, 1, 0, + }); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + LinearGradient linearGradient = new LinearGradient(0, 0, size, 0, + new int[]{0, Color.argb(0.5f, 1, 1, 1), Color.BLACK}, + new float[]{0.0f, 0.4f, 1.0f}, Shader.TileMode.CLAMP); + paint.setShader(linearGradient); + Bitmap fadeIn = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas fadeInCanvas = new Canvas(fadeIn); + drawable.clearColorFilter(); + drawable.draw(fadeInCanvas); + if (isRtl) { + // Let's flip the gradient + fadeInCanvas.translate(size, 0); + fadeInCanvas.scale(-1, 1); + } + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + fadeInCanvas.drawPaint(paint); + Paint coloredPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + coloredPaint.setColorFilter(new ColorMatrixColorFilter(m)); + coloredPaint.setAlpha((int) (0.5f * 255)); + canvas.drawBitmap(fadeIn, 0, 0, coloredPaint); + linearGradient = new LinearGradient(0, 0, size, 0, + new int[]{0, Color.argb(0.5f, 1, 1, 1), Color.BLACK}, + new float[]{0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP); + paint.setShader(linearGradient); + fadeInCanvas.drawPaint(paint); + canvas.drawBitmap(fadeIn, 0, 0, null); + return newBitmap; } - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); - fadeInCanvas.drawPaint(paint); - Paint coloredPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - coloredPaint.setColorFilter(new ColorMatrixColorFilter(m)); - coloredPaint.setAlpha((int) (0.5f * 255)); - canvas.drawBitmap(fadeIn, 0, 0, coloredPaint); - linearGradient = new LinearGradient(0, 0, size, 0, - new int[]{0, Color.argb(0.5f, 1, 1, 1), Color.BLACK}, - new float[]{0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP); - paint.setShader(linearGradient); - fadeInCanvas.drawPaint(paint); - canvas.drawBitmap(fadeIn, 0, 0, null); - return newBitmap; - } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/color/MediaNotificationProcessor.java b/app/src/main/java/code/name/monkey/retromusic/util/color/MediaNotificationProcessor.java index af4d34ddd..8558519e0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/color/MediaNotificationProcessor.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/color/MediaNotificationProcessor.java @@ -8,12 +8,11 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.VisibleForTesting; import android.support.v7.graphics.Palette; -import android.util.LayoutDirection; - -import code.name.monkey.appthemehelper.util.ColorUtil; import java.util.List; +import code.name.monkey.appthemehelper.util.ColorUtil; + /** * @author Hemanth S (h4h13). */ @@ -46,13 +45,13 @@ public class MediaNotificationProcessor { private static final int RESIZE_BITMAP_AREA = 150 * 150; private final ImageGradientColorizer mColorizer; private final Context mContext; - private float[] mFilteredBackgroundHsl = null; - private Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl); /** * The context of the notification. This is the app context of the package posting the * notification. */ private final Context mPackageContext; + private float[] mFilteredBackgroundHsl = null; + private Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl); private boolean mIsLowPriority; private onColorThing onColorThing; diff --git a/app/src/main/java/code/name/monkey/retromusic/util/color/NotificationColorUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/color/NotificationColorUtil.java index f85d97fd4..5225121ed 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/color/NotificationColorUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/color/NotificationColorUtil.java @@ -1,7 +1,6 @@ package code.name.monkey.retromusic.util.color; import android.content.Context; -import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Color; @@ -9,15 +8,13 @@ import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.style.TextAppearanceSpan; import android.util.Log; import android.util.Pair; -import code.name.monkey.retromusic.util.ImageUtil; -import java.util.Arrays; + import java.util.WeakHashMap; +import code.name.monkey.retromusic.util.ImageUtil; + /** * Helper class to process legacy (Holo) notifications to make them look like quantum * notifications. @@ -26,109 +23,109 @@ import java.util.WeakHashMap; */ public class NotificationColorUtil { - private static final String TAG = "NotificationColorUtil"; - private static final Object sLock = new Object(); - private static NotificationColorUtil sInstance; + private static final String TAG = "NotificationColorUtil"; + private static final Object sLock = new Object(); + private static NotificationColorUtil sInstance; - private final WeakHashMap> mGrayscaleBitmapCache = - new WeakHashMap>(); + private final WeakHashMap> mGrayscaleBitmapCache = + new WeakHashMap>(); - public static NotificationColorUtil getInstance() { - synchronized (sLock) { - if (sInstance == null) { - sInstance = new NotificationColorUtil(); - } - return sInstance; - } - } - - /** - * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect gray". - * - * @param bitmap The bitmap to test. - * @return Whether the bitmap is grayscale. - */ - public boolean isGrayscale(Bitmap bitmap) { - synchronized (sLock) { - Pair cached = mGrayscaleBitmapCache.get(bitmap); - if (cached != null) { - if (cached.second == bitmap.getGenerationId()) { - return cached.first; + public static NotificationColorUtil getInstance() { + synchronized (sLock) { + if (sInstance == null) { + sInstance = new NotificationColorUtil(); + } + return sInstance; } - } } - boolean result; - int generationId; - result = ImageUtil.isGrayscale(bitmap); - // generationId and the check whether the Bitmap is grayscale can't be read atomically - // here. However, since the thread is in the process of posting the notification, we can - // assume that it doesn't modify the bitmap while we are checking the pixels. - generationId = bitmap.getGenerationId(); + /** + * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect gray". + * + * @param bitmap The bitmap to test. + * @return Whether the bitmap is grayscale. + */ + public boolean isGrayscale(Bitmap bitmap) { + synchronized (sLock) { + Pair cached = mGrayscaleBitmapCache.get(bitmap); + if (cached != null) { + if (cached.second == bitmap.getGenerationId()) { + return cached.first; + } + } + } + boolean result; + int generationId; - synchronized (sLock) { - mGrayscaleBitmapCache.put(bitmap, Pair.create(result, generationId)); + result = ImageUtil.isGrayscale(bitmap); + // generationId and the check whether the Bitmap is grayscale can't be read atomically + // here. However, since the thread is in the process of posting the notification, we can + // assume that it doesn't modify the bitmap while we are checking the pixels. + generationId = bitmap.getGenerationId(); + + synchronized (sLock) { + mGrayscaleBitmapCache.put(bitmap, Pair.create(result, generationId)); + } + return result; } - return result; - } - /** - * Checks whether a drawable is grayscale. Grayscale here means "very close to a perfect gray". - * - * @param d The drawable to test. - * @return Whether the drawable is grayscale. - */ - public boolean isGrayscale(Drawable d) { - if (d == null) { - return false; - } else if (d instanceof BitmapDrawable) { - BitmapDrawable bd = (BitmapDrawable) d; - return bd.getBitmap() != null && isGrayscale(bd.getBitmap()); - } else if (d instanceof AnimationDrawable) { - AnimationDrawable ad = (AnimationDrawable) d; - int count = ad.getNumberOfFrames(); - return count > 0 && isGrayscale(ad.getFrame(0)); - } else if (d instanceof VectorDrawable) { - // We just assume you're doing the right thing if using vectors - return true; - } else { - return false; + /** + * Checks whether a drawable is grayscale. Grayscale here means "very close to a perfect gray". + * + * @param d The drawable to test. + * @return Whether the drawable is grayscale. + */ + public boolean isGrayscale(Drawable d) { + if (d == null) { + return false; + } else if (d instanceof BitmapDrawable) { + BitmapDrawable bd = (BitmapDrawable) d; + return bd.getBitmap() != null && isGrayscale(bd.getBitmap()); + } else if (d instanceof AnimationDrawable) { + AnimationDrawable ad = (AnimationDrawable) d; + int count = ad.getNumberOfFrames(); + return count > 0 && isGrayscale(ad.getFrame(0)); + } else if (d instanceof VectorDrawable) { + // We just assume you're doing the right thing if using vectors + return true; + } else { + return false; + } } - } - /** - * Checks whether a drawable with a resoure id is grayscale. Grayscale here means "very close to a - * perfect gray". - * - * @param context The context to load the drawable from. - * @return Whether the drawable is grayscale. - */ - public boolean isGrayscale(Context context, int drawableResId) { - if (drawableResId != 0) { - try { - return isGrayscale(context.getDrawable(drawableResId)); - } catch (Resources.NotFoundException ex) { - Log.e(TAG, "Drawable not found: " + drawableResId); - return false; - } - } else { - return false; + /** + * Checks whether a drawable with a resoure id is grayscale. Grayscale here means "very close to a + * perfect gray". + * + * @param context The context to load the drawable from. + * @return Whether the drawable is grayscale. + */ + public boolean isGrayscale(Context context, int drawableResId) { + if (drawableResId != 0) { + try { + return isGrayscale(context.getDrawable(drawableResId)); + } catch (Resources.NotFoundException ex) { + Log.e(TAG, "Drawable not found: " + drawableResId); + return false; + } + } else { + return false; + } } - } - /** - * Inverts all the grayscale colors set by {@link android.text.style.TextAppearanceSpan}s on the - * text. - * - * @param charSequence The text to process. - * @return The color inverted text. - */ + /** + * Inverts all the grayscale colors set by {@link android.text.style.TextAppearanceSpan}s on the + * text. + * + * @param charSequence The text to process. + * @return The color inverted text. + */ - private int processColor(int color) { - return Color.argb(Color.alpha(color), - 255 - Color.red(color), - 255 - Color.green(color), - 255 - Color.blue(color)); - } + private int processColor(int color) { + return Color.argb(Color.alpha(color), + 255 - Color.red(color), + 255 - Color.green(color), + 255 - Color.blue(color)); + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/schedulers/SchedulerProvider.java b/app/src/main/java/code/name/monkey/retromusic/util/schedulers/SchedulerProvider.java index 4ca45b14f..1750f134f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/schedulers/SchedulerProvider.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/schedulers/SchedulerProvider.java @@ -12,7 +12,7 @@ import io.reactivex.schedulers.Schedulers; public class SchedulerProvider implements BaseSchedulerProvider { @NonNull - private static SchedulerProvider INSTANCE ; + private static SchedulerProvider INSTANCE; public SchedulerProvider() { } diff --git a/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationViewEx.java b/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationViewEx.java index ce2e44212..eb13ed1dd 100644 --- a/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationViewEx.java +++ b/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationViewEx.java @@ -908,7 +908,7 @@ public class BottomNavigationViewEx extends BottomNavigationView { } public void enableShiftingMode(int position, boolean enable) { - getBottomNavigationItemView(position).setShiftingMode(enable); + //getBottomNavigationItemView(position).setShiftingMode(enable); } public void setItemBackground(int position, int background) { diff --git a/app/src/main/java/code/name/monkey/retromusic/views/CustomMediaRouteButton.java b/app/src/main/java/code/name/monkey/retromusic/views/CustomMediaRouteButton.java new file mode 100644 index 000000000..d1798d584 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/views/CustomMediaRouteButton.java @@ -0,0 +1,55 @@ +package code.name.monkey.retromusic.views; + +import android.app.Activity; +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.MediaRouteButton; +import android.util.AttributeSet; +import android.view.ContextThemeWrapper; + +import code.name.monkey.appthemehelper.util.ATHUtil; +import code.name.monkey.retromusic.R; +import code.name.monkey.retromusic.RetroApplication; +import code.name.monkey.retromusic.util.NavigationUtil; + +public class CustomMediaRouteButton extends MediaRouteButton { + + @Nullable + private Activity activity; + + public CustomMediaRouteButton(Context context) { + this(context, null); + } + + public CustomMediaRouteButton(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.mediaRouteButtonStyle); + } + + public CustomMediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(getThemedContext(context), attrs, defStyleAttr); + } + + private static Context getThemedContext(Context context) { + return ATHUtil.isWindowBackgroundDark(context) ? + new ContextThemeWrapper(new ContextThemeWrapper(context, R.style.Theme_AppCompat), R.style.Theme_MediaRouter) : + new ContextThemeWrapper(new ContextThemeWrapper(context, R.style.Theme_AppCompat_Light), R.style.Theme_MediaRouter); + } + + public void setActivity(@NonNull Activity activity) { + this.activity = activity; + } + + @Override + public boolean showDialog() { + + if (!RetroApplication.isProVersion()) { + if (activity != null) { + NavigationUtil.goToProVersion((Activity) getContext()); + } + return false; + } + + return super.showDialog(); + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/views/ThemeableMediaRouteActionProvider.java b/app/src/main/java/code/name/monkey/retromusic/views/ThemeableMediaRouteActionProvider.java new file mode 100644 index 000000000..43bb21271 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/views/ThemeableMediaRouteActionProvider.java @@ -0,0 +1,49 @@ +package code.name.monkey.retromusic.views; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.support.annotation.Nullable; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.app.MediaRouteActionProvider; +import android.support.v7.app.MediaRouteButton; +import android.support.v7.view.ContextThemeWrapper; + +import code.name.monkey.appthemehelper.ThemeStore; + +public class ThemeableMediaRouteActionProvider extends MediaRouteActionProvider { + public ThemeableMediaRouteActionProvider(Context context) { + super(context); + } + + @Override + public MediaRouteButton onCreateMediaRouteButton() { + MediaRouteButton button = super.onCreateMediaRouteButton(); + colorWorkaroundForCastIcon(button); + return button; + } + + @Nullable + @Override + public MediaRouteButton getMediaRouteButton() { + MediaRouteButton button = super.getMediaRouteButton(); + colorWorkaroundForCastIcon(button); + return button; + } + + private void colorWorkaroundForCastIcon(MediaRouteButton button) { + if (button == null) return; + Context castContext = new ContextThemeWrapper(getContext(), android.support.v7.mediarouter.R.style.Theme_MediaRouter); + + TypedArray a = castContext.obtainStyledAttributes(null, + android.support.v7.mediarouter.R.styleable.MediaRouteButton, android.support.v7.mediarouter.R.attr.mediaRouteButtonStyle, 0); + Drawable drawable = a.getDrawable( + android.support.v7.mediarouter.R.styleable.MediaRouteButton_externalRouteEnabledDrawable); + a.recycle(); + if (drawable != null) { + DrawableCompat.setTint(drawable, ThemeStore.textColorPrimary(getContext())); + drawable.setState(button.getDrawableState()); + button.setRemoteIndicatorDrawable(drawable); + } + } +} \ No newline at end of file diff --git a/app/src/main/res/animator/appbar_elevation.xml b/app/src/main/res/animator/appbar_elevation.xml new file mode 100644 index 000000000..5a9fd8f03 --- /dev/null +++ b/app/src/main/res/animator/appbar_elevation.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_circular_top_corners.xml b/app/src/main/res/drawable/bg_circular_top_corners.xml index 38b8f58b0..3fe25d2f2 100644 --- a/app/src/main/res/drawable/bg_circular_top_corners.xml +++ b/app/src/main/res/drawable/bg_circular_top_corners.xml @@ -5,5 +5,5 @@ android:topLeftRadius="12dp" android:topRightRadius="12dp" /> - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_navigation_item_colors.xml b/app/src/main/res/drawable/bottom_navigation_item_colors.xml new file mode 100644 index 000000000..330a0f98a --- /dev/null +++ b/app/src/main/res/drawable/bottom_navigation_item_colors.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/et_bg_circular_top_corners.xml b/app/src/main/res/drawable/et_bg_circular_top_corners.xml new file mode 100644 index 000000000..7f17d0598 --- /dev/null +++ b/app/src/main/res/drawable/et_bg_circular_top_corners.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_bug_report_white_24dp.xml b/app/src/main/res/drawable/ic_bug_report_white_24dp.xml new file mode 100644 index 000000000..d2c0c38cd --- /dev/null +++ b/app/src/main/res/drawable/ic_bug_report_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_keyboard_backspace_black_24dp.xml b/app/src/main/res/drawable/ic_keyboard_backspace_black_24dp.xml index e12714643..f71b92981 100644 --- a/app/src/main/res/drawable/ic_keyboard_backspace_black_24dp.xml +++ b/app/src/main/res/drawable/ic_keyboard_backspace_black_24dp.xml @@ -2,11 +2,11 @@ + android:viewportWidth="24" + android:viewportHeight="24"> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_white_24dp.xml b/app/src/main/res/drawable/ic_menu_white_24dp.xml index 046b129d5..2cdf1b836 100644 --- a/app/src/main/res/drawable/ic_menu_white_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_white_24dp.xml @@ -2,11 +2,11 @@ + android:viewportWidth="24" + android:viewportHeight="24"> \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_main_drawer_layout.xml b/app/src/main/res/layout-land/activity_main_drawer_layout.xml deleted file mode 100644 index 647dbe98d..000000000 --- a/app/src/main/res/layout-land/activity_main_drawer_layout.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_settings.xml b/app/src/main/res/layout-land/activity_settings.xml index 9559b7fbd..ecb848b94 100644 --- a/app/src/main/res/layout-land/activity_settings.xml +++ b/app/src/main/res/layout-land/activity_settings.xml @@ -1,9 +1,9 @@ + android:layout_height="match_parent"> + app:title="" + tools:ignore="UnusedAttribute"> + style="@style/BigTitleTextAppearance" + android:text="@string/action_settings" /> diff --git a/app/src/main/res/layout-land/fragment_banner_home.xml b/app/src/main/res/layout-land/fragment_banner_home.xml index 773a6607b..7cf177d7e 100644 --- a/app/src/main/res/layout-land/fragment_banner_home.xml +++ b/app/src/main/res/layout-land/fragment_banner_home.xml @@ -16,102 +16,87 @@ - + android:layout_marginStart="60dp" + android:layout_marginEnd="60dp"> - + + + + + + android:layout_weight="0" + android:elevation="0dp" + app:elevation="0dp" + tools:ignore="UnusedAttribute"> - + android:layout_height="wrap_content" + app:layout_scrollFlags="scroll|enterAlways" + app:titleEnabled="false"> - - - - - + + style="@style/BigTitleTextAppearance" + android:text="@string/app_name" /> - - - + + + - - - - - - + android:layout_marginStart="60dp" + android:layout_marginEnd="60dp" + android:layout_weight="1" + android:background="@drawable/bg_circular_top_corners" + android:elevation="@dimen/card_elevation" + app:behavior_overlapTop="24dp" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> - + - - - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/fragment_home.xml b/app/src/main/res/layout-land/fragment_home.xml index 3790019aa..036dc18e9 100644 --- a/app/src/main/res/layout-land/fragment_home.xml +++ b/app/src/main/res/layout-land/fragment_home.xml @@ -41,7 +41,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout-land/retro_backdrop.xml b/app/src/main/res/layout-land/retro_backdrop.xml new file mode 100644 index 000000000..30c226f47 --- /dev/null +++ b/app/src/main/res/layout-land/retro_backdrop.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-sw600dp/fragment_banner_home.xml b/app/src/main/res/layout-sw600dp/fragment_banner_home.xml deleted file mode 100644 index d57f8248d..000000000 --- a/app/src/main/res/layout-sw600dp/fragment_banner_home.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-sw600dp/retro_backdrop.xml b/app/src/main/res/layout-sw600dp/retro_backdrop.xml new file mode 100644 index 000000000..92854c2a3 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/retro_backdrop.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-sw600dp-land/activity_album_tag_editor.xml b/app/src/main/res/layout-xlarge-land/activity_album_tag_editor.xml similarity index 100% rename from app/src/main/res/layout-sw600dp-land/activity_album_tag_editor.xml rename to app/src/main/res/layout-xlarge-land/activity_album_tag_editor.xml diff --git a/app/src/main/res/layout-xlarge-land/fragment_banner_home.xml b/app/src/main/res/layout-xlarge-land/fragment_banner_home.xml index 6bf6f946d..3711b5cbf 100644 --- a/app/src/main/res/layout-xlarge-land/fragment_banner_home.xml +++ b/app/src/main/res/layout-xlarge-land/fragment_banner_home.xml @@ -1,116 +1,116 @@ - - + android:layout_height="match_parent" + android:orientation="vertical"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + tools:ignore="UnusedAttribute"> - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-xlarge-land/fragment_home.xml b/app/src/main/res/layout-xlarge-land/fragment_home.xml index d54e05e0c..5950ab0b5 100644 --- a/app/src/main/res/layout-xlarge-land/fragment_home.xml +++ b/app/src/main/res/layout-xlarge-land/fragment_home.xml @@ -89,6 +89,7 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-xlarge/activity_song_tag_editor.xml b/app/src/main/res/layout-xlarge/activity_song_tag_editor.xml deleted file mode 100644 index 785e03e5a..000000000 --- a/app/src/main/res/layout-xlarge/activity_song_tag_editor.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-xlarge/fragment_home.xml b/app/src/main/res/layout-xlarge/fragment_home.xml index f2a630b53..2c3145ce4 100644 --- a/app/src/main/res/layout-xlarge/fragment_home.xml +++ b/app/src/main/res/layout-xlarge/fragment_home.xml @@ -89,6 +89,7 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/abs_playlists.xml b/app/src/main/res/layout/abs_playlists.xml index d10650a2b..fc23462d9 100644 --- a/app/src/main/res/layout/abs_playlists.xml +++ b/app/src/main/res/layout/abs_playlists.xml @@ -1,10 +1,42 @@ + + + + + + + + + + + + + android:padding="12dp"> diff --git a/app/src/main/res/layout/activity_artist_details.xml b/app/src/main/res/layout/activity_artist_details.xml index 7fb84d541..fb99ea637 100755 --- a/app/src/main/res/layout/activity_artist_details.xml +++ b/app/src/main/res/layout/activity_artist_details.xml @@ -109,10 +109,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:layout_marginStart="16dp" - android:layout_marginTop="8dp" - android:layout_marginEnd="16dp" - android:layout_marginBottom="8dp" + android:layout_margin="16dp" android:layout_weight="0" android:src="@drawable/ic_shuffle_white_24dp" /> diff --git a/app/src/main/res/layout/activity_donation.xml b/app/src/main/res/layout/activity_donation.xml index 4c3170b6d..ee921c64b 100644 --- a/app/src/main/res/layout/activity_donation.xml +++ b/app/src/main/res/layout/activity_donation.xml @@ -28,14 +28,8 @@ + style="@style/BigTitleTextAppearance" + android:text="@string/support_development" /> diff --git a/app/src/main/res/layout/activity_license.xml b/app/src/main/res/layout/activity_license.xml index 95760a6a5..5cd4e2edb 100644 --- a/app/src/main/res/layout/activity_license.xml +++ b/app/src/main/res/layout/activity_license.xml @@ -30,14 +30,8 @@ + style="@style/BigTitleTextAppearance" + android:text="@string/licenses" /> diff --git a/app/src/main/res/layout/activity_main_drawer_layout.xml b/app/src/main/res/layout/activity_main_drawer_layout.xml index 647dbe98d..25b60e471 100644 --- a/app/src/main/res/layout/activity_main_drawer_layout.xml +++ b/app/src/main/res/layout/activity_main_drawer_layout.xml @@ -3,7 +3,8 @@ android:id="@+id/parent_container" android:layout_width="match_parent" android:layout_height="match_parent" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="true" + android:orientation="vertical"> + style="@style/BigTitleTextAppearance" + android:text="@string/queue" /> diff --git a/app/src/main/res/layout/activity_playlist_detail.xml b/app/src/main/res/layout/activity_playlist_detail.xml index 287b03e20..68a1067d3 100644 --- a/app/src/main/res/layout/activity_playlist_detail.xml +++ b/app/src/main/res/layout/activity_playlist_detail.xml @@ -37,7 +37,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - @@ -51,14 +51,7 @@ + style="@style/BigTitleTextAppearance" /> diff --git a/app/src/main/res/layout/activity_pro_version.xml b/app/src/main/res/layout/activity_pro_version.xml index 5fb360d87..52f21e8f7 100644 --- a/app/src/main/res/layout/activity_pro_version.xml +++ b/app/src/main/res/layout/activity_pro_version.xml @@ -19,8 +19,8 @@ + android:layout_below="@+id/status_bar_container" + android:layout_alignParentStart="true"> - + android:layout_height="wrap_content" + app:layout_scrollFlags="scroll|enterAlways" + app:titleEnabled="false"> - + android:layout_height="wrap_content"> - + - + + + + + + + + + - + android:layout_height="match_parent" + android:orientation="vertical"> - + app:cardCornerRadius="8dp" + app:cardUseCompatPadding="true"> - + android:gravity="center_vertical"> - + - - - - - - - - - - - - - - - - - - - + android:layout_weight="1" + android:background="@null" + android:hint="@string/search_hint" + android:inputType="text|textAutoComplete" /> + + + + android:layout_height="wrap_content"> - + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 08662ec0f..05c9f9107 100755 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -7,10 +7,10 @@ + android:layout_height="wrap_content" + android:elevation="0dp" + app:elevation="0dp"> + style="@style/BigTitleTextAppearance" + android:text="@string/action_settings" /> diff --git a/app/src/main/res/layout/activity_song_tag_editor.xml b/app/src/main/res/layout/activity_song_tag_editor.xml index 7aafe1c0a..88767dcf6 100755 --- a/app/src/main/res/layout/activity_song_tag_editor.xml +++ b/app/src/main/res/layout/activity_song_tag_editor.xml @@ -1,5 +1,6 @@ + android:layout_height="wrap_content" + android:elevation="0dp" + app:elevation="0dp"> - + android:layout_height="wrap_content" + tools:ignore="UnusedAttribute"> - + app:layout_scrollFlags="scroll|enterAlwaysCollapsed" + app:titleEnabled="false"> - + - + + - - + + + + + + + + + + + diff --git a/app/src/main/res/layout/card_social.xml b/app/src/main/res/layout/card_social.xml index d08070782..ca0d8e9ab 100644 --- a/app/src/main/res/layout/card_social.xml +++ b/app/src/main/res/layout/card_social.xml @@ -39,7 +39,6 @@ android:background="?attr/rectSelector" android:clickable="true" android:focusable="true" - android:gravity="center_vertical" android:minHeight="@dimen/md_listitem_height" android:orientation="horizontal" tools:ignore="PrivateResource"> @@ -55,10 +54,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingBottom="8dp" - android:paddingEnd="16dp" android:paddingStart="16dp" - android:paddingTop="8dp"> + android:paddingTop="8dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp"> @@ -100,10 +98,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingBottom="8dp" - android:paddingEnd="16dp" android:paddingStart="16dp" - android:paddingTop="8dp"> + android:paddingTop="8dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp"> @@ -145,10 +142,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingBottom="8dp" - android:paddingEnd="16dp" android:paddingStart="16dp" - android:paddingTop="8dp"> + android:paddingTop="8dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp"> @@ -189,10 +185,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingBottom="8dp" - android:paddingEnd="16dp" android:paddingStart="16dp" - android:paddingTop="8dp"> + android:paddingTop="8dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp"> @@ -234,10 +229,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingBottom="8dp" - android:paddingEnd="16dp" android:paddingStart="16dp" - android:paddingTop="8dp"> + android:paddingTop="8dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp"> diff --git a/app/src/main/res/layout/dialog_sleep_timer.xml b/app/src/main/res/layout/dialog_sleep_timer.xml index 639644ea9..d17e13ea0 100644 --- a/app/src/main/res/layout/dialog_sleep_timer.xml +++ b/app/src/main/res/layout/dialog_sleep_timer.xml @@ -74,6 +74,7 @@ android:layout_gravity="end|bottom" app:cardBackgroundColor="@color/md_red_A400" app:cardCornerRadius="6dp" + android:id="@+id/action_cancel_container" app:cardUseCompatPadding="true"> - + - + + + android:layout_weight="0" + android:elevation="0dp" + app:elevation="0dp" + tools:ignore="UnusedAttribute"> - + - - - + + style="@style/BigTitleTextAppearance" + android:text="@string/app_name" /> - - - + + + - - - - - - + android:layout_weight="1" + android:background="@drawable/bg_circular_top_corners" + android:elevation="@dimen/card_elevation" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> - + - - - + + + + + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_cast_mini_controller.xml b/app/src/main/res/layout/fragment_cast_mini_controller.xml index 3ea2a5ac3..746ccf76e 100644 --- a/app/src/main/res/layout/fragment_cast_mini_controller.xml +++ b/app/src/main/res/layout/fragment_cast_mini_controller.xml @@ -6,5 +6,4 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_gravity="bottom" - android:gravity="bottom" - android:visibility="gone" /> \ No newline at end of file + android:gravity="bottom" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_folder.xml b/app/src/main/res/layout/fragment_folder.xml index 7964f5c91..d63a6e790 100644 --- a/app/src/main/res/layout/fragment_folder.xml +++ b/app/src/main/res/layout/fragment_folder.xml @@ -16,99 +16,81 @@ - - + - + + - - + android:layout_weight="0" + android:elevation="0dp" + app:elevation="0dp"> - - - + - - - + + - + - - - - - + android:layout_weight="1" + android:background="@drawable/bg_circular_top_corners" + android:elevation="@dimen/card_elevation"> - + - + - + + + + + + - - + android:layout_gravity="center" + android:gravity="center" + android:text="@string/app_name" + android:textAppearance="@style/TextAppearance.AppCompat.Title" + tools:ignore="MissingPrefix" /> - + - - - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index 26c9475da..0342a8dfd 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -9,88 +9,90 @@ - + android:layout_height="match_parent" + android:layout_weight="1"> - + + + android:layout_height="match_parent" + android:orientation="vertical"> - + android:layout_weight="0" + android:elevation="0dp" + app:elevation="0dp"> + + + + + + - - - - - - - - - - - - + - - + android:layout_height="match_parent" + android:layout_weight="1" + android:background="@drawable/bg_circular_top_corners" + android:elevation="@dimen/card_elevation" + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> + + - + - + diff --git a/app/src/main/res/layout/fragment_mini_player.xml b/app/src/main/res/layout/fragment_mini_player.xml index 75b0e0a80..daed4b303 100644 --- a/app/src/main/res/layout/fragment_mini_player.xml +++ b/app/src/main/res/layout/fragment_mini_player.xml @@ -1,63 +1,83 @@ - - + android:layout_height="48dp" + android:background="@android:color/transparent" + android:clickable="true" + android:focusable="false" + android:transitionName="@string/transition_mini_player" + tools:ignore="UnusedAttribute"> - + - + - + - + - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/home_section_content.xml b/app/src/main/res/layout/home_section_content.xml index 3270786e3..e82fc7538 100644 --- a/app/src/main/res/layout/home_section_content.xml +++ b/app/src/main/res/layout/home_section_content.xml @@ -13,7 +13,7 @@ android:orientation="vertical" android:visibility="gone"> - - - - - - - - + android:orientation="vertical"> + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/retro_backdrop.xml b/app/src/main/res/layout/retro_backdrop.xml new file mode 100644 index 000000000..074b723b7 --- /dev/null +++ b/app/src/main/res/layout/retro_backdrop.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/sliding_music_panel_layout.xml b/app/src/main/res/layout/sliding_music_panel_layout.xml index e517d1f02..36bea509f 100644 --- a/app/src/main/res/layout/sliding_music_panel_layout.xml +++ b/app/src/main/res/layout/sliding_music_panel_layout.xml @@ -1,22 +1,22 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> - + sothree:umanoShadowHeight="0dp"> + - - - - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/user_action_details.xml b/app/src/main/res/layout/user_action_details.xml index 254eb8108..033a94970 100644 --- a/app/src/main/res/layout/user_action_details.xml +++ b/app/src/main/res/layout/user_action_details.xml @@ -42,10 +42,11 @@ android:textStyle="bold" tools:ignore="MissingPrefix" /> - - - - - - - - - - - - - + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_album_detail.xml b/app/src/main/res/menu/menu_album_detail.xml index 72bfb1f4f..94f789f83 100644 --- a/app/src/main/res/menu/menu_album_detail.xml +++ b/app/src/main/res/menu/menu_album_detail.xml @@ -11,10 +11,10 @@ + android:title="@string/sort_order_a_z" /> + android:title="@string/sort_order_z_a" /> diff --git a/app/src/main/res/menu/menu_cast.xml b/app/src/main/res/menu/menu_cast.xml index 4bd8de27b..7b49bc1e3 100644 --- a/app/src/main/res/menu/menu_cast.xml +++ b/app/src/main/res/menu/menu_cast.xml @@ -6,7 +6,7 @@ \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index cb9fa8a4d..2d2ce665f 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -3,6 +3,12 @@ xmlns:tools="http://schemas.android.com/tools" tools:context=".DrawerActivity"> + + - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 4ae7d1237..a55dbb7d0 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1 @@ - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index e458a06e95be8c61a7a96ab05cfe126c7459438b..ad11744fc07ca2ce40a55d9f2702dd549ec0100d 100644 GIT binary patch literal 4430 zcmV-U5wY%xP)^mVq0wF;ni__YN(^gxZPOT$jwF(F*t|%>TbwjPz*4?(&T@YEM zwV!dSbsOumw)&}c-n2en$F|~VpCj%HNOISlch1cX4XZ2((0TXs`F$>AspeZ&X2# zA|G(u=>z_G{!9poe%^wQ(Q}P`W^NOo`KvgrPO*I~-xr=N^x_BY@#1F}cya~1z4@

_qKAZTDqi8)~Z`3tMDu_GjTk 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 RoizXe002ovPDHLkV1nyDJ=p*N diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index e458a06e95be8c61a7a96ab05cfe126c7459438b..ad11744fc07ca2ce40a55d9f2702dd549ec0100d 100644 GIT binary patch literal 4430 zcmV-U5wY%xP)^mVq0wF;ni__YN(^gxZPOT$jwF(F*t|%>TbwjPz*4?(&T@YEM zwV!dSbsOumw)&}c-n2en$F|~VpCj%HNOISlch1cX4XZ2((0TXs`F$>AspeZ&X2# zA|G(u=>z_G{!9poe%^wQ(Q}P`W^NOo`KvgrPO*I~-xr=N^x_BY@#1F}cya~1z4@

_qKAZTDqi8)~Z`3tMDu_GjTk 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 delta 2799 zcmV8-Gh$3!PQ&Wv1I7U=NiFP@SNgC4}mBbp_T1#DyN2#e16%h<50`Gsz z+}`_U9?swl3`@leP96n+tu~*wQ=@q z>9aoH*DK!#9y;m+{oR7}g+Zl5!GDjsEzF8h90LAZ@oygP)1U73y#g;E%4tje__R{B zShU-pKeo${RhI;Cbsq%qjXMJQ#_fSz-S%Oux;ThGRvaW26$bHXTf#Z5%YBm{01>i@ zv&#Ebd}^7RJAb@a&6@Z4@vz&UK?%mr00tjm>KN!x-rsSsmJFeklF5yET;9yVdT7slW>;eYJH#&AAE$q8DGKFbg1#N4mu zE&ElhvP{LGjDV{-2}9WB&m?E%t)7$*LFF-I@F7gv7{&=3!%&>)1`Tg< z_v~JSFCWU!L+~Oh4|`?cZK6YY*)}EK*hT^GP<~^$tUhcA<3t26t&3o=E`k@=L~!$Z zO~MsCSAU4$fxwrk+61(XO$P7h3f|fi@TB~30?uq}HJq?k!(g?R!AebAQIAPDSA%jD zDCau&lSgvo~hY;jkNb#10bMovT z+P00r?^U%)WSfrQyRyyJVDG^z%C92zwM@#_aeq?2o`-kzO~nccA8!&{sqy8$++0O@ z7x4aGD&JG^sC)qe&)4zN@<>6-({r#$dn;S1PF~gdvLUUBO2T$#8*dYKvmUQW!gvO+ z!zdid;N3{~;8zfMjOCF$EYmY7&mh2($W}31fB9*`HpA0C;q3c;s@6)Ugzc4Wwp$qd zvwuW@Yo|o0J#CMx`2NIpTop^p{mLugG4c#(Ap;X~jXW$c-1<90d#EGU`>eB1)sjwi z-eDVCw`}7kVK06rf+R_9gSSo|(A=kj$a*cwt$k+z6^fj4ot)wtI z6aWLueA_PAH!0UTzq5yRp1Uh~B+zi}E`Q7ktwQk3jq(T{m0x1wVUdZ6i_8MdH8xxv zAMxoE_pO4Weu=6vmD&S&hwYEXCA>X#(3L|VG^Wlb1O`Xte)z! z3eIJ7x2pMYsl7!I`l`-*0)M5t9#)M11adT2@3&0{@HrR@M=&8fiidPV?H5We$bV&3 z+2x9SYcyDKnn`)3x9L@T;3$;};L~4UhQj$3uyyWn`0dOuA?K%`Lzd<=0_Qqa-VUCJ z>=C?_9VJ4hxv6<<#Brm%xuqa>rF2?-Z%%cz#I`3D(Lq+f=4w*jei-Sy_Q09IA|%iw;(F%w{a$r6Is)9@TmN3BrrQpfGIHzbuWdMgtik{sX95_8qoS3*@ljR zeb!a#?h*LhL7>~VQNUx&ieb{scmbxxG&R1U{hhv@K#p&PrZ}+mJ5rv`u)XQ$JN7ot z1Qr@{J{`1qcsqg7Pc9z3CGbu) zDNoab?t(A=IS-e<5<%cycU!JSbRjSw3G@^^#f&&6%|HSZVsBg;u76HdIrnpqycbx1 zoRp{0M|W&fn9M`XDY4U@hvM*uXJDSGru()bjQVdSVjkE&+M}Af zKHzGJoUHb_jn@f#bfa*e@}3CHMFL(Yth4e6emW6|XVMh3>TBkzy@+{0GBtFLNm##k z{Hih?wxOYu&D&~o{eNuN>MI1Y%r)J&4FR`@ZAzkGo0QN3FB(2uK$u-xiHW|uV>X4f z+*XE8-d3Btwr1Y*wA%JggH6+Xn*H+deEBS z>qkg=d1B^eov;7KyA>$gTnU+`Yu&dEfv2!AVRDiHuZ=igfqz(^beU2pd5{%wYz~4a zpeo3;BPm!z|z#iXrZR=D~~Zbk+-EKNwx`zEs(7LJ=v#!vh%T0Ii3YP zmJqZ$to~yAxUZ|x2gbHB9MoO%OhObzIC|(jq?(SvyeQ5q@XpGoSp=Ar#EX-XM0h>+ z+v=wtjSh8tuzz$YvuEU3N(6PMJtkotdIP8gHj&4-*A`R9dPIZ4XXtg>T!utMjqpXXrZ&uZb%J8Gcn z_%%3x>KdH*q8f?|zJxdQpCI@f-qrbz;L-D`NxYCcQh$I~qR*c~Y{Tqh#sluhjE+@! zZZu)ur^az}Suy%(#EUbi;ojYeaL8Y(wBD zCNXj1ND1DEZ(z~ED^dtsT-?ddzW{NNcxp=EN2?1ocTs)8R-omif=Ex%7IJLgJNjm{jVRA5ml&9>x3=l1{C^}1Z=-}I@MN3k4PTJ*5rjeBaeMZa zs{BDB5&A~(!F3Du^&BamYZ7gkv_g)F*}B!wyWON1Zc@BJrIM=6n+Z3$XLaT4>6yI( z?`kLG!{oz}LvzNaYtNm`F*F0sD&IDXHhE$;f=Tm7@HVe)Lyei-+c1H4SlXl}Snk3r zF@NVy8vNfLZ}+_M;cnZelhp2)Vmv@3Xc7?ltB|sS4E?2x^GtVSG8ZYHBkg=%*gZO6Psa7?4^r8(d}J{_cqWojPkSo4?jbg zVXwhtDZhaOd}DqQV$GePJynrms%y%OYJY|~(VVP2jXq|_i7+!>MCCO7 z1P-Vr9vffZ^s@P170+m`xdZTCbWgA9d|#}FgUDkTY7Cub@LQu_3@gbSqpc_(Z#Y{y z$@FdYwraTM?=h zDaLaTJMu(X9s2#l4;}Hi<8K4V-*l3)SC`)V{C@$za9Icbe$D^@002ovPDHLkV1m@( BhZ+C? diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index 40696361fe857e9960f56fda900edee706f46245..a46501728a4b1b459cf8577e5ba501b37389d068 100644 GIT binary patch delta 676 zcmV;V0$cs?1>OaaBx^ zzOV58z;h9Q9-c4G`RBFSk&zUC003B(uImn&S(!B%^|wj2+wG-|jg8ZCS3BD>Kh~~J z6#XAGO>5+Gxf^oC!Pd+Tc2D1?X0uuK`Ft1T*a(|5FW4u&2hnKsp&Z-C=FAJG^%5+R zNIaEeC)k{M!Fm!7habzalWcAXf|bkVZ*4 z5-gogzf)E9)z3+#QZFNs$eN-kcjWFanZ0byLczRV?|r%Xvdp<(=B&(7nK3qIv0(CN z@2=duzx%0%oliH0Cm09@f)R{h1OvecMlgbbU?7x7z;iT~{zWeK-`MKEi`Ixh23?*qY%kF{GStM`du#s^!}2S6}mV~xvf5^Q*a zP3r?9n6bfb6KuGGEesmW?RGB^%(!5;28ku_%QQt%=4|{j${5XN^Wq@EipAnrd6>=> zD&uU^0II4wA^ZED5Wb$<&5Llg<* z1sw(*I!Ik>C=5z3ISLjO#4ai|=^z9<2r`Jt4nY_Z7#-{oL7*#%#YWpqx3}Mz2M-}` zZ@ha)-w!;9bN~17d~p-?bvTic6Mp~z@SiaZcZfT0f z#)?ZzOSkp(ZEUQ$u`X#U#^zL#$>dFc|z%E;eVAV56g>(>|Z?zTfX3u1+8j81j0(cl6Zjnsb^xY|Tc&Mn*=S>0z&C z|GL?y>C|+vF`EU8L?YvQ_$Zqp7zjo%f)NY^BN)L527-ZL1cUh?XEhHsDb1YbO}Shi zTV7uF&&?^@qE7;0E2sTDA>w*=`1`CBkPjx95+q6KXQu$yemd+bJJw11I zse^3O0_Ae~v6*0ti;LfMnH!qjY|{eSZ1z<3H%^TPi^XD7y39q*7Js&BjkaR3_`H!= znM~%p)9DQ98pl|pg(fE_50px!kM+dT^GiOT?}?6IaBx?O^`g<}alJgg+f1xnE|(7m zgE1Yus@co-n)K4>cDuXM>GY#~KL6uyp?*s$m3r&(c!nLdJ1VRHL(grn&5Wk$)cexu z!NI|Scs%|hkw{EWOeRcBg~Q=-m&-MvuV2%2Yj(0O`)FSaa>!A;ncTbfvCC1r?UnB; fk&zP^0MM8}CiKCvCX|%I00000NkvXXu0mjfQ?^&* diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index 352f7800c7b5c041be08a23878c4b66e9ad18d94..715be81c549e2261331b8516bd51f98b17aaef2e 100644 GIT binary patch delta 2789 zcmVG>^X96m_-EIO?vW z;%F7*SwLD83KYttP$}glunS`;j~bvL1ZvUU8J~;m7P`ysA_FY$*kPr@D!O7J*g|Y_ z|NF?PMe*%BqxtqXqPey=Mzhws82)HojJRW43|~~6$Z7qqTW|}A zkWGA5KB(d+S%1~s;r(i6e=~xIy^##+F?L5W*oCn(nt=tQZZv}(F$}h2Y>j15i?Jn^ zx7Wn8!>=cZlQyd1PVYUkZ`@Yo7Flu8tX$wUhb0K(m9bgMZ&AvM)9z@+C@6&}s}=e)uNl zK{anasA9*hDh5^puI3~RVNWEJe3iF!r+geLk0FDPW74JsPS}*dBqRVElNfABWXCsZ zcypk8_Zxh}D1IJ-7g2fGF9Uaq9_3}*)CXf51-wW3O^LGlurYxX5xlfMnZf#GUR;~Z z&FeP_e}C}YHUtj@-l}p6Xd9OdKEfZoB^25A7l*l$+!S`mHt+By_SCn5(>T8))m4CuXRXQG)={o8Z5@Eq4wo>C8!nwJM z@_yhWgH%3L@Thz>0q7Z4mTMlu+?$ktoe$ME z*Zy}icnVRJ;Dg#P;Z^OmZj?vxsQgkR4}Xh|Ok8XdV6MS-ZbI^hciprKiu(1c_DR$p z$UE!+ZBl{2EAjrW&tS3UyB@(W!B}L%FbYzc$pJG`Tc`W2f^XT}tFj;7=WY>%fvWS} zfxmRI9hQy#0G4SxZnjMq@Z}f_)0j|}&O@>O(kDtT$YoX8<#vRBqrr+}C*_sirhk3_ zs*M8pLd*Bi)OZ$}-ue%`XZZ{&pZN$%wf{!otViYD;CU!Z-$JfVu39M3`OtM5dejwY>F>t`T%9I-f4k3XhskXk` zMgfoUawd~zI-hyY@D|1QsEIV8NGWb^)Kw zLrE5sN|3;`?2h(_llK_B1b^m^d@pu;)Q@eXJRQJ8vCXDz27j2t!|6r=&K(iqQ>y^h zVt)eVdRyOZqkzxBO$-SX=L#@2^UCG>Vrr7S1fIBi$;jI1>#d|bO%M7Ce#`wloc&k? zf%)&Y0)OLqW9=zehy+3fPcbu_Ni&ha#H{bnC#ff?eEYfEH%DJNMt{oF=%X*TA?42= z7C{oF9{V05u)tvJyKP8$0-wvn3?wikkB1l1|9c!U5BHAtsHSd+`nFz9R`=W{=!9)V zn>erc3|=8nX0!#Lu)fM8_?L)44wI(kGI+*xWi(CP0JHpQ*t}uG5wzw5N5wtVsiN2%-7>OuPH;PV5`mP zLt;<__HRA`^N>K$3F`|UZG*~7)661F%)NR&D}H@GVJ;{*GT67B9A|jXJY2in^D2CE!3HG{ehl-|9DP?FV`?7nD$18!6Z1NuK=%&WXK;b*xbxWlkr2SZf1^o$mtP0h<>88K#(y zVJqzDvu%pZsJoaCNy^i#vTJ*=G9_?7zr^5xB@dj3 z+Bu)Y?&>qJuH>&UIqL&F!+r_#(w)K8`L5v6^ON#eU)FClo>c(WZIN;$Ni zFctJRT7Oig6op}hv_w%QF~_VcwVjx-qO>A~NueA@La~hpy!4{k0gvdL8VF06j48-( zZ9MrvOO;R@|LMDxDV;kabM@CYU6r3==3SJq1fFd3h=2YQQa+h5$UAOmU#ZF;Bogt@#~xb0 zPXxmU@CigZ>pdFSrsfp$v;pxnlllsU-6WpFR z41evjZF)%UL4}4!B0-aY)TiRC)g`*~=jIu&%gWQlY<`A_^`4jH(FYyAG3X3S$3Qws z(P4!8`ImBdX?mUr)R>XyC*;}T*{t*D9!%N43a_oFdl=+<{RBTlnCY&;WT}YZL&7tE z6KAQIs6E|WVr=V}ooLB)wNwnjYtc1>s`CS}8Xh9UFw__+nvvt4cr2m5@>y+j!vy{3$EO&-Zk?Kb=~7W< zn{8@Vn{9IDrAsenUTl5N)N=f{`sRj*H1(Bel{d!n5fSnO?g* zKq&E2hYyL&9zEjWhZ24@_ECxmd`_>?|2*$edp;wds&46zJp!!=RfDR*`0zt8^<+Av~1gTBY000000NkvXXu0mjfVH<5k delta 2799 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 BhZ+C? diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index eb643e0a820b3d9d37baf9075e8b83b8815184a9..9f7cdd989b472d1e621025ae88a4607b53d4180d 100644 GIT binary patch literal 6406 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 delta 1622 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 bfqVQvkV?c8u~Kxu00000NkvXXu0mjfIo4Tn literal 10658 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 IM6N<$f^V@$Qvd(} diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index 1198d4eaf59b7341894e09c7ba5a12e9244afaa5..9f548f76b98649803ac934daa99f641f2cb5a04b 100644 GIT binary patch delta 2688 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 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 30ae7df30a919265daf150e834cb16b7ac8031c2..a8ffb49805b16556e864c2588cb6c93e16fd0f5a 100644 GIT binary patch literal 10625 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 bfqVQvkV?c8u~Kxu00000NkvXXu0mjfIo4Tn literal 10658 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 IM6N<$f^V@$Qvd(} diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 5e914389643e12075ecee1becdbed4aee776aef5..fc889b74ee11dbe3f8a13a3977e0139be132db14 100644 GIT binary patch literal 15851 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 literal 15934 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 P9AKdHRJ&QzA@=_O1LIG; diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index f63423f9ca76d33824fa73bee9b4d4c277ec4d1b..f029dd1b9196a611b3fbb3dca1e3c2d1df72fb78 100644 GIT binary patch literal 4607 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 literal 4792 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 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 5e914389643e12075ecee1becdbed4aee776aef5..fc889b74ee11dbe3f8a13a3977e0139be132db14 100644 GIT binary patch literal 15851 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 literal 15934 zcmZu&XCPeN(_d?KqW9i=3sItXf=CcGtlmY47H#$3MeigJA|!}jR!Q_u^s*vBlvr8T zE^GJo|Nefs_kKEe=FH5QGxM97lxI)%C`egI0RRAnfxfos{de$x4>95W+jPr^2mnwe zFwoX858XH`CkoAbu{jpE*jgR_-m~<-;_b8oeytq-Dx>wb(@84k8x3);R(co7^(pb= z6U|V6c+Yd{ciiJ&qEDNDS$9L=B@t(C>SZUE;O2_=*hQDum#>?0Bhmq&C1u* zAv-h%?c1?fPHc(zk+*y;r0v0-EqP!2Pk8F^?dvBi-`D#;eqLuL%|OPNm@8>EWGW3G z?J14azr5@V(o?uvKuR4m#op1Sif;a1F82+X>=G~IS&%{Wls&ACUgZ^12TIjCJj=G_ z5#mi%D&?&gJwh+L5d@Kc*DIyFTrnmU32_FgMC6qncS%gXA6DxvS+!rauFbiAK2@zY zJYsf%^EK1FE8mNEbXV98w`9uilu6&pipEiV527QF<%nOCcmTSQBkwJ9ac-od=JTQc z?&dF4AJAez$I2`0RvX)sSpnIkGWZMT)cM_HIo~fQLZT9tK@XOfjPJ3R4(0sec9R+n z!|jL)UiNAwbN6bExA%^WI}>9zg#cquxhDtZ{U`;CefRig2!=*aG{;HY?#Y1Kbm&T_ zi2Cn|T(rf$mlx4@bLR8Zg*j6X5+cgO+aGL4rvf4yUOfNB&fJ%^IBg}p zyG9E1~Gaf01 z1kwFq!M(b^z3a~+a(I{~o*7z`EZ9hlDhSt1At1gve^&Ht@D-cmORl3ocEq6|l{VgV zsagQrJ((}0*kmtQR+l8k=M&@i$867iik?e(^N^6H#Mq~STI5PZDEL52B|^tamwf;o z!;4=f<*-gkTtz5n`|?3D?tzK@bz-jBbz-(2!BN5*`xEJ2O%igIHd?$aVDP_rB~q+- zhbwjxu9u(HUSkypY7r=LSkcY9Jod=79!UXM>_0OVpwk1J<(h$Y@;u291fNwtHpvmW zclTE>*WPN5`x0YL!gc0rt3r}!=zyFx_TBubUBDKCmtqCHU5fMH@Szu8MvBh4pMs>}D&*>U@?h7yR zYb3NGpc@8aOV^#4g%iFbI7AU_y{rDlhFO}b52zcr9XW?2T z3}ODn1$9q0_Z?93-JDTYDZGhkmd?b8MrpqGhl$#A`XCWf`{&OEE$hcgrnJdk?M$?4 zM-ITNVTX~`{Bg*nTs2bIIqe`NclLG*AnL<3SX3GEcGFA+^WlP&1~`eOqXC4u=|q2r z?+4JUJi~B=9}>|_-w?$%blhqsn_Q<2>S?sWl8UN$Fs?u@wo-KY5!$;1ZD~QN2CPV< z7vYO%%GGs}`mD|a+h6+4^06sIHhWNu96M0ugvL4qhyyFBhO%T-zrbvxq2v)Kf_#;sjWUj*VZEY=WeCh55^Mb*&mUKPkl_zHE^0O_4*Z1zRo?Rv zg93NRvx#W@A?*R;sFlBKgiw4L=;6~)-#E_irrgVX2?T?wTm_S0HbZi_4UeET2X|xN z9_7pfP|gXZ16&c+aM!op0)pzCnAx03D{dotopTy<&rS*nLs`legmX@gmYEr{v84)dAzs+;^Tw zLt=~BbPPjNEUm9&{(D`vO7nv%s_!z>0UR^L2phA8K1TV@VjL?daUz0mu^(23KMRwA z)Y_A2@x+!3`W8))qa*gzYk0VV|E_^ZBI@%LSlu}huHv>CUQ%Xz9QgGxE4S5oW@K?6;ePK4UK6}27*j%Gp2daKn5a00F!gkHR|0n1EG*n&* z&Qz|rbZJmX6Yj4Gce&c*2zIjpFYA419_x%vC4E_H{HU*JTx zmFT98x$`6O8R~XHgc55M_rZz27aq+}+6K?Q*mgb^)dq3U1MVPJ6{%c~pOavzlwg$k zp$COGNW%8EJ;vCYgA>o9MseBMdCWGE41CZyeF`QYOvTW38UrbOw8=W`^%s4nINCK7 z^Mk_#fq5}vGN`PQ5=%S8w!K{=eB4&fDMU>SKPT-eO{{{tsbS*B5tbCt-jJZ?tXve5 zko6&s$NM4lDY=?(Y~`;#K|`JFq>9SbogP(}^$+bkEmWzwiUS z!M62ER=j^GbsjFTHbmYq`~+k5+waDv|0&nI$JesGCs4H_)Q~+YgpBNEb-e8{=GEqp zKTkGmTSgEkVnZf2XWg409Tb5z!7M>k7(b;^1Z{zB zVZ1?KcpOpTz-E%}BC%91z&y87-B86Rh|CG?>{&s7+!ycI+D!*ZR(u{h_I&&GkFvYv8aOW3l9Ty(d#Cbcbz*#w+0i0ks z5@Fe)GOIS))s5tI4s<%WKO>3vSL{BId}fWk)Vo2tvk%}~;T_^Zo}<;d5rOY(*N7eH z>;TvPO$mqF`kxx-^uMp{S{Hdc<&#BJAH_o+aK6bQcKdTa%s2DLcPj#L9J(9<04v0R zQ9H0iA``Wakl6=`*brl(ODW#V;3o$sTsu9LuG(Taq*#k6`ufp zovu$NZkE^CUhBksn)}d-f4aCa2e!?UmUv@P4)(4)YoU*#!!c%6?B~#@$_%?~@8ZZ) zAID2x)GrO(<@Drss$w`fH&|V^e2uRYOFASjIGn$J2#?!Wff)N{@uQ|<|8rydJFIC97p@xs0lVLgT1?c1$bjk8?!P_^@9->4em*!+f-_pV! zrqZ+kHPNVn_9`=m$2+$&PEIq5H% zz|+DYpSDK`+DxUvSHBYiS^3?p?HMqz8wZ=KLk4-@?f(Hi@5 zIUhfG7}heUX&uW%BDnLu`iKx%+{$qyf}c|Ti-Y2DKzlbFreamES1qjJ*oq%z`VLJ} z>SNNJ-=Rm2LnzR^es&o+BF@yP*n%e`@YN=#>}s7kRFW(P{4K=O?USFKQ-IG2O}KoA zaM?b(sR?;{uBD0fFG=Vah1n)6q50T~B53WGF=S-RI>e!76i>?I(_n&*!O9cUsqz|` zSksFyYq((8M@_XGeb1k^Nu{Wgon7!oDHsIt2)aWzK?~|vkcWE35I++d(&ojdcwjc2 z5dF}7v?#pqm}YuuOX~}3(=-Mt9`J$;QbrF)+&fFJuq`{LTJ)XSH=FI&D?HCoTvBkt z)|pwbVnY5Mx^Z)n&7#+)(7X^W-_8X)|6Epx79DMZq{9R1b{#s7{VYIa^ZKtLb0*8g zSj9)t5IK3$BKPV;cXCSytJBpHYtdhS>7AmcyvjXOnSI)d zqvxAa2H4{nL#Y=yab?bM=9H@ORIe;i6^JXI1Ik^n<~i2@pfw1BS$pi^M%y9sYu0y( z)%6o)xJStAq58<0Q^@WL|FzezB`w z#oAa5?-@fT?^WiHtCqU+n`S$sh!Q`Y zL#bhDye1#dNtUnh&psaYm};Kg-L2jRs%0*z;qc=wOSzSfrKU~6G0Ju+LI_KHSU}nt z^+=WzVbs%_22T%+WplX-B|5;FEGY5 z7jdkLSEl4oPdNHHt~!;{G3*o!uW4QrFUGxPg&mGyUeb{4Q$R+d*LXuakF+Fg*%^M^ zo&0?u7u97sj{VULx0uEV%pI*A9O0ibbjViTZAAhV&?EduwbmqYJUfY=AJw=mEv-JB zcY0g82f@cSNz{OxBWm~=Kr@=ZgT!ZZU@nG8yABDiP}~(a?yD}0)(Q}=Niw=*TiE4o zh+vi{g&h$IR(qqIn3ygYj9%Ogkq!FWmatc#-=fy_`HKIffYKk8@!ksUo}9L(Qk}PR z>@dIVN}Rz~^F`vE)Bh{kPMrw$I*QqC8G*9nG zl}A^YHlGB}?}5nT|K8AG6RJ1-LL3CjRVk_ZaH-L>ImDM~#8G8ue?DF#3@@3S&1Y;1 z75+)3^@3jSAFD9!1Uw2Pv3@YZqn7yRm@KWSB4#~s!=*?v@+W+q-V}4e z@f7f`x0!##+%QwRM+;CmyB&?4nA*F@)|uBEAPX>u4v}qa!g;{RckzcNJ@>Jnk;#7z z?lk9vRNHfURHYPBwNKYMxLt5BG!7SY&br53A+P>YEyMi2y-d?%%PKvdMwJ^3`M{Kc7n{b46Sja8VgJTH(k z(F1~!`?2q#>-A`lYGDVQ=|9sS%2BE4PH??U)k-Q2rFb; zaJFLHow0iW1LyN8yJjYz3Zv)|VrZ@4+^mQZLYO3@sfQ}FKABL*R=l1ykPuw2@ce!6 z^t~QV7#RyVxNQ9ye!8H<8PrAblM#CSilX~U?C{P&2Dt09jwpSpv=@Ys9xah;YPA61 z%Ah21rWS*+<$NjGphx6}wzd+9sZRRGHb?GOB-^}&nsu=6Li>{LjVh4~@r>b7KH+X& z%`Nc5W0F-LpEh}fr67P@O%Qk>WsA5q4+mr0p1Z|B8B6)#CPf%wDMRcm7j5uD0)!pK^v+MwHQbq?py(GhN# z?}wz6U1UxZ%3(9l+vK1y{cLYflA@%dD3FNI3woyP6& zhs;!qZ5(^e@Tff9)x8ebH@>?t+~67ug?-kC350T zXL{M0o}6GT0zEsd0o1O**C8h~g+I_uv79=jo)u+JM&PJ7MvT8J@QSukDCOQmyr57# z)GOj{EL-87@Mt(myR)A|rw*XpkN_@Na>-z^@L~OtduVxo|BxHX8jp+hovutRjigrA z8YQDsFq*n5)9wm~Y(=gCf9%qPg^0GQeql#L8qSVio_H3TnRitUTi}sv1B()MqfPOS zQa;K+E8byvERB6l6!G5EWasHFIOqAP`^_+-p2plfEO1m>!AB8%>3M8@BJeqh9+J?i zMp#i0c#xH=+dfuMYK{GB2nYoU1@xVzAk;T`wOL3M`v3y(>D1M>TR2PQGALm;$s62& zDCDV(v;+88uq@svaESh_u4hpoOb$|X4W5pSlQJ zBt~dk&VQCyU!BpYTdvDo!JB)69OW%uBa>W|u}rm#7iljIy+Y|5u7F+kBGJP48(Fb0 ze^mrOFegw;P4WB$C|a0UtxCG76)$pZFL7O$!+Srqc)?b+F#4tUw&M-3iXW2xT)5oS z{RLd|(`&&(72aqoKt(qCY-h*80Ptg|(q07cSn0Ri)eX{^iDo;8Yfh&&7mB=d! zr!L{K0BDl0c+Absl=)0YB0Xe|RQ;6`$1l6`sZU#*&L=8;!DQ-S^OH__qmC71GOTit z%;BXM_2mlWO-+lKY`h(H#|OGevMT$zM9Im{L{<~uLNj9ILp=Q-2@shyuNJ}6#Yg{_T5;J!I;;*eMkZW=2`y;o$L+)*La9w8jT30HH2kf#s#GW+`N4Q6ktFU^4WVqvE@h$|f*%o&uV;e*78Q zWc`%D$?4Sc0&w>h55q~L?z{2BI{&cJrRi)!NHY4u3XlRR_?j(w;?V|}=VOBSkGSKj z`Xs>QNrCYD-K`Gbb(TI1~owwf%41f#sp{? z0Q=rXM=TuJRwgm$?a>6_paDtE;70W9v$#^ksb3JQlLld#u8l)|8<6JHm@P*JzGz7w zT6HzP=rR!yEN6^~eGmN|jsP;PvXOB8F=>7A&8Y?O=`kam$}fh+_ySTlvqIdeO{MPJ zl{eLY4@g}TmJ(1+kiZD|0G-C6c`v^-F4?1=ryC!Fq@*rPoC|*bepTeU1GE;5E_jGc zV>KoXt>vFY=~w|uzX7v{rAL_e4-H= zg3|##5k6X_1X2V`6VEtn3!$4wU#tNtrDF5G*@Z712&$x{+h-At z-ha_xxvJIq+-ToB{ZP@Qc*?_P@ChI(HcxLTT*hGSQEvRB^7&N(+gCQOoq(UIUU$t; z8&#mhSm{{6L*gTL+&+EeJUp%JjU)*Pdig; ztZM_v=S*j~T8FMo<;lpIAvxWy>!A5Gz;k$&x|)lLE1jH)j7g|(XfU6aE}}m_Ud7q? z%hwE&e?N_U+BVFR?sW|2i%uzdh}RXC<6WX;8QS;{!-FEgb8&w+Xm&9+n<)R+V^B=g z_3Xq}9Iz)vo(c=1z8;_@8>FuQe+c>CZN@qMD zbznVx|KqcGh#rLIX{1qG@iuoz$O8FRpZ0LmlpHD7$5z1m&Bmpa53>gadvm%g?nZA? znRB8TE9N4QhO6h`grLyS-`C+Y=>0|QO;D$<-vT|hG0sD!!fhBDBcGn8G1 zjqY2kPac3{mXs-d&XjPP^7cwmEZUfKQ7q*SBM}P;vIWjUwD664Ik6%HuyS~TD|Y(f zz3>+gVuTs1>`otSamlOy-u=uBnM=3=CKq0fj3nZwvuop80CL*7Oc8B$vLBla8fZr# z|I`*^c547X(|r8wcr@nJT1Zi2>m_J3({`CGFP=K?{nA0fg6JEmk^kk)J`C!)pJ8PF`^|C1xGZcVzUV9h} zOEReY@kL+g17xxRZU*Cs%^&^Mi5>2N2}}tD**XhxOU=X1C7rv&r~k-rH+TV@`C|8{ zGkfdHzRVgmSFS8Ce5So6Xaeg)ljr5*h(u#9Va-m$vqBRKioW97z8R)5ZXQke*kzbNsqD& ztBIsVy6L(u%qv}hifM9`*xFdBvVSZWJcI(%-88lD4s(@!U(?qS{WOyGMR)u5a3v2OjI)+a-dWbEW z={{x)zvdsO^b@kFMqSfd0CEI+EZCj3*KzxdA1sky`Jx}h0s zA%Fd)_!%c=NJE0UvU{=!FQioubj1tBI2M$wageK}Qjg=ZCY`45%p^6Hu4avktg*R$ zVI2kPQ!ip`TzoMXK4F^k;c+Xvxyv@Pl`!2r^FIpk$~+p|DA8J$awn^ z)Ng59Wh$G~TIZ=>9**mzPMyC7axs~d!!49Ydha|B|QHUqYTd|!~QFd zVP37S-P_hG!;MY1Vxi3{$httDbfuF?*FPFZ)~Yh6C(Pb81|XFO!>5_k=SP1j!u9fU zD*^jmgJdvazG&(DSVCuU=f|JHGm0OsSn8v<+5(;&>#;Ed!tPZG0Tj)+sS%bs!Z-5Y zH&yYP6{wLfCQV=S3w`PZ+uvH&lHu&)bYoVtDRQUdI|(DhAEXQNx1Y3qRCvR~xnW5a z?b0d#esiF4sd+kUZ%k7i3$ZF73FC_lw`G+5PtdL1x=kCBgX=T|d_Y7ySig40dK3pZ z{>q=T!qYDH$m%cCT<&z=Wm;?!?EjPjhW6icd=Dq9QOu=+V4ogCWo!$VJ%*>U0K7I1 zc-c~Y1jdxpU3gi4C^@v!M*}{!PH&vyre^S8hyRf(W+CGW2tyT0eoB=a^5XZ@g_o8` z(KqCkTAVRywr;b&mxvxAGWh~L`Vc`hdUT)c(HZtw9xBugndG}g-2+5#waG@elzz*8 zO}6f3liBE!p7M$-LE%yn07n(|odv((+uxJDtCpg|>Y5H))`#HJ=uF>}-aH>e+6FR* zxsuP>fC6s3J|bs|Ln}OeuW+rzHWesSRBjw6k@r1(to>8I)4Y15$62>jc7f>M6)wqJ zBzo~G8ek2ONczco?R211p)!s?7g}>h-6#rl;~t(!K@*R% zN(_GeM)SwVZ~V1JIdu8nX(-~6yW=w52;YNskcZ{GeRmq^H}Z`JcTf@5WvTbiHHCJZ zRU|IA-R(0ZG_agmK+ryXh2Lv6$kYCv?qI{*`+k}T6`Vx+pB|FOx_yLq&Xhynxl;AUE?^> z*Njom4)-RX#`T#?-20CHp~t9hh6Kbuu|nUI4ca|0w3pZ}Fs-`HiI?0ILXh|KGcSrH zX@kRH6)n0}u~&1d)`eC%VuKuMI>cRytzgG_fZjIPPm6@r9yH6(1jpo#6wx7#V2#?r zk4&llW74{}5_MD|o(v_+G+(LdeiJ zNnN&&*eseuFAeAIA+R^oHR-O4b8|UAIMdRcgjk5C-i6jwpE7p29WB;xUOtf}?TgjL z^V}J=l1AkH-pG4%U)J_7byB!i(=-JNb=d-IdwuRbx9bs0JAUFxRE#@~UmJ3aHcL9Q zLl|L+r*>;3GcsR4ukwdTK=>a{z=?rXF8#rn&ts>HlYulRV^`w~7poY}Tg_h$I0?Pl z!Nvv9S?YbwW5mRw14YS!JJz ziWo(e^F#;a?FqCBgeH0LS-{Ai*JX;Q36IJp2T%E?_Z%CR|8R}s4tPE-{cdo$Lw;{q zlK)i#?(Z2-=&SKpp?nb>f##nQ23jhsPazzxIH$S$(wNS{XVR#9VlC@9kwB@waJCl4 zG3hmhHS4@x5y6qJeGBzCE3xRc817O533IHseKn6X%FX@s`0E_P5+6FCjOoS*!D9yi zD-XIKSmGY`5MXnifGd(1whf(`q~L!a=C#lQ>GOA_iYcA*zgB-eA7k>}{$GmezNaVs zmB4j$9UA^G>%c|luh_JG9HGb-=3}vpAoPWCBI~1rcS+!AN4MpR5kn_ZM*ycUhk(ae214h9Db~wmk8jnMV_*>vlDtl6G%ub zpCctNPy;G2hIfZqP~E4y@^=rvE^(qbEAMJir@Q0D_gl40ylA)uSc8z@v=RM#lhC?%2xg%P6cg(k;px=6*&wL+3%;u|pz2WzL25|KSdfMFLaWzugQK98HH%8sDTlF#!8P4iRyC!?auebZic zxL~_6GbdXF%b;mFI&NwV{(hj^(2fp79h(45iuHS#3rB}ThqHsdtBkEUPSCyDzFNgN z;&kaUJ^1eLkE%3COy=6+OL5HF4Ht^YZ71&OMbax>BGv6u1on3;#LjXU+`>1x!@u$= z>K&=diEaWEcO3Ei(Yt%BohD}eMepN7%dVJh3)G=GaRmM?*##(^aaDHv1ydc4x#Ap2 z5rMj?e0>&xM=z57hH zSMWSJ*C^!+_QhPeYke$5$yi_F?cLGwkF;-_6>*`+2)pt-@fm`WN7)2FgJRZnA3}e* z*G8e=I!@b>E4N$u20X-EUtq#EA3_0;b^c{55I_7A7R>Y+jh|TmU}SkrEk+!HBZ6}D zlIsu%G#(Js8E9a?Z^9R_l@2RkROYRFi8)A0DbIdS1J zam7cU8!38_7o4pqdm_BJU8o zVqYY4%GEc3GF0e72r5Lt`6BMtJr!;)IN^V6p@RcKyMzCVIfsjyORLjfk$klj)3$Jz zo5YYc$!C+RACIMFpIDxg1%bnoKG5+?nf^oR>QWK%Xq z?_F*9%i@SS5^z(mnm9y^Sx8)x;>L=UUeyV(Zd;uBhn>)PtEh2mt(Byyv%mptRr;Hx%x<3zIAjYp(9i1o>( zPDoLk(9wUR25qw`v?7B#MA|GGUB9d3Hf(njb*r~mDfsZZqKWXYQ22&2_d${R6rV@aZ#BUjfkz4JPt$^9EiX;mlr|XxzL2mqTin# zEg~ZE(RucZovJ^qxjRJS<)_e-5f_s06wI-ID~dltvx(E)Km%wjt^M`8$Lu+1@r??) zxxV;~o)!N0zVLkyol>IC_tV>EnOcT3i6K(F4-4{dq-#EA{c17^D*cTjco&Ik^IpwGq^Y|#EK=2IZrN;JxAVmNn{H|X z!RH&(Pd95$3GkW(wbgdfn()~`VKW8ve6&B!ngbrL%xcO_94x~ z*QV#(AU6$P+vY7GFzq~nk8%+cX7D>KJwu=J6F@<5dH@0stk|J>vEjz9l$r53?BMN{ zng^fMLcx7?KgxL%UurMt;y2x+7--1N?)+2S3R(&mY$r`SFtA6Ma~^W?)Wm&;XpQ$> zM&t+NomkRigXz8MotW(URkGf0BDo zJC#Lxv|U(wnRubcX05Bdcx@}gNoDg%@Ag(#07hcsHh2`}TstsZ+0>n?E+~}NWXMJV z>5KEt`u6u1%i+Na|9$jD4yjC^n&J&#?!J)_1v zS$J>x^87PzBR}QEdn+rx5$J0p=iO%(pc(wu%GpfSFtp+o|M*gg2WVjZ9^@n+F!ae1 zy+$bAFgYu3YHe7YX~3_aKmlhtXVed&lGp2Fa53UEJJogy?6HVeXq;KPS|LF$&E1XK zqld@y#qQ2?BQMKyG`^JjiUf@sZXv?AUcBeobUxhSczMXV80%DFq!cI6=ZB@Ihw~vZ z20{qI3wY9MVya2TWUY`nc8fLlz1PZr``Pt4jI2&1;lYvNbaz;8&Q`qqgiND~^J%LJ zawN+#^n%ASYk7B%dV2>vf^QS-xObg0Sh{6SKUqG|&K_m>dqEe2M`QuYh|pTk;i}Tm zo{CAmeqfS%$Bs1tC+p(pHXCoU3aow!N-?}fZ0@7c8NP8gEPE;MDy!OqZ!L{k#%=fK zR#xTt3_uXT+#o(a^n;eH6ZN}Ul8A_i#juHxv+}AN0=GY}Y8Rs4+nJvEx1RZr6&y$h zgZ16P_wb||*9k|U!3|;S^2yF{CI2~^_P)cL8k@w)hiat7Qxb&dqse&m|1fm8d3GE_ z_?Ozz9p#F8|V~jB6inK0Limdm8A}2?dnv0Ry07e8$u86v$)vmzBBtb=}iD{ib zeE#ImcH?aU@{MyhX8BJ!P?~6l%wCFV#)Hn`Wlul10dSM`B?+{txKeF2xlzUBzw$(+ z|G9l;R?*}9xLx_2Td{T1I;p*@Verg1E1?|SDVM9jj0nx#1=iwEJ~#S@QP2G#k0g+c zMUGt=j&(W2(+}>uUuK&St6!HDhg_Qsg-UG;;d)GeVyp?~#nIs&P3!(*(@#(HHG6M-7s-d@+#K`%S*3bE+4y$mjpA=;mre9xNj1&lc zAFI=FyB;@IN^BSqnOLxMQ0i3SG?8w!8YL9?er&i4Yd%$SwJKL*-1(a?L-#&A7Mm9_ z%FDN4dfI%(I;{0U3T3VR)p8vYLxs=UShqdo_9=Ze z)GNOt<`wr|KNr2ax#q=JQh8Bur3Eh_WXDoaFo5bLr%W6 z6-^K1pkHjE3KI!|DC$Qp7d0sn5IHxOL@={$Y zBz8JX$KBg%$T9O#$?IiQa`2(0GU|_O@R%esv~w%`M-JKo~pM92+#)=pj4$t`H5I-|A(8-FLwA6D>l;+ClGaFze8+AkW4LU3#~cbR*y+`Hp+0i;7F3ro4eZe9;P zD@u$8>DVgfd-$d_560%*TY*wpE-`g~sKuF>a;=H)OeNK?-=F`{yYj1?O}B1QREA&J z(+`^LMGhoFBzaNS7WHs*F~6|A5QN9G1%`y=1ENnTNv*W4dQY)^|pg)rY=fh@u3+yRQ@59|KXbbCfL(A$`{1ntUGc z0Qsu6tlG188N?6q1>?B}YKBs{C!vwoa?}X>Dsnd!4tp4-8yhN+AI--7((cUj=@)5IB4AXY!1t<8CeCm@S81OE&Rl;=4G49` z&uYKMoeLc=^PQ;xXorf0w{+TLfHy&aV*-y_g*P=t8z?DvnVbk~z zk^kt)&&j$Ev~J`&Ht)en72?0T-#B+_%czdj58jW*eAJd@kg)ZE zo?3!9a_Ha})KYf&J4)xg^TGIK8d=h3Qo`kX}rJvVfi(SlI<~%Xv8e$!Gia7u*I3`h6=KOsXr*m54prC%FJ}lN^XFnP2{gg z%9%h*J^oGTQnwQ$?MWad*UywdJzAX;P2qnqR;)S_Ujx$pz&Jd9*(;@dCqQo8OBiP= zp^5voG%tSvx_p9aejmP-Q%a0n%JK^4^!@O%SDxNA)EJ(fld<4EvZ?tENr-#!X?wpa zi`F%K=pg>$Tt^1me-R6wO5idoNch(fYMv}6Nm#7$#ejmfpj764>O*hcyqmo&uCxUi zX@zi8i*IRUHhPk_tK4pPw(KvI9t17)p|@#Et|8~mcQ7ByZFj~>S+;?H@AjapVax+P z3!;BZOZ$IjZAXyap(na(SHoB`U;c4KVVo@QAhW?M?8+Bi$eHuU9>GyNPuurqj4oA{ zc;0{?#=Dvf2mj}P2k+onThjh4N&sC+bVZIn!@C(LUAGr?R5*^B*qA(Lr)=UoMSe(AOy5l7Acz@2e@m_~gr|m&x4Upr! zj{FEK`vBvvqn@N%jTH}reI5c&`N>UDp^;xFzsUar_uN2!|X+GOPqCzRm*VO zz>nid%J3lw{G;yA=hYPi(jWu_ckK0qlg3fkX=?vhKb}s45y=nLCZW1*i8;(=R~`ix z8eDR^TF7wN`S&Q3%YN&N>dFL&!1;axxg?=#DrCkHbN-C_CXBwdzw_fhJN*+qf)*y0 z3x=Vr>xo#dUw!$>2j0J1`o7QqlRADYwcnLKP0CkX=|Prb!`%fvgk};c$J}z9UN0hc zZKrUbtg!BSh|f9_K-Fx>j4^t*n&)!I=RDF%oDjTFra=`ir3t#|;7RXfqE=3Su3Yql z!!%!?Pt!vr9)AxVl3OJejxk@gVP`uEgrQ5LTYgI21Lc>@p-Q>i+ zCXZqKqcIaP{I^nZZ}qj_IuKM7b02D9LWWlp{^8_`aP~2nYvew}f)?v8Pv;tHI2eAv zo{c9lHTDZ%>Wy+bg*LCQbB=yVsxFlwHwGtA16kDvy~W46Tg7hE!0nIvDGO6-YNSB^ zsATP*qBLnAs2~}r-IXR_pycUJ(l5~SdG P9AKdHRJ&QzA@=_O1LIG; diff --git a/app/src/main/res/values-land/integers.xml b/app/src/main/res/values-land/integers.xml new file mode 100644 index 000000000..e6db07117 --- /dev/null +++ b/app/src/main/res/values-land/integers.xml @@ -0,0 +1,4 @@ + + + 96 + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index dc0dc1cac..a9ccfced9 100755 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -18,6 +18,7 @@ @string/card_style @string/card_color_style @string/card_circular_style + @string/image @@ -25,6 +26,7 @@ 1 2 3 + 4 @@ -32,6 +34,7 @@ @layout/item_card @layout/item_card_color @layout/item_grid_circle + @layout/image @@ -152,4 +155,10 @@ Normal style Card style + + @id/cast_button_type_empty + @id/cast_button_type_skip_previous + @id/cast_button_type_skip_next + @id/cast_button_type_mute_toggle + \ No newline at end of file diff --git a/app/src/main/res/values/cast_style.xml b/app/src/main/res/values/cast_style.xml new file mode 100644 index 000000000..b717340d6 --- /dev/null +++ b/app/src/main/res/values/cast_style.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index f5afcb525..e27406e77 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -21,10 +21,6 @@ 100dp - 5dp 4dp 16dp @@ -63,6 +59,11 @@ http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout 48dp 104dp + + 96dp 96dp @@ -90,5 +91,6 @@ http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout 80dp 32dp - -12dp + -12dp + diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml index 83ec9ec74..980dbf72a 100644 --- a/app/src/main/res/values/integers.xml +++ b/app/src/main/res/values/integers.xml @@ -14,4 +14,5 @@ 1 300 400 + 240 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cdb6735e4..6b0a090b7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -121,6 +121,7 @@ Card Colored Card Circular + Image Card Cast diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5a455ce16..ec403f733 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -131,5 +131,25 @@ center_horizontal + + + + + diff --git a/app/src/main/res/values/styles_parents.xml b/app/src/main/res/values/styles_parents.xml index eae78513e..592834788 100644 --- a/app/src/main/res/values/styles_parents.xml +++ b/app/src/main/res/values/styles_parents.xml @@ -11,8 +11,10 @@ @color/md_grey_900 - @android:color/black - ?android:attr/windowBackground + @color/md_black_1000 + + @android:color/black + @android:color/black @style/Theme.Design.BottomSheetDialog @@ -37,9 +39,9 @@ @style/ThemeOverlay.AppCompat.Light - @android:color/black @color/md_white_1000 - ?android:attr/windowBackground + @android:color/black + @color/md_white_1000 @style/Widget.ActionButton.Overflow @@ -69,9 +71,9 @@ @style/ThemeOverlay.AppCompat - @android:color/white @color/md_grey_900 - ?android:attr/windowBackground + @android:color/white + @color/md_grey_900 @style/Widget.ActionButton.Overflow diff --git a/app/src/main/res/xml/pref_advanced.xml b/app/src/main/res/xml/pref_advanced.xml index 64640dab4..0d48761a7 100755 --- a/app/src/main/res/xml/pref_advanced.xml +++ b/app/src/main/res/xml/pref_advanced.xml @@ -1,55 +1,61 @@ - + - + - + - + - + - + - + - + - + + + \ No newline at end of file diff --git a/appthemehelper/appthemehelper.iml b/appthemehelper/appthemehelper.iml index eea171cd4..ef43777f9 100644 --- a/appthemehelper/appthemehelper.iml +++ b/appthemehelper/appthemehelper.iml @@ -1,5 +1,5 @@ - + @@ -30,16 +30,16 @@ - + - + - + - + @@ -88,7 +88,6 @@ - @@ -108,31 +107,44 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/appthemehelper/build.gradle b/appthemehelper/build.gradle index 6b36df566..b50570606 100644 --- a/appthemehelper/build.gradle +++ b/appthemehelper/build.gradle @@ -1,10 +1,10 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 27 + compileSdkVersion 28 defaultConfig { minSdkVersion 21 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 1 versionName "1.0" @@ -17,17 +17,17 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - buildToolsVersion '27.0.3' + buildToolsVersion '28.0.2' productFlavors { } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:27.1.1' - implementation 'com.android.support:design:27.1.1' - implementation 'com.android.support:preference-v7:27.1.1' - implementation 'com.android.support:cardview-v7:27.1.1' + implementation 'com.android.support:appcompat-v7:28.0.0-rc01' + implementation 'com.android.support:design:28.0.0-rc01' + implementation 'com.android.support:preference-v7:28.0.0-rc01' + implementation 'com.android.support:cardview-v7:28.0.0-rc01' // Used for the list preference classes implementation 'com.afollestad.material-dialogs:core:0.9.6.0' implementation 'com.afollestad.material-dialogs:commons:0.9.6.0' diff --git a/appthemehelper/src/main/java/code/name/monkey/appthemehelper/common/prefs/ATESwitchPreference.java b/appthemehelper/src/main/java/code/name/monkey/appthemehelper/common/prefs/ATESwitchPreference.java index 512afc825..b2609ec87 100755 --- a/appthemehelper/src/main/java/code/name/monkey/appthemehelper/common/prefs/ATESwitchPreference.java +++ b/appthemehelper/src/main/java/code/name/monkey/appthemehelper/common/prefs/ATESwitchPreference.java @@ -4,29 +4,17 @@ import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.preference.Preference; -import android.preference.SwitchPreference; -import android.support.annotation.Nullable; -import android.support.v7.widget.SwitchCompat; +import android.support.v7.preference.PreferenceViewHolder; import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Switch; - -import code.name.monkey.appthemehelper.ATH; -import code.name.monkey.appthemehelper.R; -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.common.views.ATESwitch; import java.lang.reflect.Field; +import code.name.monkey.appthemehelper.R; + /** * @author Aidan Follestad (afollestad) */ -public class ATESwitchPreference extends SwitchPreference { - - static final boolean COMPAT_MODE = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; - - private ATESwitch mSwitch; +public class ATESwitchPreference extends ATEPreference { public ATESwitchPreference(Context context) { super(context); @@ -51,64 +39,20 @@ public class ATESwitchPreference extends SwitchPreference { private void init(Context context, AttributeSet attrs) { setLayoutResource(R.layout.ate_preference_custom); - if (COMPAT_MODE) { - setWidgetLayoutResource(R.layout.ate_preference_switch); - } else { - try { - Field canRecycleLayoutField = Preference.class.getDeclaredField("mCanRecycleLayout"); - canRecycleLayoutField.setAccessible(true); - canRecycleLayoutField.setBoolean(this, true); - } catch (Exception ignored) { - } - try { - Field hasSpecifiedLayout = Preference.class.getDeclaredField("mHasSpecifiedLayout"); - hasSpecifiedLayout.setAccessible(true); - hasSpecifiedLayout.setBoolean(this, true); - } catch (Exception ignored) { - } + try { + Field canRecycleLayoutField = Preference.class.getDeclaredField("mCanRecycleLayout"); + canRecycleLayoutField.setAccessible(true); + canRecycleLayoutField.setBoolean(this, true); + } catch (Exception ignored) { + } + + try { + Field hasSpecifiedLayout = Preference.class.getDeclaredField("mHasSpecifiedLayout"); + hasSpecifiedLayout.setAccessible(true); + hasSpecifiedLayout.setBoolean(this, true); + } catch (Exception ignored) { } } - @Override - protected void onBindView(View view) { - super.onBindView(view); - if (COMPAT_MODE) { - mSwitch = (ATESwitch) view.findViewById(R.id.switchWidget); - mSwitch.setChecked(isChecked()); - } else { - View parentSwitch = findSwitchView(view); - if (parentSwitch != null) { - ATH.setTint(parentSwitch, ThemeStore.accentColor(view.getContext())); - } - } - } - - @Nullable - private View findSwitchView(View view) { - if (view instanceof ViewGroup) { - for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { - View child = ((ViewGroup) view).getChildAt(i); - if (child instanceof Switch || child instanceof SwitchCompat) { - return child; - } else if (child instanceof ViewGroup) { - View potentialSwitch = findSwitchView(child); - if (potentialSwitch != null) return potentialSwitch; - } - } - } else if (view instanceof Switch || view instanceof SwitchCompat) { - return view; - } - return null; - } - - @Override - public void setChecked(boolean checked) { - super.setChecked(checked); - if (COMPAT_MODE) { - if (mSwitch != null) { - mSwitch.setChecked(checked); - } - } - } } diff --git a/appthemehelper/src/main/java/code/name/monkey/appthemehelper/util/NavigationViewUtil.java b/appthemehelper/src/main/java/code/name/monkey/appthemehelper/util/NavigationViewUtil.java new file mode 100644 index 000000000..20e53fe3b --- /dev/null +++ b/appthemehelper/src/main/java/code/name/monkey/appthemehelper/util/NavigationViewUtil.java @@ -0,0 +1,68 @@ +package code.name.monkey.appthemehelper.util; + +import android.content.res.ColorStateList; +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.design.widget.BottomNavigationView; +import android.support.design.widget.NavigationView; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public final class NavigationViewUtil { + + private NavigationViewUtil() { + } + + public static void setItemIconColors(@NonNull NavigationView navigationView, @ColorInt int normalColor, @ColorInt int selectedColor) { + final ColorStateList iconSl = new ColorStateList( + new int[][]{ + new int[]{-android.R.attr.state_checked}, + new int[]{android.R.attr.state_checked} + }, + new int[]{ + normalColor, + selectedColor + }); + navigationView.setItemIconTintList(iconSl); + } + + public static void setItemTextColors(@NonNull NavigationView navigationView, @ColorInt int normalColor, @ColorInt int selectedColor) { + final ColorStateList textSl = new ColorStateList( + new int[][]{ + new int[]{-android.R.attr.state_checked}, + new int[]{android.R.attr.state_checked} + }, + new int[]{ + normalColor, + selectedColor + }); + navigationView.setItemTextColor(textSl); + } + + public static void setItemIconColors(@NonNull BottomNavigationView bottomNavigationView, @ColorInt int normalColor, @ColorInt int selectedColor) { + final ColorStateList iconSl = new ColorStateList( + new int[][]{ + new int[]{-android.R.attr.state_checked}, + new int[]{android.R.attr.state_checked} + }, + new int[]{ + normalColor, + selectedColor + }); + bottomNavigationView.setItemIconTintList(iconSl); + } + + public static void setItemTextColors(@NonNull BottomNavigationView bottomNavigationView, @ColorInt int normalColor, @ColorInt int selectedColor) { + final ColorStateList textSl = new ColorStateList( + new int[][]{ + new int[]{-android.R.attr.state_checked}, + new int[]{android.R.attr.state_checked} + }, + new int[]{ + normalColor, + selectedColor + }); + bottomNavigationView.setItemTextColor(textSl); + } +} \ No newline at end of file diff --git a/appthemehelper/src/main/res/layout/ate_preference_switch.xml b/appthemehelper/src/main/res/layout/ate_preference_switch.xml index 312647f22..147bda072 100755 --- a/appthemehelper/src/main/res/layout/ate_preference_switch.xml +++ b/appthemehelper/src/main/res/layout/ate_preference_switch.xml @@ -1,9 +1,9 @@ \ No newline at end of file + android:id="@+id/switchWidget" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@null" + android:button="@drawable/ate_switch" + android:clickable="false" + android:focusable="false" /> \ No newline at end of file diff --git a/appthemehelper/src/main/res/layout/ate_preference_switch_support.xml b/appthemehelper/src/main/res/layout/ate_preference_switch_support.xml index e7bc585b9..8c719e75b 100755 --- a/appthemehelper/src/main/res/layout/ate_preference_switch_support.xml +++ b/appthemehelper/src/main/res/layout/ate_preference_switch_support.xml @@ -1,8 +1,8 @@ \ No newline at end of file + android:id="@android:id/checkbox" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@null" + android:clickable="false" + android:focusable="false" /> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 4fa581e6b..95e0d530f 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0-alpha05' + classpath 'com.android.tools.build:gradle:3.3.0-alpha06' } } @@ -16,6 +16,7 @@ allprojects { google() jcenter() maven { url "https://jitpack.io" } + maven { url "https://oss.sonatype.org/content/repositories/snapshots" } } }