From 051ca50929ca34ddf658224de6cab880329fbce7 Mon Sep 17 00:00:00 2001 From: Milind Goel <45682747+milindgoel15@users.noreply.github.com> Date: Sun, 2 Aug 2020 20:58:04 +0530 Subject: [PATCH 01/20] Update lyric guide to be more simple and straight --- FAQ.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/FAQ.md b/FAQ.md index 2ac44e716..efa40c7ac 100644 --- a/FAQ.md +++ b/FAQ.md @@ -31,8 +31,9 @@ A ".lrc" is like a text file which contains the time stamped lyrics for example, ##### STEP 3: Now you have to rename the file you created in this way: - .lrc or for better matching copy the and the from the tag editor and then rename the file. ##### STEP 4: -You have to copy or move this file into a location on the SD Card: whatever_sdcard/RetroMusic/lyrics/ and paste it there. -Finally you will be able to see the lyrics working. +Now paste the LRC files to the following path : /sdcard/Retromusic/lyrics/ +Here sdcard is your internal storage. + > If you want to skip to particular time stamp, simply scroll to the time stamp from where you want to start from and a 'Play' icon will appear left to the particular stamp. Tap on play button to play from there. From 287638af90731287f4377534dcdaa0ce10dca5e8 Mon Sep 17 00:00:00 2001 From: "Daksh P. Jain" Date: Mon, 10 Aug 2020 11:10:16 +0530 Subject: [PATCH 02/20] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..6670d3998 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a report to help us improve. Please write in English only. +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**How To Reproduce** +Steps to reproduce the behavior: +1. +2. +3. +4. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + + +**Device info:** + - Device: [e.g. OnePlus 7] + - Android version: [e.g. Android 9] + - App version [e.g. 3.5.300_0517] + +**Additional context** +Add any other context about the problem here. From 213f106caca6ddb8c721bef513eb3573355e93bb Mon Sep 17 00:00:00 2001 From: "Daksh P. Jain" Date: Mon, 10 Aug 2020 11:12:23 +0530 Subject: [PATCH 03/20] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 6670d3998..ee6f409d5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve. Please write in English only. title: '' -labels: '' +labels: bug assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..e2d9f3239 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project. Please write in English only. +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 8d5d7054c65f7c022d93ec0a4d16043aa55270a4 Mon Sep 17 00:00:00 2001 From: "Daksh P. Jain" Date: Mon, 10 Aug 2020 11:25:04 +0530 Subject: [PATCH 04/20] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..810f53156 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at retromusicapp@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq From 31c0d8bc98f58574ad16632c01b605579ed71e1d Mon Sep 17 00:00:00 2001 From: "Daksh P. Jain" Date: Mon, 10 Aug 2020 11:27:08 +0530 Subject: [PATCH 05/20] Create CONTRIBUTING.md --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..d0979ee9a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,6 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. From 9655d58432c7c8756986effdd5bbc855e246c273 Mon Sep 17 00:00:00 2001 From: "Daksh P. Jain" Date: Mon, 10 Aug 2020 11:41:12 +0530 Subject: [PATCH 06/20] Update README.md --- README.md | 57 ++++++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 84d54d1ef..b720431db 100644 --- a/README.md +++ b/README.md @@ -29,53 +29,52 @@ | Classic | Adaptive | Blur | Tiny | Peak | ### 🧭 Navigation never made easier -Self-explanatory interface without overloaded menus +Self-explanatory interface without overloaded menus. ### 🎨 Colorful -You can choose between three different main themes: Clearly white, Kind -dark and Just black for AMOLED displays. Select your favorite accent +You can choose between three different main themes: Clearly White, Kinda +Dark and Just Black for AMOLED displays. Select your favorite accent color from a color palette. ### 🏠 Home -Where you can have your recently/ top played Artists, Albums and -Favorite Songs. No other music player has this feature +Where you can have your recently/top played artists, albums and +favorite songs. No other music player has this feature. ### 📦 Included Features -- Base 3 themes (Clearly white, Kinda dark and Just Black) +- Base 3 themes (Clearly White, Kinda Dark and Just Black) - Choose from 10+ now playing themes -- Drive Mode +- Driving Mode - Headset/Bluetooth support -- Music Duration Filter +- Music duration filter - Folder support - Play song by folder - Gapless playback - Volume controls -- More than 10 Now playing themes - Carousel effect for an album cover -- Home screen Widgets -- Lock Screen Playback Controls -- Lyrics Screen(download and sync with music) +- Home screen widgets +- Lock screen playback controls +- Lyrics screen (download and sync with music) - Sleep Timer -- Home screen Widgets -- Easy Drag to Sort Playlist & Play Queue +- Easy drag to sort playlist & play queue - Tag editor -- Create, Edit, Import playlists +- Create, edit and import playlists - Playing queue with reorder - User profile - 30 Languages support -- Browse and play your music by Songs, Albums, Artists, Playlists, - Genre -- Smart Auto Playlists - Recently played/Top Played/History Fully - playlist support & Build your own playlist on the go +- Browse and play your music by songs, albums, artists, playlists and + genre +- Smart Auto Playlists - Recently played, most played and history +- Build your own playlist on the go -We are trying our best to bring you the best user experience. Until now -it is a beta version - bug fixes (if any) and more features are on the -way. for FAQ's https://goo.gl/DR2mE2 +We are trying our best to bring you the best user experience. The app is regulary being updated for bug fixes and new features. -In any case, you find or notice any Bugs/ Crashes please report them by -sending us an [e-mail](mailto:monkeycode@gmail.com). We will respond or fix Bugs/ Crashes as soon as -possible and if you have any Features/ Suggestions in mind please Follow -below links to support +### FAQ +Please read the FAQ here: https://goo.gl/DR2mE2 + +In any case, you find or notice any bugs please report them by +sending us an [e-mail](mailto:retromusicapp@gmail.com). We will fix bugs as soon as +possible. +If you have any feature suggestions, please create an issue with detailed information. ### 🔗 Social links **Telegram:** https://t.me/retromusicapp @@ -88,8 +87,6 @@ below links to support Retro Music Player is released under the GNU General Public License v3.0 (GPLv3), which can be found here: [License](LICENSE.md) -### ☝️ FAQ's -Try the link [here](FAQ.md) ->Please note: Retro Music player is offline local mp3 player app. It ->doesn't support online music download or music streaming. +>Please note: Retro Music player is an offline music player app. It +>doesn't support music downloading or online music streaming. From 55205bbd74b0911ee0b7030e427cec17b4cd7a90 Mon Sep 17 00:00:00 2001 From: "Daksh P. Jain" Date: Mon, 10 Aug 2020 12:33:27 +0530 Subject: [PATCH 07/20] Update CONTRIBUTING.md --- CONTRIBUTING.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d0979ee9a..9c190e380 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,35 @@ # Contributing -When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. +## Using the issue tracker +The [issue tracker](https://github.com/h4h13/RetroMusicPlayer/issues) is the preferred channel for bug reports, feature requests and submitting pull requests, but please follow these rules: + +* Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others. + +* Please **do not** post comments consisting solely of "+1" or "👍". Use [GitHub's "reactions" feature](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) instead. + +* Please **do not** write [enhancement]/[bug]/[feature request] or similar stuff in the title of issues, as there are labels for that purpose that will be added by devs or collaborators. + +## Bug reports +A bug is a _demonstrable problem_. Good bug reports are extremely helpful, so thanks! + +Guidelines for bug reports: + +* Use the GitHub issue search, check if the issue has already been reported both in the open issues and in the closed ones, if there are some missing details, add them in issue comments, without creating a new one +* Check if the issue has been fixed — try to reproduce it using the latest available build +* Isolate the problem — ideally create a reproducible scenario and a live example +* Do not report multiple bugs in a single ticket, otherwise it will be confusing. + +A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report. + + +## Feature requests +Feature requests are welcome, please make sure to be as detailed as possible and use screenshots, videos, GIFs, to demonstrate it better, if possible. + + +## Pull requests +**Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code), otherwise you risk spending a lot of time working on something that developers might not want to merge into the project. To avoid that, you can join the official [Telegram group](https://t.me/retromusicapp) or open an issue. + +## License +By contributing your code, you agree to license your contribution under the [GNU General Public License](https://github.com/h4h13/RetroMusicPlayer/blob/dev/LICENSE.md). Please note we have a code of conduct, please follow it in all your interactions with the project. From e887b42ef529a001f445df52ace4d7d44c95a905 Mon Sep 17 00:00:00 2001 From: "Daksh P. Jain" Date: Mon, 10 Aug 2020 17:05:07 +0530 Subject: [PATCH 08/20] Update bug report template to include crash logs --- .github/ISSUE_TEMPLATE/bug_report.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index ee6f409d5..4f5b72a92 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -23,6 +23,12 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. +**Crash log** +If the app is crashing, add a crash log +
+ Click to view logs +PASTE YOUR LOGS HERE. +
**Device info:** - Device: [e.g. OnePlus 7] From 45972f33cb30d74c5a48c8b75c542f31a1be9008 Mon Sep 17 00:00:00 2001 From: Milind Goel <45682747+milindgoel15@users.noreply.github.com> Date: Tue, 11 Aug 2020 19:51:49 +0530 Subject: [PATCH 09/20] Update faq link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b720431db..147dc995e 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ favorite songs. No other music player has this feature. We are trying our best to bring you the best user experience. The app is regulary being updated for bug fixes and new features. ### FAQ -Please read the FAQ here: https://goo.gl/DR2mE2 +Please read the FAQ here: https://del.dog/RetroFaq In any case, you find or notice any bugs please report them by sending us an [e-mail](mailto:retromusicapp@gmail.com). We will fix bugs as soon as From 218401319179f48f805ed80435cf66f29cee265f Mon Sep 17 00:00:00 2001 From: "Daksh P. Jain" Date: Tue, 11 Aug 2020 20:19:13 +0530 Subject: [PATCH 10/20] Update bug report template to include FAQ link --- .github/ISSUE_TEMPLATE/bug_report.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 4f5b72a92..58639a31a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,9 @@ assignees: '' --- +**Have you read the [FAQ](https://www.github.com/h4h13/RetroMusicPlayer/tree/dev/FAQ.md)?** +[Yes/No] + **Describe the bug** A clear and concise description of what the bug is. From 3ebf777d7b9d25c00c6393488eb225ba2aa95f86 Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Tue, 11 Aug 2020 23:59:44 +0530 Subject: [PATCH 11/20] Working towards single activity design --- app/build.gradle | 7 +- .../{output.json => output-metadata.json} | 2 +- app/src/main/AndroidManifest.xml | 9 +- .../code/name/monkey/retromusic/Constants.kt | 9 +- .../code/name/monkey/retromusic/MainModule.kt | 28 +- .../retromusic/activities/MainActivity.kt | 628 +----------------- .../retromusic/activities/SettingsActivity.kt | 104 --- .../activities/albums/AlbumDetailsActivity.kt | 399 ----------- .../artists/ArtistDetailActivity.kt | 324 --------- .../base/AbsSlidingMusicPanelActivity.kt | 24 +- .../activities/genre/GenreDetailsActivity.kt | 143 ---- .../activities/search/SearchActivity.kt | 16 +- .../activities/search/SearchFragment.kt | 134 ++++ .../monkey/retromusic/adapter/GenreAdapter.kt | 15 +- .../monkey/retromusic/adapter/HomeAdapter.kt | 37 +- .../retromusic/adapter/SearchAdapter.kt | 65 +- .../retromusic/adapter/album/AlbumAdapter.kt | 14 +- .../adapter/album/HorizontalAlbumAdapter.kt | 16 +- .../adapter/artist/ArtistAdapter.kt | 14 +- .../adapter/playlist/PlaylistAdapter.kt | 18 +- .../song/OrderablePlaylistSongAdapter.kt | 17 +- .../adapter/song/PlayingQueueAdapter.kt | 3 +- .../adapter/song/PlaylistSongAdapter.kt | 20 +- .../adapter/song/SimpleSongAdapter.kt | 4 +- .../retromusic/adapter/song/SongAdapter.kt | 20 +- .../monkey/retromusic/extensions/ColorExt.kt | 19 + .../retromusic/extensions/DimenExtension.kt | 20 + .../retromusic/extensions/FragmentExt.kt | 17 + .../extensions/NavigationExtensions.kt | 9 +- .../retromusic/extensions/ViewExtensions.kt | 6 - .../fragments/MainActivityFragment.kt | 11 + .../about}/AboutActivity.kt | 2 +- .../fragments/about/AboutFragment.kt | 128 ++++ .../albums/AlbumDetailsFragment.kt | 61 +- .../albums/AlbumDetailsViewModel.kt | 2 +- .../fragments/albums/AlbumsFragment.kt | 30 +- .../artists/ArtistDetailsFragment.kt | 194 ++++++ .../artists/ArtistDetailsViewModel.kt | 2 +- .../fragments/artists/ArtistsFragment.kt | 24 +- .../fragments/base/AbsPlayerFragment.kt | 14 +- .../fragments/base/AbsRecyclerViewFragment.kt | 12 +- .../fragments/genres/GenreDetailsFragment.kt | 76 +++ .../genres}/GenreDetailsViewModel.kt | 2 +- .../fragments/genres/GenresFragment.kt | 2 +- ...{BannerHomeFragment.kt => HomeFragment.kt} | 27 +- .../fragments/library/LibraryFragment.kt | 130 ++++ .../player/NowPlayingPlayerFragment.kt | 4 +- .../player/full/FullPlayerFragment.kt | 21 +- .../playlists/PlaylistDetailsFragment.kt} | 111 +--- .../playlists}/PlaylistDetailsViewModel.kt | 2 +- .../fragments/playlists/PlaylistsFragment.kt | 20 +- .../settings/MainSettingsFragment.kt | 10 - .../fragments/settings/SettingsFragment.kt | 23 + .../fragments/songs/SongsFragment.kt | 91 +-- .../helper/menu/PlaylistMenuHelper.kt | 4 +- .../retromusic/helper/menu/SongMenuHelper.kt | 18 +- .../retromusic/loaders/PlaylistSongsLoader.kt | 12 +- .../monkey/retromusic/loaders/SongLoader.kt | 23 +- .../monkey/retromusic/model/CategoryInfo.java | 6 +- .../monkey/retromusic/model/PlaylistSong.java | 6 +- .../code/name/monkey/retromusic/model/Song.kt | 4 +- .../providers/MusicPlaybackQueueStore.java | 5 +- .../name/monkey/retromusic/util/AppRater.kt | 21 +- .../retromusic/util/NavigationUtil.java | 83 --- app/src/main/res/drawable/ic_baseline.xml | 5 + app/src/main/res/drawable/ic_layout.xml | 5 + ...y_album.xml => activity_album_details.xml} | 0 ...y_album.xml => activity_album_details.xml} | 0 .../main/res/layout/activity_main_content.xml | 62 +- app/src/main/res/layout/fragment_about.xml | 11 + .../res/layout/fragment_album_details.xml | 207 +++--- .../res/layout/fragment_artist_details.xml | 126 ++++ app/src/main/res/layout/fragment_library.xml | 69 +- app/src/main/res/layout/fragment_main.xml | 2 +- .../res/layout/fragment_playlist_detail.xml | 88 +++ app/src/main/res/layout/fragment_search.xml | 116 ++++ app/src/main/res/layout/fragment_settings.xml | 53 ++ app/src/main/res/layout/item_card.xml | 3 +- app/src/main/res/layout/item_card_color.xml | 7 +- .../res/layout/item_list_quick_actions.xml | 31 +- .../res/layout/sliding_music_panel_layout.xml | 4 +- app/src/main/res/menu/menu_main.xml | 87 +-- .../{retro_graph.xml => library_graph.xml} | 28 +- app/src/main/res/navigation/main_graph.xml | 85 +++ .../main/res/navigation/settings_graph.xml | 16 +- app/src/main/res/values/arrays.xml | 2 +- app/src/main/res/values/strings.xml | 2 + 87 files changed, 1908 insertions(+), 2423 deletions(-) rename app/release/{output.json => output-metadata.json} (90%) delete mode 100755 app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsActivity.kt delete mode 100755 app/src/main/java/code/name/monkey/retromusic/activities/artists/ArtistDetailActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/activities/genre/GenreDetailsActivity.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/activities/search/SearchFragment.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/extensions/DimenExtension.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/MainActivityFragment.kt rename app/src/main/java/code/name/monkey/retromusic/{activities => fragments/about}/AboutActivity.kt (99%) create mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt rename app/src/main/java/code/name/monkey/retromusic/{activities => fragments}/albums/AlbumDetailsFragment.kt (86%) rename app/src/main/java/code/name/monkey/retromusic/{activities => fragments}/albums/AlbumDetailsViewModel.kt (97%) create mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt rename app/src/main/java/code/name/monkey/retromusic/{activities => fragments}/artists/ArtistDetailsViewModel.kt (97%) create mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt rename app/src/main/java/code/name/monkey/retromusic/{activities/genre => fragments/genres}/GenreDetailsViewModel.kt (96%) rename app/src/main/java/code/name/monkey/retromusic/fragments/home/{BannerHomeFragment.kt => HomeFragment.kt} (83%) create mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt rename app/src/main/java/code/name/monkey/retromusic/{activities/playlist/PlaylistDetailActivity.kt => fragments/playlists/PlaylistDetailsFragment.kt} (59%) rename app/src/main/java/code/name/monkey/retromusic/{activities/playlist => fragments/playlists}/PlaylistDetailsViewModel.kt (97%) create mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt create mode 100644 app/src/main/res/drawable/ic_baseline.xml create mode 100644 app/src/main/res/drawable/ic_layout.xml rename app/src/main/res/layout-land/{activity_album.xml => activity_album_details.xml} (100%) rename app/src/main/res/layout/{activity_album.xml => activity_album_details.xml} (100%) create mode 100644 app/src/main/res/layout/fragment_about.xml create mode 100644 app/src/main/res/layout/fragment_artist_details.xml create mode 100644 app/src/main/res/layout/fragment_playlist_detail.xml create mode 100644 app/src/main/res/layout/fragment_search.xml create mode 100644 app/src/main/res/layout/fragment_settings.xml rename app/src/main/res/navigation/{retro_graph.xml => library_graph.xml} (65%) create mode 100644 app/src/main/res/navigation/main_graph.xml diff --git a/app/build.gradle b/app/build.gradle index bf1c7c9a1..b70c4c28c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,7 +34,7 @@ android { } signingConfigs { release { - Properties properties = getProperties('/Users/h4h13/Documents/Github/retro.properties') + Properties properties = getProperties('/Users/apple/Documents/Github/retro.properties') storeFile file(getProperty(properties, 'storeFile')) keyAlias getProperty(properties, 'keyAlias') storePassword getProperty(properties, 'storePassword') @@ -120,6 +120,7 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'com.google.android.material:material:1.3.0-alpha01' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' def retrofit_version = '2.9.0' implementation "com.squareup.retrofit2:retrofit:$retrofit_version" @@ -160,7 +161,8 @@ dependencies { implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" - implementation 'com.google.android.play:core:1.7.3' + def playcore_version = "1.8.1" + implementation "com.google.android.play:core-ktx:$playcore_version" implementation 'me.jorgecastillo:androidcolorx:0.2.0' debugImplementation 'com.amitshekhar.android:debug-db:1.0.4' implementation 'com.github.dhaval2404:imagepicker:1.7.1' @@ -176,5 +178,4 @@ dependencies { def nav_version = "2.3.0" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" - } \ No newline at end of file diff --git a/app/release/output.json b/app/release/output-metadata.json similarity index 90% rename from app/release/output.json rename to app/release/output-metadata.json index 758f3ea4a..c3bff6d72 100644 --- a/app/release/output.json +++ b/app/release/output-metadata.json @@ -12,7 +12,7 @@ "filters": [], "properties": [], "versionCode": 10438, - "versionName": "10438", + "versionName": "3.5.650_0810", "enabled": true, "outputFile": "app-release.apk" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 07c6c27df..50f044269 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,12 +21,12 @@ - - - - + - - diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.kt b/app/src/main/java/code/name/monkey/retromusic/Constants.kt index b5399322c..0f037ba76 100644 --- a/app/src/main/java/code/name/monkey/retromusic/Constants.kt +++ b/app/src/main/java/code/name/monkey/retromusic/Constants.kt @@ -48,13 +48,16 @@ object Constants { MediaStore.Audio.AudioColumns.ALBUM, // 8 MediaStore.Audio.AudioColumns.ARTIST_ID, // 9 MediaStore.Audio.AudioColumns.ARTIST,// 10 - MediaStore.Audio.AudioColumns.COMPOSER// 11 + MediaStore.Audio.AudioColumns.COMPOSER,// 11 + "album_artist"//12 ) const val NUMBER_OF_TOP_TRACKS = 99 } - +const val EXTRA_GENRE = "extra_genre" +const val EXTRA_PLAYLIST = "extra_playlist" +const val EXTRA_ALBUM_ID = "extra_album_id" +const val EXTRA_ARTIST_ID = "extra_artist_id" const val EXTRA_SONG = "extra_songs" -const val EXTRA_PLAYLIST = "extra_list" const val LIBRARY_CATEGORIES = "library_categories" const val EXTRA_SONG_INFO = "extra_song_info" const val DESATURATED_COLOR = "desaturated_color" diff --git a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt index f551bd945..e89826fa7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt +++ b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt @@ -1,9 +1,9 @@ package code.name.monkey.retromusic -import code.name.monkey.retromusic.activities.albums.AlbumDetailsViewModel -import code.name.monkey.retromusic.activities.artists.ArtistDetailsViewModel -import code.name.monkey.retromusic.activities.genre.GenreDetailsViewModel -import code.name.monkey.retromusic.activities.playlist.PlaylistDetailsViewModel +import code.name.monkey.retromusic.fragments.albums.AlbumDetailsViewModel +import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel +import code.name.monkey.retromusic.fragments.genres.GenreDetailsViewModel +import code.name.monkey.retromusic.fragments.playlists.PlaylistDetailsViewModel import code.name.monkey.retromusic.activities.search.SearchViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.model.Genre @@ -28,19 +28,31 @@ private val viewModules = module { } viewModel { (albumId: Int) -> - AlbumDetailsViewModel(get(), albumId) + AlbumDetailsViewModel( + get(), + albumId + ) } viewModel { (artistId: Int) -> - ArtistDetailsViewModel(get(), artistId) + ArtistDetailsViewModel( + get(), + artistId + ) } viewModel { (playlist: Playlist) -> - PlaylistDetailsViewModel(get(), playlist) + PlaylistDetailsViewModel( + get(), + playlist + ) } viewModel { (genre: Genre) -> - GenreDetailsViewModel(get(), genre) + GenreDetailsViewModel( + get(), + genre + ) } viewModel { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt index c2a515dc3..d5e39b772 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt @@ -1,113 +1,51 @@ package code.name.monkey.retromusic.activities -import android.app.ActivityOptions -import android.content.* -import android.content.IntentSender.SendIntentException +import android.content.Intent +import android.content.SharedPreferences import android.os.Bundle import android.provider.MediaStore import android.util.Log -import android.view.Menu -import android.view.MenuItem -import android.view.SubMenu import android.view.View -import androidx.core.app.ActivityCompat -import androidx.fragment.app.Fragment -import androidx.fragment.app.commit -import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor +import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil -import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.retromusic.* import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity -import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog.Companion.create +import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager +import code.name.monkey.retromusic.extensions.findNavController import code.name.monkey.retromusic.fragments.LibraryViewModel -import code.name.monkey.retromusic.fragments.albums.AlbumsFragment -import code.name.monkey.retromusic.fragments.artists.ArtistsFragment -import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment -import code.name.monkey.retromusic.fragments.folder.FoldersFragment -import code.name.monkey.retromusic.fragments.genres.GenresFragment -import code.name.monkey.retromusic.fragments.home.BannerHomeFragment -import code.name.monkey.retromusic.fragments.playlists.PlaylistsFragment -import code.name.monkey.retromusic.fragments.queue.PlayingQueueFragment -import code.name.monkey.retromusic.fragments.songs.SongsFragment -import code.name.monkey.retromusic.helper.MusicPlayerRemote.isPlaying import code.name.monkey.retromusic.helper.MusicPlayerRemote.openAndShuffleQueue import code.name.monkey.retromusic.helper.MusicPlayerRemote.openQueue import code.name.monkey.retromusic.helper.MusicPlayerRemote.playFromUri import code.name.monkey.retromusic.helper.MusicPlayerRemote.shuffleMode import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs -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.AlbumLoader.getAlbum import code.name.monkey.retromusic.loaders.ArtistLoader.getArtist import code.name.monkey.retromusic.loaders.PlaylistSongsLoader.getPlaylistSongList import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.AppRater.appLaunched -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 com.afollestad.materialcab.MaterialCab -import com.google.android.material.appbar.AppBarLayout -import com.google.android.material.snackbar.Snackbar -import com.google.android.play.core.appupdate.AppUpdateInfo -import com.google.android.play.core.appupdate.AppUpdateManager -import com.google.android.play.core.appupdate.AppUpdateManagerFactory -import com.google.android.play.core.install.InstallState -import com.google.android.play.core.install.InstallStateUpdatedListener -import com.google.android.play.core.install.model.AppUpdateType -import com.google.android.play.core.install.model.InstallStatus.DOWNLOADED -import com.google.android.play.core.install.model.InstallStatus.INSTALLED -import com.google.android.play.core.install.model.UpdateAvailability -import com.google.android.play.core.tasks.Task -import kotlinx.android.synthetic.main.activity_main_content.* +import com.afollestad.materialdialogs.color.ColorChooserDialog import org.koin.android.ext.android.inject import java.util.* class MainActivity : AbsSlidingMusicPanelActivity(), - SharedPreferences.OnSharedPreferenceChangeListener, CabHolder { + SharedPreferences.OnSharedPreferenceChangeListener, CabHolder, + ColorChooserDialog.ColorCallback { companion object { const val TAG = "MainActivity" const val EXPAND_PANEL = "expand_panel" const val APP_UPDATE_REQUEST_CODE = 9002 } + private val libraryViewModel: LibraryViewModel by inject() private var cab: MaterialCab? = null - private val intentFilter = IntentFilter(Intent.ACTION_SCREEN_OFF) - private lateinit var currentFragment: MainActivityFragmentCallbacks - private var appUpdateManager: AppUpdateManager? = null private var blockRequestPermissions = false - private val listener = object : InstallStateUpdatedListener { - override fun onStateUpdate(state: InstallState) { - when { - state.installStatus() == DOWNLOADED -> { - popupSnackBarForCompleteUpdate() - } - state.installStatus() == INSTALLED -> { - appUpdateManager?.unregisterListener(this) - } - else -> { - Log.i(TAG, "InstallStateUpdatedListener: state: " + state.installStatus()) - } - } - } - } - private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val action = intent.action - if (action != null && action == Intent.ACTION_SCREEN_OFF) { - if (PreferenceUtil.isLockScreen && isPlaying) { - val activity = Intent(context, LockScreenActivity::class.java) - activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - ActivityCompat.startActivity(context, activity, null) - } - } - } - } override fun createContentView(): View { return wrapSlidingMusicPanel(R.layout.activity_main_content) @@ -122,29 +60,16 @@ class MainActivity : AbsSlidingMusicPanelActivity(), setTaskDescriptionColorAuto() hideStatusBar() setBottomBarVisibility(View.VISIBLE) - - addMusicServiceEventListener(libraryViewModel) - if (savedInstanceState == null) { - selectedFragment(PreferenceUtil.lastPage) - } else { - restoreCurrentFragment() - } - appLaunched(this) - setupToolbar() - checkUpdate() + addMusicServiceEventListener(libraryViewModel) updateTabs() - getBottomNavigationView().selectedItemId = PreferenceUtil.lastPage - getBottomNavigationView().setOnNavigationItemSelectedListener { - PreferenceUtil.lastPage = it.itemId - selectedFragment(it.itemId) - true - } } + override fun onSupportNavigateUp(): Boolean = + findNavController(R.id.fragment_container).navigateUp() + override fun onResume() { super.onResume() - registerReceiver(broadcastReceiver, intentFilter) PreferenceUtil.registerOnSharedPreferenceChangedListener(this) if (intent.hasExtra(EXPAND_PANEL) && intent.getBooleanExtra(EXPAND_PANEL, false) && @@ -153,411 +78,13 @@ class MainActivity : AbsSlidingMusicPanelActivity(), expandPanel() intent.removeExtra(EXPAND_PANEL) } - - appUpdateManager?.appUpdateInfo - ?.addOnSuccessListener { appUpdateInfo: AppUpdateInfo -> - if (appUpdateInfo.installStatus() == DOWNLOADED) { - popupSnackBarForCompleteUpdate() - } - try { - if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) { - appUpdateManager!!.startUpdateFlowForResult( - appUpdateInfo, - AppUpdateType.IMMEDIATE, - this, - APP_UPDATE_REQUEST_CODE - ) - } - } catch (e: SendIntentException) { - e.printStackTrace() - } - } } override fun onDestroy() { super.onDestroy() - unregisterReceiver(broadcastReceiver) PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this) } - override fun onPrepareOptionsMenu(menu: Menu?): Boolean { - ToolbarContentTintHelper.handleOnPrepareOptionsMenu(this, toolbar) - return super.onPrepareOptionsMenu(menu) - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.menu_main, menu) - menu ?: return super.onCreateOptionsMenu(menu) - if (isPlaylistPage()) { - menu.add(0, R.id.action_new_playlist, 1, R.string.new_playlist_title) - .setIcon(R.drawable.ic_playlist_add) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) - } - if (isHomePage()) { - menu.add(0, R.id.action_mic, 1, getString(R.string.action_search)) - .setIcon(R.drawable.ic_mic) - .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM) - } - if (isFolderPage()) { - menu.add(0, R.id.action_scan, 0, R.string.scan_media) - .setIcon(R.drawable.ic_scanner) - .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM) - menu.add(0, R.id.action_go_to_start_directory, 1, R.string.action_go_to_start_directory) - .setIcon(R.drawable.ic_bookmark_music) - .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM) - } - val fragment: Fragment? = getCurrentFragment() - if (fragment != null && fragment is AbsRecyclerViewCustomGridSizeFragment<*, *>) { - val gridSizeItem: MenuItem = menu.findItem(R.id.action_grid_size) - if (RetroUtil.isLandscape()) { - gridSizeItem.setTitle(R.string.action_grid_size_land) - } - setUpGridSizeMenu(fragment, gridSizeItem.subMenu) - setupLayoutMenu(fragment, menu.findItem(R.id.action_layout_type).subMenu) - setUpSortOrderMenu(fragment, menu.findItem(R.id.action_sort_order).subMenu) - } else { - menu.removeItem(R.id.action_layout_type) - menu.removeItem(R.id.action_grid_size) - menu.removeItem(R.id.action_sort_order) - } - menu.add(0, R.id.action_settings, 6, getString(R.string.action_settings)) - .setIcon(R.drawable.ic_settings) - .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM) - menu.add(0, R.id.action_search, 0, getString(R.string.action_search)) - .setIcon(R.drawable.ic_search) - .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS) - ToolbarContentTintHelper.handleOnCreateOptionsMenu( - this, - toolbar, - menu, - getToolbarBackgroundColor(toolbar) - ) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - val fragment = getCurrentFragment() - if (fragment is AbsRecyclerViewCustomGridSizeFragment<*, *>) { - if (handleGridSizeMenuItem(fragment, item)) { - return true - } - if (handleLayoutResType(fragment, item)) { - return true - } - if (handleSortOrderMenuItem(fragment, item)) { - return true - } - } - when (item.itemId) { - R.id.action_search -> NavigationUtil.goToSearch(this) - R.id.action_new_playlist -> { - create().show(supportFragmentManager, "CREATE_PLAYLIST") - return true - } - R.id.action_mic -> { - val options = ActivityOptions.makeSceneTransitionAnimation( - this, toolbar, - getString(R.string.transition_toolbar) - ) - NavigationUtil.goToSearch(this, true, options) - return true - } - R.id.action_settings -> { - NavigationUtil.goToSettings(this) - return true - } - } - return super.onOptionsItemSelected(item) - } - - private fun handleSortOrderMenuItem( - fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>, - item: MenuItem - ): Boolean { - var sortOrder: String? = null - when (fragment) { - is AlbumsFragment -> { - when (item.itemId) { - R.id.action_album_sort_order_asc -> sortOrder = AlbumSortOrder.ALBUM_A_Z - R.id.action_album_sort_order_desc -> sortOrder = AlbumSortOrder.ALBUM_Z_A - R.id.action_album_sort_order_artist -> sortOrder = AlbumSortOrder.ALBUM_ARTIST - R.id.action_album_sort_order_year -> sortOrder = AlbumSortOrder.ALBUM_YEAR - } - } - is ArtistsFragment -> { - when (item.itemId) { - R.id.action_artist_sort_order_asc -> sortOrder = ArtistSortOrder.ARTIST_A_Z - R.id.action_artist_sort_order_desc -> sortOrder = ArtistSortOrder.ARTIST_Z_A - } - } - is SongsFragment -> { - when (item.itemId) { - R.id.action_song_sort_order_asc -> sortOrder = SongSortOrder.SONG_A_Z - R.id.action_song_sort_order_desc -> sortOrder = SongSortOrder.SONG_Z_A - R.id.action_song_sort_order_artist -> sortOrder = SongSortOrder.SONG_ARTIST - R.id.action_song_sort_order_album -> sortOrder = SongSortOrder.SONG_ALBUM - R.id.action_song_sort_order_year -> sortOrder = SongSortOrder.SONG_YEAR - R.id.action_song_sort_order_date -> sortOrder = SongSortOrder.SONG_DATE - R.id.action_song_sort_order_composer -> sortOrder = SongSortOrder.COMPOSER - R.id.action_song_sort_order_date_modified -> - sortOrder = SongSortOrder.SONG_DATE_MODIFIED - } - } - } - - if (sortOrder != null) { - item.isChecked = true - fragment.setAndSaveSortOrder(sortOrder) - return true - } - - return false - } - - private fun handleLayoutResType( - fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>, - item: MenuItem - ): Boolean { - var layoutRes = -1 - when (item.itemId) { - R.id.action_layout_normal -> layoutRes = R.layout.item_grid - R.id.action_layout_card -> layoutRes = R.layout.item_card - R.id.action_layout_colored_card -> layoutRes = R.layout.item_card_color - R.id.action_layout_circular -> layoutRes = R.layout.item_grid_circle - R.id.action_layout_image -> layoutRes = R.layout.image - R.id.action_layout_gradient_image -> layoutRes = R.layout.item_image_gradient - } - if (layoutRes != -1) { - item.isChecked = true - fragment.setAndSaveLayoutRes(layoutRes) - return true - } - return false - } - - private fun handleGridSizeMenuItem( - fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>, - item: MenuItem - ): Boolean { - var gridSize = 0 - when (item.itemId) { - R.id.action_grid_size_1 -> gridSize = 1 - R.id.action_grid_size_2 -> gridSize = 2 - R.id.action_grid_size_3 -> gridSize = 3 - R.id.action_grid_size_4 -> gridSize = 4 - R.id.action_grid_size_5 -> gridSize = 5 - R.id.action_grid_size_6 -> gridSize = 6 - R.id.action_grid_size_7 -> gridSize = 7 - R.id.action_grid_size_8 -> gridSize = 8 - } - if (gridSize > 0) { - item.isChecked = true - fragment.setAndSaveGridSize(gridSize) - return true - } - return false - } - - private fun setUpGridSizeMenu( - fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>, - gridSizeMenu: SubMenu - ) { - when (fragment.getGridSize()) { - 1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = true - 2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true - 3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true - 4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true - 5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true - 6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true - 7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true - 8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true - } - val maxGridSize = fragment.maxGridSize - if (maxGridSize < 8) { - gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false - } - if (maxGridSize < 7) { - gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false - } - if (maxGridSize < 6) { - gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false - } - if (maxGridSize < 5) { - gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false - } - if (maxGridSize < 4) { - gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false - } - if (maxGridSize < 3) { - gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false - } - } - - private fun setupLayoutMenu( - fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>, - subMenu: SubMenu - ) { - when (fragment.itemLayoutRes()) { - R.layout.item_card -> - subMenu.findItem(R.id.action_layout_card).isChecked = true - R.layout.item_card_color -> - subMenu.findItem(R.id.action_layout_colored_card).isChecked = true - R.layout.item_grid_circle -> - subMenu.findItem(R.id.action_layout_circular).isChecked = true - R.layout.image -> - subMenu.findItem(R.id.action_layout_image).isChecked = true - R.layout.item_image_gradient -> - subMenu.findItem(R.id.action_layout_gradient_image).isChecked = true - R.layout.item_grid -> - subMenu.findItem(R.id.action_layout_normal).isChecked = true - else -> - subMenu.findItem(R.id.action_layout_normal).isChecked = true - } - } - - private fun setUpSortOrderMenu( - fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>, - sortOrderMenu: SubMenu - ) { - val currentSortOrder = fragment.getSortOrder() - sortOrderMenu.clear() - when (fragment) { - is AlbumsFragment -> { - sortOrderMenu.add( - 0, - R.id.action_album_sort_order_asc, - 0, - R.string.sort_order_a_z - ).isChecked = currentSortOrder == AlbumSortOrder.ALBUM_A_Z - sortOrderMenu.add( - 0, - R.id.action_album_sort_order_desc, - 1, - R.string.sort_order_z_a - ).isChecked = - currentSortOrder == AlbumSortOrder.ALBUM_Z_A - sortOrderMenu.add( - 0, - R.id.action_album_sort_order_artist, - 2, - R.string.sort_order_artist - ).isChecked = - currentSortOrder == AlbumSortOrder.ALBUM_ARTIST - sortOrderMenu.add( - 0, - R.id.action_album_sort_order_year, - 3, - R.string.sort_order_year - ).isChecked = - currentSortOrder == AlbumSortOrder.ALBUM_YEAR - } - is ArtistsFragment -> { - sortOrderMenu.add( - 0, - R.id.action_artist_sort_order_asc, - 0, - R.string.sort_order_a_z - ).isChecked = - currentSortOrder == ArtistSortOrder.ARTIST_A_Z - sortOrderMenu.add( - 0, - R.id.action_artist_sort_order_desc, - 1, - R.string.sort_order_z_a - ).isChecked = - currentSortOrder == ArtistSortOrder.ARTIST_Z_A - } - is SongsFragment -> { - sortOrderMenu.add( - 0, - R.id.action_song_sort_order_asc, - 0, - R.string.sort_order_a_z - ).isChecked = - currentSortOrder == SongSortOrder.SONG_A_Z - sortOrderMenu.add( - 0, - R.id.action_song_sort_order_desc, - 1, - R.string.sort_order_z_a - ).isChecked = - currentSortOrder == SongSortOrder.SONG_Z_A - sortOrderMenu.add( - 0, - R.id.action_song_sort_order_artist, - 2, - R.string.sort_order_artist - ).isChecked = - currentSortOrder == SongSortOrder.SONG_ARTIST - sortOrderMenu.add( - 0, - R.id.action_song_sort_order_album, - 3, - R.string.sort_order_album - ).isChecked = - currentSortOrder == SongSortOrder.SONG_ALBUM - sortOrderMenu.add( - 0, - R.id.action_song_sort_order_year, - 4, - R.string.sort_order_year - ).isChecked = - currentSortOrder == SongSortOrder.SONG_YEAR - sortOrderMenu.add( - 0, - R.id.action_song_sort_order_date, - 5, - R.string.sort_order_date - ).isChecked = - currentSortOrder == SongSortOrder.SONG_DATE - sortOrderMenu.add( - 0, - R.id.action_song_sort_order_date_modified, - 6, - R.string.sort_order_date_modified - ).isChecked = currentSortOrder == SongSortOrder.SONG_DATE_MODIFIED - sortOrderMenu.add( - 0, - R.id.action_song_sort_order_composer, - 7, - R.string.sort_order_composer - ).isChecked = currentSortOrder == SongSortOrder.COMPOSER - } - } - sortOrderMenu.setGroupCheckable(0, true, true) - } - - private fun getCurrentFragment(): Fragment? { - return supportFragmentManager.findFragmentById(R.id.fragment_container) - } - - private fun isFolderPage(): Boolean { - return supportFragmentManager.findFragmentByTag(FoldersFragment.TAG) is FoldersFragment - } - - private fun isHomePage(): Boolean { - return supportFragmentManager.findFragmentByTag(BannerHomeFragment.TAG) is BannerHomeFragment - } - - private fun isPlaylistPage(): Boolean { - return supportFragmentManager.findFragmentByTag(PlaylistsFragment.TAG) is PlaylistsFragment - } - - fun addOnAppBarOffsetChangedListener( - changedListener: AppBarLayout.OnOffsetChangedListener - ) { - appBarLayout.addOnOffsetChangedListener(changedListener) - } - - fun removeOnAppBarOffsetChangedListener( - changedListener: AppBarLayout.OnOffsetChangedListener - ) { - appBarLayout.removeOnOffsetChangedListener(changedListener) - } - - fun getTotalAppBarScrollingRange(): Int { - return appBarLayout.totalScrollRange - } override fun requestPermissions() { if (!blockRequestPermissions) { @@ -572,115 +99,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), } } - private fun setupToolbar() { - toolbar.setBackgroundColor(resolveColor(this, R.attr.colorSurface)) - appBarLayout.setBackgroundColor(resolveColor(this, R.attr.colorSurface)) - setSupportActionBar(toolbar) - } - - private fun checkUpdate() { - appUpdateManager = AppUpdateManagerFactory.create(this) - appUpdateManager?.registerListener(listener) - - val appUpdateInfoTask: Task? = appUpdateManager?.appUpdateInfo - appUpdateInfoTask?.addOnSuccessListener { appUpdateInfo -> - if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE - && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) - ) { - try { - appUpdateManager?.startUpdateFlowForResult( - appUpdateInfo, - AppUpdateType.IMMEDIATE, - this, - APP_UPDATE_REQUEST_CODE - ) - } catch (e: SendIntentException) { - e.printStackTrace() - } - } - } - } - - private fun popupSnackBarForCompleteUpdate() { - val snackBar = - Snackbar.make( - findViewById(R.id.mainContent), - "New app is ready!", - Snackbar.LENGTH_INDEFINITE - ) - snackBar.setAction( - "Install" - ) { - appUpdateManager?.completeUpdate() - } - snackBar.setActionTextColor(accentColor(this)) - snackBar.show() - } - - private fun setCurrentFragment( - fragment: Fragment, - tag: String - ) { - supportFragmentManager.commit { - setCustomAnimations( - R.anim.retro_fragment_open_enter, - R.anim.retro_fragment_open_exit, - R.anim.retro_fragment_fade_enter, - R.anim.retro_fragment_fade_exit - ) - replace(R.id.fragment_container, fragment, tag) - } - currentFragment = fragment as MainActivityFragmentCallbacks - } - - private fun selectedFragment(itemId: Int) { - when (itemId) { - R.id.action_album -> setCurrentFragment( - AlbumsFragment.newInstance(), - AlbumsFragment.TAG - ) - R.id.action_artist -> setCurrentFragment( - ArtistsFragment.newInstance(), - ArtistsFragment.TAG - ) - R.id.action_playlist -> setCurrentFragment( - PlaylistsFragment.newInstance(), - PlaylistsFragment.TAG - ) - R.id.action_genre -> setCurrentFragment( - GenresFragment.newInstance(), - GenresFragment.TAG - ) - R.id.action_playing_queue -> setCurrentFragment( - PlayingQueueFragment.newInstance(), - PlayingQueueFragment.TAG - ) - R.id.action_song -> setCurrentFragment( - SongsFragment.newInstance(), - SongsFragment.TAG - ) - R.id.action_folder -> setCurrentFragment( - FoldersFragment.newInstance(this), - FoldersFragment.TAG - ) - R.id.action_home -> setCurrentFragment( - BannerHomeFragment.newInstance(), - BannerHomeFragment.TAG - ) - else -> setCurrentFragment( - BannerHomeFragment.newInstance(), - BannerHomeFragment.TAG - ) - } - } - - private fun restoreCurrentFragment() { - val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container) - if (fragment != null) { - currentFragment = fragment as MainActivityFragmentCallbacks - } - } - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { if (key == GENERAL_THEME || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES ) { @@ -762,11 +180,12 @@ class MainActivity : AbsSlidingMusicPanelActivity(), } override fun handleBackPress(): Boolean { + getBottomNavigationView().menu.getItem(0).isChecked = true if (cab != null && cab!!.isActive) { cab?.finish() return true } - return super.handleBackPress() || currentFragment.handleBackPress() + return super.handleBackPress() } override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { @@ -787,4 +206,19 @@ class MainActivity : AbsSlidingMusicPanelActivity(), .start(callback) return cab as MaterialCab } + + override fun onColorSelection(dialog: ColorChooserDialog, selectedColor: Int) { + when (dialog.title) { + R.string.accent_color -> { + ThemeStore.editTheme(this).accentColor(selectedColor).commit() + if (VersionUtils.hasNougatMR()) + DynamicShortcutManager(this).updateDynamicShortcuts() + } + } + recreate() + } + + override fun onColorChooserDismissed(dialog: ColorChooserDialog) { + + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt deleted file mode 100755 index 7f728214f..000000000 --- a/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt +++ /dev/null @@ -1,104 +0,0 @@ -package code.name.monkey.retromusic.activities - -import android.os.Bundle -import android.view.MenuItem -import androidx.annotation.StringRes -import androidx.fragment.app.Fragment -import androidx.navigation.NavController -import androidx.navigation.fragment.NavHostFragment -import androidx.navigation.ui.AppBarConfiguration -import code.name.monkey.appthemehelper.ThemeStore -import code.name.monkey.appthemehelper.util.VersionUtils -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.base.AbsBaseActivity -import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager -import code.name.monkey.retromusic.extensions.applyToolbar -import com.afollestad.materialdialogs.color.ColorChooserDialog -import kotlinx.android.synthetic.main.activity_settings.* - -class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback { - - private val fragmentManager = supportFragmentManager - private lateinit var appBarConfiguration: AppBarConfiguration - private lateinit var navController: NavController - - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_settings) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setLightNavigationBar(true) - setupToolbar() - } - - private fun setupToolbar() { - setTitle(R.string.action_settings) - applyToolbar(toolbar) - val navHostFragment = - supportFragmentManager.findFragmentById(R.id.contentFrame) as NavHostFragment - val navController: NavController = navHostFragment.navController - navController.addOnDestinationChangedListener { _, _, _ -> - toolbar.title = navController.currentDestination?.label - } - - //It removes the back button - //appBarConfiguration = AppBarConfiguration(navController.graph) - //setupActionBarWithNavController(navController, appBarConfiguration) - } - - override fun onSupportNavigateUp(): Boolean { - return navController.navigateUp() || super.onSupportNavigateUp() - } - - fun setupFragment(fragment: Fragment, @StringRes titleName: Int) { - val fragmentTransaction = fragmentManager - .beginTransaction() - .setCustomAnimations( - R.anim.sliding_in_left, - R.anim.sliding_out_right, - android.R.anim.slide_in_left, - android.R.anim.slide_out_right - ) - fragmentTransaction.replace(R.id.contentFrame, fragment, fragment.tag) - fragmentTransaction.addToBackStack(null) - fragmentTransaction.commit() - setTitle(titleName) - } - - override fun onBackPressed() { - if (fragmentManager.backStackEntryCount == 0) { - super.onBackPressed() - } else { - setTitle(R.string.action_settings) - fragmentManager.popBackStack() - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - onBackPressed() - return true - } - return super.onOptionsItemSelected(item) - } - - companion object { - const val TAG: String = "SettingsActivity" - } - - override fun onColorSelection(dialog: ColorChooserDialog, selectedColor: Int) { - when (dialog.title) { - R.string.accent_color -> { - ThemeStore.editTheme(this).accentColor(selectedColor).commit() - if (VersionUtils.hasNougatMR()) - DynamicShortcutManager(this).updateDynamicShortcuts() - } - } - recreate() - } - - override fun onColorChooserDismissed(dialog: ColorChooserDialog) { - - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsActivity.kt deleted file mode 100644 index 18c0511a9..000000000 --- a/app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsActivity.kt +++ /dev/null @@ -1,399 +0,0 @@ -package code.name.monkey.retromusic.activities.albums - -import android.app.ActivityOptions -import android.content.Intent -import android.os.Bundle -import android.transition.Slide -import android.view.Menu -import android.view.MenuItem -import android.view.SubMenu -import android.view.View -import androidx.core.app.ActivityCompat -import androidx.recyclerview.widget.DefaultItemAnimator -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.LinearLayoutManager -import code.name.monkey.appthemehelper.util.ATHUtil -import code.name.monkey.appthemehelper.util.MaterialUtil -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity -import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity -import code.name.monkey.retromusic.activities.tageditor.AlbumTagEditorActivity -import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter -import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter -import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog -import code.name.monkey.retromusic.dialogs.DeleteSongsDialog -import code.name.monkey.retromusic.extensions.extraNotNull -import code.name.monkey.retromusic.extensions.show -import code.name.monkey.retromusic.extensions.surfaceColor -import code.name.monkey.retromusic.glide.AlbumGlideRequest -import code.name.monkey.retromusic.glide.ArtistGlideRequest -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget -import code.name.monkey.retromusic.helper.MusicPlayerRemote -import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder -import code.name.monkey.retromusic.interfaces.CabHolder -import code.name.monkey.retromusic.model.Album -import code.name.monkey.retromusic.model.Artist -import code.name.monkey.retromusic.network.model.LastFmAlbum -import code.name.monkey.retromusic.util.* -import code.name.monkey.retromusic.util.color.MediaNotificationProcessor -import com.afollestad.materialcab.MaterialCab -import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.activity_album.* -import kotlinx.android.synthetic.main.activity_album_content.* -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf -import java.util.* -import android.util.Pair as UtilPair - -class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { - override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { - cab?.let { - if (it.isActive) it.finish() - } - cab = MaterialCab(this, R.id.cab_stub) - .setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor( - RetroColorUtil.shiftBackgroundColorForLightText( - ATHUtil.resolveColor( - this, - R.attr.colorSurface - ) - ) - ) - .start(callback) - return cab as MaterialCab - } - - private val detailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_ALBUM_ID).value) - } - private lateinit var simpleSongAdapter: SimpleSongAdapter - private lateinit var album: Album - private var cab: MaterialCab? = null - private val savedSortOrder: String - get() = PreferenceUtil.albumDetailSongSortOrder - - override fun createContentView(): View { - return wrapSlidingMusicPanel(R.layout.activity_album) - } - - private fun windowEnterTransition() { - val slide = Slide() - slide.excludeTarget(R.id.appBarLayout, true) - slide.excludeTarget(R.id.status_bar, true) - slide.excludeTarget(android.R.id.statusBarBackground, true) - slide.excludeTarget(android.R.id.navigationBarBackground, true) - window.enterTransition = slide - } - - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - super.onCreate(savedInstanceState) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setTaskDescriptionColorAuto() - setLightNavigationBar(true) - setBottomBarVisibility(View.GONE) - window.sharedElementsUseOverlay = true - windowEnterTransition() - toolbar.setBackgroundColor(surfaceColor()) - - addMusicServiceEventListener(detailsViewModel) - ActivityCompat.postponeEnterTransition(this) - - detailsViewModel.getAlbum().observe(this, androidx.lifecycle.Observer { - ActivityCompat.startPostponedEnterTransition(this@AlbumDetailsActivity) - showAlbum(it) - }) - detailsViewModel.getArtist().observe(this, androidx.lifecycle.Observer { - loadArtistImage(it) - }) - detailsViewModel.getMoreAlbums().observe(this, androidx.lifecycle.Observer { - moreAlbums(it) - }) - detailsViewModel.getAlbumInfo().observe(this, androidx.lifecycle.Observer { - aboutAlbum(it) - }) - setupRecyclerView() - artistImage.setOnClickListener { - val artistPairs = ActivityOptions.makeSceneTransitionAnimation( - this, - UtilPair.create( - artistImage, - getString(R.string.transition_artist_image) - ) - ) - NavigationUtil.goToArtistOptions(this, album.artistId, artistPairs) - } - playAction.apply { - setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) } - } - shuffleAction.apply { - setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(album.songs!!, true) } - } - - aboutAlbumText.setOnClickListener { - if (aboutAlbumText.maxLines == 4) { - aboutAlbumText.maxLines = Integer.MAX_VALUE - } else { - aboutAlbumText.maxLines = 4 - } - } - } - - private fun setupRecyclerView() { - simpleSongAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this) - recyclerView.apply { - layoutManager = LinearLayoutManager(this@AlbumDetailsActivity) - itemAnimator = DefaultItemAnimator() - isNestedScrollingEnabled = false - adapter = simpleSongAdapter - } - } - - private fun showAlbum(album: Album) { - if (album.songs!!.isEmpty()) { - finish() - return - } - this.album = album - - albumTitle.text = album.title - val songText = - resources.getQuantityString( - R.plurals.albumSongs, - album.songCount, - album.songCount - ) - songTitle.text = songText - - if (MusicUtil.getYearString(album.year) == "-") { - albumText.text = String.format( - "%s • %s", - album.artistName, - MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs)) - ) - } else { - albumText.text = String.format( - "%s • %s • %s", - album.artistName, - MusicUtil.getYearString(album.year), - MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs)) - ) - } - loadAlbumCover() - simpleSongAdapter.swapDataSet(album.songs) - detailsViewModel.loadArtist(album.artistId) - detailsViewModel.loadAlbumInfo(album) - } - - private fun moreAlbums(albums: List) { - moreTitle.show() - moreRecyclerView.show() - moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName) - - val albumAdapter = HorizontalAlbumAdapter(this, albums, null) - moreRecyclerView.layoutManager = GridLayoutManager( - this, - 1, - GridLayoutManager.HORIZONTAL, - false - ) - moreRecyclerView.adapter = albumAdapter - } - - private fun aboutAlbum(lastFmAlbum: LastFmAlbum) { - if (lastFmAlbum.album != null) { - if (lastFmAlbum.album.wiki != null) { - aboutAlbumText.show() - aboutAlbumTitle.show() - aboutAlbumTitle.text = - String.format(getString(R.string.about_album_label), lastFmAlbum.album.name) - aboutAlbumText.text = lastFmAlbum.album.wiki.content - } - if (lastFmAlbum.album.listeners.isNotEmpty()) { - listeners.show() - listenersLabel.show() - scrobbles.show() - scrobblesLabel.show() - - listeners.text = RetroUtil.formatValue(lastFmAlbum.album.listeners.toFloat()) - scrobbles.text = RetroUtil.formatValue(lastFmAlbum.album.playcount.toFloat()) - } - } - } - - private fun loadArtistImage(artist: Artist) { - ArtistGlideRequest.Builder.from(Glide.with(this), artist) - .generatePalette(this) - .build() - .dontAnimate() - .dontTransform() - .into(object : RetroMusicColoredTarget(artistImage) { - override fun onColorReady(colors: MediaNotificationProcessor) { - } - }) - } - - private fun loadAlbumCover() { - AlbumGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong()) - .checkIgnoreMediaStore(this) - .ignoreMediaStore(PreferenceUtil.isIgnoreMediaStoreArtwork) - .generatePalette(this) - .build() - .dontAnimate() - .dontTransform() - .into(object : RetroMusicColoredTarget(image) { - override fun onColorReady(colors: MediaNotificationProcessor) { - setColors(colors) - } - }) - } - - private fun setColors(color: MediaNotificationProcessor) { - MaterialUtil.tintColor( - button = shuffleAction, - textColor = color.primaryTextColor, - backgroundColor = color.backgroundColor - ) - MaterialUtil.tintColor( - button = playAction, - textColor = color.primaryTextColor, - backgroundColor = color.backgroundColor - ) - - setSupportActionBar(toolbar) - supportActionBar?.title = null - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.menu_album_detail, menu) - val sortOrder = menu.findItem(R.id.action_sort_order) - setUpSortOrderMenu(sortOrder.subMenu) - ToolbarContentTintHelper.handleOnCreateOptionsMenu( - this, - toolbar, - menu, - getToolbarBackgroundColor(toolbar) - ) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return handleSortOrderMenuItem(item) - } - - private fun handleSortOrderMenuItem(item: MenuItem): Boolean { - var sortOrder: String? = null - val songs = simpleSongAdapter.dataSet - when (item.itemId) { - R.id.action_play_next -> { - MusicPlayerRemote.playNext(songs) - return true - } - R.id.action_add_to_current_playing -> { - MusicPlayerRemote.enqueue(songs) - return true - } - R.id.action_add_to_playlist -> { - AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST") - return true - } - R.id.action_delete_from_device -> { - DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS") - return true - } - android.R.id.home -> { - super.onBackPressed() - return true - } - R.id.action_tag_editor -> { - val intent = Intent(this, AlbumTagEditorActivity::class.java) - intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id) - val options = ActivityOptions.makeSceneTransitionAnimation( - this, - albumCoverContainer, - "${getString(R.string.transition_album_art)}_${album.id}" - ) - startActivityForResult( - intent, - TAG_EDITOR_REQUEST, options.toBundle() - ) - return true - } - /*Sort*/ - R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z - R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A - R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST - R.id.action_sort_order_artist_song_duration -> - sortOrder = AlbumSongSortOrder.SONG_DURATION - } - if (sortOrder != null) { - item.isChecked = true - setSaveSortOrder(sortOrder) - } - return true - } - - private fun setUpSortOrderMenu(sortOrder: SubMenu) { - when (savedSortOrder) { - AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title) - .isChecked = true - AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc) - .isChecked = true - AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list) - .isChecked = true - AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration) - .isChecked = true - } - } - - private fun setSaveSortOrder(sortOrder: String) { - PreferenceUtil.albumDetailSongSortOrder = sortOrder - when (sortOrder) { - AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 -> - o1.trackNumber.compareTo( - o2.trackNumber - ) - }) - AlbumSongSortOrder.SONG_A_Z -> album.songs?.sortWith(Comparator { o1, o2 -> - o1.title.compareTo( - o2.title - ) - }) - AlbumSongSortOrder.SONG_Z_A -> album.songs?.sortWith(Comparator { o1, o2 -> - o2.title.compareTo( - o1.title - ) - }) - AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 -> - o1.duration.compareTo( - o2.duration - ) - }) - } - album.songs?.let { simpleSongAdapter.swapDataSet(it) } - } - - - override fun onBackPressed() { - if (cab != null && cab!!.isActive) { - cab?.finish() - } else { - super.onBackPressed() - } - } - - override fun onDestroy() { - super.onDestroy() - removeMusicServiceEventListener(detailsViewModel) - } - - companion object { - - const val EXTRA_ALBUM_ID = "extra_album_id" - private const val TAG_EDITOR_REQUEST = 2001 - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/artists/ArtistDetailActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/artists/ArtistDetailActivity.kt deleted file mode 100755 index 420a1f402..000000000 --- a/app/src/main/java/code/name/monkey/retromusic/activities/artists/ArtistDetailActivity.kt +++ /dev/null @@ -1,324 +0,0 @@ -package code.name.monkey.retromusic.activities.artists - -import android.app.Activity -import android.content.Intent -import android.os.Bundle -import android.text.Spanned -import android.transition.Slide -import android.view.Menu -import android.view.MenuItem -import android.view.View -import android.widget.Toast -import androidx.core.app.ActivityCompat -import androidx.core.text.HtmlCompat -import androidx.recyclerview.widget.DefaultItemAnimator -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.LinearLayoutManager -import code.name.monkey.appthemehelper.util.ATHUtil -import code.name.monkey.appthemehelper.util.MaterialUtil -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity -import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter -import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter -import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog -import code.name.monkey.retromusic.extensions.extraNotNull -import code.name.monkey.retromusic.extensions.show -import code.name.monkey.retromusic.extensions.surfaceColor -import code.name.monkey.retromusic.glide.ArtistGlideRequest -import code.name.monkey.retromusic.glide.RetroMusicColoredTarget -import code.name.monkey.retromusic.helper.MusicPlayerRemote -import code.name.monkey.retromusic.interfaces.CabHolder -import code.name.monkey.retromusic.model.Artist -import code.name.monkey.retromusic.network.model.LastFmArtist -import code.name.monkey.retromusic.util.CustomArtistImageUtil -import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.RetroColorUtil -import code.name.monkey.retromusic.util.RetroUtil -import code.name.monkey.retromusic.util.color.MediaNotificationProcessor -import com.afollestad.materialcab.MaterialCab -import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.activity_artist_content.* -import kotlinx.android.synthetic.main.activity_artist_details.* -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf -import java.util.* -import kotlin.collections.ArrayList - -class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { - override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { - cab?.let { - if (it.isActive) it.finish() - } - cab = MaterialCab(this, R.id.cab_stub) - .setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor( - RetroColorUtil.shiftBackgroundColorForLightText( - ATHUtil.resolveColor( - this, - R.attr.colorSurface - ) - ) - ) - .start(callback) - return cab as MaterialCab - } - - private var cab: MaterialCab? = null - private var biography: Spanned? = null - private lateinit var artist: Artist - private lateinit var songAdapter: SimpleSongAdapter - private lateinit var albumAdapter: HorizontalAlbumAdapter - private var forceDownload: Boolean = false - private val detailsViewModel: ArtistDetailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_ARTIST_ID).value) - } - - override fun createContentView(): View { - return wrapSlidingMusicPanel(R.layout.activity_artist_details) - } - - private fun windowEnterTransition() { - val slide = Slide() - slide.excludeTarget(R.id.appBarLayout, true) - slide.excludeTarget(R.id.status_bar, true) - slide.excludeTarget(android.R.id.statusBarBackground, true) - slide.excludeTarget(android.R.id.navigationBarBackground, true) - window.enterTransition = slide - } - - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - super.onCreate(savedInstanceState) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setTaskDescriptionColorAuto() - setLightNavigationBar(true) - setBottomBarVisibility(View.GONE) - window.sharedElementsUseOverlay = true - windowEnterTransition() - toolbar.setBackgroundColor(surfaceColor()) - - addMusicServiceEventListener(detailsViewModel) - - ActivityCompat.postponeEnterTransition(this) - detailsViewModel.getArtist().observe(this, androidx.lifecycle.Observer { - ActivityCompat.startPostponedEnterTransition(this@ArtistDetailActivity) - artist(it) - }) - detailsViewModel.getArtistInfo().observe(this, androidx.lifecycle.Observer { - artistInfo(it) - }) - setupRecyclerView() - playAction.apply { - setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } - } - shuffleAction.apply { - setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) } - } - - biographyText.setOnClickListener { - if (biographyText.maxLines == 4) { - biographyText.maxLines = Integer.MAX_VALUE - } else { - biographyText.maxLines = 4 - } - } - } - - private fun setupRecyclerView() { - albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), null) - albumRecyclerView.apply { - itemAnimator = DefaultItemAnimator() - layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false) - adapter = albumAdapter - } - songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this) - recyclerView.apply { - itemAnimator = DefaultItemAnimator() - layoutManager = LinearLayoutManager(this.context) - adapter = songAdapter - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - when (requestCode) { - REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) { - data?.data?.let { - CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, it) - } - } - else -> if (resultCode == Activity.RESULT_OK) { - //reload() - } - } - } - - fun artist(artist: Artist) { - if (artist.songs.isEmpty()) { - finish() - } - this.artist = artist - loadArtistImage(artist) - if (RetroUtil.isAllowedToDownloadMetadata(this)) { - loadBiography(artist.name) - } - artistTitle.text = artist.name - text.text = String.format( - "%s • %s", - MusicUtil.getArtistInfoString(this, artist), - MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs)) - ) - val songText = - resources.getQuantityString( - R.plurals.albumSongs, - artist.songCount, - artist.songCount - ) - val albumText = - resources.getQuantityString( - R.plurals.albums, - artist.songCount, - artist.songCount - ) - songTitle.text = songText - albumTitle.text = albumText - songAdapter.swapDataSet(artist.songs) - artist.albums?.let { albumAdapter.swapDataSet(it) } - } - - private fun loadBiography( - name: String, - lang: String? = Locale.getDefault().language - ) { - biography = null - this.lang = lang - detailsViewModel.loadBiography(name, lang, null) - } - - private fun artistInfo(lastFmArtist: LastFmArtist?) { - if (lastFmArtist != null && lastFmArtist.artist != null) { - val bioContent = lastFmArtist.artist.bio.content - if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) { - biographyText.visibility = View.VISIBLE - biographyTitle.visibility = View.VISIBLE - biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY) - biographyText.text = biography - if (lastFmArtist.artist.stats.listeners.isNotEmpty()) { - listeners.show() - listenersLabel.show() - scrobbles.show() - scrobblesLabel.show() - listeners.text = - RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat()) - scrobbles.text = - RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat()) - } - } - } - - // If the "lang" parameter is set and no biography is given, retry with default language - if (biography == null && lang != null) { - loadBiography(artist.name, null) - } - } - - private var lang: String? = null - - private fun loadArtistImage(artist: Artist) { - ArtistGlideRequest.Builder.from(Glide.with(this), artist) - .generatePalette(this).build() - .dontAnimate().into(object : RetroMusicColoredTarget(image) { - override fun onColorReady(colors: MediaNotificationProcessor) { - setColors(colors) - } - }) - } - - private fun setColors(color: MediaNotificationProcessor) { - MaterialUtil.tintColor( - button = shuffleAction, - textColor = color.primaryTextColor, - backgroundColor = color.backgroundColor - ) - MaterialUtil.tintColor( - button = playAction, - textColor = color.primaryTextColor, - backgroundColor = color.backgroundColor - ) - setSupportActionBar(toolbar) - supportActionBar?.title = null - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return handleSortOrderMenuItem(item) - } - - private fun handleSortOrderMenuItem(item: MenuItem): Boolean { - val songs = artist.songs - when (item.itemId) { - android.R.id.home -> { - super.onBackPressed() - return true - } - R.id.action_play_next -> { - MusicPlayerRemote.playNext(songs) - return true - } - R.id.action_add_to_current_playing -> { - MusicPlayerRemote.enqueue(songs) - return true - } - R.id.action_add_to_playlist -> { - AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST") - return true - } - R.id.action_set_artist_image -> { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.type = "image/*" - startActivityForResult( - Intent.createChooser(intent, getString(R.string.pick_from_local_storage)), - REQUEST_CODE_SELECT_IMAGE - ) - return true - } - R.id.action_reset_artist_image -> { - Toast.makeText( - this@ArtistDetailActivity, - resources.getString(R.string.updating), - Toast.LENGTH_SHORT - ) - .show() - CustomArtistImageUtil.getInstance(this@ArtistDetailActivity) - .resetCustomArtistImage(artist) - forceDownload = true - return true - } - } - return true - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.menu_artist_detail, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onBackPressed() { - if (cab != null && cab!!.isActive) { - cab?.finish() - } else { - super.onBackPressed() - } - } - - override fun onDestroy() { - super.onDestroy() - removeMusicServiceEventListener(detailsViewModel) - } - - companion object { - const val EXTRA_ARTIST_ID = "extra_artist_id" - const val REQUEST_CODE_SELECT_IMAGE = 9003 - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt index 2f3f5eed6..f218bc02e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt @@ -12,15 +12,13 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.retromusic.R import code.name.monkey.retromusic.RetroBottomSheetBehavior -import code.name.monkey.retromusic.extensions.hide -import code.name.monkey.retromusic.extensions.show +import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.MiniPlayerFragment import code.name.monkey.retromusic.fragments.NowPlayingScreen import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.CategoryInfo -import code.name.monkey.retromusic.util.DensityUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.views.BottomNavigationBarTinted import com.google.android.material.bottomsheet.BottomSheetBehavior @@ -178,19 +176,18 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { } private fun hideBottomBar(hide: Boolean) { - val heightOfBar = resources.getDimensionPixelSize(R.dimen.mini_player_height) - val heightOfBarWithTabs = - resources.getDimensionPixelSize(R.dimen.mini_player_height_expanded) + val heightOfBar = dimToPixel(R.dimen.mini_player_height) + val heightOfBarWithTabs = dimToPixel(R.dimen.mini_player_height_expanded) if (hide) { behavior.isHideable = true behavior.peekHeight = 0 - bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat() + bottomNavigationView.elevation = dipToPix(10f) collapsePanel() } else { if (MusicPlayerRemote.playingQueue.isNotEmpty()) { - slidingPanel.elevation = DensityUtil.dip2px(this, 10f).toFloat() - bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat() + slidingPanel.elevation = dipToPix(10f) + bottomNavigationView.elevation = dipToPix(10f) behavior.isHideable = false behavior.peekHeight = if (bottomNavigationView.visibility == View.VISIBLE) { @@ -204,8 +201,7 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { private fun chooseFragmentForTheme() { cps = PreferenceUtil.nowPlayingScreen - miniPlayerFragment = - supportFragmentManager.findFragmentById(R.id.miniPlayerFragment) as MiniPlayerFragment + miniPlayerFragment = whichFragment(R.id.miniPlayerFragment) miniPlayerFragment?.view?.setOnClickListener { expandPanel() } } @@ -232,7 +228,6 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { } open fun handleBackPress(): Boolean { - if (panelState == BottomSheetBehavior.STATE_EXPANDED) { collapsePanel() return true @@ -305,6 +300,11 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { } } + fun hideBottomNavigation() { + behavior.isHideable = true + behavior.state == BottomSheetBehavior.STATE_HIDDEN + } + fun updateTabs() { bottomNavigationView.menu.clear() val currentTabs: List = PreferenceUtil.libraryCategory diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/genre/GenreDetailsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/genre/GenreDetailsActivity.kt deleted file mode 100644 index 3af3facf6..000000000 --- a/app/src/main/java/code/name/monkey/retromusic/activities/genre/GenreDetailsActivity.kt +++ /dev/null @@ -1,143 +0,0 @@ -package code.name.monkey.retromusic.activities.genre - -import android.os.Bundle -import android.view.Menu -import android.view.MenuItem -import android.view.View -import androidx.recyclerview.widget.DefaultItemAnimator -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import code.name.monkey.appthemehelper.util.ATHUtil -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity -import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter -import code.name.monkey.retromusic.extensions.applyToolbar -import code.name.monkey.retromusic.extensions.extraNotNull -import code.name.monkey.retromusic.helper.menu.GenreMenuHelper -import code.name.monkey.retromusic.interfaces.CabHolder -import code.name.monkey.retromusic.model.Genre -import code.name.monkey.retromusic.model.Song -import code.name.monkey.retromusic.util.DensityUtil -import code.name.monkey.retromusic.util.RetroColorUtil -import com.afollestad.materialcab.MaterialCab -import kotlinx.android.synthetic.main.activity_playlist_detail.* -import org.koin.androidx.viewmodel.ext.android.viewModel -import org.koin.core.parameter.parametersOf -import java.util.* - -/** - * @author Hemanth S (h4h13). - */ - -class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { - - - private val detailsViewModel: GenreDetailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_GENRE_ID).value) - } - - private lateinit var genre: Genre - private lateinit var songAdapter: ShuffleButtonSongAdapter - private var cab: MaterialCab? = null - - private fun getEmojiByUnicode(unicode: Int): String { - return String(Character.toChars(unicode)) - } - - private fun checkIsEmpty() { - checkForPadding() - emptyEmoji.text = getEmojiByUnicode(0x1F631) - empty?.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE - } - - private fun checkForPadding() { - val height = DensityUtil.dip2px(this, 52f) - recyclerView.setPadding(0, 0, 0, (height)) - } - - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - super.onCreate(savedInstanceState) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setTaskDescriptionColorAuto() - setLightNavigationBar(true) - setBottomBarVisibility(View.GONE) - applyToolbar(toolbar) - setupRecyclerView() - - detailsViewModel.getSongs().observe(this, androidx.lifecycle.Observer { - songs(it) - }) - - detailsViewModel.getGenre().observe(this, androidx.lifecycle.Observer { - genre = it - supportActionBar?.title = it.name - }) - - addMusicServiceEventListener(detailsViewModel) - } - - override fun createContentView(): View { - return wrapSlidingMusicPanel(R.layout.activity_playlist_detail) - } - - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.menu_genre_detail, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - onBackPressed() - } - return GenreMenuHelper.handleMenuClick(this, genre, item) - } - - private fun setupRecyclerView() { - songAdapter = ShuffleButtonSongAdapter(this, ArrayList(), R.layout.item_list, this) - recyclerView.apply { - itemAnimator = DefaultItemAnimator() - layoutManager = LinearLayoutManager(this@GenreDetailsActivity) - adapter = songAdapter - } - songAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - override fun onChanged() { - super.onChanged() - checkIsEmpty() - } - }) - } - - fun songs(songs: List) { - songAdapter.swapDataSet(songs) - } - - override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { - if (cab != null && cab!!.isActive) cab?.finish() - cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor( - RetroColorUtil.shiftBackgroundColorForLightText( - ATHUtil.resolveColor( - this, - R.attr.colorSurface - ) - ) - ).start(callback) - return cab!! - } - - override fun onBackPressed() { - if (cab != null && cab!!.isActive) cab!!.finish() - else { - recyclerView!!.stopScroll() - super.onBackPressed() - } - } - - companion object { - const val EXTRA_GENRE_ID = "extra_genre_id" - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchActivity.kt index fbc657899..5ac7f813e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchActivity.kt @@ -35,7 +35,7 @@ import kotlin.collections.ArrayList class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatcher { private val viewModel: SearchViewModel by inject() - private var searchAdapter: SearchAdapter? = null + private lateinit var searchAdapter: SearchAdapter private var query: String? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -87,10 +87,10 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatch private fun setupRecyclerView() { searchAdapter = SearchAdapter(this, emptyList()) - searchAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + searchAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onChanged() { super.onChanged() - empty.visibility = if (searchAdapter!!.itemCount < 1) View.VISIBLE else View.GONE + empty.visibility = if (searchAdapter.itemCount < 1) View.VISIBLE else View.GONE } }) recyclerView.apply { @@ -152,15 +152,11 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatch } } - private fun showEmptyView() { - searchAdapter?.swapDataSet(ArrayList()) - } - private fun showData(data: MutableList) { if (data.isNotEmpty()) { - searchAdapter?.swapDataSet(data) + searchAdapter.swapDataSet(data) } else { - showEmptyView() + searchAdapter.swapDataSet(ArrayList()) } } @@ -215,7 +211,7 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatch const val EXTRA_SHOW_MIC = "extra_show_mic" const val QUERY: String = "query" - private const val REQ_CODE_SPEECH_INPUT = 9002 + const val REQ_CODE_SPEECH_INPUT = 9002 } } diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchFragment.kt b/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchFragment.kt new file mode 100644 index 000000000..75dc04422 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchFragment.kt @@ -0,0 +1,134 @@ +package code.name.monkey.retromusic.activities.search + +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.speech.RecognizerIntent +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.view.inputmethod.InputMethodManager +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat.getSystemService +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.transition.TransitionManager +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.adapter.SearchAdapter +import code.name.monkey.retromusic.extensions.accentColor +import code.name.monkey.retromusic.extensions.showToast +import code.name.monkey.retromusic.fragments.MainActivityFragment +import kotlinx.android.synthetic.main.fragment_search.* +import org.koin.android.ext.android.inject +import java.util.* +import kotlin.collections.ArrayList + +class SearchFragment : MainActivityFragment(R.layout.fragment_search), TextWatcher { + private val viewModel: SearchViewModel by inject() + private lateinit var searchAdapter: SearchAdapter + private var query: String? = null + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupRecyclerView() + setupSearchView() + mainActivity.setSupportActionBar(toolbar) + mainActivity.setBottomBarVisibility(View.GONE) + voiceSearch.setOnClickListener { startMicSearch() } + clearText.setOnClickListener { searchView.clearText() } + + keyboardPopup.setOnClickListener { + val inputManager = + getSystemService( + requireContext(), + InputMethodManager::class.java + ) + inputManager?.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT) + } + keyboardPopup.accentColor() + if (savedInstanceState != null) { + query = savedInstanceState.getString(SearchActivity.QUERY) + } + + viewModel.getSearchResult().observe(viewLifecycleOwner, Observer { + showData(it) + }) + } + + private fun showData(data: MutableList) { + if (data.isNotEmpty()) { + searchAdapter.swapDataSet(data) + } else { + searchAdapter.swapDataSet(ArrayList()) + } + } + + + private fun setupRecyclerView() { + searchAdapter = SearchAdapter(requireActivity() as AppCompatActivity, emptyList()) + searchAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + super.onChanged() + empty.visibility = if (searchAdapter.itemCount < 1) View.VISIBLE else View.GONE + } + }) + recyclerView.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = searchAdapter + addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (dy > 0) { + keyboardPopup.shrink() + } else if (dy < 0) { + keyboardPopup.extend() + } + } + }) + } + } + + private fun setupSearchView() { + searchView.addTextChangedListener(this) + } + + override fun afterTextChanged(newText: Editable?) { + search(newText.toString()) + } + + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + private fun search(query: String) { + this.query = query + TransitionManager.beginDelayedTransition(appBarLayout) + voiceSearch.visibility = if (query.isNotEmpty()) View.GONE else View.VISIBLE + clearText.visibility = if (query.isNotEmpty()) View.VISIBLE else View.GONE + viewModel.search(query) + } + + private fun startMicSearch() { + val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) + 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 { + startActivityForResult( + intent, + SearchActivity.REQ_CODE_SPEECH_INPUT + ) + } catch (e: ActivityNotFoundException) { + e.printStackTrace() + showToast(getString(R.string.speech_not_supported)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt index 570912a30..a015b1ad6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/GenreAdapter.kt @@ -1,14 +1,16 @@ package code.name.monkey.retromusic.adapter -import android.app.Activity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.os.bundleOf +import androidx.fragment.app.FragmentActivity +import androidx.navigation.findNavController import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.EXTRA_GENRE import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder import code.name.monkey.retromusic.model.Genre -import code.name.monkey.retromusic.util.NavigationUtil import java.util.* /** @@ -16,7 +18,7 @@ import java.util.* */ class GenreAdapter( - private val activity: Activity, + private val activity: FragmentActivity, var dataSet: List, private val mItemLayoutRes: Int ) : RecyclerView.Adapter() { @@ -48,9 +50,10 @@ class GenreAdapter( inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { override fun onClick(v: View?) { - super.onClick(v) - val genre = dataSet[layoutPosition] - NavigationUtil.goToGenre(activity, genre) + activity.findNavController(R.id.fragment_container).navigate( + R.id.genreDetailsFragment, + bundleOf(EXTRA_GENRE to dataSet[layoutPosition]) + ) } } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt index 77590c89c..20a356cc6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt @@ -1,6 +1,5 @@ package code.name.monkey.retromusic.adapter -import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -8,18 +7,24 @@ import android.widget.TextView import androidx.annotation.IntDef import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.AppCompatTextView +import androidx.core.os.bundleOf +import androidx.navigation.findNavController import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.HORIZONTAL import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.retromusic.EXTRA_ALBUM_ID +import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.PeekingLinearLayoutManager import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.AlbumAdapter import code.name.monkey.retromusic.adapter.artist.ArtistAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.extensions.show +import code.name.monkey.retromusic.fragments.albums.AlbumClickListener +import code.name.monkey.retromusic.fragments.artists.ArtistClickListener import code.name.monkey.retromusic.glide.SongGlideRequest import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.loaders.PlaylistSongsLoader @@ -29,8 +34,7 @@ import com.bumptech.glide.Glide import com.google.android.material.card.MaterialCardView class HomeAdapter( - private val activity: AppCompatActivity, - private val displayMetrics: DisplayMetrics + private val activity: AppCompatActivity ) : RecyclerView.Adapter() { private var list = listOf() @@ -143,21 +147,28 @@ class HomeAdapter( const val GENRES = 6 } - private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) { + private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view), AlbumClickListener { fun bindView(list: List, titleRes: Int) { if (list.isNotEmpty()) { + val albumAdapter = AlbumAdapter(activity, list, R.layout.pager_item, null, this) recyclerView.apply { show() - adapter = AlbumAdapter(activity, list, R.layout.pager_item, null) - layoutManager = - PeekingLinearLayoutManager(activity, HORIZONTAL, false) + adapter = albumAdapter + layoutManager = PeekingLinearLayoutManager(activity, HORIZONTAL, false) } title.text = activity.getString(titleRes) } } + + override fun onAlbumClick(albumId: Int) { + activity.findNavController(R.id.fragment_container).navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to albumId) + ) + } } - inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) { + private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view), ArtistClickListener { fun bindView(list: List, titleRes: Int) { if (list.isNotEmpty()) { val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) @@ -165,7 +176,8 @@ class HomeAdapter( activity, list, PreferenceUtil.homeGridStyle, - null + null, + this ) recyclerView.apply { show() @@ -175,6 +187,13 @@ class HomeAdapter( title.text = activity.getString(titleRes) } } + + override fun onArtist(artistId: Int) { + activity.findNavController(R.id.fragment_container).navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to artistId) + ) + } } private inner class SuggestionsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/SearchAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/SearchAdapter.kt index 0b257978d..8baa05021 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/SearchAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/SearchAdapter.kt @@ -1,13 +1,14 @@ package code.name.monkey.retromusic.adapter -import android.app.ActivityOptions import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity +import androidx.core.os.bundleOf +import androidx.fragment.app.FragmentActivity +import androidx.navigation.findNavController import androidx.recyclerview.widget.RecyclerView import code.name.monkey.appthemehelper.ThemeStore -import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.* import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder import code.name.monkey.retromusic.glide.AlbumGlideRequest import code.name.monkey.retromusic.glide.ArtistGlideRequest @@ -17,13 +18,11 @@ import code.name.monkey.retromusic.loaders.PlaylistSongsLoader import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.NavigationUtil import com.bumptech.glide.Glide -import android.util.Pair as UtilPair class SearchAdapter( - private val activity: AppCompatActivity, - private var dataSet: List? + private val activity: FragmentActivity, + private var dataSet: List ) : RecyclerView.Adapter() { fun swapDataSet(dataSet: MutableList) { @@ -32,11 +31,11 @@ class SearchAdapter( } override fun getItemViewType(position: Int): Int { - if (dataSet!![position] is Album) return ALBUM - if (dataSet!![position] is Artist) return ARTIST - if (dataSet!![position] is Genre) return GENRE - if (dataSet!![position] is Playlist) return PLAYLIST - return if (dataSet!![position] is Song) SONG else HEADER + if (dataSet[position] is Album) return ALBUM + if (dataSet[position] is Artist) return ARTIST + if (dataSet[position] is Genre) return GENRE + if (dataSet[position] is Playlist) return PLAYLIST + return if (dataSet[position] is Song) SONG else HEADER } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { @@ -57,35 +56,35 @@ class SearchAdapter( override fun onBindViewHolder(holder: ViewHolder, position: Int) { when (getItemViewType(position)) { ALBUM -> { - val album = dataSet?.get(position) as Album + val album = dataSet.get(position) as Album holder.title?.text = album.title holder.text?.text = album.artistName AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) .checkIgnoreMediaStore(activity).build().into(holder.image) } ARTIST -> { - val artist = dataSet?.get(position) as Artist + val artist = dataSet.get(position) as Artist holder.title?.text = artist.name holder.text?.text = MusicUtil.getArtistInfoString(activity, artist) ArtistGlideRequest.Builder.from(Glide.with(activity), artist).build() .into(holder.image) } SONG -> { - val song = dataSet?.get(position) as Song + val song = dataSet.get(position) as Song holder.title?.text = song.title holder.text?.text = song.albumName } GENRE -> { - val genre = dataSet?.get(position) as Genre + val genre = dataSet.get(position) as Genre holder.title?.text = genre.name } PLAYLIST -> { - val playlist = dataSet?.get(position) as Playlist + val playlist = dataSet.get(position) as Playlist holder.title?.text = playlist.name holder.text?.text = MusicUtil.getPlaylistInfoString(activity, getSongs(playlist)) } else -> { - holder.title?.text = dataSet?.get(position).toString() + holder.title?.text = dataSet.get(position).toString() holder.title?.setTextColor(ThemeStore.accentColor(activity)) } } @@ -102,7 +101,7 @@ class SearchAdapter( } override fun getItemCount(): Int { - return dataSet!!.size + return dataSet.size } inner class ViewHolder(itemView: View, itemViewType: Int) : MediaEntryViewHolder(itemView) { @@ -113,7 +112,7 @@ class SearchAdapter( menu?.visibility = View.VISIBLE menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) { override val song: Song - get() = dataSet!![layoutPosition] as Song + get() = dataSet[layoutPosition] as Song }) } else { menu?.visibility = View.GONE @@ -130,27 +129,31 @@ class SearchAdapter( } override fun onClick(v: View?) { - val item = dataSet!![layoutPosition] + val item = dataSet[layoutPosition] when (itemViewType) { ALBUM -> { - val options = ActivityOptions.makeSceneTransitionAnimation( - activity, - UtilPair.create(image, activity.getString(R.string.transition_album_art)) + activity.findNavController(R.id.fragment_container).navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to (item as Album).id) ) - NavigationUtil.goToAlbumOptions(activity, (item as Album).id, options) } ARTIST -> { - val options = ActivityOptions.makeSceneTransitionAnimation( - activity, - UtilPair.create(image, activity.getString(R.string.transition_artist_image)) + activity.findNavController(R.id.fragment_container).navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to (item as Artist).id) ) - NavigationUtil.goToArtistOptions(activity, (item as Artist).id, options) } GENRE -> { - NavigationUtil.goToGenre(activity, item as Genre) + activity.findNavController(R.id.fragment_container).navigate( + R.id.genreDetailsFragment, + bundleOf(EXTRA_GENRE to (item as Genre)) + ) } PLAYLIST -> { - NavigationUtil.goToPlaylistNew(activity, item as Playlist) + activity.findNavController(R.id.fragment_container).navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_PLAYLIST to (item as Playlist)) + ) } SONG -> { val playList = ArrayList() diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt index a8ae9ce31..6b3cdb338 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt @@ -7,10 +7,11 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder +import code.name.monkey.retromusic.fragments.albums.AlbumClickListener import code.name.monkey.retromusic.glide.AlbumGlideRequest import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -20,17 +21,17 @@ import code.name.monkey.retromusic.interfaces.CabHolder import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.bumptech.glide.Glide import me.zhanghai.android.fastscroll.PopupTextProvider open class AlbumAdapter( - protected val activity: AppCompatActivity, + protected val activity: FragmentActivity, var dataSet: List, protected var itemLayoutRes: Int, - cabHolder: CabHolder? + cabHolder: CabHolder?, + private val albumClickListener: AlbumClickListener? ) : AbsMultiSelectAdapter( activity, cabHolder, @@ -177,11 +178,12 @@ open class AlbumAdapter( imageContainerCard ?: image, activity.getString(R.string.transition_album_art) ) - NavigationUtil.goToAlbumOptions( + albumClickListener?.onAlbumClick(dataSet[layoutPosition].id) + /*NavigationUtil.goToAlbumOptions( activity, dataSet[layoutPosition].id, activityOptions - ) + )*/ } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/HorizontalAlbumAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/HorizontalAlbumAdapter.kt index c19221de4..9b8dcb03d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/HorizontalAlbumAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/HorizontalAlbumAdapter.kt @@ -1,10 +1,10 @@ package code.name.monkey.retromusic.adapter.album -import android.graphics.drawable.Drawable import android.view.View import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.retromusic.fragments.albums.AlbumClickListener import code.name.monkey.retromusic.glide.AlbumGlideRequest import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.helper.HorizontalAdapterHelper @@ -15,11 +15,12 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.bumptech.glide.Glide class HorizontalAlbumAdapter( - activity: AppCompatActivity, + activity: FragmentActivity, dataSet: List, - cabHolder: CabHolder? + cabHolder: CabHolder?, + albumClickListener: AlbumClickListener ) : AlbumAdapter( - activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, cabHolder + activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, cabHolder, albumClickListener ) { override fun createViewHolder(view: View, viewType: Int): ViewHolder { @@ -40,11 +41,6 @@ class HorizontalAlbumAdapter( .generatePalette(activity) .build() .into(object : RetroMusicColoredTarget(holder.image!!) { - override fun onLoadCleared(placeholder: Drawable?) { - super.onLoadCleared(placeholder) - //setColors(albumArtistFooterColor, holder) - } - override fun onColorReady(colors: MediaNotificationProcessor) { setColors(colors, holder) } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt index 65aa15b5a..1d6b020b7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt @@ -7,11 +7,12 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder import code.name.monkey.retromusic.extensions.hide +import code.name.monkey.retromusic.fragments.artists.ArtistClickListener import code.name.monkey.retromusic.glide.ArtistGlideRequest import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.helper.menu.SongsMenuHelper @@ -19,17 +20,17 @@ import code.name.monkey.retromusic.interfaces.CabHolder import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.bumptech.glide.Glide import me.zhanghai.android.fastscroll.PopupTextProvider import java.util.* class ArtistAdapter( - val activity: AppCompatActivity, + val activity: FragmentActivity, var dataSet: List, var itemLayoutRes: Int, - cabHolder: CabHolder? + cabHolder: CabHolder?, + private val artistClickListener: ArtistClickListener ) : AbsMultiSelectAdapter( activity, cabHolder, R.menu.menu_media_selection ), PopupTextProvider { @@ -144,9 +145,10 @@ class ArtistAdapter( imageContainerCard ?: image, activity.getString(R.string.transition_artist_image) ) - NavigationUtil.goToArtistOptions( + artistClickListener.onArtist(dataSet[layoutPosition].id) + /*NavigationUtil.goToArtistOptions( activity, dataSet[layoutPosition].id, activityOptions - ) + )*/ } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt index ab6cb02fb..947e51c91 100755 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt @@ -9,11 +9,14 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.PopupMenu +import androidx.core.os.bundleOf +import androidx.fragment.app.FragmentActivity +import androidx.navigation.findNavController import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.retromusic.EXTRA_PLAYLIST import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder @@ -29,12 +32,11 @@ import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.RetroColorUtil import java.util.* class PlaylistAdapter( - private val activity: AppCompatActivity, + private val activity: FragmentActivity, var dataSet: List, private var itemLayoutRes: Int, cabHolder: CabHolder? @@ -165,9 +167,7 @@ class PlaylistAdapter( val popupMenu = PopupMenu(activity, view) popupMenu.inflate(R.menu.menu_item_playlist) popupMenu.setOnMenuItemClickListener { item -> - PlaylistMenuHelper.handleMenuClick( - activity, dataSet[layoutPosition], item - ) + PlaylistMenuHelper.handleMenuClick(activity, dataSet[layoutPosition], item) } popupMenu.show() } @@ -182,8 +182,10 @@ class PlaylistAdapter( if (isInQuickSelectMode) { toggleChecked(layoutPosition) } else { - val playlist = dataSet[layoutPosition] - NavigationUtil.goToPlaylistNew(activity, playlist) + activity.findNavController(R.id.fragment_container).navigate( + R.id.playlistDetailsFragment, + bundleOf(EXTRA_PLAYLIST to dataSet[layoutPosition]) + ) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt index 74a2f7ebd..8d92296b3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/OrderablePlaylistSongAdapter.kt @@ -2,7 +2,7 @@ package code.name.monkey.retromusic.adapter.song import android.view.MenuItem import android.view.View -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R.menu import code.name.monkey.retromusic.dialogs.RemoveFromPlaylistDialog @@ -16,13 +16,16 @@ import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags class OrderablePlaylistSongAdapter( - activity: AppCompatActivity, + activity: FragmentActivity, dataSet: ArrayList, itemLayoutRes: Int, cabHolder: CabHolder?, private val onMoveItemListener: OnMoveItemListener? -) : PlaylistSongAdapter( - activity, dataSet, itemLayoutRes, cabHolder +) : SongAdapter( + activity, + dataSet, + itemLayoutRes, + cabHolder ), DraggableItemAdapter { init { @@ -91,7 +94,7 @@ class OrderablePlaylistSongAdapter( fun onMoveItem(fromPosition: Int, toPosition: Int) } - inner class ViewHolder(itemView: View) : PlaylistSongAdapter.ViewHolder(itemView), + inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView), DraggableItemViewHolder { @DraggableItemStateFlags private var mDragStateFlags: Int = 0 @@ -132,8 +135,4 @@ class OrderablePlaylistSongAdapter( mDragStateFlags = flags } } - - companion object { - val TAG: String = OrderablePlaylistSongAdapter::class.java.simpleName - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt index 0a628f2f6..b699b39cb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt @@ -3,6 +3,7 @@ package code.name.monkey.retromusic.adapter.song import android.view.MenuItem import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.R import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote.isPlaying @@ -192,7 +193,7 @@ class PlayingQueueAdapter( internal class SwipedResultActionRemoveItem( private val adapter: PlayingQueueAdapter, private val position: Int, - private val activity: AppCompatActivity + private val activity: FragmentActivity ) : SwipeResultActionRemoveItem() { private var songToRemove: Song? = null diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlaylistSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlaylistSongAdapter.kt index 252c837aa..7d73b9529 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlaylistSongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlaylistSongAdapter.kt @@ -1,14 +1,15 @@ package code.name.monkey.retromusic.adapter.song -import android.app.ActivityOptions import android.view.MenuItem import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.core.os.bundleOf +import androidx.navigation.findNavController +import code.name.monkey.retromusic.EXTRA_ALBUM_ID 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 code.name.monkey.retromusic.util.NavigationUtil import com.google.android.material.button.MaterialButton open class PlaylistSongAdapter( @@ -57,19 +58,14 @@ open class PlaylistSongAdapter( override fun onSongMenuItemClick(item: MenuItem): Boolean { if (item.itemId == R.id.action_go_to_album) { - val activityOptions = ActivityOptions.makeSceneTransitionAnimation( - activity, - imageContainerCard ?: image, - activity.getString(R.string.transition_album_art) - ) - NavigationUtil.goToAlbumOptions(activity, song.albumId, activityOptions) + activity.findNavController(R.id.fragment_container) + .navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to song.albumId) + ) return true } return super.onSongMenuItemClick(item) } } - - companion object { - val TAG: String = PlaylistSongAdapter::class.java.simpleName - } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SimpleSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SimpleSongAdapter.kt index 53a952426..d75400289 100755 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SimpleSongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SimpleSongAdapter.kt @@ -2,14 +2,14 @@ package code.name.monkey.retromusic.adapter.song import android.view.LayoutInflater import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.interfaces.CabHolder import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.MusicUtil import java.util.* class SimpleSongAdapter( - context: AppCompatActivity, + context: FragmentActivity, songs: ArrayList, layoutRes: Int, cabHolder: CabHolder? diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt index 1fa9f52c9..c2d27315e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt @@ -1,13 +1,15 @@ package code.name.monkey.retromusic.adapter.song -import android.app.ActivityOptions import android.content.res.ColorStateList import android.content.res.Resources import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity +import androidx.core.os.bundleOf +import androidx.fragment.app.FragmentActivity +import androidx.navigation.findNavController +import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder @@ -22,7 +24,6 @@ import code.name.monkey.retromusic.helper.menu.SongsMenuHelper import code.name.monkey.retromusic.interfaces.CabHolder import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.afollestad.materialcab.MaterialCab @@ -34,7 +35,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider */ open class SongAdapter( - protected val activity: AppCompatActivity, + protected val activity: FragmentActivity, var dataSet: MutableList, protected var itemLayoutRes: Int, cabHolder: CabHolder?, @@ -175,12 +176,11 @@ open class SongAdapter( if (image != null && image!!.visibility == View.VISIBLE) { when (item.itemId) { R.id.action_go_to_album -> { - val activityOptions = ActivityOptions.makeSceneTransitionAnimation( - activity, - imageContainerCard ?: image, - activity.getString(R.string.transition_album_art) - ) - NavigationUtil.goToAlbumOptions(activity, song.albumId, activityOptions) + activity.findNavController(R.id.fragment_container) + .navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to song.albumId) + ) return true } } diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt index d89a2ca6d..e2c4d2580 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt @@ -22,13 +22,16 @@ import android.widget.Button import android.widget.CheckBox import android.widget.SeekBar import androidx.annotation.AttrRes +import androidx.annotation.ColorInt import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment 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.retromusic.App import code.name.monkey.retromusic.R +import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fun Int.ripAlpha(): Int { return ColorUtil.stripAlpha(this) @@ -83,4 +86,20 @@ fun SeekBar.addAccentColor() { fun Button.accentTextColor() { setTextColor(ThemeStore.accentColor(App.getContext())) +} + +fun SeekBar.applyColor(@ColorInt color: Int) { + thumbTintList = ColorStateList.valueOf(color) + progressTintList = ColorStateList.valueOf(color) + progressBackgroundTintList = ColorStateList.valueOf(color) +} + +fun ExtendedFloatingActionButton.accentColor() { + val color = ThemeStore.accentColor(context) + val textColor = MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)) + val colorStateList = ColorStateList.valueOf(color) + val textColorStateList = ColorStateList.valueOf(textColor) + backgroundTintList = colorStateList + setTextColor(textColorStateList) + iconTint = textColorStateList } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/DimenExtension.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/DimenExtension.kt new file mode 100644 index 000000000..f02e51875 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/DimenExtension.kt @@ -0,0 +1,20 @@ +package code.name.monkey.retromusic.extensions + +import android.app.Activity +import androidx.annotation.DimenRes +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment + +fun AppCompatActivity.dimToPixel(@DimenRes dimenRes: Int): Int { + return resources.getDimensionPixelSize(dimenRes) +} + +fun Activity.dipToPix(dpInFloat: Float): Float { + val scale = resources.displayMetrics.density + return dpInFloat * scale + 0.5f +} + +fun Fragment.dipToPix(dpInFloat: Float): Float { + val scale = resources.displayMetrics.density + return dpInFloat * scale + 0.5f +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt index 4a0dc22f5..aaebf6b65 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt @@ -3,7 +3,10 @@ package code.name.monkey.retromusic.extensions import android.content.Context import android.content.res.Configuration import android.os.PowerManager +import android.widget.Toast +import androidx.annotation.IdRes import androidx.annotation.IntegerRes +import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -53,3 +56,17 @@ fun AppCompatActivity.currentFragment(navHostId: Int): Fragment? { navHostFragment.targetFragment return navHostFragment?.childFragmentManager?.fragments?.first() } + +@Suppress("UNCHECKED_CAST") +fun AppCompatActivity.whichFragment(@IdRes id: Int): T { + return supportFragmentManager.findFragmentById(id) as T +} + + +fun Fragment.showToast(@StringRes stringRes: Int) { + showToast(getString(stringRes)) +} + +fun Fragment.showToast(message: String) { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/NavigationExtensions.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/NavigationExtensions.kt index a4f3d8732..2ea5ade8c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/NavigationExtensions.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/NavigationExtensions.kt @@ -4,17 +4,22 @@ import androidx.annotation.IdRes import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.navigation.NavController +import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController fun Fragment.navigate(@IdRes id: Int) = findNavController().navigate(id) -fun Fragment.navController(@IdRes id: Int): NavController { +fun Fragment.findNavController(@IdRes id: Int): NavController { val fragment = childFragmentManager.findFragmentById(id) as NavHostFragment return fragment.navController } -fun AppCompatActivity.navController(@IdRes id: Int): NavController { +fun Fragment.findActivityNavController(@IdRes id: Int): NavController { + return requireActivity().findNavController(id) +} + +fun AppCompatActivity.findNavController(@IdRes id: Int): NavController { val fragment = supportFragmentManager.findFragmentById(id) as NavHostFragment return fragment.navController } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt index d25c953eb..da321935e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt @@ -48,9 +48,3 @@ fun EditText.appHandleColor(): EditText { TintHelper.colorHandles(this, ThemeStore.accentColor(context)) return this } - -fun SeekBar.applyColor(@ColorInt color: Int) { - thumbTintList = ColorStateList.valueOf(color) - progressTintList = ColorStateList.valueOf(color) - progressBackgroundTintList = ColorStateList.valueOf(color) -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/MainActivityFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/MainActivityFragment.kt new file mode 100644 index 000000000..46507d44c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/MainActivityFragment.kt @@ -0,0 +1,11 @@ +package code.name.monkey.retromusic.fragments + +import androidx.annotation.LayoutRes +import code.name.monkey.retromusic.activities.MainActivity +import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment + +open class MainActivityFragment(@LayoutRes layoutRes: Int) : AbsMusicServiceFragment(layoutRes) { + val mainActivity by lazy { + requireActivity() as MainActivity + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/AboutActivity.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutActivity.kt similarity index 99% rename from app/src/main/java/code/name/monkey/retromusic/activities/AboutActivity.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutActivity.kt index 58650582e..0704256f7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/AboutActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutActivity.kt @@ -1,4 +1,4 @@ -package code.name.monkey.retromusic.activities +package code.name.monkey.retromusic.fragments.about import android.content.Intent import android.content.pm.PackageManager diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt new file mode 100644 index 000000000..3dbaf9ae6 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt @@ -0,0 +1,128 @@ +package code.name.monkey.retromusic.fragments.about + +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Bundle +import android.view.View +import androidx.core.app.ShareCompat +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.LinearLayoutManager +import code.name.monkey.retromusic.App +import code.name.monkey.retromusic.Constants +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.adapter.ContributorAdapter +import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.model.Contributor +import code.name.monkey.retromusic.util.NavigationUtil +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import kotlinx.android.synthetic.main.activity_about.* +import kotlinx.android.synthetic.main.card_credit.* +import kotlinx.android.synthetic.main.card_other.* +import kotlinx.android.synthetic.main.card_retro_info.* +import kotlinx.android.synthetic.main.card_social.* +import java.io.IOException +import java.nio.charset.StandardCharsets + +class AboutFragment : MainActivityFragment(R.layout.fragment_about), View.OnClickListener { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mainActivity.setSupportActionBar(toolbar) + version.setSummary(getAppVersion()) + setUpView() + loadContributors() + } + + private val contributorsJson: String? + get() { + val json: String + try { + val inputStream = requireActivity().assets.open("contributors.json") + val size = inputStream.available() + val buffer = ByteArray(size) + inputStream.read(buffer) + inputStream.close() + json = String(buffer, StandardCharsets.UTF_8) + } catch (ex: IOException) { + ex.printStackTrace() + return null + } + return json + } + + + private fun openUrl(url: String) { + val i = Intent(Intent.ACTION_VIEW) + i.data = Uri.parse(url) + i.flags = Intent.FLAG_ACTIVITY_NEW_TASK + startActivity(i) + } + + private fun setUpView() { + appGithub.setOnClickListener(this) + faqLink.setOnClickListener(this) + telegramLink.setOnClickListener(this) + appRate.setOnClickListener(this) + appTranslation.setOnClickListener(this) + appShare.setOnClickListener(this) + donateLink.setOnClickListener(this) + instagramLink.setOnClickListener(this) + twitterLink.setOnClickListener(this) + changelog.setOnClickListener(this) + openSource.setOnClickListener(this) + pinterestLink.setOnClickListener(this) + bugReportLink.setOnClickListener(this) + } + + override fun onClick(view: View) { + when (view.id) { + R.id.pinterestLink -> openUrl(Constants.PINTEREST) + R.id.faqLink -> openUrl(Constants.FAQ_LINK) + R.id.telegramLink -> openUrl(Constants.APP_TELEGRAM_LINK) + R.id.appGithub -> openUrl(Constants.GITHUB_PROJECT) + R.id.appTranslation -> openUrl(Constants.TRANSLATE) + R.id.appRate -> openUrl(Constants.RATE_ON_GOOGLE_PLAY) + R.id.appShare -> shareApp() + R.id.donateLink -> NavigationUtil.goToSupportDevelopment(requireActivity()) + R.id.instagramLink -> openUrl(Constants.APP_INSTAGRAM_LINK) + R.id.twitterLink -> openUrl(Constants.APP_TWITTER_LINK) + R.id.changelog -> openUrl(Constants.TELEGRAM_CHANGE_LOG) + R.id.openSource -> NavigationUtil.goToOpenSource(requireActivity()) + R.id.bugReportLink -> NavigationUtil.bugReport(requireActivity()) + } + } + + private fun getAppVersion(): String { + return try { + val isPro = if (App.isProVersion()) "Pro" else "Free" + val packageInfo = + requireActivity().packageManager.getPackageInfo(requireActivity().packageName, 0) + "${packageInfo.versionName} $isPro" + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + "0.0.0" + } + } + + private fun shareApp() { + ShareCompat.IntentBuilder.from(requireActivity()).setType("text/plain") + .setChooserTitle(R.string.share_app) + .setText(String.format(getString(R.string.app_share), requireActivity().packageName)) + .startChooser() + } + + private fun loadContributors() { + val type = object : TypeToken>() { + + }.type + val contributors = Gson().fromJson>(contributorsJson, type) + + val contributorAdapter = ContributorAdapter(contributors) + recyclerView.apply { + layoutManager = LinearLayoutManager(requireContext()) + itemAnimator = DefaultItemAnimator() + adapter = contributorAdapter + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt similarity index 86% rename from app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsFragment.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt index 8140f4a6b..ab8be6e97 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt @@ -1,23 +1,25 @@ -package code.name.monkey.retromusic.activities.albums +package code.name.monkey.retromusic.fragments.albums -import android.app.ActivityOptions import android.os.Bundle -import android.transition.TransitionInflater -import android.util.Pair import android.view.MenuItem import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.core.os.bundleOf +import androidx.lifecycle.Observer +import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import code.name.monkey.appthemehelper.util.MaterialUtil +import code.name.monkey.retromusic.EXTRA_ALBUM_ID +import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter import code.name.monkey.retromusic.extensions.extraNotNull import code.name.monkey.retromusic.extensions.show -import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment +import code.name.monkey.retromusic.fragments.MainActivityFragment import code.name.monkey.retromusic.glide.AlbumGlideRequest import code.name.monkey.retromusic.glide.ArtistGlideRequest import code.name.monkey.retromusic.glide.RetroMusicColoredTarget @@ -26,60 +28,56 @@ import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.activity_album.* import kotlinx.android.synthetic.main.activity_album_content.* +import kotlinx.android.synthetic.main.activity_album_details.* import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.util.* -class AlbumDetailsFragment : AbsMusicServiceFragment(R.layout.fragment_album_details) { +class AlbumDetailsFragment : MainActivityFragment(R.layout.fragment_album_details), + AlbumClickListener { private lateinit var simpleSongAdapter: SimpleSongAdapter private lateinit var album: Album + private val savedSortOrder: String get() = PreferenceUtil.albumDetailSongSortOrder - private val detailsViewModel by viewModel { - parametersOf(extraNotNull(AlbumDetailsActivity.EXTRA_ALBUM_ID).value) - } - private fun setSharedElementTransitionOnEnter() { - sharedElementEnterTransition = TransitionInflater.from(context) - .inflateTransition(R.transition.change_bounds) + private val detailsViewModel by viewModel { + parametersOf(extraNotNull(EXTRA_ALBUM_ID).value) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - setSharedElementTransitionOnEnter() + mainActivity.setSupportActionBar(toolbar) + mainActivity.setBottomBarVisibility(View.GONE) + toolbar.title = null + postponeEnterTransition() playerActivity?.addMusicServiceEventListener(detailsViewModel) - - detailsViewModel.getAlbum().observe(viewLifecycleOwner, androidx.lifecycle.Observer { + detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer { startPostponedEnterTransition() showAlbum(it) }) - detailsViewModel.getArtist().observe(viewLifecycleOwner, androidx.lifecycle.Observer { + detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { loadArtistImage(it) }) - detailsViewModel.getMoreAlbums().observe(viewLifecycleOwner, androidx.lifecycle.Observer { + detailsViewModel.getMoreAlbums().observe(viewLifecycleOwner, Observer { moreAlbums(it) }) - detailsViewModel.getAlbumInfo().observe(viewLifecycleOwner, androidx.lifecycle.Observer { + detailsViewModel.getAlbumInfo().observe(viewLifecycleOwner, Observer { aboutAlbum(it) }) setupRecyclerView() artistImage.setOnClickListener { - val artistPairs = ActivityOptions.makeSceneTransitionAnimation( - requireActivity(), - Pair.create( - artistImage, - getString(R.string.transition_artist_image) + requireActivity().findNavController(R.id.fragment_container) + .navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to album.artistId) ) - ) - NavigationUtil.goToArtistOptions(requireActivity(), album.artistId, artistPairs) } playAction.setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) } @@ -177,7 +175,7 @@ class AlbumDetailsFragment : AbsMusicServiceFragment(R.layout.fragment_album_det moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName) val albumAdapter = - HorizontalAlbumAdapter(requireActivity() as AppCompatActivity, albums, null) + HorizontalAlbumAdapter(requireActivity() as AppCompatActivity, albums, null, this) moreRecyclerView.layoutManager = GridLayoutManager( requireContext(), 1, @@ -247,4 +245,11 @@ class AlbumDetailsFragment : AbsMusicServiceFragment(R.layout.fragment_album_det backgroundColor = color.backgroundColor ) } + + override fun onAlbumClick(albumId: Int) { + findNavController().navigate( + R.id.albumDetailsFragment, + bundleOf("extra_album_id" to albumId) + ) + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt similarity index 97% rename from app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsViewModel.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt index 2a9d83595..13839ab43 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/albums/AlbumDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt @@ -1,4 +1,4 @@ -package code.name.monkey.retromusic.activities.albums +package code.name.monkey.retromusic.fragments.albums import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt index 3c0759724..2e2a9c71c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt @@ -2,22 +2,20 @@ package code.name.monkey.retromusic.fragments.albums import android.os.Bundle import android.view.View +import androidx.core.os.bundleOf import androidx.lifecycle.Observer +import androidx.navigation.findNavController import androidx.recyclerview.widget.GridLayoutManager +import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.AlbumAdapter import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment -import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks import code.name.monkey.retromusic.util.PreferenceUtil class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment(), - MainActivityFragmentCallbacks { - - override fun handleBackPress(): Boolean { - return false - } + AlbumClickListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -40,10 +38,11 @@ class AlbumsFragment : override fun createAdapter(): AlbumAdapter { val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet return AlbumAdapter( - mainActivity, + requireActivity(), dataSet, - itemLayoutRes(), - mainActivity + R.layout.item_grid, + null, + this ) } @@ -90,12 +89,17 @@ class AlbumsFragment : companion object { - @JvmField - var TAG: String = AlbumsFragment::class.java.simpleName - - @JvmStatic fun newInstance(): AlbumsFragment { return AlbumsFragment() } } + + override fun onAlbumClick(albumId: Int) { + val controller = requireActivity().findNavController(R.id.fragment_container) + controller.navigate(R.id.albumDetailsFragment, bundleOf(EXTRA_ALBUM_ID to albumId)) + } } + +interface AlbumClickListener { + fun onAlbumClick(albumId: Int) +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt new file mode 100644 index 000000000..d38b2e0b3 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt @@ -0,0 +1,194 @@ +package code.name.monkey.retromusic.fragments.artists + +import android.os.Bundle +import android.text.Spanned +import android.view.View +import androidx.core.os.bundleOf +import androidx.core.text.HtmlCompat +import androidx.lifecycle.Observer +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import code.name.monkey.appthemehelper.util.MaterialUtil +import code.name.monkey.retromusic.EXTRA_ARTIST_ID +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter +import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter +import code.name.monkey.retromusic.extensions.extraNotNull +import code.name.monkey.retromusic.extensions.show +import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.fragments.albums.AlbumClickListener +import code.name.monkey.retromusic.glide.ArtistGlideRequest +import code.name.monkey.retromusic.glide.RetroMusicColoredTarget +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.network.model.LastFmArtist +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.RetroUtil +import code.name.monkey.retromusic.util.color.MediaNotificationProcessor +import com.bumptech.glide.Glide +import kotlinx.android.synthetic.main.activity_artist_content.* +import kotlinx.android.synthetic.main.activity_artist_details.* +import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf +import java.util.* +import kotlin.collections.ArrayList + +class ArtistDetailsFragment : MainActivityFragment(R.layout.fragment_artist_details), + AlbumClickListener { + + private var biography: Spanned? = null + private lateinit var artist: Artist + private lateinit var songAdapter: SimpleSongAdapter + private lateinit var albumAdapter: HorizontalAlbumAdapter + private var forceDownload: Boolean = false + private var lang: String? = null + + private val detailsViewModel: ArtistDetailsViewModel by viewModel { + parametersOf(extraNotNull(EXTRA_ARTIST_ID).value) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mainActivity.setSupportActionBar(toolbar) + mainActivity.setBottomBarVisibility(View.GONE) + toolbar.title = null + + postponeEnterTransition() + detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { + startPostponedEnterTransition() + showArtist(it) + }) + detailsViewModel.getArtistInfo().observe(viewLifecycleOwner, Observer { + artistInfo(it) + }) + setupRecyclerView() + playAction.apply { + setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } + } + shuffleAction.apply { + setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) } + } + + biographyText.setOnClickListener { + if (biographyText.maxLines == 4) { + biographyText.maxLines = Integer.MAX_VALUE + } else { + biographyText.maxLines = 4 + } + } + } + + private fun setupRecyclerView() { + albumAdapter = HorizontalAlbumAdapter(requireActivity(), ArrayList(), null, this) + albumRecyclerView.apply { + itemAnimator = DefaultItemAnimator() + layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false) + adapter = albumAdapter + } + songAdapter = SimpleSongAdapter(requireActivity(), ArrayList(), R.layout.item_song, null) + recyclerView.apply { + itemAnimator = DefaultItemAnimator() + layoutManager = LinearLayoutManager(this.context) + adapter = songAdapter + } + } + + fun showArtist(artist: Artist) { + this.artist = artist + loadArtistImage(artist) + if (RetroUtil.isAllowedToDownloadMetadata(requireContext())) { + loadBiography(artist.name) + } + artistTitle.text = artist.name + text.text = String.format( + "%s • %s", + MusicUtil.getArtistInfoString(requireContext(), artist), + MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs)) + ) + val songText = + resources.getQuantityString( + R.plurals.albumSongs, + artist.songCount, + artist.songCount + ) + val albumText = + resources.getQuantityString( + R.plurals.albums, + artist.songCount, + artist.songCount + ) + songTitle.text = songText + albumTitle.text = albumText + songAdapter.swapDataSet(artist.songs) + artist.albums?.let { albumAdapter.swapDataSet(it) } + } + + private fun loadBiography( + name: String, + lang: String? = Locale.getDefault().language + ) { + biography = null + this.lang = lang + detailsViewModel.loadBiography(name, lang, null) + } + + private fun artistInfo(lastFmArtist: LastFmArtist?) { + if (lastFmArtist != null && lastFmArtist.artist != null) { + val bioContent = lastFmArtist.artist.bio.content + if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) { + biographyText.visibility = View.VISIBLE + biographyTitle.visibility = View.VISIBLE + biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY) + biographyText.text = biography + if (lastFmArtist.artist.stats.listeners.isNotEmpty()) { + listeners.show() + listenersLabel.show() + scrobbles.show() + scrobblesLabel.show() + listeners.text = + RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat()) + scrobbles.text = + RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat()) + } + } + } + + // If the "lang" parameter is set and no biography is given, retry with default language + if (biography == null && lang != null) { + loadBiography(artist.name, null) + } + } + + + private fun loadArtistImage(artist: Artist) { + ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist) + .generatePalette(requireContext()).build() + .dontAnimate().into(object : RetroMusicColoredTarget(image) { + override fun onColorReady(colors: MediaNotificationProcessor) { + setColors(colors) + } + }) + } + + private fun setColors(color: MediaNotificationProcessor) { + MaterialUtil.tintColor( + button = shuffleAction, + textColor = color.primaryTextColor, + backgroundColor = color.backgroundColor + ) + MaterialUtil.tintColor( + button = playAction, + textColor = color.primaryTextColor, + backgroundColor = color.backgroundColor + ) + } + + override fun onAlbumClick(albumId: Int) { + findNavController().navigate( + R.id.albumDetailsFragment, + bundleOf("extra_album_id" to albumId) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/artists/ArtistDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt similarity index 97% rename from app/src/main/java/code/name/monkey/retromusic/activities/artists/ArtistDetailsViewModel.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt index 9802c594b..b16ea87a0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/artists/ArtistDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt @@ -1,4 +1,4 @@ -package code.name.monkey.retromusic.activities.artists +package code.name.monkey.retromusic.fragments.artists import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt index 111010d37..aaf032349 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt @@ -2,10 +2,13 @@ package code.name.monkey.retromusic.fragments.artists import android.os.Bundle import android.view.View +import androidx.core.os.bundleOf import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager +import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.artist.ArtistAdapter +import code.name.monkey.retromusic.extensions.findActivityNavController import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks @@ -13,7 +16,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment(), - MainActivityFragmentCallbacks { + MainActivityFragmentCallbacks, ArtistClickListener { override fun handleBackPress(): Boolean { return false @@ -46,10 +49,11 @@ class ArtistsFragment : override fun createAdapter(): ArtistAdapter { val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet return ArtistAdapter( - mainActivity, + requireActivity(), dataSet, - itemLayoutRes(), - mainActivity + R.layout.item_grid_circle, + null, + this ) } @@ -91,12 +95,18 @@ class ArtistsFragment : } companion object { - @JvmField - val TAG: String = ArtistsFragment::class.java.simpleName - @JvmStatic fun newInstance(): ArtistsFragment { return ArtistsFragment() } } + + override fun onArtist(artistId: Int) { + val controller = findActivityNavController(R.id.fragment_container) + controller.navigate(R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to artistId)) + } +} + +interface ArtistClickListener { + fun onArtist(artistId: Int) } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt index b12e778f6..73d9ed453 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt @@ -14,6 +14,10 @@ import android.view.View import android.widget.Toast import androidx.annotation.LayoutRes import androidx.appcompat.widget.Toolbar +import androidx.core.os.bundleOf +import androidx.navigation.findNavController +import code.name.monkey.retromusic.EXTRA_ALBUM_ID +import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity @@ -86,11 +90,17 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragme return true } R.id.action_go_to_album -> { - NavigationUtil.goToAlbum(requireActivity(), song.albumId) + requireActivity().findNavController(R.id.fragment_container).navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to song.albumId) + ) return true } R.id.action_go_to_artist -> { - NavigationUtil.goToArtist(requireActivity(), song.artistId) + requireActivity().findNavController(R.id.fragment_container).navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to song.artistId) + ) return true } R.id.now_playing -> { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt index de144f961..cd5f11745 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt @@ -7,7 +7,6 @@ import androidx.annotation.NonNull import androidx.annotation.StringRes import androidx.recyclerview.widget.RecyclerView import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.util.DensityUtil @@ -24,8 +23,7 @@ abstract class AbsRecyclerViewFragment, LM : Recycle AppBarLayout.OnOffsetChangedListener { val libraryViewModel: LibraryViewModel by sharedViewModel() - val mainActivity: MainActivity - get() = requireActivity() as MainActivity + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) @@ -38,7 +36,6 @@ abstract class AbsRecyclerViewFragment, LM : Recycle override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - mainActivity.addOnAppBarOffsetChangedListener(this) initLayoutManager() initAdapter() setUpRecyclerView() @@ -112,7 +109,7 @@ abstract class AbsRecyclerViewFragment, LM : Recycle container.paddingLeft, container.paddingTop, container.paddingRight, - mainActivity.getTotalAppBarScrollingRange() + i + i ) } @@ -137,11 +134,6 @@ abstract class AbsRecyclerViewFragment, LM : Recycle recyclerView.adapter = adapter } - override fun onDestroyView() { - super.onDestroyView() - mainActivity.removeOnAppBarOffsetChangedListener(this) - } - fun recyclerView(): RecyclerView { return recyclerView } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt new file mode 100644 index 000000000..13a96b9e8 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt @@ -0,0 +1,76 @@ +package code.name.monkey.retromusic.fragments.genres + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.EXTRA_GENRE +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.adapter.song.SongAdapter +import code.name.monkey.retromusic.extensions.dipToPix +import code.name.monkey.retromusic.extensions.extraNotNull +import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.model.Genre +import code.name.monkey.retromusic.model.Song +import kotlinx.android.synthetic.main.fragment_playlist_detail.* +import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf +import java.util.* + +class GenreDetailsFragment : MainActivityFragment(R.layout.fragment_playlist_detail) { + private val detailsViewModel: GenreDetailsViewModel by viewModel { + parametersOf(extraNotNull(EXTRA_GENRE).value) + } + + private lateinit var genre: Genre + private lateinit var songAdapter: SongAdapter + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mainActivity.addMusicServiceEventListener(detailsViewModel) + mainActivity.setSupportActionBar(toolbar) + + setupRecyclerView() + detailsViewModel.getSongs().observe(viewLifecycleOwner, androidx.lifecycle.Observer { + songs(it) + }) + detailsViewModel.getGenre().observe(viewLifecycleOwner, androidx.lifecycle.Observer { + genre = it + toolbar?.title = it.name + }) + } + + private fun setupRecyclerView() { + songAdapter = SongAdapter(requireActivity(), ArrayList(), R.layout.item_list, null) + recyclerView.apply { + itemAnimator = DefaultItemAnimator() + layoutManager = LinearLayoutManager(requireContext()) + adapter = songAdapter + } + songAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + super.onChanged() + checkIsEmpty() + } + }) + } + + fun songs(songs: List) { + songAdapter.swapDataSet(songs) + } + + private fun getEmojiByUnicode(unicode: Int): String { + return String(Character.toChars(unicode)) + } + + private fun checkIsEmpty() { + checkForPadding() + emptyEmoji.text = getEmojiByUnicode(0x1F631) + empty?.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE + } + + private fun checkForPadding() { + val height = dipToPix(52f).toInt() + recyclerView.setPadding(0, 0, 0, height) + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/genre/GenreDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsViewModel.kt similarity index 96% rename from app/src/main/java/code/name/monkey/retromusic/activities/genre/GenreDetailsViewModel.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsViewModel.kt index 03aba809a..5d87889d1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/genre/GenreDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsViewModel.kt @@ -1,4 +1,4 @@ -package code.name.monkey.retromusic.activities.genre +package code.name.monkey.retromusic.fragments.genres import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt index 6f7f32445..7610476c0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt @@ -49,7 +49,7 @@ class GenresFragment : AbsRecyclerViewFragment navController.navigate( + R.id.action_folder, + Bundle().apply { + putSerializable( + FoldersFragment.PATH, + PreferenceUtil.startDirectory + ) + }, + navOptions + ) + } + return@setOnNavigationItemSelectedListener handled + } + } + + override fun onPrepareOptionsMenu(menu: Menu) { + super.onPrepareOptionsMenu(menu) + ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), toolbar) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.menu_main, menu) + ToolbarContentTintHelper.handleOnCreateOptionsMenu( + requireContext(), + toolbar, + menu, + getToolbarBackgroundColor(toolbar) + ) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_search -> findNavController().navigate( + R.id.searchFragment, + null, + navOptions + ) + R.id.action_settings -> findNavController().navigate( + R.id.settingsFragment, + null, + navOptions + ) + } + return super.onOptionsItemSelected(item) + } + + fun addOnAppBarOffsetChangedListener(changedListener: AppBarLayout.OnOffsetChangedListener) { + appBarLayout.addOnOffsetChangedListener(changedListener) + } + + fun removeOnAppBarOffsetChangedListener(changedListener: AppBarLayout.OnOffsetChangedListener) { + appBarLayout.removeOnOffsetChangedListener(changedListener) + } + + fun getTotalAppBarScrollingRange(): Int { + return 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/NowPlayingPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/NowPlayingPlayerFragment.kt index cd26082bc..3de375140 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/NowPlayingPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/NowPlayingPlayerFragment.kt @@ -4,7 +4,7 @@ import android.os.Bundle import androidx.fragment.app.Fragment import androidx.navigation.NavController import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.extensions.navController +import code.name.monkey.retromusic.extensions.findNavController import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.util.PreferenceUtil @@ -15,7 +15,7 @@ class NowPlayingPlayerFragment : Fragment(R.layout.fragment_now_playing_player) override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - val navController = navController(R.id.playerFragmentContainer) + val navController = findNavController(R.id.playerFragmentContainer) updateNowPlaying(navController) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt index f2141bfbc..33aa882c5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt @@ -1,6 +1,5 @@ package code.name.monkey.retromusic.fragments.player.full -import android.app.ActivityOptions import android.content.res.ColorStateList import android.graphics.Color import android.os.Bundle @@ -8,7 +7,10 @@ import android.view.View import android.widget.FrameLayout import android.widget.TextView import androidx.appcompat.widget.Toolbar +import androidx.core.os.bundleOf +import androidx.navigation.findNavController import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.show @@ -22,7 +24,6 @@ import code.name.monkey.retromusic.loaders.ArtistLoader import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics import code.name.monkey.retromusic.model.lyrics.Lyrics -import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.bumptech.glide.Glide import kotlinx.android.synthetic.main.fragment_full.* @@ -150,19 +151,11 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full), private fun setupArtist() { artistImage.setOnClickListener { - val transitionName = - "${getString(R.string.transition_artist_image)}_${MusicPlayerRemote.currentSong.artistId}" - val activityOptions = - ActivityOptions.makeSceneTransitionAnimation( - requireActivity(), - artistImage, - transitionName + requireActivity().findNavController(R.id.fragment_container) + .navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to MusicPlayerRemote.currentSong.artistId) ) - NavigationUtil.goToArtistOptions( - requireActivity(), - MusicPlayerRemote.currentSong.artistId, - activityOptions - ) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/playlist/PlaylistDetailActivity.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt similarity index 59% rename from app/src/main/java/code/name/monkey/retromusic/activities/playlist/PlaylistDetailActivity.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt index 5bb0fbc48..c3dba8cc0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/playlist/PlaylistDetailActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt @@ -1,29 +1,23 @@ -package code.name.monkey.retromusic.activities.playlist +package code.name.monkey.retromusic.fragments.playlists import android.os.Bundle import android.view.Menu -import android.view.MenuItem +import android.view.MenuInflater import android.view.View import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.retromusic.EXTRA_PLAYLIST import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity import code.name.monkey.retromusic.adapter.song.OrderablePlaylistSongAdapter -import code.name.monkey.retromusic.adapter.song.PlaylistSongAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter -import code.name.monkey.retromusic.extensions.applyToolbar +import code.name.monkey.retromusic.extensions.dipToPix import code.name.monkey.retromusic.extensions.extraNotNull -import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper -import code.name.monkey.retromusic.interfaces.CabHolder +import code.name.monkey.retromusic.fragments.MainActivityFragment import code.name.monkey.retromusic.model.AbsCustomPlaylist import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Song -import code.name.monkey.retromusic.util.DensityUtil import code.name.monkey.retromusic.util.PlaylistsUtil -import code.name.monkey.retromusic.util.RetroColorUtil -import com.afollestad.materialcab.MaterialCab import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils @@ -31,63 +25,52 @@ import kotlinx.android.synthetic.main.activity_playlist_detail.* import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf -class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { - - +class PlaylistDetailsFragment : MainActivityFragment(R.layout.fragment_playlist_detail) { private val viewModel: PlaylistDetailsViewModel by viewModel { parametersOf(extraNotNull(EXTRA_PLAYLIST).value) } private lateinit var playlist: Playlist - private var cab: MaterialCab? = null private lateinit var adapter: SongAdapter + private var wrappedAdapter: RecyclerView.Adapter<*>? = null private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - super.onCreate(savedInstanceState) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setTaskDescriptionColorAuto() - setLightNavigationBar(true) - setBottomBarVisibility(View.GONE) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mainActivity.addMusicServiceEventListener(viewModel) + mainActivity.setSupportActionBar(toolbar) playlist = extraNotNull(EXTRA_PLAYLIST).value - setUpToolBar() setUpRecyclerView() - viewModel.getSongs().observe(this, Observer { + viewModel.getSongs().observe(viewLifecycleOwner, Observer { songs(it) }) - viewModel.getPlaylist().observe(this, Observer { + viewModel.getPlaylist().observe(viewLifecycleOwner, Observer { playlist = it - supportActionBar?.title = it.name + toolbar.title = it.name }) - addMusicServiceEventListener(viewModel) - } - override fun createContentView(): View { - return wrapSlidingMusicPanel(R.layout.activity_playlist_detail) } private fun setUpRecyclerView() { - recyclerView.layoutManager = LinearLayoutManager(this) + recyclerView.layoutManager = LinearLayoutManager(requireContext()) if (playlist is AbsCustomPlaylist) { - adapter = PlaylistSongAdapter(this, ArrayList(), R.layout.item_list, this) + adapter = SongAdapter(requireActivity(), ArrayList(), R.layout.item_list, null) recyclerView.adapter = adapter } else { recyclerViewDragDropManager = RecyclerViewDragDropManager() val animator = RefactoredDefaultItemAnimator() - adapter = OrderablePlaylistSongAdapter(this, + adapter = OrderablePlaylistSongAdapter(requireActivity(), ArrayList(), R.layout.item_list, - this, + null, object : OrderablePlaylistSongAdapter.OnMoveItemListener { override fun onMoveItem(fromPosition: Int, toPosition: Int) { if (PlaylistsUtil.moveItem( - this@PlaylistDetailActivity, + requireContext(), playlist.id, fromPosition, toPosition @@ -114,58 +97,18 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { }) } - private fun setUpToolBar() { - applyToolbar(toolbar) - title = playlist.name - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate( + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate( if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail else R.menu.menu_playlist_detail, menu ) - return super.onCreateOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> { - onBackPressed() - return true - } - } - return PlaylistMenuHelper.handleMenuClick(this, playlist, item) - } - - override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { - if (cab != null && cab!!.isActive) { - cab!!.finish() - } - cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor( - RetroColorUtil.shiftBackgroundColorForLightText( - ATHUtil.resolveColor( - this, - R.attr.colorSurface - ) - ) - ).start(callback) - return cab!! - } - - override fun onBackPressed() { - if (cab != null && cab!!.isActive) { - cab!!.finish() - } else { - recyclerView!!.stopScroll() - super.onBackPressed() - } - } private fun checkForPadding() { - val height = DensityUtil.dip2px(this, 52f) - recyclerView.setPadding(0, 0, 0, (height)) + val height = dipToPix(52f) + recyclerView.setPadding(0, 0, 0, height.toInt()) } private fun checkIsEmpty() { @@ -216,8 +159,4 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { showEmptyView() } } - - companion object { - var EXTRA_PLAYLIST = "extra_playlist" - } -} +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/playlist/PlaylistDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt similarity index 97% rename from app/src/main/java/code/name/monkey/retromusic/activities/playlist/PlaylistDetailsViewModel.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt index 0ea4bc037..4935fdf39 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/playlist/PlaylistDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsViewModel.kt @@ -1,4 +1,4 @@ -package code.name.monkey.retromusic.activities.playlist +package code.name.monkey.retromusic.fragments.playlists import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt index 26b197f75..2ab2d3c0d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt @@ -1,8 +1,6 @@ package code.name.monkey.retromusic.fragments.playlists import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater import android.view.View import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager @@ -12,7 +10,7 @@ import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks class PlaylistsFragment : - AbsRecyclerViewFragment() , + AbsRecyclerViewFragment(), MainActivityFragmentCallbacks { override fun handleBackPress(): Boolean { @@ -39,26 +37,14 @@ class PlaylistsFragment : override fun createAdapter(): PlaylistAdapter { return PlaylistAdapter( - mainActivity, + requireActivity(), ArrayList(), R.layout.item_list, - mainActivity + null ) } - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - super.onCreateOptionsMenu(menu, inflater) - menu.apply { - removeItem(R.id.action_sort_order) - removeItem(R.id.action_grid_size) - } - } - companion object { - @JvmField - val TAG: String = PlaylistsFragment::class.java.simpleName - - @JvmStatic fun newInstance(): PlaylistsFragment { return PlaylistsFragment() } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/MainSettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/MainSettingsFragment.kt index 9b523575f..d0c15861d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/MainSettingsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/MainSettingsFragment.kt @@ -19,13 +19,11 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.annotation.StringRes import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.retromusic.App import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.SettingsActivity import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.util.NavigationUtil @@ -78,12 +76,4 @@ class MainSettingsFragment : Fragment(), View.OnClickListener { diamondIcon.imageTintList = ColorStateList.valueOf(it) } } - - companion object { - - } - - private fun inflateFragment(fragment: Fragment, @StringRes title: Int) { - (requireActivity() as SettingsActivity).setupFragment(fragment, title) - } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt new file mode 100644 index 000000000..d1df247c0 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt @@ -0,0 +1,23 @@ +package code.name.monkey.retromusic.fragments.settings + +import android.os.Bundle +import android.view.View +import androidx.navigation.NavController +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.extensions.findNavController +import code.name.monkey.retromusic.fragments.MainActivityFragment +import kotlinx.android.synthetic.main.fragment_settings.* + +class SettingsFragment : MainActivityFragment(R.layout.fragment_settings) { + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mainActivity.setSupportActionBar(toolbar) + mainActivity.hideBottomNavigation() + mainActivity.setBottomBarVisibility(View.GONE) + val navController: NavController = findNavController(R.id.contentFrame) + navController.addOnDestinationChangedListener { _, _, _ -> + toolbar.title = navController.currentDestination?.label + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt index d2b62bb17..0d28c804d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt @@ -1,12 +1,11 @@ package code.name.monkey.retromusic.fragments.songs import android.os.Bundle -import android.view.* +import android.view.View import androidx.annotation.LayoutRes import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment @@ -36,26 +35,16 @@ class SongsFragment : get() = R.string.no_songs override fun createLayoutManager(): GridLayoutManager { - return GridLayoutManager(requireActivity(), getGridSize()).apply { - spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - return if (position == 0) { - getGridSize() - } else { - 1 - } - } - } - } + return GridLayoutManager(requireActivity(), getGridSize()) } override fun createAdapter(): SongAdapter { val dataSet = if (adapter == null) mutableListOf() else adapter!!.dataSet - return ShuffleButtonSongAdapter( - mainActivity, + return SongAdapter( + requireActivity(), dataSet, - itemLayoutRes(), - mainActivity + R.layout.item_list, + null ) } @@ -109,72 +98,4 @@ class SongsFragment : return SongsFragment() } } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - super.onCreateOptionsMenu(menu, inflater) - setUpGridSizeMenu(menu.findItem(R.id.action_grid_size).subMenu) - } - - private fun setUpGridSizeMenu( - - gridSizeMenu: SubMenu - ) { - println(getGridSize()) - when (getGridSize()) { - 1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = true - 2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true - 3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true - 4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true - 5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true - 6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true - 7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true - 8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true - } - val maxGridSize = maxGridSize - if (maxGridSize < 8) { - gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false - } - if (maxGridSize < 7) { - gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false - } - if (maxGridSize < 6) { - gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false - } - if (maxGridSize < 5) { - gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false - } - if (maxGridSize < 4) { - gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false - } - if (maxGridSize < 3) { - gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (handleGridSizeMenuItem(item)) return true - return super.onOptionsItemSelected(item) - } - - fun handleGridSizeMenuItem( - item: MenuItem - ): Boolean { - var gridSize = 0 - when (item.itemId) { - R.id.action_grid_size_1 -> gridSize = 1 - R.id.action_grid_size_2 -> gridSize = 2 - R.id.action_grid_size_3 -> gridSize = 3 - R.id.action_grid_size_4 -> gridSize = 4 - R.id.action_grid_size_5 -> gridSize = 5 - R.id.action_grid_size_6 -> gridSize = 6 - R.id.action_grid_size_7 -> gridSize = 7 - R.id.action_grid_size_8 -> gridSize = 8 - } - if (gridSize > 0) { - item.isChecked = true - setAndSaveGridSize(gridSize) - return true - } - return false - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt index ef9729dc2..6d7de7cfd 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt @@ -19,7 +19,7 @@ import android.app.Activity import android.content.Context import android.view.MenuItem import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.App import code.name.monkey.retromusic.R import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog @@ -38,7 +38,7 @@ import java.util.* object PlaylistMenuHelper { fun handleMenuClick( - activity: AppCompatActivity, + activity: FragmentActivity, playlist: Playlist, item: MenuItem ): Boolean { when (item.itemId) { diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt index 933ec60eb..0fb27c981 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt @@ -18,8 +18,11 @@ import android.content.Intent import android.view.MenuItem import android.view.View import android.widget.PopupMenu -import androidx.appcompat.app.AppCompatActivity +import androidx.core.os.bundleOf import androidx.fragment.app.FragmentActivity +import androidx.navigation.findNavController +import code.name.monkey.retromusic.EXTRA_ALBUM_ID +import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity @@ -30,7 +33,6 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.interfaces.PaletteColorHolder import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.RingtoneManager object SongMenuHelper { @@ -89,18 +91,24 @@ object SongMenuHelper { return true } R.id.action_go_to_album -> { - NavigationUtil.goToAlbum(activity, song.albumId) + activity.findNavController(R.id.fragment_container).navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to song.albumId) + ) return true } R.id.action_go_to_artist -> { - NavigationUtil.goToArtist(activity, song.artistId) + activity.findNavController(R.id.fragment_container).navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to song.artistId) + ) return true } } return false } - abstract class OnClickSongMenu protected constructor(private val activity: AppCompatActivity) : + abstract class OnClickSongMenu(private val activity: FragmentActivity) : View.OnClickListener, PopupMenu.OnMenuItemClickListener { open val menuRes: Int diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt index 6d94ef1d5..e46f452e0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/PlaylistSongsLoader.kt @@ -67,7 +67,7 @@ object PlaylistSongsLoader { val artistName = cursor.getString(10) val idInPlaylist = cursor.getInt(11) val composer = cursor.getString(12) - + val albumArtist = cursor.getString(13) return PlaylistSong( id, title, @@ -82,7 +82,8 @@ object PlaylistSongsLoader { artistName, playlistId, idInPlaylist, - composer + composer, + albumArtist ) } @@ -103,10 +104,9 @@ object PlaylistSongsLoader { AudioColumns.ARTIST_ID, // 9 AudioColumns.ARTIST, // 10 MediaStore.Audio.Playlists.Members._ID,//11 - AudioColumns.COMPOSER - )// 12 - , IS_MUSIC, null, - MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER + AudioColumns.COMPOSER,//12 + "album_artist"//13 + ), IS_MUSIC, null, MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER ) } catch (e: SecurityException) { return null diff --git a/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt b/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt index 39c191bf8..641f2eb3f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/loaders/SongLoader.kt @@ -95,10 +95,21 @@ object SongLoader { val artistId = cursor.getInt(9) val artistName = cursor.getString(10) val composer = cursor.getString(11) - + val albumArtist = cursor.getString(12) return Song( - id, title, trackNumber, year, duration, data, dateModified, albumId, - albumName ?: "", artistId, artistName, composer ?: "" + id, + title, + trackNumber, + year, + duration, + data, + dateModified, + albumId, + albumName ?: "", + artistId, + artistName ?: "", + composer ?: "", + albumArtist ?: "" ) } @@ -123,13 +134,13 @@ object SongLoader { selectionFinal = generateBlacklistSelection(selectionFinal, paths.size) selectionValuesFinal = addBlacklistSelectionValues(selectionValuesFinal, paths) } - + selectionFinal = + selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000) try { return context.contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, baseProjection, - selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + - (PreferenceUtil.filterLength * 1000), + selectionFinal, selectionValuesFinal, sortOrder ) diff --git a/app/src/main/java/code/name/monkey/retromusic/model/CategoryInfo.java b/app/src/main/java/code/name/monkey/retromusic/model/CategoryInfo.java index 80bdfc9e6..ce7037905 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/CategoryInfo.java +++ b/app/src/main/java/code/name/monkey/retromusic/model/CategoryInfo.java @@ -59,9 +59,9 @@ public class CategoryInfo implements Parcelable { } public enum Category { - Home(R.id.action_home, R.string.home, R.drawable.asld_home), - Songs(R.id.action_song, R.string.songs, R.drawable.asld_music_note), - Albums(R.id.action_album, R.string.albums, R.drawable.asld_album), + Home(R.id.action_home, R.string.for_you, R.drawable.ic_baseline), + Songs(R.id.action_song, R.string.songs, R.drawable.ic_audiotrack), + Albums(R.id.action_album, R.string.albums, R.drawable.ic_album), Artists(R.id.action_artist, R.string.artists, R.drawable.ic_artist), Playlists(R.id.action_playlist, R.string.playlists, R.drawable.ic_playlist_play), Genres(R.id.action_genre, R.string.genres, R.drawable.ic_guitar), diff --git a/app/src/main/java/code/name/monkey/retromusic/model/PlaylistSong.java b/app/src/main/java/code/name/monkey/retromusic/model/PlaylistSong.java index 01b335bf8..a2b556f56 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/PlaylistSong.java +++ b/app/src/main/java/code/name/monkey/retromusic/model/PlaylistSong.java @@ -41,9 +41,9 @@ public class PlaylistSong extends Song { @NotNull String artistName, int playlistId, int idInPlayList, - @NotNull String composer) { - super(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, - composer); + @NotNull String composer, + String albumArtist) { + super(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, composer, albumArtist); this.playlistId = playlistId; this.idInPlayList = idInPlayList; } diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Song.kt b/app/src/main/java/code/name/monkey/retromusic/model/Song.kt index 0033b518d..9b3822954 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Song.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/Song.kt @@ -29,7 +29,8 @@ open class Song( val albumName: String, val artistId: Int, val artistName: String, - val composer: String? + val composer: String?, + val albumArtist: String? ) : Parcelable { @@ -48,6 +49,7 @@ open class Song( "", -1, "", + "", "" ) } diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java b/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java index 9ee3fae79..9c7213221 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java @@ -43,7 +43,7 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { public static final String ORIGINAL_PLAYING_QUEUE_TABLE_NAME = "original_playing_queue"; - private static final int VERSION = 10; + private static final int VERSION = 12; @Nullable private static MusicPlaybackQueueStore sInstance = null; @@ -148,6 +148,9 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { builder.append(" STRING NOT NULL,"); builder.append(AudioColumns.COMPOSER); + builder.append(" STRING,"); + + builder.append("album_artist"); builder.append(" STRING);"); db.execSQL(builder.toString()); diff --git a/app/src/main/java/code/name/monkey/retromusic/util/AppRater.kt b/app/src/main/java/code/name/monkey/retromusic/util/AppRater.kt index 3f06af688..6cbeb1fb8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/AppRater.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/AppRater.kt @@ -14,12 +14,15 @@ package code.name.monkey.retromusic.util +import android.app.Activity import android.content.Context import android.content.Intent import android.content.SharedPreferences import android.net.Uri +import android.widget.Toast import code.name.monkey.retromusic.R import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.play.core.review.ReviewManagerFactory object AppRater { private const val DO_NOT_SHOW_AGAIN = "do_not_show_again"// Package Name @@ -53,13 +56,29 @@ object AppRater { // Wait at least n days before opening if (launchCount >= LAUNCHES_UNTIL_PROMPT) { if (System.currentTimeMillis() >= dateFirstLaunch + DAYS_UNTIL_PROMPT * 24 * 60 * 60 * 1000) { - showRateDialog(context, editor) + //showRateDialog(context, editor) + showPlayStoreReviewDialog(context) } } editor.commit() } + private fun showPlayStoreReviewDialog(context: Context) { + val manager = ReviewManagerFactory.create(context) + manager.requestReviewFlow().addOnCompleteListener { request -> + if (request.isSuccessful) { + val reviewInfo = request.result + manager.launchReviewFlow(context as Activity, reviewInfo).addOnCompleteListener { + if (it.isSuccessful) { + Toast.makeText(context, "Thanks for the feedback", Toast.LENGTH_SHORT) + .show() + } + } + } + } + } + private fun showRateDialog(context: Context, editor: SharedPreferences.Editor) { MaterialAlertDialogBuilder(context) .setTitle("Rate this App") diff --git a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java index a95fa8813..406bb22b8 100755 --- a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java @@ -28,28 +28,16 @@ import androidx.core.app.ActivityCompat; import org.jetbrains.annotations.NotNull; import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.activities.AboutActivity; import code.name.monkey.retromusic.activities.DriveModeActivity; -import code.name.monkey.retromusic.activities.genre.GenreDetailsActivity; import code.name.monkey.retromusic.activities.LicenseActivity; import code.name.monkey.retromusic.activities.LyricsActivity; import code.name.monkey.retromusic.activities.PlayingQueueActivity; -import code.name.monkey.retromusic.activities.playlist.PlaylistDetailActivity; import code.name.monkey.retromusic.activities.PurchaseActivity; -import code.name.monkey.retromusic.activities.search.SearchActivity; -import code.name.monkey.retromusic.activities.SettingsActivity; import code.name.monkey.retromusic.activities.SupportDevelopmentActivity; import code.name.monkey.retromusic.activities.UserInfoActivity; import code.name.monkey.retromusic.activities.WhatsNewActivity; -import code.name.monkey.retromusic.activities.albums.AlbumDetailsActivity; -import code.name.monkey.retromusic.activities.artists.ArtistDetailActivity; import code.name.monkey.retromusic.activities.bugreport.BugReportActivity; import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.model.Genre; -import code.name.monkey.retromusic.model.Playlist; - -import static code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY; -import static code.name.monkey.retromusic.util.RetroUtil.openUrl; public class NavigationUtil { @@ -58,45 +46,6 @@ public class NavigationUtil { ActivityCompat.startActivity(activity, new Intent(activity, BugReportActivity.class), null); } - public static void goToAbout(@NonNull Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, AboutActivity.class), null); - } - - public static void goToAlbum(@NonNull Activity activity, int albumId) { - Intent intent = new Intent(activity, AlbumDetailsActivity.class); - intent.putExtra(AlbumDetailsActivity.EXTRA_ALBUM_ID, albumId); - ActivityCompat.startActivity(activity, intent, null); - } - - public static void goToAlbumOptions(@NonNull Activity activity, - int albumId, - @NonNull ActivityOptions options) { - Intent intent = new Intent(activity, AlbumDetailsActivity.class); - intent.putExtra(AlbumDetailsActivity.EXTRA_ALBUM_ID, albumId); - ActivityCompat.startActivity(activity, intent, options.toBundle()); - } - - public static void goToArtist(@NonNull Activity activity, int i) { - Intent intent = new Intent(activity, ArtistDetailActivity.class); - intent.putExtra(ArtistDetailActivity.EXTRA_ARTIST_ID, i); - ActivityCompat.startActivity(activity, intent, null); - } - - public static void goToArtistOptions(@NotNull Activity activity, - int artistId, - @NonNull ActivityOptions options) { - - Intent intent = new Intent(activity, ArtistDetailActivity.class); - intent.putExtra(ArtistDetailActivity.EXTRA_ARTIST_ID, artistId); - ActivityCompat.startActivity(activity, intent, options.toBundle()); - } - - public static void goToGenre(@NonNull Activity activity, @NonNull Genre genre) { - Intent intent = new Intent(activity, GenreDetailsActivity.class); - intent.putExtra(GenreDetailsActivity.EXTRA_GENRE_ID, genre); - ActivityCompat.startActivity(activity, intent, null); - } - public static void goToLyrics(@NonNull Activity activity) { Intent intent = new Intent(activity, LyricsActivity.class); ActivityCompat.startActivity(activity, intent, null); @@ -106,47 +55,15 @@ public class NavigationUtil { ActivityCompat.startActivity(activity, new Intent(activity, LicenseActivity.class), null); } - public static void goToPlayStore(@NonNull Activity activity) { - openUrl(activity, RATE_ON_GOOGLE_PLAY); - } - public static void goToPlayingQueue(@NonNull Activity activity) { Intent intent = new Intent(activity, PlayingQueueActivity.class); ActivityCompat.startActivity(activity, intent, null); } - public static void goToPlaylistNew(@NonNull Activity activity, @NonNull Playlist playlist) { - Intent intent = new Intent(activity, PlaylistDetailActivity.class); - intent.putExtra(PlaylistDetailActivity.Companion.getEXTRA_PLAYLIST(), playlist); - ActivityCompat.startActivity(activity, intent, null); - } - public static void goToProVersion(@NonNull Context context) { ActivityCompat.startActivity(context, new Intent(context, PurchaseActivity.class), null); } - public static void goToSearch(@NonNull Activity activity, - @NonNull ActivityOptions activityOptions) { - ActivityCompat.startActivity(activity, new Intent(activity, SearchActivity.class), - activityOptions.toBundle()); - } - - public static void goToSearch(@NonNull Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, SearchActivity.class), - null); - } - - public static void goToSearch(@NonNull Activity activity, boolean isMicOpen, - @NonNull ActivityOptions activityOptions) { - ActivityCompat.startActivity(activity, new Intent(activity, SearchActivity.class) - .putExtra(SearchActivity.EXTRA_SHOW_MIC, isMicOpen), - activityOptions.toBundle()); - } - - public static void goToSettings(@NonNull Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, SettingsActivity.class), null); - } - public static void goToSupportDevelopment(@NonNull Activity activity) { ActivityCompat.startActivity(activity, new Intent(activity, SupportDevelopmentActivity.class), null); } diff --git a/app/src/main/res/drawable/ic_baseline.xml b/app/src/main/res/drawable/ic_baseline.xml new file mode 100644 index 000000000..ef34eabe6 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_layout.xml b/app/src/main/res/drawable/ic_layout.xml new file mode 100644 index 000000000..dddf8672b --- /dev/null +++ b/app/src/main/res/drawable/ic_layout.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout-land/activity_album.xml b/app/src/main/res/layout-land/activity_album_details.xml similarity index 100% rename from app/src/main/res/layout-land/activity_album.xml rename to app/src/main/res/layout-land/activity_album_details.xml diff --git a/app/src/main/res/layout/activity_album.xml b/app/src/main/res/layout/activity_album_details.xml similarity index 100% rename from app/src/main/res/layout/activity_album.xml rename to app/src/main/res/layout/activity_album_details.xml diff --git a/app/src/main/res/layout/activity_main_content.xml b/app/src/main/res/layout/activity_main_content.xml index 604812182..ffd74617b 100644 --- a/app/src/main/res/layout/activity_main_content.xml +++ b/app/src/main/res/layout/activity_main_content.xml @@ -1,61 +1,11 @@ - + app:defaultNavHost="true" + app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" + app:navGraph="@navigation/main_graph" /> - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml new file mode 100644 index 000000000..33efb3af8 --- /dev/null +++ b/app/src/main/res/layout/fragment_about.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_album_details.xml b/app/src/main/res/layout/fragment_album_details.xml index 39c667b35..56bff8c38 100644 --- a/app/src/main/res/layout/fragment_album_details.xml +++ b/app/src/main/res/layout/fragment_album_details.xml @@ -1,102 +1,141 @@ - + android:layout_height="match_parent" + android:background="?attr/colorSurface" + android:orientation="vertical" + tools:ignore="UnusedAttribute"> - + android:layout_height="wrap_content"> - + + + + + + app:liftOnScroll="true"> - + - + + + android:layout_height="48dp" /> + - + - + - - - - - - + android:orientation="vertical"> - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_artist_details.xml b/app/src/main/res/layout/fragment_artist_details.xml new file mode 100644 index 000000000..b757c2771 --- /dev/null +++ b/app/src/main/res/layout/fragment_artist_details.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 323b5f130..844a8c1d0 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -1,17 +1,4 @@ - - + @@ -38,47 +26,38 @@ android:id="@+id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@android:color/transparent" - android:elevation="0dp" - app:elevation="0dp" app:liftOnScroll="true"> - - + android:layout_height="wrap_content" + android:background="?attr/colorSurface" + app:popupTheme="?attr/toolbarPopupTheme" + app:title="@string/app_name" + app:titleTextAppearance="@style/ToolbarTextAppearanceNormal.Library" + app:titleTextColor="?attr/colorControlNormal" + tools:ignore="UnusedAttribute" /> - - - - - + + - - + app:defaultNavHost="true" + app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" + app:navGraph="@navigation/library_graph" /> - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index f940f3c74..3c20e7963 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -58,6 +58,6 @@ android:layout_height="match_parent" app:defaultNavHost="true" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" - app:navGraph="@navigation/retro_graph" /> + app:navGraph="@navigation/main_graph" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_playlist_detail.xml b/app/src/main/res/layout/fragment_playlist_detail.xml new file mode 100644 index 000000000..019edd95f --- /dev/null +++ b/app/src/main/res/layout/fragment_playlist_detail.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml new file mode 100644 index 000000000..9c4e3b86d --- /dev/null +++ b/app/src/main/res/layout/fragment_search.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml new file mode 100644 index 000000000..d59ab25e0 --- /dev/null +++ b/app/src/main/res/layout/fragment_settings.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_card.xml b/app/src/main/res/layout/item_card.xml index 8ce8c74e7..86037eefe 100644 --- a/app/src/main/res/layout/item_card.xml +++ b/app/src/main/res/layout/item_card.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_margin="1dp" android:background="?attr/rectSelector" android:clickable="true" android:focusable="true"> @@ -11,7 +12,7 @@ diff --git a/app/src/main/res/layout/item_card_color.xml b/app/src/main/res/layout/item_card_color.xml index 14f009703..438bfde9e 100644 --- a/app/src/main/res/layout/item_card_color.xml +++ b/app/src/main/res/layout/item_card_color.xml @@ -4,15 +4,15 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="4dp" + android:layout_margin="1dp" android:background="?attr/rectSelector" android:clickable="true" - android:focusable="true" - tools:ignore="MissingPrefix"> + android:focusable="true"> @@ -33,7 +33,6 @@ tools:ignore="ContentDescription" tools:src="@tools:sample/avatars" /> - @@ -38,7 +39,35 @@ android:layout_marginEnd="16dp" android:layout_weight="1" android:text="@string/shuffle" + android:visibility="gone" app:backgroundTint="?attr/colorSurface" app:icon="@drawable/ic_shuffle" /> + + + + + + \ 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 810d401c8..56956d884 100644 --- a/app/src/main/res/layout/sliding_music_panel_layout.xml +++ b/app/src/main/res/layout/sliding_music_panel_layout.xml @@ -30,11 +30,11 @@ - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:id="@+id/action_settings" + android:icon="@drawable/ic_settings" + android:title="@string/action_settings" + app:showAsAction="ifRoom" /> \ No newline at end of file diff --git a/app/src/main/res/navigation/retro_graph.xml b/app/src/main/res/navigation/library_graph.xml similarity index 65% rename from app/src/main/res/navigation/retro_graph.xml rename to app/src/main/res/navigation/library_graph.xml index fa8ee86ba..9223239e0 100644 --- a/app/src/main/res/navigation/retro_graph.xml +++ b/app/src/main/res/navigation/library_graph.xml @@ -1,51 +1,47 @@ + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/library_graph" + app:startDestination="@id/action_home"> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_folder" /> - - + android:name="code.name.monkey.retromusic.fragments.home.HomeFragment" + tools:layout="@layout/fragment_banner_home" /> \ No newline at end of file diff --git a/app/src/main/res/navigation/main_graph.xml b/app/src/main/res/navigation/main_graph.xml new file mode 100644 index 000000000..2282e9fb5 --- /dev/null +++ b/app/src/main/res/navigation/main_graph.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/settings_graph.xml b/app/src/main/res/navigation/settings_graph.xml index 7f03655b6..513b5670f 100644 --- a/app/src/main/res/navigation/settings_graph.xml +++ b/app/src/main/res/navigation/settings_graph.xml @@ -18,6 +18,7 @@ app:exitAnim="@anim/retro_fragment_open_exit" app:popEnterAnim="@anim/retro_fragment_close_enter" app:popExitAnim="@anim/retro_fragment_close_exit" /> + + + + + + app:popExitAnim="@anim/retro_fragment_close_exit" /> + - + android:name="code.name.monkey.retromusic.fragments.about.AboutFragment" + android:label="About" /> \ 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 071611526..6f0c93878 100755 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -126,7 +126,7 @@ - @layout/activity_album + @layout/activity_album_details System default diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5ef8c08a7..fbae73809 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -880,4 +880,6 @@ %d Artist %d Artists + + Hello blank fragment From 4a87a900be473d93e01583ad7aad2745c1617669 Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Wed, 12 Aug 2020 00:28:14 +0530 Subject: [PATCH 12/20] Code refactor Towards single activity *Removed SearchActvitiy --- app/src/main/AndroidManifest.xml | 2 +- .../code/name/monkey/retromusic/MainModule.kt | 2 +- .../activities/search/SearchActivity.kt | 220 ------------------ .../monkey/retromusic/adapter/HomeAdapter.kt | 24 +- .../AppShortcutLauncherActivity.kt | 13 +- .../appshortcuts/DynamicShortcutManager.kt | 3 - .../shortcuttype/SearchShortCutType.kt | 45 ---- .../search/SearchFragment.kt | 22 +- .../search/SearchViewModel.kt | 2 +- app/src/main/res/layout/fragment_search.xml | 42 ++-- app/src/main/res/layout/fragment_settings.xml | 1 + app/src/main/res/layout/item_suggestions.xml | 28 ++- .../layout/metal_section_recycler_view.xml | 19 +- .../main/res/layout/section_recycler_view.xml | 17 +- app/src/main/res/navigation/main_graph.xml | 10 +- 15 files changed, 125 insertions(+), 325 deletions(-) delete mode 100644 app/src/main/java/code/name/monkey/retromusic/activities/search/SearchActivity.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/SearchShortCutType.kt rename app/src/main/java/code/name/monkey/retromusic/{activities => fragments}/search/SearchFragment.kt (90%) rename app/src/main/java/code/name/monkey/retromusic/{activities => fragments}/search/SearchViewModel.kt (93%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 50f044269..92ee215fc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -119,7 +119,7 @@ (EXTRA_SHOW_MIC).value == true) { - startMicSearch() - } - - back.setOnClickListener { onBackPressed() } - voiceSearch.setOnClickListener { startMicSearch() } - clearText.setOnClickListener { searchView.clearText() } - searchContainer.backgroundTintList = - ColorStateList.valueOf(ATHUtil.resolveColor(this, R.attr.colorSurface)) - - keyboardPopup.setOnClickListener { - val inputManager = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager - inputManager.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT) - } - - keyboardPopup.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this)) - ColorStateList.valueOf( - MaterialValueHelper.getPrimaryTextColor( - this, - ColorUtil.isColorLight(ThemeStore.accentColor(this)) - ) - ).apply { - keyboardPopup.setTextColor(this) - keyboardPopup.iconTint = this - } - if (savedInstanceState != null) { - query = savedInstanceState.getString(QUERY) - } - - viewModel.getSearchResult().observe(this, androidx.lifecycle.Observer { - showData(it) - }) - } - - private fun setupRecyclerView() { - searchAdapter = SearchAdapter(this, emptyList()) - searchAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - override fun onChanged() { - super.onChanged() - empty.visibility = if (searchAdapter.itemCount < 1) View.VISIBLE else View.GONE - } - }) - recyclerView.apply { - layoutManager = LinearLayoutManager(this@SearchActivity) - adapter = searchAdapter - } - recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - if (dy > 0) { - keyboardPopup.shrink() - } else if (dy < 0) { - keyboardPopup.extend() - } - } - }) - } - - private fun setupSearchView() { - searchView.addTextChangedListener(this) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putString(QUERY, query) - } - - private fun setUpToolBar() { - title = null - } - - private fun search(query: String) { - this.query = query - TransitionManager.beginDelayedTransition(appBarLayout) - voiceSearch.visibility = if (query.isNotEmpty()) View.GONE else View.VISIBLE - clearText.visibility = if (query.isNotEmpty()) View.VISIBLE else View.GONE - viewModel.search(query) - } - - override fun onMediaStoreChanged() { - super.onMediaStoreChanged() - query?.let { search(it) } - } - - override fun onQueryTextSubmit(query: String): Boolean { - hideSoftKeyboard() - return false - } - - override fun onQueryTextChange(newText: String): Boolean { - search(newText) - return false - } - - private fun hideSoftKeyboard() { - RetroUtil.hideSoftKeyboard(this@SearchActivity) - if (searchView != null) { - searchView.clearFocus() - } - } - - private fun showData(data: MutableList) { - if (data.isNotEmpty()) { - searchAdapter.swapDataSet(data) - } else { - searchAdapter.swapDataSet(ArrayList()) - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - when (requestCode) { - REQ_CODE_SPEECH_INPUT -> { - if (resultCode == Activity.RESULT_OK && null != data) { - val result: ArrayList? = - data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS) - query = result?.get(0) - searchView.setText(query, BufferType.EDITABLE) - viewModel.search(query!!) - } - } - } - } - - private fun startMicSearch() { - val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) - 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 { - startActivityForResult( - intent, - REQ_CODE_SPEECH_INPUT - ) - } catch (e: ActivityNotFoundException) { - e.printStackTrace() - Toast.makeText(this, getString(R.string.speech_not_supported), Toast.LENGTH_SHORT) - .show() - } - } - - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - } - - override fun onTextChanged(newText: CharSequence, start: Int, before: Int, count: Int) { - search(newText.toString()) - } - - override fun afterTextChanged(s: Editable) { - } - - companion object { - val TAG: String = SearchActivity::class.java.simpleName - - const val EXTRA_SHOW_MIC = "extra_show_mic" - const val QUERY: String = "query" - - const val REQ_CODE_SPEECH_INPUT = 9002 - } -} - -fun TextInputEditText.clearText() { - text = null -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt index 20a356cc6..7c5bc3c94 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt @@ -74,26 +74,33 @@ class HomeAdapter( val viewHolder = holder as AlbumViewHolder viewHolder.bindView( list[position].arrayList as List, - R.string.recent_albums + R.string.recent_albums, + "Most recently added albums" ) } TOP_ALBUMS -> { val viewHolder = holder as AlbumViewHolder viewHolder.bindView( list[position].arrayList as List, - R.string.top_albums + R.string.top_albums, + "Most played albums" ) } RECENT_ARTISTS -> { val viewHolder = holder as ArtistViewHolder viewHolder.bindView( list[position].arrayList as List, - R.string.recent_artists + R.string.recent_artists, + "Most recently added artists" ) } TOP_ARTISTS -> { val viewHolder = holder as ArtistViewHolder - viewHolder.bindView(list[position].arrayList as List, R.string.top_artists) + viewHolder.bindView( + list[position].arrayList as List, + R.string.top_artists, + "Most played artists" + ) } SUGGESTIONS -> { val viewHolder = holder as SuggestionsViewHolder @@ -148,7 +155,7 @@ class HomeAdapter( } private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view), AlbumClickListener { - fun bindView(list: List, titleRes: Int) { + fun bindView(list: List, titleRes: Int, message: String) { if (list.isNotEmpty()) { val albumAdapter = AlbumAdapter(activity, list, R.layout.pager_item, null, this) recyclerView.apply { @@ -169,7 +176,7 @@ class HomeAdapter( } private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view), ArtistClickListener { - fun bindView(list: List, titleRes: Int) { + fun bindView(list: List, titleRes: Int, message: String) { if (list.isNotEmpty()) { val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) val artistAdapter = ArtistAdapter( @@ -210,7 +217,7 @@ class HomeAdapter( fun bindView(arrayList: List) { val color = ThemeStore.accentColor(activity) - itemView.findViewById(R.id.text).setTextColor(color) + itemView.findViewById(R.id.message).setTextColor(color) itemView.findViewById(R.id.card6).apply { setCardBackgroundColor(ColorUtil.withAlpha(color, 0.2f)) } @@ -230,6 +237,7 @@ class HomeAdapter( private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) { fun bindView(arrayList: List, titleRes: Int) { + text.text = "You're all time favorites" if (arrayList.isNotEmpty()) { val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0]) if (songs.isNotEmpty()) { @@ -250,6 +258,7 @@ class HomeAdapter( private inner class GenreViewHolder(itemView: View) : AbsHomeViewItem(itemView) { fun bind(genres: List, titleRes: Int) { title.text = activity.getString(titleRes) + text.text = "Genres for you" recyclerView.apply { show() layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.HORIZONTAL, false) @@ -262,5 +271,6 @@ class HomeAdapter( open inner class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) { val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView) val title: AppCompatTextView = itemView.findViewById(R.id.title) + val text: AppCompatTextView = itemView.findViewById(R.id.text) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.kt b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.kt index 96a9123b3..1bc47922c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/AppShortcutLauncherActivity.kt @@ -17,9 +17,7 @@ package code.name.monkey.retromusic.appshortcuts import android.app.Activity import android.content.Intent import android.os.Bundle -import code.name.monkey.retromusic.activities.search.SearchActivity import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType -import code.name.monkey.retromusic.appshortcuts.shortcuttype.SearchShortCutType import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType import code.name.monkey.retromusic.model.Playlist @@ -45,26 +43,22 @@ class AppShortcutLauncherActivity : Activity() { when (shortcutType) { SHORTCUT_TYPE_SHUFFLE_ALL -> { startServiceWithPlaylist( - MusicService.SHUFFLE_MODE_SHUFFLE, ShuffleAllPlaylist(applicationContext) + SHUFFLE_MODE_SHUFFLE, ShuffleAllPlaylist(applicationContext) ) DynamicShortcutManager.reportShortcutUsed(this, ShuffleAllShortcutType.id) } SHORTCUT_TYPE_TOP_TRACKS -> { startServiceWithPlaylist( - MusicService.SHUFFLE_MODE_NONE, MyTopTracksPlaylist(applicationContext) + SHUFFLE_MODE_NONE, MyTopTracksPlaylist(applicationContext) ) DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.id) } SHORTCUT_TYPE_LAST_ADDED -> { startServiceWithPlaylist( - MusicService.SHUFFLE_MODE_NONE, LastAddedPlaylist(applicationContext) + SHUFFLE_MODE_NONE, LastAddedPlaylist(applicationContext) ) DynamicShortcutManager.reportShortcutUsed(this, LastAddedShortcutType.id) } - SHORTCUT_TYPE_SEARCH -> { - startActivity(Intent(this, SearchActivity::class.java)) - DynamicShortcutManager.reportShortcutUsed(this, SearchShortCutType.id) - } } finish() } @@ -87,7 +81,6 @@ class AppShortcutLauncherActivity : Activity() { const val SHORTCUT_TYPE_SHUFFLE_ALL = 0 const val SHORTCUT_TYPE_TOP_TRACKS = 1 const val SHORTCUT_TYPE_LAST_ADDED = 2 - const val SHORTCUT_TYPE_SEARCH = 3 const val SHORTCUT_TYPE_NONE = 4 } } diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/DynamicShortcutManager.kt b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/DynamicShortcutManager.kt index c77df6b54..c5f0ed6dc 100644 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/DynamicShortcutManager.kt +++ b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/DynamicShortcutManager.kt @@ -22,7 +22,6 @@ import android.content.pm.ShortcutManager import android.graphics.drawable.Icon import android.os.Build import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType -import code.name.monkey.retromusic.appshortcuts.shortcuttype.SearchShortCutType import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType import java.util.* @@ -34,11 +33,9 @@ class DynamicShortcutManager(private val context: Context) { private val defaultShortcuts: List get() = Arrays.asList( - SearchShortCutType(context).shortcutInfo, ShuffleAllShortcutType(context).shortcutInfo, TopTracksShortcutType(context).shortcutInfo, LastAddedShortcutType(context).shortcutInfo - ) fun initDynamicShortcuts() { diff --git a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/SearchShortCutType.kt b/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/SearchShortCutType.kt deleted file mode 100644 index a025688ec..000000000 --- a/app/src/main/java/code/name/monkey/retromusic/appshortcuts/shortcuttype/SearchShortCutType.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2019 Hemanth Savarala. - * - * Licensed under the GNU General Public License v3 - * - * This is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by - * the Free Software Foundation either version 3 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - */ - -package code.name.monkey.retromusic.appshortcuts.shortcuttype - -import android.annotation.TargetApi -import android.content.Context -import android.content.pm.ShortcutInfo -import android.os.Build -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator -import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity - -@TargetApi(Build.VERSION_CODES.N_MR1) -class SearchShortCutType(context: Context) : BaseShortcutType(context) { - companion object { - - val id: String - get() = BaseShortcutType.ID_PREFIX + "search" - } - - override val shortcutInfo: ShortcutInfo - get() = ShortcutInfo.Builder( - context, - id - ).setShortLabel(context.getString(R.string.action_search)) - .setLongLabel(context.getString(R.string.search_hint)).setIcon( - AppShortcutIconGenerator.generateThemedIcon( - context, - R.drawable.ic_app_shortcut_search - ) - ).setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_SEARCH)) - .build() -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt similarity index 90% rename from app/src/main/java/code/name/monkey/retromusic/activities/search/SearchFragment.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt index 75dc04422..75c1500f6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt @@ -1,4 +1,4 @@ -package code.name.monkey.retromusic.activities.search +package code.name.monkey.retromusic.fragments.search import android.content.ActivityNotFoundException import android.content.Intent @@ -19,22 +19,30 @@ import code.name.monkey.retromusic.adapter.SearchAdapter import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.showToast import code.name.monkey.retromusic.fragments.MainActivityFragment +import com.google.android.material.textfield.TextInputEditText import kotlinx.android.synthetic.main.fragment_search.* +import kotlinx.android.synthetic.main.fragment_search.view.* import org.koin.android.ext.android.inject import java.util.* import kotlin.collections.ArrayList class SearchFragment : MainActivityFragment(R.layout.fragment_search), TextWatcher { + companion object { + const val QUERY = "query" + const val REQ_CODE_SPEECH_INPUT = 9001 + } + private val viewModel: SearchViewModel by inject() private lateinit var searchAdapter: SearchAdapter private var query: String? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + mainActivity.setSupportActionBar(toolbar) + mainActivity.hideBottomNavigation() + mainActivity.setBottomBarVisibility(View.GONE) setupRecyclerView() setupSearchView() - mainActivity.setSupportActionBar(toolbar) - mainActivity.setBottomBarVisibility(View.GONE) voiceSearch.setOnClickListener { startMicSearch() } clearText.setOnClickListener { searchView.clearText() } @@ -48,7 +56,7 @@ class SearchFragment : MainActivityFragment(R.layout.fragment_search), TextWatch } keyboardPopup.accentColor() if (savedInstanceState != null) { - query = savedInstanceState.getString(SearchActivity.QUERY) + query = savedInstanceState.getString(QUERY) } viewModel.getSearchResult().observe(viewLifecycleOwner, Observer { @@ -124,11 +132,15 @@ class SearchFragment : MainActivityFragment(R.layout.fragment_search), TextWatch try { startActivityForResult( intent, - SearchActivity.REQ_CODE_SPEECH_INPUT + REQ_CODE_SPEECH_INPUT ) } catch (e: ActivityNotFoundException) { e.printStackTrace() showToast(getString(R.string.speech_not_supported)) } } +} + +fun TextInputEditText.clearText() { + text = null } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt similarity index 93% rename from app/src/main/java/code/name/monkey/retromusic/activities/search/SearchViewModel.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt index 550a8d237..ee6b3b518 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/search/SearchViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt @@ -1,4 +1,4 @@ -package code.name.monkey.retromusic.activities.search +package code.name.monkey.retromusic.fragments.search import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index 9c4e3b86d..335a7d5c9 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -27,29 +27,45 @@ app:liftOnScroll="true"> + style="@style/Toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:navigationIcon="@drawable/ic_keyboard_backspace_black" + app:titleTextAppearance="@style/ToolbarTextAppearanceNormal"> - + app:boxCornerRadiusBottomEnd="8dp" + app:boxCornerRadiusBottomStart="8dp" + app:boxCornerRadiusTopEnd="8dp" + app:boxCornerRadiusTopStart="8dp" + app:boxStrokeColor="?attr/colorControlNormal" + app:boxStrokeWidth="1dp" + app:hintEnabled="false"> - - + + + + + + + + app:layout_constraintTop_toBottomOf="@id/text"> + app:layout_constraintTop_toBottomOf="@id/text"> + app:layout_constraintTop_toBottomOf="@id/text"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/section_recycler_view.xml b/app/src/main/res/layout/section_recycler_view.xml index 4db63851b..9107539bc 100644 --- a/app/src/main/res/layout/section_recycler_view.xml +++ b/app/src/main/res/layout/section_recycler_view.xml @@ -13,7 +13,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:paddingHorizontal="16dp" - android:paddingVertical="14dp" + android:paddingTop="16dp" android:textAppearance="@style/TextViewHeadline6" android:textStyle="bold" app:layout_constrainedWidth="true" @@ -22,17 +22,30 @@ app:layout_constraintTop_toTopOf="parent" tools:text="@tools:sample/full_names" /> + \ No newline at end of file diff --git a/app/src/main/res/navigation/main_graph.xml b/app/src/main/res/navigation/main_graph.xml index 2282e9fb5..9f4bba396 100644 --- a/app/src/main/res/navigation/main_graph.xml +++ b/app/src/main/res/navigation/main_graph.xml @@ -49,13 +49,6 @@ app:argType="integer" /> - - - @@ -81,5 +74,4 @@ android:label="SettingsFragment" tools:layout="@layout/fragment_settings" /> - \ No newline at end of file From ec47bc3d2e0f465edd52f3305bd2b63e1ea1d4c1 Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Wed, 12 Aug 2020 03:01:09 +0530 Subject: [PATCH 13/20] Add detail navigation --- app/src/main/AndroidManifest.xml | 4 - .../retromusic/activities/MainActivity.kt | 2 - .../base/AbsSlidingMusicPanelActivity.kt | 5 +- .../monkey/retromusic/adapter/HomeAdapter.kt | 9 +- .../retromusic/adapter/album/AlbumAdapter.kt | 13 +- .../monkey/retromusic/extensions/ColorExt.kt | 14 ++ .../retromusic/extensions/FragmentExt.kt | 4 + .../retromusic/fragments/LibraryViewModel.kt | 4 +- .../fragments/MainActivityFragment.kt | 11 -- .../fragments/about/AboutActivity.kt | 148 ------------------ .../fragments/about/AboutFragment.kt | 6 +- .../fragments/albums/AlbumDetailsFragment.kt | 35 ++--- .../fragments/albums/AlbumsFragment.kt | 9 +- .../artists/ArtistDetailsFragment.kt | 35 ++--- .../fragments/base/AbsMusicServiceFragment.kt | 14 +- .../fragments/base/AbsPlayerFragment.kt | 12 +- .../fragments/base/AbsRecyclerViewFragment.kt | 3 +- .../fragments/genres/GenreDetailsFragment.kt | 4 +- .../retromusic/fragments/home/HomeFragment.kt | 8 +- .../fragments/library/LibraryFragment.kt | 39 +---- .../player/PlayerAlbumCoverFragment.kt | 1 + .../player/full/FullPlayerFragment.kt | 17 +- .../playlists/PlaylistDetailsFragment.kt | 6 +- .../fragments/search/SearchFragment.kt | 6 +- .../fragments/settings/SettingsFragment.kt | 4 +- .../layout-land/fragment_album_details.xml | 140 +++++++++++++++++ .../layout-land/fragment_artist_details.xml | 119 ++++++++++++++ .../res/layout/activity_album_details.xml | 4 +- .../res/layout/fragment_album_details.xml | 7 +- .../res/layout/fragment_artist_details.xml | 5 +- app/src/main/res/layout/fragment_material.xml | 4 +- .../res/layout/sliding_music_panel_layout.xml | 2 +- app/src/main/res/navigation/library_graph.xml | 3 +- app/src/main/res/transition/change_bounds.xml | 8 +- 34 files changed, 381 insertions(+), 324 deletions(-) delete mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/MainActivityFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutActivity.kt create mode 100644 app/src/main/res/layout-land/fragment_album_details.xml create mode 100644 app/src/main/res/layout-land/fragment_artist_details.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 92ee215fc..16a51f6b9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -106,7 +106,6 @@ - @@ -118,9 +117,6 @@ - AppCompatActivity.whichFragment(@IdRes id: Int): T { return supportFragmentManager.findFragmentById(id) as T } +@Suppress("UNCHECKED_CAST") +fun Fragment.whichFragment(@IdRes id: Int): T { + return childFragmentManager.findFragmentById(id) as T +} fun Fragment.showToast(@StringRes stringRes: Int) { showToast(getString(stringRes)) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt index f0c3cce0c..e0a5498ca 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt @@ -55,10 +55,10 @@ class LibraryViewModel( repository.topArtists(), repository.topAlbums(), repository.recentArtists(), - repository.recentAlbums(), + repository.recentAlbums()/*, repository.suggestions(), repository.favoritePlaylist(), - repository.homeGenres() + repository.homeGenres()*/ ) result.forEach { if (it != null && it.arrayList.isNotEmpty()) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/MainActivityFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/MainActivityFragment.kt deleted file mode 100644 index 46507d44c..000000000 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/MainActivityFragment.kt +++ /dev/null @@ -1,11 +0,0 @@ -package code.name.monkey.retromusic.fragments - -import androidx.annotation.LayoutRes -import code.name.monkey.retromusic.activities.MainActivity -import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment - -open class MainActivityFragment(@LayoutRes layoutRes: Int) : AbsMusicServiceFragment(layoutRes) { - val mainActivity by lazy { - requireActivity() as MainActivity - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutActivity.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutActivity.kt deleted file mode 100644 index 0704256f7..000000000 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutActivity.kt +++ /dev/null @@ -1,148 +0,0 @@ -package code.name.monkey.retromusic.fragments.about - -import android.content.Intent -import android.content.pm.PackageManager -import android.net.Uri -import android.os.Bundle -import android.view.MenuItem -import android.view.View -import androidx.core.app.ShareCompat -import androidx.recyclerview.widget.DefaultItemAnimator -import androidx.recyclerview.widget.LinearLayoutManager -import code.name.monkey.retromusic.App -import code.name.monkey.retromusic.Constants.APP_INSTAGRAM_LINK -import code.name.monkey.retromusic.Constants.APP_TELEGRAM_LINK -import code.name.monkey.retromusic.Constants.APP_TWITTER_LINK -import code.name.monkey.retromusic.Constants.FAQ_LINK -import code.name.monkey.retromusic.Constants.GITHUB_PROJECT -import code.name.monkey.retromusic.Constants.PINTEREST -import code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY -import code.name.monkey.retromusic.Constants.TELEGRAM_CHANGE_LOG -import code.name.monkey.retromusic.Constants.TRANSLATE -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.base.AbsBaseActivity -import code.name.monkey.retromusic.adapter.ContributorAdapter -import code.name.monkey.retromusic.extensions.applyToolbar -import code.name.monkey.retromusic.model.Contributor -import code.name.monkey.retromusic.util.NavigationUtil -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import kotlinx.android.synthetic.main.activity_about.* -import kotlinx.android.synthetic.main.card_credit.* -import kotlinx.android.synthetic.main.card_other.* -import kotlinx.android.synthetic.main.card_retro_info.* -import kotlinx.android.synthetic.main.card_social.* -import java.io.IOException -import java.nio.charset.StandardCharsets - -class AboutActivity : AbsBaseActivity(), View.OnClickListener { - - private val contributorsJson: String? - get() { - val json: String - try { - val inputStream = assets.open("contributors.json") - val size = inputStream.available() - val buffer = ByteArray(size) - inputStream.read(buffer) - inputStream.close() - json = String(buffer, StandardCharsets.UTF_8) - } catch (ex: IOException) { - ex.printStackTrace() - return null - } - return json - } - - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_about) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setLightNavigationBar(true) - - applyToolbar(toolbar) - version.setSummary(getAppVersion()) - setUpView() - loadContributors() - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - onBackPressed() - return true - } - return super.onOptionsItemSelected(item) - } - - private fun openUrl(url: String) { - val i = Intent(Intent.ACTION_VIEW) - i.data = Uri.parse(url) - i.flags = Intent.FLAG_ACTIVITY_NEW_TASK - startActivity(i) - } - - private fun setUpView() { - appGithub.setOnClickListener(this) - faqLink.setOnClickListener(this) - telegramLink.setOnClickListener(this) - appRate.setOnClickListener(this) - appTranslation.setOnClickListener(this) - appShare.setOnClickListener(this) - donateLink.setOnClickListener(this) - instagramLink.setOnClickListener(this) - twitterLink.setOnClickListener(this) - changelog.setOnClickListener(this) - openSource.setOnClickListener(this) - pinterestLink.setOnClickListener(this) - bugReportLink.setOnClickListener(this) - } - - override fun onClick(view: View) { - when (view.id) { - R.id.pinterestLink -> openUrl(PINTEREST) - R.id.faqLink -> openUrl(FAQ_LINK) - R.id.telegramLink -> openUrl(APP_TELEGRAM_LINK) - R.id.appGithub -> openUrl(GITHUB_PROJECT) - R.id.appTranslation -> openUrl(TRANSLATE) - R.id.appRate -> openUrl(RATE_ON_GOOGLE_PLAY) - R.id.appShare -> shareApp() - R.id.donateLink -> NavigationUtil.goToSupportDevelopment(this) - R.id.instagramLink -> openUrl(APP_INSTAGRAM_LINK) - R.id.twitterLink -> openUrl(APP_TWITTER_LINK) - R.id.changelog -> openUrl(TELEGRAM_CHANGE_LOG) - R.id.openSource -> NavigationUtil.goToOpenSource(this) - R.id.bugReportLink -> NavigationUtil.bugReport(this) - } - } - - private fun getAppVersion(): String { - return try { - val isPro = if (App.isProVersion()) "Pro" else "Free" - val packageInfo = packageManager.getPackageInfo(packageName, 0) - "${packageInfo.versionName} $isPro" - } catch (e: PackageManager.NameNotFoundException) { - e.printStackTrace() - "0.0.0" - } - } - - private fun shareApp() { - ShareCompat.IntentBuilder.from(this).setType("text/plain") - .setChooserTitle(R.string.share_app) - .setText(String.format(getString(R.string.app_share), packageName)).startChooser() - } - - private fun loadContributors() { - val type = object : TypeToken>() { - - }.type - val contributors = Gson().fromJson>(contributorsJson, type) - - val contributorAdapter = ContributorAdapter(contributors) - recyclerView.layoutManager = LinearLayoutManager(this) - recyclerView.itemAnimator = DefaultItemAnimator() - recyclerView.adapter = contributorAdapter - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt index 3dbaf9ae6..4f030ac0c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt @@ -12,12 +12,11 @@ import code.name.monkey.retromusic.App import code.name.monkey.retromusic.Constants import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.ContributorAdapter -import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.model.Contributor import code.name.monkey.retromusic.util.NavigationUtil import com.google.gson.Gson import com.google.gson.reflect.TypeToken -import kotlinx.android.synthetic.main.activity_about.* import kotlinx.android.synthetic.main.card_credit.* import kotlinx.android.synthetic.main.card_other.* import kotlinx.android.synthetic.main.card_retro_info.* @@ -25,10 +24,9 @@ import kotlinx.android.synthetic.main.card_social.* import java.io.IOException import java.nio.charset.StandardCharsets -class AboutFragment : MainActivityFragment(R.layout.fragment_about), View.OnClickListener { +class AboutFragment : AbsMainActivityFragment(R.layout.fragment_about), View.OnClickListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - mainActivity.setSupportActionBar(toolbar) version.setSummary(getAppVersion()) setUpView() loadContributors() diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt index ab8be6e97..66f5a231b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt @@ -8,18 +8,17 @@ import androidx.core.os.bundleOf import androidx.lifecycle.Observer import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager -import code.name.monkey.appthemehelper.util.MaterialUtil -import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter -import code.name.monkey.retromusic.extensions.extraNotNull +import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.show -import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.glide.AlbumGlideRequest import code.name.monkey.retromusic.glide.ArtistGlideRequest import code.name.monkey.retromusic.glide.RetroMusicColoredTarget @@ -38,16 +37,17 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.util.* -class AlbumDetailsFragment : MainActivityFragment(R.layout.fragment_album_details), +class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_details), AlbumClickListener { private lateinit var simpleSongAdapter: SimpleSongAdapter private lateinit var album: Album + private val args: AlbumDetailsFragmentArgs by navArgs() private val savedSortOrder: String get() = PreferenceUtil.albumDetailSongSortOrder private val detailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_ALBUM_ID).value) + parametersOf(args.extraAlbumId) } override fun onActivityCreated(savedInstanceState: Bundle?) { @@ -56,6 +56,8 @@ class AlbumDetailsFragment : MainActivityFragment(R.layout.fragment_album_detail mainActivity.setBottomBarVisibility(View.GONE) toolbar.title = null + image.transitionName = getString(R.string.transition_album_art) + postponeEnterTransition() playerActivity?.addMusicServiceEventListener(detailsViewModel) detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer { @@ -100,13 +102,6 @@ class AlbumDetailsFragment : MainActivityFragment(R.layout.fragment_album_detail } } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - val activity = activity as AppCompatActivity - activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) - } - override fun onDestroy() { super.onDestroy() playerActivity?.removeMusicServiceEventListener(detailsViewModel) @@ -234,19 +229,11 @@ class AlbumDetailsFragment : MainActivityFragment(R.layout.fragment_album_detail } private fun setColors(color: MediaNotificationProcessor) { - MaterialUtil.tintColor( - button = shuffleAction, - textColor = color.primaryTextColor, - backgroundColor = color.backgroundColor - ) - MaterialUtil.tintColor( - button = playAction, - textColor = color.primaryTextColor, - backgroundColor = color.backgroundColor - ) + shuffleAction.applyColor(color.backgroundColor) + playAction.applyColor(color.backgroundColor) } - override fun onAlbumClick(albumId: Int) { + override fun onAlbumClick(albumId: Int, view: View) { findNavController().navigate( R.id.albumDetailsFragment, bundleOf("extra_album_id" to albumId) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt index 2e2a9c71c..5c4b5aa68 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt @@ -94,12 +94,15 @@ class AlbumsFragment : } } - override fun onAlbumClick(albumId: Int) { + override fun onAlbumClick(albumId: Int, view: View) { val controller = requireActivity().findNavController(R.id.fragment_container) - controller.navigate(R.id.albumDetailsFragment, bundleOf(EXTRA_ALBUM_ID to albumId)) + controller.navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to albumId) + ) } } interface AlbumClickListener { - fun onAlbumClick(albumId: Int) + fun onAlbumClick(albumId: Int, view: View) } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt index d38b2e0b3..d834ae31f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt @@ -6,19 +6,20 @@ import android.view.View import androidx.core.os.bundleOf import androidx.core.text.HtmlCompat import androidx.lifecycle.Observer +import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager -import code.name.monkey.appthemehelper.util.MaterialUtil import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter +import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.extraNotNull import code.name.monkey.retromusic.extensions.show -import code.name.monkey.retromusic.fragments.MainActivityFragment import code.name.monkey.retromusic.fragments.albums.AlbumClickListener +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.glide.ArtistGlideRequest import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -35,7 +36,7 @@ import org.koin.core.parameter.parametersOf import java.util.* import kotlin.collections.ArrayList -class ArtistDetailsFragment : MainActivityFragment(R.layout.fragment_artist_details), +class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_details), AlbumClickListener { private var biography: Spanned? = null @@ -49,12 +50,12 @@ class ArtistDetailsFragment : MainActivityFragment(R.layout.fragment_artist_deta parametersOf(extraNotNull(EXTRA_ARTIST_ID).value) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) mainActivity.setSupportActionBar(toolbar) mainActivity.setBottomBarVisibility(View.GONE) toolbar.title = null - + setupRecyclerView() postponeEnterTransition() detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { startPostponedEnterTransition() @@ -63,7 +64,7 @@ class ArtistDetailsFragment : MainActivityFragment(R.layout.fragment_artist_deta detailsViewModel.getArtistInfo().observe(viewLifecycleOwner, Observer { artistInfo(it) }) - setupRecyclerView() + playAction.apply { setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } } @@ -173,22 +174,18 @@ class ArtistDetailsFragment : MainActivityFragment(R.layout.fragment_artist_deta } private fun setColors(color: MediaNotificationProcessor) { - MaterialUtil.tintColor( - button = shuffleAction, - textColor = color.primaryTextColor, - backgroundColor = color.backgroundColor - ) - MaterialUtil.tintColor( - button = playAction, - textColor = color.primaryTextColor, - backgroundColor = color.backgroundColor - ) + shuffleAction.applyColor(color.backgroundColor) + playAction.applyColor(color.backgroundColor) } - override fun onAlbumClick(albumId: Int) { + override fun onAlbumClick(albumId: Int, view: View) { findNavController().navigate( R.id.albumDetailsFragment, - bundleOf("extra_album_id" to albumId) + bundleOf("extra_album_id" to albumId), + null, + FragmentNavigatorExtras( + view to getString(R.string.transition_album_art) + ) ) } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsMusicServiceFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsMusicServiceFragment.kt index b5d666f1f..d6e8d1eea 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsMusicServiceFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsMusicServiceFragment.kt @@ -7,6 +7,8 @@ import android.view.View import android.webkit.MimeTypeMap import androidx.annotation.LayoutRes import androidx.fragment.app.Fragment +import androidx.navigation.navOptions +import code.name.monkey.retromusic.R import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.Song @@ -22,7 +24,17 @@ import java.util.* open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout), MusicServiceEventListener { - + val navOptions by lazy { + navOptions { + launchSingleTop = true + anim { + enter = R.anim.retro_fragment_open_enter + exit = R.anim.retro_fragment_open_exit + popEnter = R.anim.retro_fragment_close_enter + popExit = R.anim.retro_fragment_close_exit + } + } + } var playerActivity: AbsMusicServiceActivity? = null private set diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt index 73d9ed453..62089b49f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt @@ -34,11 +34,8 @@ import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.* import org.koin.androidx.viewmodel.ext.android.sharedViewModel import java.io.FileNotFoundException -abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragment(layout), - Toolbar.OnMenuItemClickListener, - PaletteColorHolder, - PlayerAlbumCoverFragment.Callbacks { - +abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout), + Toolbar.OnMenuItemClickListener, PaletteColorHolder, PlayerAlbumCoverFragment.Callbacks { private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null @@ -264,11 +261,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragme statusBarShadow?.hide() } - interface Callbacks { - - fun onPaletteColorChanged() - } - companion object { val TAG: String = AbsPlayerFragment::class.java.simpleName const val VISIBILITY_ANIM_DURATION: Long = 300 diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt index cd5f11745..2ba05ce72 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt @@ -23,8 +23,7 @@ abstract class AbsRecyclerViewFragment, LM : Recycle AppBarLayout.OnOffsetChangedListener { val libraryViewModel: LibraryViewModel by sharedViewModel() - - + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setHasOptionsMenu(true) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt index 13a96b9e8..bb9fa8fb2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt @@ -10,7 +10,7 @@ import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.extensions.dipToPix import code.name.monkey.retromusic.extensions.extraNotNull -import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Song import kotlinx.android.synthetic.main.fragment_playlist_detail.* @@ -18,7 +18,7 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.util.* -class GenreDetailsFragment : MainActivityFragment(R.layout.fragment_playlist_detail) { +class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail) { private val detailsViewModel: GenreDetailsViewModel by viewModel { parametersOf(extraNotNull(EXTRA_GENRE).value) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt index 39ef69aae..6c0d9881c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt @@ -30,7 +30,6 @@ import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.glide.ProfileBannerGlideRequest import code.name.monkey.retromusic.glide.UserProfileGlideRequest 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.model.smartplaylist.HistoryPlaylist import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist @@ -44,12 +43,7 @@ import kotlinx.android.synthetic.main.home_content.* import org.koin.androidx.viewmodel.ext.android.sharedViewModel class HomeFragment : - AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home), - MainActivityFragmentCallbacks { - - override fun handleBackPress(): Boolean { - return false - } + AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) { private val libraryViewModel: LibraryViewModel by sharedViewModel() diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt index d0227df14..93859b58e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt @@ -6,40 +6,17 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.navigation.fragment.findNavController -import androidx.navigation.navOptions -import androidx.navigation.ui.AppBarConfiguration import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.findNavController -import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.fragments.folder.FoldersFragment import code.name.monkey.retromusic.util.PreferenceUtil import com.google.android.material.appbar.AppBarLayout import kotlinx.android.synthetic.main.fragment_library.* -class LibraryFragment : MainActivityFragment(R.layout.fragment_library) { - private val navOptions by lazy { - navOptions { - launchSingleTop = true - anim { - enter = R.anim.retro_fragment_open_enter - exit = R.anim.retro_fragment_open_exit - popEnter = R.anim.retro_fragment_close_enter - popExit = R.anim.retro_fragment_close_exit - } - } - } - private val appBarConfiguration by lazy { - AppBarConfiguration( - setOf( - R.id.libraryFragment, - R.id.settingsFragment, - R.id.searchFragment - ) - ) - } - +class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setHasOptionsMenu(true) @@ -50,18 +27,6 @@ class LibraryFragment : MainActivityFragment(R.layout.fragment_library) { private fun setupNavigationController() { val navController = findNavController(R.id.fragment_container) - val navOptions = navOptions { - launchSingleTop = true - anim { - enter = R.anim.retro_fragment_open_enter - exit = R.anim.retro_fragment_open_exit - popEnter = R.anim.retro_fragment_close_enter - popExit = R.anim.retro_fragment_close_exit - } - popUpTo(navController.graph.startDestination) { - inclusive = true - } - } mainActivity.getBottomNavigationView().setOnNavigationItemSelectedListener { var handled = false if (navController.graph.findNode(it.itemId) != null) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt index 9caeb6997..be5d8c836 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt @@ -18,6 +18,7 @@ import kotlinx.android.synthetic.main.fragment_player_album_cover.* class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_player_album_cover), ViewPager.OnPageChangeListener { + private var callbacks: Callbacks? = null private var currentPosition: Int = 0 private val colorReceiver = object : AlbumCoverFragment.ColorReceiver { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt index 33aa882c5..cca5df70f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt @@ -8,12 +8,13 @@ import android.widget.FrameLayout import android.widget.TextView import androidx.appcompat.widget.Toolbar import androidx.core.os.bundleOf -import androidx.navigation.findNavController import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.extensions.findActivityNavController import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.show +import code.name.monkey.retromusic.extensions.whichFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.glide.ArtistGlideRequest @@ -151,7 +152,8 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full), private fun setupArtist() { artistImage.setOnClickListener { - requireActivity().findNavController(R.id.fragment_container) + mainActivity.collapsePanel() + findActivityNavController(R.id.fragment_container) .navigate( R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to MusicPlayerRemote.currentSong.artistId) @@ -160,13 +162,10 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full), } private fun setUpSubFragments() { - controlsFragment = - childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FullPlaybackControlsFragment - - val playerAlbumCoverFragment = - childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment - playerAlbumCoverFragment.setCallbacks(this) - playerAlbumCoverFragment.removeSlideEffect() + controlsFragment = whichFragment(R.id.playbackControlsFragment) + val coverFragment: PlayerAlbumCoverFragment = whichFragment(R.id.playerAlbumCoverFragment) + coverFragment.setCallbacks(this) + coverFragment.removeSlideEffect() } override fun onShow() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt index c3dba8cc0..872128e6f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt @@ -13,7 +13,7 @@ import code.name.monkey.retromusic.adapter.song.OrderablePlaylistSongAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.extensions.dipToPix import code.name.monkey.retromusic.extensions.extraNotNull -import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.model.AbsCustomPlaylist import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Song @@ -21,11 +21,11 @@ import code.name.monkey.retromusic.util.PlaylistsUtil 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 kotlinx.android.synthetic.main.activity_playlist_detail.* +import kotlinx.android.synthetic.main.fragment_playlist_detail.* import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf -class PlaylistDetailsFragment : MainActivityFragment(R.layout.fragment_playlist_detail) { +class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail) { private val viewModel: PlaylistDetailsViewModel by viewModel { parametersOf(extraNotNull(EXTRA_PLAYLIST).value) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt index 75c1500f6..a53f4e138 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt @@ -18,15 +18,14 @@ import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.SearchAdapter import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.showToast -import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import com.google.android.material.textfield.TextInputEditText import kotlinx.android.synthetic.main.fragment_search.* -import kotlinx.android.synthetic.main.fragment_search.view.* import org.koin.android.ext.android.inject import java.util.* import kotlin.collections.ArrayList -class SearchFragment : MainActivityFragment(R.layout.fragment_search), TextWatcher { +class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWatcher { companion object { const val QUERY = "query" const val REQ_CODE_SPEECH_INPUT = 9001 @@ -41,6 +40,7 @@ class SearchFragment : MainActivityFragment(R.layout.fragment_search), TextWatch mainActivity.setSupportActionBar(toolbar) mainActivity.hideBottomNavigation() mainActivity.setBottomBarVisibility(View.GONE) + setupRecyclerView() setupSearchView() voiceSearch.setOnClickListener { startMicSearch() } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt index d1df247c0..72355ec95 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt @@ -5,10 +5,10 @@ import android.view.View import androidx.navigation.NavController import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.findNavController -import code.name.monkey.retromusic.fragments.MainActivityFragment +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import kotlinx.android.synthetic.main.fragment_settings.* -class SettingsFragment : MainActivityFragment(R.layout.fragment_settings) { +class SettingsFragment : AbsMainActivityFragment(R.layout.fragment_settings) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/res/layout-land/fragment_album_details.xml b/app/src/main/res/layout-land/fragment_album_details.xml new file mode 100644 index 000000000..32d4c7f69 --- /dev/null +++ b/app/src/main/res/layout-land/fragment_album_details.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout-land/fragment_artist_details.xml b/app/src/main/res/layout-land/fragment_artist_details.xml new file mode 100644 index 000000000..c697c814c --- /dev/null +++ b/app/src/main/res/layout-land/fragment_artist_details.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_album_details.xml b/app/src/main/res/layout/activity_album_details.xml index 56bff8c38..473a20eed 100755 --- a/app/src/main/res/layout/activity_album_details.xml +++ b/app/src/main/res/layout/activity_album_details.xml @@ -105,7 +105,7 @@ app:layout_constraintStart_toEndOf="@id/artistImage" app:layout_constraintTop_toTopOf="@id/artistImage" tools:ignore="MissingPrefix" - tools:text="@tools:sample/lorem/random" /> + tools:text="@tools:sample/full_names" /> + tools:text="@tools:sample/full_names" /> - diff --git a/app/src/main/res/layout/fragment_artist_details.xml b/app/src/main/res/layout/fragment_artist_details.xml index b757c2771..09d2fe809 100644 --- a/app/src/main/res/layout/fragment_artist_details.xml +++ b/app/src/main/res/layout/fragment_artist_details.xml @@ -76,8 +76,7 @@ tools:srcCompat="@tools:sample/backgrounds/scenic" /> - - + - - diff --git a/app/src/main/res/navigation/library_graph.xml b/app/src/main/res/navigation/library_graph.xml index 9223239e0..9749f4d27 100644 --- a/app/src/main/res/navigation/library_graph.xml +++ b/app/src/main/res/navigation/library_graph.xml @@ -8,7 +8,7 @@ + tools:layout="@layout/fragment_main_activity_recycler_view"/> + \ No newline at end of file diff --git a/app/src/main/res/transition/change_bounds.xml b/app/src/main/res/transition/change_bounds.xml index 8595f5a97..5da16fb1a 100644 --- a/app/src/main/res/transition/change_bounds.xml +++ b/app/src/main/res/transition/change_bounds.xml @@ -1,3 +1,7 @@ - \ No newline at end of file + + + + + + \ No newline at end of file From 9552e617b51f63b2f479c5aa7de789a39a396880 Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Wed, 12 Aug 2020 03:50:22 +0530 Subject: [PATCH 14/20] code refactor --- .../retromusic/activities/LyricsActivity.kt | 1 - .../activities/SupportDevelopmentActivity.kt | 1 - .../base/AbsSlidingMusicPanelActivity.kt | 1 + .../retromusic/extensions/FragmentExt.kt | 5 +- .../fragments/albums/AlbumDetailsFragment.kt | 144 ++++++++++++++++-- .../artists/ArtistDetailsFragment.kt | 73 +++++++-- .../fragments/base/AbsPlayerFragment.kt | 2 + .../fragments/genres/GenreDetailsFragment.kt | 26 +++- .../playlists/PlaylistDetailsFragment.kt | 32 ++-- .../retromusic/helper/menu/GenreMenuHelper.kt | 5 +- .../monkey/retromusic/lyrics/LrcView.java | 9 +- .../monkey/retromusic/model/CategoryInfo.java | 2 +- app/src/main/res/drawable/ic_baseline.xml | 5 - app/src/main/res/drawable/ic_car.xml | 5 + app/src/main/res/drawable/ic_face.xml | 10 ++ .../layout-land/activity_album_details.xml | 141 ----------------- .../layout-land/activity_artist_details.xml | 119 --------------- .../layout-land/fragment_album_details.xml | 2 +- .../layout-land/fragment_artist_details.xml | 2 +- ...content.xml => fragment_about_content.xml} | 0 app/src/main/res/layout/activity_about.xml | 49 ------ .../res/layout/activity_album_details.xml | 141 ----------------- .../res/layout/activity_artist_details.xml | 126 --------------- .../main/res/layout/activity_drive_mode.xml | 20 ++- app/src/main/res/layout/activity_lyrics.xml | 36 +++-- .../res/layout/activity_playlist_detail.xml | 87 ----------- app/src/main/res/layout/activity_search.xml | 128 ---------------- app/src/main/res/layout/activity_settings.xml | 52 ------- app/src/main/res/layout/fragment_about.xml | 2 +- ...content.xml => fragment_about_content.xml} | 0 ...content.xml => fragment_album_content.xml} | 0 .../res/layout/fragment_album_details.xml | 4 +- ...ontent.xml => fragment_artist_content.xml} | 0 .../res/layout/fragment_artist_details.xml | 4 +- app/src/main/res/navigation/main_graph.xml | 2 +- app/src/main/res/values/arrays.xml | 4 +- 36 files changed, 309 insertions(+), 931 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_baseline.xml create mode 100644 app/src/main/res/drawable/ic_car.xml create mode 100644 app/src/main/res/drawable/ic_face.xml delete mode 100644 app/src/main/res/layout-land/activity_album_details.xml delete mode 100644 app/src/main/res/layout-land/activity_artist_details.xml rename app/src/main/res/layout-xlarge/{activity_about_content.xml => fragment_about_content.xml} (100%) delete mode 100644 app/src/main/res/layout/activity_about.xml delete mode 100755 app/src/main/res/layout/activity_album_details.xml delete mode 100755 app/src/main/res/layout/activity_artist_details.xml delete mode 100644 app/src/main/res/layout/activity_playlist_detail.xml delete mode 100755 app/src/main/res/layout/activity_search.xml delete mode 100755 app/src/main/res/layout/activity_settings.xml rename app/src/main/res/layout/{activity_about_content.xml => fragment_about_content.xml} (100%) rename app/src/main/res/layout/{activity_album_content.xml => fragment_album_content.xml} (100%) rename app/src/main/res/layout/{activity_artist_content.xml => fragment_artist_content.xml} (100%) diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt index 76bc76652..2c64c8a31 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt @@ -20,7 +20,6 @@ import kotlinx.android.synthetic.main.activity_lyrics.* class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.Callback { private lateinit var updateHelper: MusicProgressViewUpdateHelper - private lateinit var song: Song private val googleSearchLrcUrl: String diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt index 56cbe177e..66bd0aa8e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt @@ -28,7 +28,6 @@ import code.name.monkey.retromusic.extensions.textColorSecondary import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.SkuDetails import com.anjlab.android.iab.v3.TransactionDetails -import kotlinx.android.synthetic.main.activity_about.toolbar import kotlinx.android.synthetic.main.activity_donation.* import java.lang.ref.WeakReference import java.util.* diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt index 3a7ff7e2d..b14b44bd5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt @@ -120,6 +120,7 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { fun collapsePanel() { behavior.state = BottomSheetBehavior.STATE_COLLAPSED + setMiniPlayerAlphaProgress(0f) } fun expandPanel() { diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt index 7a6086cc7..8f53f469a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt @@ -51,10 +51,9 @@ val FragmentManager.currentNavigationFragment: Fragment? get() = primaryNavigationFragment?.childFragmentManager?.fragments?.first() fun AppCompatActivity.currentFragment(navHostId: Int): Fragment? { - val navHostFragment: NavHostFragment = - supportFragmentManager.findFragmentById(navHostId) as NavHostFragment + val navHostFragment: NavHostFragment = supportFragmentManager.findFragmentById(navHostId) as NavHostFragment navHostFragment.targetFragment - return navHostFragment?.childFragmentManager?.fragments?.first() + return navHostFragment.childFragmentManager.fragments.first() } @Suppress("UNCHECKED_CAST") diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt index 66f5a231b..c8398f63f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt @@ -1,8 +1,9 @@ package code.name.monkey.retromusic.fragments.albums +import android.app.ActivityOptions +import android.content.Intent import android.os.Bundle -import android.view.MenuItem -import android.view.View +import android.view.* import androidx.appcompat.app.AppCompatActivity import androidx.core.os.bundleOf import androidx.lifecycle.Observer @@ -12,10 +13,16 @@ import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager +import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity +import code.name.monkey.retromusic.activities.tageditor.AlbumTagEditorActivity import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter +import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog +import code.name.monkey.retromusic.dialogs.DeleteSongsDialog import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment @@ -23,6 +30,7 @@ import code.name.monkey.retromusic.glide.AlbumGlideRequest import code.name.monkey.retromusic.glide.ArtistGlideRequest import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.network.model.LastFmAlbum @@ -31,27 +39,29 @@ import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.activity_album_content.* -import kotlinx.android.synthetic.main.activity_album_details.* +import kotlinx.android.synthetic.main.fragment_album_content.* +import kotlinx.android.synthetic.main.fragment_album_details.* import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.util.* class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_details), AlbumClickListener { + + private val arguments by navArgs() + private val detailsViewModel by viewModel { + parametersOf(arguments.extraAlbumId) + } + private lateinit var simpleSongAdapter: SimpleSongAdapter private lateinit var album: Album - private val args: AlbumDetailsFragmentArgs by navArgs() private val savedSortOrder: String get() = PreferenceUtil.albumDetailSongSortOrder - private val detailsViewModel by viewModel { - parametersOf(args.extraAlbumId) - } - override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) mainActivity.setSupportActionBar(toolbar) mainActivity.setBottomBarVisibility(View.GONE) toolbar.title = null @@ -107,12 +117,6 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det playerActivity?.removeMusicServiceEventListener(detailsViewModel) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> findNavController().navigateUp() - } - return super.onOptionsItemSelected(item) - } private fun setupRecyclerView() { simpleSongAdapter = SimpleSongAdapter( @@ -239,4 +243,114 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det bundleOf("extra_album_id" to albumId) ) } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.menu_album_detail, menu) + val sortOrder = menu.findItem(R.id.action_sort_order) + setUpSortOrderMenu(sortOrder.subMenu) + ToolbarContentTintHelper.handleOnCreateOptionsMenu( + requireContext(), + toolbar, + menu, + getToolbarBackgroundColor(toolbar) + ) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return handleSortOrderMenuItem(item) + } + + private fun handleSortOrderMenuItem(item: MenuItem): Boolean { + var sortOrder: String? = null + val songs = simpleSongAdapter.dataSet + when (item.itemId) { + R.id.action_play_next -> { + MusicPlayerRemote.playNext(songs) + return true + } + R.id.action_add_to_current_playing -> { + MusicPlayerRemote.enqueue(songs) + return true + } + R.id.action_add_to_playlist -> { + AddToPlaylistDialog.create(songs).show(childFragmentManager, "ADD_PLAYLIST") + return true + } + R.id.action_delete_from_device -> { + DeleteSongsDialog.create(songs).show(childFragmentManager, "DELETE_SONGS") + return true + } + R.id.action_tag_editor -> { + val intent = Intent(requireContext(), AlbumTagEditorActivity::class.java) + intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id) + val options = ActivityOptions.makeSceneTransitionAnimation( + requireActivity(), + albumCoverContainer, + "${getString(R.string.transition_album_art)}_${album.id}" + ) + startActivityForResult( + intent, + TAG_EDITOR_REQUEST, options.toBundle() + ) + return true + } + /*Sort*/ + R.id.action_sort_order_title -> sortOrder = SortOrder.AlbumSongSortOrder.SONG_A_Z + R.id.action_sort_order_title_desc -> sortOrder = SortOrder.AlbumSongSortOrder.SONG_Z_A + R.id.action_sort_order_track_list -> sortOrder = + SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST + R.id.action_sort_order_artist_song_duration -> + sortOrder = SortOrder.AlbumSongSortOrder.SONG_DURATION + } + if (sortOrder != null) { + item.isChecked = true + setSaveSortOrder(sortOrder) + } + return true + } + + private fun setUpSortOrderMenu(sortOrder: SubMenu) { + when (savedSortOrder) { + SortOrder.AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title) + .isChecked = true + SortOrder.AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc) + .isChecked = true + SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list) + .isChecked = true + SortOrder.AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration) + .isChecked = true + } + } + + private fun setSaveSortOrder(sortOrder: String) { + PreferenceUtil.albumDetailSongSortOrder = sortOrder + when (sortOrder) { + SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 -> + o1.trackNumber.compareTo( + o2.trackNumber + ) + }) + SortOrder.AlbumSongSortOrder.SONG_A_Z -> album.songs?.sortWith(Comparator { o1, o2 -> + o1.title.compareTo( + o2.title + ) + }) + SortOrder.AlbumSongSortOrder.SONG_Z_A -> album.songs?.sortWith(Comparator { o1, o2 -> + o2.title.compareTo( + o1.title + ) + }) + SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 -> + o1.duration.compareTo( + o2.duration + ) + }) + } + album.songs?.let { simpleSongAdapter.swapDataSet(it) } + } + + companion object { + const val TAG_EDITOR_REQUEST = 9002 + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt index d834ae31f..38dec7c6a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt @@ -1,23 +1,28 @@ package code.name.monkey.retromusic.fragments.artists +import android.content.Intent import android.os.Bundle import android.text.Spanned +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem import android.view.View import androidx.core.os.bundleOf import androidx.core.text.HtmlCompat import androidx.lifecycle.Observer import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager -import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter +import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.extensions.applyColor -import code.name.monkey.retromusic.extensions.extraNotNull import code.name.monkey.retromusic.extensions.show +import code.name.monkey.retromusic.extensions.showToast import code.name.monkey.retromusic.fragments.albums.AlbumClickListener import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.glide.ArtistGlideRequest @@ -25,12 +30,13 @@ import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.network.model.LastFmArtist +import code.name.monkey.retromusic.util.CustomArtistImageUtil import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.activity_artist_content.* -import kotlinx.android.synthetic.main.activity_artist_details.* +import kotlinx.android.synthetic.main.fragment_artist_content.* +import kotlinx.android.synthetic.main.fragment_artist_details.* import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.util.* @@ -38,20 +44,21 @@ import kotlin.collections.ArrayList class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_details), AlbumClickListener { + private val arguments by navArgs() + private val detailsViewModel: ArtistDetailsViewModel by viewModel { + parametersOf(arguments.extraArtistId) + } - private var biography: Spanned? = null private lateinit var artist: Artist private lateinit var songAdapter: SimpleSongAdapter private lateinit var albumAdapter: HorizontalAlbumAdapter private var forceDownload: Boolean = false private var lang: String? = null - - private val detailsViewModel: ArtistDetailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_ARTIST_ID).value) - } + private var biography: Spanned? = null override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) mainActivity.setSupportActionBar(toolbar) mainActivity.setBottomBarVisibility(View.GONE) toolbar.title = null @@ -188,4 +195,52 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d ) ) } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return handleSortOrderMenuItem(item) + } + + private fun handleSortOrderMenuItem(item: MenuItem): Boolean { + val songs = artist.songs + when (item.itemId) { + android.R.id.home -> findNavController().navigateUp() + R.id.action_play_next -> { + MusicPlayerRemote.playNext(songs) + return true + } + R.id.action_add_to_current_playing -> { + MusicPlayerRemote.enqueue(songs) + return true + } + R.id.action_add_to_playlist -> { + AddToPlaylistDialog.create(songs).show(childFragmentManager, "ADD_PLAYLIST") + return true + } + R.id.action_set_artist_image -> { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "image/*" + startActivityForResult( + Intent.createChooser(intent, getString(R.string.pick_from_local_storage)), + REQUEST_CODE_SELECT_IMAGE + ) + return true + } + R.id.action_reset_artist_image -> { + showToast(resources.getString(R.string.updating)) + CustomArtistImageUtil.getInstance(requireContext()).resetCustomArtistImage(artist) + forceDownload = true + return true + } + } + return true + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.menu_artist_detail, menu) + } + + companion object { + const val REQUEST_CODE_SELECT_IMAGE = 9002 + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt index 62089b49f..f37423fe2 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt @@ -87,6 +87,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme return true } R.id.action_go_to_album -> { + mainActivity.collapsePanel() requireActivity().findNavController(R.id.fragment_container).navigate( R.id.albumDetailsFragment, bundleOf(EXTRA_ALBUM_ID to song.albumId) @@ -94,6 +95,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme return true } R.id.action_go_to_artist -> { + mainActivity.collapsePanel() requireActivity().findNavController(R.id.fragment_container).navigate( R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to song.artistId) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt index bb9fa8fb2..0cbeed7ba 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt @@ -1,16 +1,19 @@ package code.name.monkey.retromusic.fragments.genres import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem import android.view.View +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import code.name.monkey.retromusic.EXTRA_GENRE import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.extensions.dipToPix -import code.name.monkey.retromusic.extensions.extraNotNull import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment +import code.name.monkey.retromusic.helper.menu.GenreMenuHelper import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Song import kotlinx.android.synthetic.main.fragment_playlist_detail.* @@ -19,16 +22,20 @@ import org.koin.core.parameter.parametersOf import java.util.* class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail) { + private val arguments by navArgs() private val detailsViewModel: GenreDetailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_GENRE).value) + parametersOf(arguments.extraGenre) } private lateinit var genre: Genre private lateinit var songAdapter: SongAdapter - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) mainActivity.addMusicServiceEventListener(detailsViewModel) mainActivity.setSupportActionBar(toolbar) + mainActivity.setBottomBarVisibility(View.GONE) setupRecyclerView() detailsViewModel.getSongs().observe(viewLifecycleOwner, androidx.lifecycle.Observer { @@ -73,4 +80,13 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_ val height = dipToPix(52f).toInt() recyclerView.setPadding(0, 0, 0, height) } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.menu_genre_detail, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return GenreMenuHelper.handleMenuClick(requireActivity(), genre, item) + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt index 872128e6f..474df7dd4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt @@ -3,17 +3,18 @@ package code.name.monkey.retromusic.fragments.playlists import android.os.Bundle import android.view.Menu import android.view.MenuInflater +import android.view.MenuItem import android.view.View import androidx.lifecycle.Observer +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import code.name.monkey.retromusic.EXTRA_PLAYLIST import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.song.OrderablePlaylistSongAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter import code.name.monkey.retromusic.extensions.dipToPix -import code.name.monkey.retromusic.extensions.extraNotNull import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment +import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper import code.name.monkey.retromusic.model.AbsCustomPlaylist import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Song @@ -26,21 +27,25 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail) { + private val arguments by navArgs() private val viewModel: PlaylistDetailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_PLAYLIST).value) + parametersOf(arguments.extraPlaylist) } + private lateinit var playlist: Playlist private lateinit var adapter: SongAdapter private var wrappedAdapter: RecyclerView.Adapter<*>? = null private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) mainActivity.addMusicServiceEventListener(viewModel) mainActivity.setSupportActionBar(toolbar) + mainActivity.setBottomBarVisibility(View.GONE) - playlist = extraNotNull(EXTRA_PLAYLIST).value + playlist = arguments.extraPlaylist setUpRecyclerView() @@ -52,7 +57,6 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli playlist = it toolbar.title = it.name }) - } private fun setUpRecyclerView() { @@ -99,12 +103,15 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) - inflater.inflate( - if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail - else R.menu.menu_playlist_detail, menu - ) + val menuRes = if (playlist is AbsCustomPlaylist) + R.menu.menu_smart_playlist_detail + else R.menu.menu_playlist_detail + inflater.inflate(menuRes, menu) } + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return PlaylistMenuHelper.handleMenuClick(requireActivity(), playlist, item) + } private fun checkForPadding() { val height = dipToPix(52f) @@ -147,7 +154,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli super.onDestroy() } - fun showEmptyView() { + private fun showEmptyView() { empty.visibility = View.VISIBLE emptyText.visibility = View.VISIBLE } @@ -159,4 +166,5 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli showEmptyView() } } + } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt index af790d95e..b6238a85d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt @@ -16,8 +16,7 @@ package code.name.monkey.retromusic.helper.menu import android.app.Activity import android.view.MenuItem -import androidx.appcompat.app.AppCompatActivity - +import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.R import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -27,7 +26,7 @@ import code.name.monkey.retromusic.model.Song import java.util.* object GenreMenuHelper { - fun handleMenuClick(activity: AppCompatActivity, genre: Genre, item: MenuItem): Boolean { + fun handleMenuClick(activity: FragmentActivity, genre: Genre, item: MenuItem): Boolean { when (item.itemId) { R.id.action_play -> { MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true) diff --git a/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.java b/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.java index 707ad20a7..abd4d750b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.java +++ b/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.java @@ -89,11 +89,6 @@ public class LrcView extends View { } } }; - - public LrcView(Context context) { - this(context, null); - } - /** * 手势监听器 */ @@ -151,6 +146,10 @@ public class LrcView extends View { } }; + public LrcView(Context context) { + this(context, null); + } + public LrcView(Context context, AttributeSet attrs) { this(context, attrs, 0); } diff --git a/app/src/main/java/code/name/monkey/retromusic/model/CategoryInfo.java b/app/src/main/java/code/name/monkey/retromusic/model/CategoryInfo.java index ce7037905..f8689439a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/CategoryInfo.java +++ b/app/src/main/java/code/name/monkey/retromusic/model/CategoryInfo.java @@ -59,7 +59,7 @@ public class CategoryInfo implements Parcelable { } public enum Category { - Home(R.id.action_home, R.string.for_you, R.drawable.ic_baseline), + Home(R.id.action_home, R.string.for_you, R.drawable.ic_face), Songs(R.id.action_song, R.string.songs, R.drawable.ic_audiotrack), Albums(R.id.action_album, R.string.albums, R.drawable.ic_album), Artists(R.id.action_artist, R.string.artists, R.drawable.ic_artist), diff --git a/app/src/main/res/drawable/ic_baseline.xml b/app/src/main/res/drawable/ic_baseline.xml deleted file mode 100644 index ef34eabe6..000000000 --- a/app/src/main/res/drawable/ic_baseline.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_car.xml b/app/src/main/res/drawable/ic_car.xml new file mode 100644 index 000000000..e3839d9ba --- /dev/null +++ b/app/src/main/res/drawable/ic_car.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_face.xml b/app/src/main/res/drawable/ic_face.xml new file mode 100644 index 000000000..602d3dc1d --- /dev/null +++ b/app/src/main/res/drawable/ic_face.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout-land/activity_album_details.xml b/app/src/main/res/layout-land/activity_album_details.xml deleted file mode 100644 index 3376fb29b..000000000 --- a/app/src/main/res/layout-land/activity_album_details.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-land/activity_artist_details.xml b/app/src/main/res/layout-land/activity_artist_details.xml deleted file mode 100644 index c697c814c..000000000 --- a/app/src/main/res/layout-land/activity_artist_details.xml +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-land/fragment_album_details.xml b/app/src/main/res/layout-land/fragment_album_details.xml index 32d4c7f69..4b4d503e8 100644 --- a/app/src/main/res/layout-land/fragment_album_details.xml +++ b/app/src/main/res/layout-land/fragment_album_details.xml @@ -133,7 +133,7 @@ - + diff --git a/app/src/main/res/layout-land/fragment_artist_details.xml b/app/src/main/res/layout-land/fragment_artist_details.xml index c697c814c..2a0cae060 100644 --- a/app/src/main/res/layout-land/fragment_artist_details.xml +++ b/app/src/main/res/layout-land/fragment_artist_details.xml @@ -112,7 +112,7 @@ - + diff --git a/app/src/main/res/layout-xlarge/activity_about_content.xml b/app/src/main/res/layout-xlarge/fragment_about_content.xml similarity index 100% rename from app/src/main/res/layout-xlarge/activity_about_content.xml rename to app/src/main/res/layout-xlarge/fragment_about_content.xml diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml deleted file mode 100644 index 6fb10ecd8..000000000 --- a/app/src/main/res/layout/activity_about.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_album_details.xml b/app/src/main/res/layout/activity_album_details.xml deleted file mode 100755 index 473a20eed..000000000 --- a/app/src/main/res/layout/activity_album_details.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_artist_details.xml b/app/src/main/res/layout/activity_artist_details.xml deleted file mode 100755 index a222a3360..000000000 --- a/app/src/main/res/layout/activity_artist_details.xml +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_drive_mode.xml b/app/src/main/res/layout/activity_drive_mode.xml index e7eb13d8d..5e2c17a2e 100644 --- a/app/src/main/res/layout/activity_drive_mode.xml +++ b/app/src/main/res/layout/activity_drive_mode.xml @@ -50,7 +50,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - + + @@ -161,10 +173,10 @@ android:id="@+id/songText" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="4dp" + android:layout_marginTop="8dp" android:gravity="center" android:maxLines="3" - android:textAppearance="@style/TextViewHeadline6" + android:textAppearance="@style/TextViewHeadline5" android:textColor="@color/md_grey_200" tools:text="@tools:sample/lorem/random" /> diff --git a/app/src/main/res/layout/activity_lyrics.xml b/app/src/main/res/layout/activity_lyrics.xml index d95ba4424..ee6c03823 100644 --- a/app/src/main/res/layout/activity_lyrics.xml +++ b/app/src/main/res/layout/activity_lyrics.xml @@ -6,10 +6,30 @@ android:layout_height="match_parent" android:transitionName="@string/transition_lyrics"> - + + + android:layout_gravity="bottom" + android:padding="0dp" + app:contentInsetLeft="0dp" + app:contentInsetStart="0dp" + app:contentInsetStartWithNavigation="0dp" + app:hideOnScroll="true" + app:titleMargin="0dp" + app:titleMarginStart="0dp" + tools:backgroundTint="@color/md_red_400"> - - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_playlist_detail.xml b/app/src/main/res/layout/activity_playlist_detail.xml deleted file mode 100644 index af405033a..000000000 --- a/app/src/main/res/layout/activity_playlist_detail.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml deleted file mode 100755 index 6d0b4087c..000000000 --- a/app/src/main/res/layout/activity_search.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml deleted file mode 100755 index 4e115feb5..000000000 --- a/app/src/main/res/layout/activity_settings.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml index 33efb3af8..d64fb15ad 100644 --- a/app/src/main/res/layout/fragment_about.xml +++ b/app/src/main/res/layout/fragment_about.xml @@ -7,5 +7,5 @@ android:overScrollMode="never" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about_content.xml b/app/src/main/res/layout/fragment_about_content.xml similarity index 100% rename from app/src/main/res/layout/activity_about_content.xml rename to app/src/main/res/layout/fragment_about_content.xml diff --git a/app/src/main/res/layout/activity_album_content.xml b/app/src/main/res/layout/fragment_album_content.xml similarity index 100% rename from app/src/main/res/layout/activity_album_content.xml rename to app/src/main/res/layout/fragment_album_content.xml diff --git a/app/src/main/res/layout/fragment_album_details.xml b/app/src/main/res/layout/fragment_album_details.xml index 8a24f5e15..f84cb563a 100644 --- a/app/src/main/res/layout/fragment_album_details.xml +++ b/app/src/main/res/layout/fragment_album_details.xml @@ -97,7 +97,7 @@ android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:maxLines="3" - android:textAppearance="@style/TextViewHeadline4" + android:textAppearance="@style/TextViewHeadline5" android:textColor="?android:attr/textColorPrimary" android:textStyle="bold" app:layout_constrainedWidth="true" @@ -126,7 +126,7 @@ tools:text="@tools:sample/lorem/random" /> + tools:layout="@layout/fragment_album_details"> diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 6f0c93878..8d29c0632 100755 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -125,9 +125,7 @@ this_year - - @layout/activity_album_details - + System default Afrikaans From 23f4fee8728b2e4f3a3d69a0116ab4d3b850d225 Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Thu, 13 Aug 2020 13:54:36 +0530 Subject: [PATCH 15/20] Android Navigation code refactor --- app/release/output-metadata.json | 2 +- app/src/main/AndroidManifest.xml | 4 + app/src/main/ic_launcher-playstore.png | Bin 13284 -> 17720 bytes .../name/monkey/retromusic/HomeSection.kt | 25 ++ .../code/name/monkey/retromusic/Result.kt | 25 ++ .../retromusic/activities/MainActivity.kt | 58 +---- .../activities/PlayingQueueActivity.kt | 3 +- .../retromusic/activities/SettingsActivity.kt | 61 +++++ .../retromusic/activities/UserInfoActivity.kt | 3 +- .../base/AbsSlidingMusicPanelActivity.kt | 34 ++- .../monkey/retromusic/adapter/HomeAdapter.kt | 242 ++++++++---------- .../adapter/artist/ArtistAdapter.kt | 19 +- .../retromusic/adapter/song/SongAdapter.kt | 1 - .../monkey/retromusic/extensions/ColorExt.kt | 13 + .../fragments/DetailListFragment.kt | 130 ++++++++++ .../retromusic/fragments/LibraryViewModel.kt | 72 +++--- .../fragments/about/AboutFragment.kt | 4 +- .../fragments/albums/AlbumDetailsFragment.kt | 8 +- .../fragments/albums/AlbumsFragment.kt | 20 +- .../artists/ArtistDetailsFragment.kt | 9 +- .../fragments/artists/ArtistsFragment.kt | 19 +- .../fragments/base/AbsRecyclerViewFragment.kt | 21 +- .../fragments/genres/GenreDetailsFragment.kt | 2 +- .../fragments/genres/GenresFragment.kt | 14 +- .../retromusic/fragments/home/HomeFragment.kt | 12 +- .../fragments/library/LibraryFragment.kt | 24 +- .../playlists/PlaylistDetailsFragment.kt | 2 +- .../fragments/playlists/PlaylistsFragment.kt | 9 +- .../fragments/search/SearchFragment.kt | 2 +- .../fragments/settings/SettingsFragment.kt | 23 -- .../fragments/songs/SongsFragment.kt | 5 +- .../code/name/monkey/retromusic/model/Home.kt | 9 +- .../retromusic/providers/RepositoryImpl.kt | 239 +++++++++++------ .../providers/interfaces/Repository.kt | 40 ++- .../views/BottomNavigationBarTinted.kt | 2 +- app/src/main/res/font/pacifico.xml | 7 + .../main/res/layout/activity_main_content.xml | 29 ++- app/src/main/res/layout/activity_settings.xml | 51 ++++ .../res/layout/fragment_album_details.xml | 227 ++++++++-------- .../res/layout/fragment_artist_details.xml | 197 +++++++------- app/src/main/res/layout/fragment_library.xml | 83 +++--- .../res/layout/fragment_main_settings.xml | 10 +- .../res/layout/fragment_playlist_detail.xml | 119 ++++----- app/src/main/res/layout/item_album_card.xml | 41 +-- app/src/main/res/layout/item_grid_genre.xml | 2 +- app/src/main/res/layout/item_image.xml | 4 +- app/src/main/res/layout/item_suggestions.xml | 24 +- .../layout/metal_section_recycler_view.xml | 20 +- .../main/res/layout/section_recycler_view.xml | 54 ++-- .../res/layout/sliding_music_panel_layout.xml | 1 - app/src/main/res/menu/menu_main.xml | 5 - .../res/mipmap-anydpi-v26/ic_launcher.xml | 4 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 4 +- app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3766 -> 3512 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 2606 -> 2022 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 3766 -> 3512 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2344 -> 2186 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 1655 -> 1315 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2344 -> 2186 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 5134 -> 5055 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 3889 -> 2713 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 5134 -> 5055 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 8055 -> 8041 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 8242 -> 4727 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 8055 -> 8041 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 11445 -> 11693 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 14697 -> 6513 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 11445 -> 11693 bytes app/src/main/res/navigation/library_graph.xml | 5 +- app/src/main/res/navigation/main_graph.xml | 21 +- .../main/res/navigation/settings_graph.xml | 7 +- app/src/main/res/transition/change_bounds.xml | 11 +- .../res/transition/grid_exit_transition.xml | 27 ++ app/src/main/res/values-night-v27/styles.xml | 4 - app/src/main/res/values-night/styles.xml | 3 - app/src/main/res/values-v23/styles.xml | 1 - .../main/res/values-v27/styles_parents.xml | 6 +- app/src/main/res/values/font_certs.xml | 17 ++ app/src/main/res/values/preloaded_fonts.xml | 6 + app/src/main/res/values/styles.xml | 4 + app/src/main/res/values/styles_parents.xml | 4 +- 81 files changed, 1235 insertions(+), 919 deletions(-) create mode 100644 app/src/main/java/code/name/monkey/retromusic/HomeSection.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/Result.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt delete mode 100644 app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt create mode 100644 app/src/main/res/font/pacifico.xml create mode 100644 app/src/main/res/layout/activity_settings.xml create mode 100644 app/src/main/res/transition/grid_exit_transition.xml create mode 100644 app/src/main/res/values/font_certs.xml create mode 100644 app/src/main/res/values/preloaded_fonts.xml diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index c3bff6d72..b45c7e165 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -12,7 +12,7 @@ "filters": [], "properties": [], "versionCode": 10438, - "versionName": "3.5.650_0810", + "versionName": "3.5.650_0812", "enabled": true, "outputFile": "app-release.apk" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 16a51f6b9..bb4f033df 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -106,6 +106,7 @@ + @@ -257,5 +258,8 @@ + diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png index 991844b5e6025daa453157bd7b0435bed646e7e1..6f15eaae49052c4b4f204b68d1bd6d307512dc88 100644 GIT binary patch literal 17720 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

;h^Zm>#c8^|^(N2V!rl;fusX8o%to(Hjd!**O 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 L9nSg1{nq~gTb%NZ diff --git a/app/src/main/java/code/name/monkey/retromusic/HomeSection.kt b/app/src/main/java/code/name/monkey/retromusic/HomeSection.kt new file mode 100644 index 000000000..80574b8a5 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/HomeSection.kt @@ -0,0 +1,25 @@ +package code.name.monkey.retromusic + +import androidx.annotation.IntDef + +@IntDef( + RECENT_ALBUMS, + TOP_ALBUMS, + RECENT_ARTISTS, + TOP_ARTISTS, + SUGGESTIONS, + FAVOURITES, + GENRES, + PLAYLISTS +) +@Retention(AnnotationRetention.SOURCE) +annotation class HomeSection + +const val RECENT_ALBUMS = 3 +const val TOP_ALBUMS = 1 +const val RECENT_ARTISTS = 2 +const val TOP_ARTISTS = 0 +const val SUGGESTIONS = 5 +const val FAVOURITES = 4 +const val GENRES = 6 +const val PLAYLISTS = 7 \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/Result.kt b/app/src/main/java/code/name/monkey/retromusic/Result.kt new file mode 100644 index 000000000..4c241a143 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/Result.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 Fatih Giris. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package code.name.monkey.retromusic + +/** + * Generic class that holds the network state + */ +sealed class Result { + data class Success(val data: T) : Result() + object Loading : Result() + object Error : Result() +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt index 655777580..a9fbbee69 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt @@ -2,16 +2,13 @@ package code.name.monkey.retromusic.activities import android.content.Intent import android.content.SharedPreferences +import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.os.Bundle import android.provider.MediaStore import android.util.Log import android.view.View -import code.name.monkey.appthemehelper.ThemeStore -import code.name.monkey.appthemehelper.util.ATHUtil -import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.retromusic.* import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity -import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager import code.name.monkey.retromusic.extensions.findNavController import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.helper.MusicPlayerRemote.openAndShuffleQueue @@ -19,7 +16,6 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote.openQueue import code.name.monkey.retromusic.helper.MusicPlayerRemote.playFromUri import code.name.monkey.retromusic.helper.MusicPlayerRemote.shuffleMode import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs -import code.name.monkey.retromusic.interfaces.CabHolder import code.name.monkey.retromusic.loaders.AlbumLoader.getAlbum import code.name.monkey.retromusic.loaders.ArtistLoader.getArtist import code.name.monkey.retromusic.loaders.PlaylistSongsLoader.getPlaylistSongList @@ -27,15 +23,10 @@ import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.AppRater.appLaunched import code.name.monkey.retromusic.util.PreferenceUtil -import code.name.monkey.retromusic.util.RetroColorUtil -import com.afollestad.materialcab.MaterialCab -import com.afollestad.materialdialogs.color.ColorChooserDialog import org.koin.android.ext.android.inject import java.util.* -class MainActivity : AbsSlidingMusicPanelActivity(), - SharedPreferences.OnSharedPreferenceChangeListener, CabHolder, - ColorChooserDialog.ColorCallback { +class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeListener { companion object { const val TAG = "MainActivity" const val EXPAND_PANEL = "expand_panel" @@ -44,7 +35,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), private val libraryViewModel: LibraryViewModel by inject() - private var cab: MaterialCab? = null + private var blockRequestPermissions = false override fun createContentView(): View { @@ -59,7 +50,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), setLightNavigationBar(true) setTaskDescriptionColorAuto() hideStatusBar() - setBottomBarVisibility(View.VISIBLE) appLaunched(this) addMusicServiceEventListener(libraryViewModel) updateTabs() @@ -177,46 +167,4 @@ class MainActivity : AbsSlidingMusicPanelActivity(), } return id } - - override fun handleBackPress(): Boolean { - if (cab != null && cab!!.isActive) { - cab?.finish() - return true - } - return super.handleBackPress() - } - - override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { - cab?.let { - if (it.isActive) it.finish() - } - cab = MaterialCab(this, R.id.cab_stub) - .setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor( - RetroColorUtil.shiftBackgroundColorForLightText( - ATHUtil.resolveColor( - this, - R.attr.colorSurface - ) - ) - ) - .start(callback) - return cab as MaterialCab - } - - override fun onColorSelection(dialog: ColorChooserDialog, selectedColor: Int) { - when (dialog.title) { - R.string.accent_color -> { - ThemeStore.editTheme(this).accentColor(selectedColor).commit() - if (VersionUtils.hasNougatMR()) - DynamicShortcutManager(this).updateDynamicShortcuts() - } - } - recreate() - } - - override fun onColorChooserDismissed(dialog: ColorChooserDialog) { - - } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt index ef9ffbe31..e15d4e154 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt @@ -14,6 +14,7 @@ import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.ThemedFastScroller import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager @@ -103,7 +104,7 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() { } } }) - //ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView) + val fastScroller = ThemedFastScroller.create(recyclerView) } private fun checkForPadding() { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt new file mode 100644 index 000000000..2e1384528 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt @@ -0,0 +1,61 @@ +package code.name.monkey.retromusic.activities + +import android.os.Bundle +import android.view.MenuItem +import androidx.navigation.NavController +import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.appthemehelper.util.VersionUtils +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.activities.base.AbsBaseActivity +import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager +import code.name.monkey.retromusic.extensions.applyToolbar +import code.name.monkey.retromusic.extensions.findNavController +import com.afollestad.materialdialogs.color.ColorChooserDialog +import kotlinx.android.synthetic.main.activity_settings.* + +class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback { + override fun onCreate(savedInstanceState: Bundle?) { + setDrawUnderStatusBar() + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_settings) + setStatusbarColorAuto() + setNavigationbarColorAuto() + setLightNavigationBar(true) + setupToolbar() + } + + private fun setupToolbar() { + setTitle(R.string.action_settings) + applyToolbar(toolbar) + val navController: NavController = findNavController(R.id.contentFrame) + navController.addOnDestinationChangedListener { _, _, _ -> + toolbar.title = navController.currentDestination?.label + } + } + + override fun onSupportNavigateUp(): Boolean { + return findNavController(R.id.contentFrame).navigateUp() || super.onSupportNavigateUp() + } + + override fun onColorSelection(dialog: ColorChooserDialog, selectedColor: Int) { + when (dialog.title) { + R.string.accent_color -> { + ThemeStore.editTheme(this).accentColor(selectedColor).commit() + if (VersionUtils.hasNougatMR()) + DynamicShortcutManager(this).updateDynamicShortcuts() + } + } + recreate() + } + + override fun onColorChooserDismissed(dialog: ColorChooserDialog) { + + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + onBackPressed() + } + return super.onOptionsItemSelected(item) + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt index 1bcd78ef2..904f48ff3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt @@ -10,7 +10,6 @@ import android.text.TextUtils import android.view.MenuItem import android.widget.Toast import code.name.monkey.appthemehelper.util.ColorUtil -import code.name.monkey.appthemehelper.util.MaterialUtil import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.retromusic.Constants.USER_BANNER import code.name.monkey.retromusic.Constants.USER_PROFILE @@ -49,7 +48,7 @@ class UserInfoActivity : AbsBaseActivity() { setLightNavigationBar(true) applyToolbar(toolbar) - MaterialUtil.setTint(nameContainer, false) + nameContainer.accentColor() name.setText(PreferenceUtil.userName) userImage.setOnClickListener { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt index b14b44bd5..13b0727e9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt @@ -7,12 +7,17 @@ import android.view.ViewGroup import android.view.ViewTreeObserver import android.widget.FrameLayout import androidx.annotation.LayoutRes +import androidx.core.view.ViewCompat +import androidx.core.view.isVisible import androidx.lifecycle.Observer import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.retromusic.R import code.name.monkey.retromusic.RetroBottomSheetBehavior -import code.name.monkey.retromusic.extensions.* +import code.name.monkey.retromusic.extensions.dimToPixel +import code.name.monkey.retromusic.extensions.hide +import code.name.monkey.retromusic.extensions.show +import code.name.monkey.retromusic.extensions.whichFragment import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.MiniPlayerFragment import code.name.monkey.retromusic.fragments.NowPlayingScreen @@ -132,9 +137,7 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { if (miniPlayerFragment?.view == null) return val alpha = 1 - progress miniPlayerFragment?.view?.alpha = alpha - // necessary to make the views below clickable miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE - bottomNavigationView.translationY = progress * 500 bottomNavigationView.alpha = alpha } @@ -171,32 +174,25 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { return bottomNavigationView } - fun setBottomBarVisibility(visible: Int) { - bottomNavigationView.visibility = visible + fun hideBottomBarVisibility(visible: Boolean) { + bottomNavigationView.isVisible = visible hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty()) } private fun hideBottomBar(hide: Boolean) { val heightOfBar = dimToPixel(R.dimen.mini_player_height) - val heightOfBarWithTabs = dimToPixel(R.dimen.mini_player_height_expanded) + val isBottomBarVisible = bottomNavigationView.isVisible if (hide) { behavior.isHideable = true behavior.peekHeight = 0 - bottomNavigationView.elevation = dipToPix(10f) collapsePanel() + ViewCompat.setElevation(bottomNavigationView, 10f) } else { - if (MusicPlayerRemote.playingQueue.isNotEmpty()) { - slidingPanel.elevation = dipToPix(10f) - bottomNavigationView.elevation = dipToPix(10f) - behavior.isHideable = false - behavior.peekHeight = - if (bottomNavigationView.visibility == View.VISIBLE) { - heightOfBarWithTabs - } else { - heightOfBar - } - } + ViewCompat.setElevation(bottomNavigationView, 10f) + ViewCompat.setElevation(slidingPanel, 10f) + behavior.isHideable = false + behavior.peekHeight = if (isBottomBarVisible) heightOfBar * 2 else heightOfBar } } @@ -304,7 +300,7 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { fun hideBottomNavigation() { behavior.isHideable = true behavior.peekHeight = 0 - setBottomBarVisibility(View.GONE) + hideBottomBarVisibility(false) } fun updateTabs() { diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt index 1e385502a..d33067bd3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/HomeAdapter.kt @@ -3,8 +3,8 @@ package code.name.monkey.retromusic.adapter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ImageView import android.widget.TextView -import androidx.annotation.IntDef import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.AppCompatTextView import androidx.core.os.bundleOf @@ -13,22 +13,17 @@ import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.HORIZONTAL import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ColorUtil -import code.name.monkey.retromusic.EXTRA_ALBUM_ID -import code.name.monkey.retromusic.EXTRA_ARTIST_ID -import code.name.monkey.retromusic.PeekingLinearLayoutManager -import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.* import code.name.monkey.retromusic.adapter.album.AlbumAdapter import code.name.monkey.retromusic.adapter.artist.ArtistAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter -import code.name.monkey.retromusic.extensions.show +import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.fragments.albums.AlbumClickListener import code.name.monkey.retromusic.fragments.artists.ArtistClickListener import code.name.monkey.retromusic.glide.SongGlideRequest import code.name.monkey.retromusic.helper.MusicPlayerRemote -import code.name.monkey.retromusic.loaders.PlaylistSongsLoader import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.util.PreferenceUtil import com.bumptech.glide.Glide @@ -36,7 +31,7 @@ import com.google.android.material.card.MaterialCardView class HomeAdapter( private val activity: AppCompatActivity -) : RecyclerView.Adapter() { +) : RecyclerView.Adapter(), ArtistClickListener, AlbumClickListener { private var list = listOf() @@ -49,14 +44,9 @@ class HomeAdapter( .inflate(R.layout.section_recycler_view, parent, false) return when (viewType) { RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout) - TOP_ALBUMS, RECENT_ALBUMS -> { - AlbumViewHolder( - LayoutInflater.from(activity) - .inflate(R.layout.metal_section_recycler_view, parent, false) - ) - } GENRES -> GenreViewHolder(layout) FAVOURITES -> PlaylistViewHolder(layout) + TOP_ALBUMS, RECENT_ALBUMS -> AlbumViewHolder(layout) else -> { SuggestionsViewHolder( LayoutInflater.from(activity).inflate( @@ -70,55 +60,62 @@ class HomeAdapter( } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val home = list[position] when (getItemViewType(position)) { RECENT_ALBUMS -> { val viewHolder = holder as AlbumViewHolder - viewHolder.bindView( - list[position].arrayList as List, - R.string.recent_albums, - "Most recently added albums" - ) + viewHolder.bindView(home.arrayList as List, R.string.recent_albums) + viewHolder.clickableArea.setOnClickListener { + activity.findNavController(R.id.fragment_container).navigate( + R.id.detailListFragment, + bundleOf("type" to RECENT_ALBUMS) + ) + } } TOP_ALBUMS -> { val viewHolder = holder as AlbumViewHolder - viewHolder.bindView( - list[position].arrayList as List, - R.string.top_albums, - "Most played albums" - ) + viewHolder.bindView(home.arrayList as List, R.string.top_albums) + viewHolder.clickableArea.setOnClickListener { + activity.findNavController(R.id.fragment_container).navigate( + R.id.detailListFragment, + bundleOf("type" to TOP_ALBUMS) + ) + } } RECENT_ARTISTS -> { val viewHolder = holder as ArtistViewHolder - viewHolder.bindView( - list[position].arrayList as List, - R.string.recent_artists, - "Most recently added artists" - ) + viewHolder.bindView(home.arrayList, R.string.recent_artists) + viewHolder.clickableArea.setOnClickListener { + activity.findNavController(R.id.fragment_container).navigate( + R.id.detailListFragment, + bundleOf("type" to RECENT_ARTISTS) + ) + } } TOP_ARTISTS -> { val viewHolder = holder as ArtistViewHolder - viewHolder.bindView( - list[position].arrayList as List, - R.string.top_artists, - "Most played artists" - ) + viewHolder.bindView(home.arrayList, R.string.top_artists) + viewHolder.clickableArea.setOnClickListener { + activity.findNavController(R.id.fragment_container).navigate( + R.id.detailListFragment, + bundleOf("type" to TOP_ARTISTS) + ) + } } SUGGESTIONS -> { val viewHolder = holder as SuggestionsViewHolder - viewHolder.bindView( - list[position].arrayList as List - ) + viewHolder.bindView(home.arrayList) } FAVOURITES -> { val viewHolder = holder as PlaylistViewHolder - viewHolder.bindView( - list[position].arrayList as List, - R.string.favorites - ) + viewHolder.bindView(home.arrayList, R.string.favorites) } GENRES -> { val viewHolder = holder as GenreViewHolder - viewHolder.bind(list[position].arrayList as List, R.string.genres) + viewHolder.bind(home.arrayList, R.string.genres) + } + PLAYLISTS -> { + } } } @@ -132,79 +129,23 @@ class HomeAdapter( notifyDataSetChanged() } - companion object { - - @IntDef( - RECENT_ALBUMS, - TOP_ALBUMS, - RECENT_ARTISTS, - TOP_ARTISTS, - SUGGESTIONS, - FAVOURITES, - GENRES - ) - @Retention(AnnotationRetention.SOURCE) - annotation class HomeSection - - const val RECENT_ALBUMS = 3 - const val TOP_ALBUMS = 1 - const val RECENT_ARTISTS = 2 - const val TOP_ARTISTS = 0 - const val SUGGESTIONS = 5 - const val FAVOURITES = 4 - const val GENRES = 6 - } - - private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view), AlbumClickListener { - fun bindView(list: List, titleRes: Int, message: String) { - if (list.isNotEmpty()) { - val albumAdapter = AlbumAdapter(activity, list, R.layout.pager_item, null, this) - recyclerView.apply { - show() - adapter = albumAdapter - layoutManager = PeekingLinearLayoutManager(activity, HORIZONTAL, false) - } - title.text = activity.getString(titleRes) + private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) { + fun bindView(albums: List, titleRes: Int) { + title.text = activity.getString(titleRes) + recyclerView.apply { + adapter = albumAdapter(albums) + layoutManager = gridLayoutManager() } } - - override fun onAlbumClick(albumId: Int, view: View) { - activity.findNavController(R.id.fragment_container).navigate( - R.id.albumDetailsFragment, - bundleOf(EXTRA_ALBUM_ID to albumId), - null, - FragmentNavigatorExtras( - view to activity.getString(R.string.transition_album_art) - ) - ) - } } - private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view), ArtistClickListener { - fun bindView(list: List, titleRes: Int, message: String) { - if (list.isNotEmpty()) { - val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) - val artistAdapter = ArtistAdapter( - activity, - list, - PreferenceUtil.homeGridStyle, - null, - this - ) - recyclerView.apply { - show() - layoutManager = manager - adapter = artistAdapter - } - title.text = activity.getString(titleRes) + private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) { + fun bindView(artists: List, titleRes: Int) { + recyclerView.apply { + layoutManager = linearLayoutManager() + adapter = artistsAdapter(artists as List) } - } - - override fun onArtist(artistId: Int) { - activity.findNavController(R.id.fragment_container).navigate( - R.id.artistDetailsFragment, - bundleOf(EXTRA_ARTIST_ID to artistId) - ) + title.text = activity.getString(titleRes) } } @@ -220,18 +161,19 @@ class HomeAdapter( R.id.image8 ) - fun bindView(arrayList: List) { + fun bindView(songs: List) { + songs as List val color = ThemeStore.accentColor(activity) itemView.findViewById(R.id.message).setTextColor(color) itemView.findViewById(R.id.card6).apply { setCardBackgroundColor(ColorUtil.withAlpha(color, 0.2f)) } - if (arrayList.size > 9) + if (songs.size > 9) images.forEachIndexed { index, i -> itemView.findViewById(i).setOnClickListener { - MusicPlayerRemote.playNext(arrayList[index]) + MusicPlayerRemote.playNext(songs[index]) } - SongGlideRequest.Builder.from(Glide.with(activity), arrayList[index]) + SongGlideRequest.Builder.from(Glide.with(activity), songs[index]) .asBitmap() .build() .into(itemView.findViewById(i)) @@ -241,33 +183,29 @@ class HomeAdapter( } private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) { - fun bindView(arrayList: List, titleRes: Int) { - text.text = "You're all time favorites" - if (arrayList.isNotEmpty()) { - val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0]) - if (songs.isNotEmpty()) { - recyclerView.apply { - show() - val songAdapter = - SongAdapter(activity, songs, R.layout.item_album_card, null) - layoutManager = - GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false) - adapter = songAdapter - } - title.text = activity.getString(titleRes) - } + fun bindView(songs: List, titleRes: Int) { + arrow.hide() + recyclerView.apply { + val songAdapter = SongAdapter( + activity, + songs as MutableList, + R.layout.item_album_card, null + ) + layoutManager = linearLayoutManager() + adapter = songAdapter } + title.text = activity.getString(titleRes) } } private inner class GenreViewHolder(itemView: View) : AbsHomeViewItem(itemView) { - fun bind(genres: List, titleRes: Int) { + fun bind(genres: List, titleRes: Int) { + arrow.hide() title.text = activity.getString(titleRes) - text.text = "Genres for you" recyclerView.apply { - show() - layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.HORIZONTAL, false) - val genreAdapter = GenreAdapter(activity, genres, R.layout.item_grid_genre) + layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.HORIZONTAL, false) + val genreAdapter = + GenreAdapter(activity, genres as List, R.layout.item_grid_genre) adapter = genreAdapter } } @@ -276,6 +214,38 @@ class HomeAdapter( open inner class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) { val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView) val title: AppCompatTextView = itemView.findViewById(R.id.title) - val text: AppCompatTextView = itemView.findViewById(R.id.text) + val arrow: ImageView = itemView.findViewById(R.id.arrow) + val clickableArea: ViewGroup = itemView.findViewById(R.id.clickable_area) + } + + fun artistsAdapter(artists: List) = + ArtistAdapter(activity, artists, PreferenceUtil.homeGridStyle, null, this) + + fun albumAdapter(albums: List) = + AlbumAdapter(activity, albums, R.layout.item_image, null, this) + + fun gridLayoutManager() = GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false) + fun linearLayoutManager() = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) + + override fun onArtist(artistId: Int, imageView: ImageView) { + activity.findNavController(R.id.fragment_container).navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to artistId), + null, + FragmentNavigatorExtras( + imageView to activity.getString(R.string.transition_album_art) + ) + ) + } + + override fun onAlbumClick(albumId: Int, view: View) { + activity.findNavController(R.id.fragment_container).navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to albumId), + null, + FragmentNavigatorExtras( + view to activity.getString(R.string.transition_album_art) + ) + ) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt index 1d6b020b7..28505826d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt @@ -1,12 +1,12 @@ package code.name.monkey.retromusic.adapter.artist -import android.app.ActivityOptions import android.content.res.ColorStateList import android.content.res.Resources import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import androidx.core.view.ViewCompat import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter @@ -131,7 +131,6 @@ class ArtistAdapter( inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { init { - setImageTransitionName(activity.getString(R.string.transition_artist_image)) menu?.visibility = View.GONE } @@ -140,15 +139,13 @@ class ArtistAdapter( if (isInQuickSelectMode) { toggleChecked(layoutPosition) } else { - val activityOptions = ActivityOptions.makeSceneTransitionAnimation( - activity, - imageContainerCard ?: image, - activity.getString(R.string.transition_artist_image) - ) - artistClickListener.onArtist(dataSet[layoutPosition].id) - /*NavigationUtil.goToArtistOptions( - activity, dataSet[layoutPosition].id, activityOptions - )*/ + image?.let { + ViewCompat.setTransitionName( + it, + activity.getString(R.string.transition_artist_image) + ) + artistClickListener.onArtist(dataSet[layoutPosition].id, it) + } } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt index c2d27315e..6f416f23c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt @@ -148,7 +148,6 @@ open class SongAdapter( return "" } } - return MusicUtil.getSectionName(sectionName) } diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt index 0e1892b31..368cb0bf5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt @@ -33,6 +33,8 @@ import code.name.monkey.retromusic.App import code.name.monkey.retromusic.R import com.google.android.material.button.MaterialButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton +import com.google.android.material.textfield.TextInputEditText +import com.google.android.material.textfield.TextInputLayout fun Int.ripAlpha(): Int { return ColorUtil.stripAlpha(this) @@ -116,4 +118,15 @@ fun MaterialButton.applyColor(color: Int) { backgroundTintList = backgroundColorStateList setTextColor(textColorColorStateList) iconTint = textColorColorStateList +} + +fun TextInputLayout.accentColor() { + val accentColor = ThemeStore.accentColor(context) + val colorState = ColorStateList.valueOf(accentColor) + boxStrokeColor = accentColor + defaultHintTextColor = colorState + isHintAnimationEnabled = true +} +fun TextInputEditText.accentColor(){ + } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt new file mode 100644 index 000000000..57d4e9bbf --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt @@ -0,0 +1,130 @@ +package code.name.monkey.retromusic.fragments + +import android.os.Bundle +import android.view.View +import android.widget.ImageView +import androidx.navigation.fragment.navArgs +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import code.name.monkey.retromusic.* +import code.name.monkey.retromusic.adapter.album.AlbumAdapter +import code.name.monkey.retromusic.adapter.artist.ArtistAdapter +import code.name.monkey.retromusic.adapter.song.SongAdapter +import code.name.monkey.retromusic.fragments.albums.AlbumClickListener +import code.name.monkey.retromusic.fragments.artists.ArtistClickListener +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment +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.providers.RepositoryImpl +import kotlinx.android.synthetic.main.fragment_playlist_detail.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.koin.android.ext.android.inject + +class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail), + ArtistClickListener, AlbumClickListener { + private val args by navArgs() + private val repository by inject() + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + mainActivity.setSupportActionBar(toolbar) + mainActivity.hideBottomBarVisibility(false) + when (args.type) { + TOP_ARTISTS -> { + loadArtists(R.string.top_artists, TOP_ARTISTS) + } + RECENT_ARTISTS -> { + loadArtists(R.string.recent_artists, RECENT_ARTISTS) + } + TOP_ALBUMS -> { + loadAlbums(R.string.top_albums, TOP_ALBUMS) + } + RECENT_ALBUMS -> { + loadAlbums(R.string.recent_albums, RECENT_ALBUMS) + } + FAVOURITES -> { + loadFavorite() + } + } + } + + private fun loadFavorite() { + toolbar.setTitle(R.string.favorites) + CoroutineScope(IO).launch { + val songs = repository.favoritePlaylistHome() + withContext(Main) { + recyclerView.apply { + adapter = SongAdapter( + requireActivity(), + songs.arrayList as MutableList, + R.layout.item_list, null + ) + layoutManager = linearLayoutManager() + } + } + } + } + + private fun loadArtists(title: Int, type: Int) { + toolbar.setTitle(title) + CoroutineScope(IO).launch { + val artists = + if (type == TOP_ARTISTS) repository.topArtists() else repository.recentArtists() + withContext(Main) { + recyclerView.apply { + adapter = artistAdapter(artists) + layoutManager = gridLayoutManager() + } + } + } + } + + private fun loadAlbums(title: Int, type: Int) { + toolbar.setTitle(title) + CoroutineScope(IO).launch { + val albums = + if (type == TOP_ALBUMS) repository.topAlbums() else repository.recentAlbums() + withContext(Main) { + recyclerView.apply { + adapter = albumAdapter(albums) + layoutManager = gridLayoutManager() + + } + } + } + } + + private fun artistAdapter(artists: List): ArtistAdapter = ArtistAdapter( + requireActivity(), + artists, + R.layout.item_grid_circle, + null, this@DetailListFragment + ) + + private fun albumAdapter(albums: List): AlbumAdapter = AlbumAdapter( + requireActivity(), + albums, + R.layout.item_grid, + null, this@DetailListFragment + ) + + private fun linearLayoutManager(): LinearLayoutManager = + LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + + private fun gridLayoutManager(): GridLayoutManager = + GridLayoutManager(requireContext(), 2, GridLayoutManager.VERTICAL, false) + + + override fun onArtist(artistId: Int, imageView: ImageView) { + + } + + override fun onAlbumClick(albumId: Int, view: View) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt index e0a5498ca..8f514c5a7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import code.name.monkey.retromusic.adapter.HomeAdapter import code.name.monkey.retromusic.fragments.ReloadType.* import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.* @@ -18,13 +17,13 @@ class LibraryViewModel( private val repository: RepositoryImpl ) : ViewModel(), MusicServiceEventListener { + private val paletteColor = MutableLiveData() private val albums = MutableLiveData>() private val songs = MutableLiveData>() private val artists = MutableLiveData>() private val playlists = MutableLiveData>() private val genres = MutableLiveData>() private val home = MutableLiveData>() - private val paletteColor = MutableLiveData() val paletteColorLiveData: LiveData = paletteColor val homeLiveData: LiveData> = home @@ -46,38 +45,14 @@ class LibraryViewModel( artists.value = loadArtists.await() playlists.value = loadPlaylists.await() genres.value = loadGenres.await() - loadHomeSections() + home.value = loadHome.await() } - private fun loadHomeSections() = viewModelScope.launch { - val list = mutableListOf() - val result = listOf( - repository.topArtists(), - repository.topAlbums(), - repository.recentArtists(), - repository.recentAlbums()/*, - repository.suggestions(), - repository.favoritePlaylist(), - repository.homeGenres()*/ - ) - result.forEach { - if (it != null && it.arrayList.isNotEmpty()) { - if (it.homeSection == HomeAdapter.SUGGESTIONS) { - if (it.arrayList.size > 9) { - list.add(it) - } - } else { - list.add(it) - } - } - } - home.value = list - } + private val loadHome: Deferred> + get() = viewModelScope.async { repository.homeSections() } private val loadSongs: Deferred> - get() = viewModelScope.async(IO) { - repository.allSongs() - } + get() = viewModelScope.async(IO) { repository.allSongs() } private val loadAlbums: Deferred> get() = viewModelScope.async(IO) { @@ -99,6 +74,7 @@ class LibraryViewModel( repository.allGenres() } + fun forceReload(reloadType: ReloadType) = viewModelScope.launch { when (reloadType) { Songs -> songs.value = loadSongs.await() @@ -114,15 +90,37 @@ class LibraryViewModel( override fun onMediaStoreChanged() { loadLibraryContent() + println("onMediaStoreChanged") } - override fun onServiceConnected() {} - override fun onServiceDisconnected() {} - override fun onQueueChanged() {} - override fun onPlayingMetaChanged() {} - override fun onPlayStateChanged() {} - override fun onRepeatModeChanged() {} - override fun onShuffleModeChanged() {} + + override fun onServiceConnected() { + println("onServiceConnected") + } + + override fun onServiceDisconnected() { + println("onServiceDisconnected") + } + + override fun onQueueChanged() { + println("onQueueChanged") + } + + override fun onPlayingMetaChanged() { + println("onPlayingMetaChanged") + } + + override fun onPlayStateChanged() { + println("onPlayStateChanged") + } + + override fun onRepeatModeChanged() { + println("onRepeatModeChanged") + } + + override fun onShuffleModeChanged() { + println("onShuffleModeChanged") + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt index 4f030ac0c..5cbe0b76b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt @@ -6,13 +6,13 @@ import android.net.Uri import android.os.Bundle import android.view.View import androidx.core.app.ShareCompat +import androidx.fragment.app.Fragment import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import code.name.monkey.retromusic.App import code.name.monkey.retromusic.Constants import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.ContributorAdapter -import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.model.Contributor import code.name.monkey.retromusic.util.NavigationUtil import com.google.gson.Gson @@ -24,7 +24,7 @@ import kotlinx.android.synthetic.main.card_social.* import java.io.IOException import java.nio.charset.StandardCharsets -class AboutFragment : AbsMainActivityFragment(R.layout.fragment_about), View.OnClickListener { +class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) version.setSummary(getAppVersion()) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt index c8398f63f..d5d485a26 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt @@ -62,17 +62,15 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setHasOptionsMenu(true) + mainActivity.hideBottomBarVisibility(false) + mainActivity.addMusicServiceEventListener(detailsViewModel) mainActivity.setSupportActionBar(toolbar) - mainActivity.setBottomBarVisibility(View.GONE) toolbar.title = null - image.transitionName = getString(R.string.transition_album_art) - postponeEnterTransition() - playerActivity?.addMusicServiceEventListener(detailsViewModel) detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer { - startPostponedEnterTransition() showAlbum(it) + startPostponedEnterTransition() }) detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { loadArtistImage(it) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt index 5c4b5aa68..47c060da4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt @@ -5,6 +5,7 @@ import android.view.View import androidx.core.os.bundleOf import androidx.lifecycle.Observer import androidx.navigation.findNavController +import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.recyclerview.widget.GridLayoutManager import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.R @@ -19,13 +20,12 @@ class AlbumsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - libraryViewModel.albumsLiveData - .observe(viewLifecycleOwner, Observer { albums -> - if (albums.isNotEmpty()) - adapter?.swapDataSet(albums) - else - adapter?.swapDataSet(listOf()) - }) + libraryViewModel.albumsLiveData.observe(viewLifecycleOwner, Observer { + if (it.isNotEmpty()) + adapter?.swapDataSet(it) + else + adapter?.swapDataSet(listOf()) + }) } override val emptyMessage: Int @@ -98,7 +98,11 @@ class AlbumsFragment : val controller = requireActivity().findNavController(R.id.fragment_container) controller.navigate( R.id.albumDetailsFragment, - bundleOf(EXTRA_ALBUM_ID to albumId) + bundleOf(EXTRA_ALBUM_ID to albumId), + null, + FragmentNavigatorExtras( + view to getString(R.string.transition_album_art) + ) ) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt index 38dec7c6a..8fc574eba 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt @@ -56,17 +56,19 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d private var lang: String? = null private var biography: Spanned? = null + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setHasOptionsMenu(true) mainActivity.setSupportActionBar(toolbar) - mainActivity.setBottomBarVisibility(View.GONE) + mainActivity.hideBottomBarVisibility(false) toolbar.title = null setupRecyclerView() postponeEnterTransition() detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { - startPostponedEnterTransition() + showArtist(it) + startPostponedEnterTransition() }) detailsViewModel.getArtistInfo().observe(viewLifecycleOwner, Observer { artistInfo(it) @@ -129,7 +131,7 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d ) songTitle.text = songText albumTitle.text = albumText - songAdapter.swapDataSet(artist.songs) + songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber }) artist.albums?.let { albumAdapter.swapDataSet(it) } } @@ -175,6 +177,7 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d .generatePalette(requireContext()).build() .dontAnimate().into(object : RetroMusicColoredTarget(image) { override fun onColorReady(colors: MediaNotificationProcessor) { + startPostponedEnterTransition() setColors(colors) } }) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt index aaf032349..6f2d91a1a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt @@ -2,6 +2,7 @@ package code.name.monkey.retromusic.fragments.artists import android.os.Bundle import android.view.View +import android.widget.ImageView import androidx.core.os.bundleOf import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager @@ -25,14 +26,12 @@ class ArtistsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - libraryViewModel.artistsLiveData - .observe(viewLifecycleOwner, Observer { artists -> - if (artists.isNotEmpty()) { - adapter?.swapDataSet(artists) - } else { - adapter?.swapDataSet(listOf()) - } - }) + libraryViewModel.artistsLiveData.observe(viewLifecycleOwner, Observer { + if (it.isNotEmpty()) + adapter?.swapDataSet(it) + else + adapter?.swapDataSet(listOf()) + }) } override val emptyMessage: Int @@ -101,12 +100,12 @@ class ArtistsFragment : } } - override fun onArtist(artistId: Int) { + override fun onArtist(artistId: Int, imageView: ImageView) { val controller = findActivityNavController(R.id.fragment_container) controller.navigate(R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to artistId)) } } interface ArtistClickListener { - fun onArtist(artistId: Int) + fun onArtist(artistId: Int, imageView: ImageView) } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt index 2ba05ce72..a78f16188 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt @@ -18,12 +18,13 @@ import me.zhanghai.android.fastscroll.FastScroller import me.zhanghai.android.fastscroll.FastScrollerBuilder import org.koin.androidx.viewmodel.ext.android.sharedViewModel + abstract class AbsRecyclerViewFragment, LM : RecyclerView.LayoutManager> : AbsMusicServiceFragment(R.layout.fragment_main_activity_recycler_view), AppBarLayout.OnOffsetChangedListener { val libraryViewModel: LibraryViewModel by sharedViewModel() - + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setHasOptionsMenu(true) @@ -41,15 +42,17 @@ abstract class AbsRecyclerViewFragment, LM : Recycle } private fun setUpRecyclerView() { - recyclerView.layoutManager = layoutManager - recyclerView.adapter = adapter - val fastScroller = create(recyclerView) - recyclerView.setOnApplyWindowInsetsListener( - ScrollingViewOnApplyWindowInsetsListener( - recyclerView, - fastScroller + recyclerView.apply { + layoutManager = this@AbsRecyclerViewFragment.layoutManager + adapter = this@AbsRecyclerViewFragment.adapter + val fastScroller = create(this) + setOnApplyWindowInsetsListener( + ScrollingViewOnApplyWindowInsetsListener( + recyclerView, + fastScroller + ) ) - ) + } checkForPadding() } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt index 0cbeed7ba..8924ffd94 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt @@ -35,7 +35,7 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_ setHasOptionsMenu(true) mainActivity.addMusicServiceEventListener(detailsViewModel) mainActivity.setSupportActionBar(toolbar) - mainActivity.setBottomBarVisibility(View.GONE) + mainActivity.hideBottomBarVisibility(false) setupRecyclerView() detailsViewModel.getSongs().observe(viewLifecycleOwner, androidx.lifecycle.Observer { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt index 7610476c0..e4017e573 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt @@ -32,14 +32,12 @@ class GenresFragment : AbsRecyclerViewFragment - if (genres.isNotEmpty()) { - adapter?.swapDataSet(genres) - } else { - adapter?.swapDataSet(listOf()) - } - }) + libraryViewModel.genresLiveData.observe(viewLifecycleOwner, Observer { + if (it.isNotEmpty()) + adapter?.swapDataSet(it) + else + adapter?.swapDataSet(listOf()) + }) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt index 6c0d9881c..01128bb92 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt @@ -25,6 +25,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import code.name.monkey.retromusic.EXTRA_PLAYLIST import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.HomeAdapter +import code.name.monkey.retromusic.extensions.findActivityNavController import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.glide.ProfileBannerGlideRequest @@ -57,9 +58,7 @@ class HomeFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setStatusBarColorAuto(view) - bannerImage?.setOnClickListener { val options = ActivityOptions.makeSceneTransitionAnimation( mainActivity, @@ -70,14 +69,14 @@ class HomeFragment : } lastAdded.setOnClickListener { - requireActivity().findNavController(R.id.fragment_container).navigate( + findActivityNavController(R.id.fragment_container).navigate( R.id.playlistDetailsFragment, bundleOf(EXTRA_PLAYLIST to LastAddedPlaylist(requireActivity())) ) } topPlayed.setOnClickListener { - requireActivity().findNavController(R.id.fragment_container).navigate( + findActivityNavController(R.id.fragment_container).navigate( R.id.playlistDetailsFragment, bundleOf(EXTRA_PLAYLIST to MyTopTracksPlaylist(requireActivity())) ) @@ -110,9 +109,8 @@ class HomeFragment : adapter = homeAdapter } - libraryViewModel.homeLiveData - .observe(viewLifecycleOwner, Observer { sections -> - homeAdapter.swapData(sections) + libraryViewModel.homeLiveData.observe(viewLifecycleOwner, Observer { + homeAdapter.swapData(it) }) loadProfile() diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt index 93859b58e..1c743f923 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.Menu import android.view.MenuInflater import android.view.MenuItem -import android.view.View import androidx.navigation.fragment.findNavController import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper @@ -17,11 +16,21 @@ import com.google.android.material.appbar.AppBarLayout import kotlinx.android.synthetic.main.fragment_library.* class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) { - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) + + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) setHasOptionsMenu(true) - mainActivity.setBottomBarVisibility(View.VISIBLE) + mainActivity.hideBottomBarVisibility(true) mainActivity.setSupportActionBar(toolbar) + mainActivity.supportActionBar?.title = null + toolbar.setNavigationOnClickListener { + findNavController().navigate( + R.id.searchFragment, + null, + navOptions + ) + } setupNavigationController() } @@ -67,13 +76,8 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) { override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.action_search -> findNavController().navigate( - R.id.searchFragment, - null, - navOptions - ) R.id.action_settings -> findNavController().navigate( - R.id.settingsFragment, + R.id.settingsActivity, null, navOptions ) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt index 474df7dd4..5c2d989bb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt @@ -43,7 +43,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli setHasOptionsMenu(true) mainActivity.addMusicServiceEventListener(viewModel) mainActivity.setSupportActionBar(toolbar) - mainActivity.setBottomBarVisibility(View.GONE) + mainActivity.hideBottomBarVisibility(false) playlist = arguments.extraPlaylist diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt index 2ab2d3c0d..226817155 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt @@ -19,12 +19,11 @@ class PlaylistsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - libraryViewModel.playlisitsLiveData.observe(viewLifecycleOwner, Observer { playlists -> - if (playlists.isNotEmpty()) { - adapter?.swapDataSet(playlists) - } else { + libraryViewModel.playlisitsLiveData.observe(viewLifecycleOwner, Observer { + if (it.isNotEmpty()) + adapter?.swapDataSet(it) + else adapter?.swapDataSet(listOf()) - } }) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt index a53f4e138..c83dab446 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchFragment.kt @@ -39,7 +39,7 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa super.onViewCreated(view, savedInstanceState) mainActivity.setSupportActionBar(toolbar) mainActivity.hideBottomNavigation() - mainActivity.setBottomBarVisibility(View.GONE) + mainActivity.hideBottomBarVisibility(false) setupRecyclerView() setupSearchView() diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt deleted file mode 100644 index 72355ec95..000000000 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/SettingsFragment.kt +++ /dev/null @@ -1,23 +0,0 @@ -package code.name.monkey.retromusic.fragments.settings - -import android.os.Bundle -import android.view.View -import androidx.navigation.NavController -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.extensions.findNavController -import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment -import kotlinx.android.synthetic.main.fragment_settings.* - -class SettingsFragment : AbsMainActivityFragment(R.layout.fragment_settings) { - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - mainActivity.setSupportActionBar(toolbar) - mainActivity.hideBottomNavigation() - mainActivity.setBottomBarVisibility(View.GONE) - val navController: NavController = findNavController(R.id.contentFrame) - navController.addOnDestinationChangedListener { _, _, _ -> - toolbar.title = navController.currentDestination?.label - } - } -} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt index 0d28c804d..3db01eb75 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt @@ -23,11 +23,10 @@ class SongsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) libraryViewModel.songsLiveData.observe(viewLifecycleOwner, Observer { - if (it.isNotEmpty()) { + if (it.isNotEmpty()) adapter?.swapDataSet(it) - } else { + else adapter?.swapDataSet(listOf()) - } }) } diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Home.kt b/app/src/main/java/code/name/monkey/retromusic/model/Home.kt index 3c015e1ab..e848d25c8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Home.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/Home.kt @@ -14,13 +14,10 @@ package code.name.monkey.retromusic.model -import androidx.annotation.DrawableRes -import code.name.monkey.retromusic.adapter.HomeAdapter.Companion.HomeSection +import code.name.monkey.retromusic.HomeSection class Home( - val arrayList: List<*>, + val arrayList: List, @HomeSection - val homeSection: Int, - @DrawableRes - val icon: Int + val homeSection: Int ) \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt b/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt index 8a1dbf11d..c2fd7aeb5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt +++ b/app/src/main/java/code/name/monkey/retromusic/providers/RepositoryImpl.kt @@ -15,8 +15,7 @@ package code.name.monkey.retromusic.providers import android.content.Context -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.adapter.HomeAdapter +import code.name.monkey.retromusic.* import code.name.monkey.retromusic.loaders.* import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.model.smartplaylist.NotRecentlyPlayedPlaylist @@ -24,6 +23,10 @@ import code.name.monkey.retromusic.network.LastFMService import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.providers.interfaces.Repository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flow class RepositoryImpl( private val context: Context, @@ -39,41 +42,24 @@ class RepositoryImpl( override suspend fun artistById(artistId: Int): Artist = ArtistLoader.getArtist(context, artistId) + override suspend fun recentArtists(): List = + LastAddedSongsLoader.getLastAddedArtists(context) + + override suspend fun topArtists(): List = + TopAndRecentlyPlayedTracksLoader.getTopArtists(context) + + override suspend fun topAlbums(): List = + TopAndRecentlyPlayedTracksLoader.getTopAlbums(context) + + override suspend fun recentAlbums(): List = + LastAddedSongsLoader.getLastAddedAlbums(context) + override suspend fun allPlaylists(): List = PlaylistLoader.getAllPlaylists(context) override suspend fun allGenres(): List = GenreLoader.getAllGenres(context) override suspend fun allSongs(): List = SongLoader.getAllSongs(context) - override suspend fun suggestions(): Home? { - val songs = NotRecentlyPlayedPlaylist(context).getSongs(context).shuffled().apply { - if (size > 9) subList(0, 9) - } - if (songs.isNotEmpty()) { - return Home( - songs, - HomeAdapter.SUGGESTIONS, - R.drawable.ic_audiotrack - ) - } - return null - } - - override suspend fun homeGenres(): Home? { - val genres = - GenreLoader.getAllGenres(context) - .shuffled() - .filter { it.name.length in 5..10 } - - if (genres.isNotEmpty()) { - return Home( - genres, - HomeAdapter.GENRES, - R.drawable.ic_guitar - ) - } - return null - } override suspend fun search(query: String?): MutableList = SearchLoader.searchAll(context, query) @@ -89,52 +75,6 @@ class RepositoryImpl( override suspend fun getGenre(genreId: Int): ArrayList = GenreLoader.getSongs(context, genreId) - override suspend fun recentArtists(): Home? { - val artists = LastAddedSongsLoader.getLastAddedArtists(context) - return if (artists.isNotEmpty()) Home( - artists, - HomeAdapter.RECENT_ARTISTS, - R.drawable.ic_artist - ) else null - } - - override suspend fun recentAlbums(): Home? { - val albums = LastAddedSongsLoader.getLastAddedAlbums(context) - return if (albums.isNotEmpty()) Home( - albums, - HomeAdapter.RECENT_ALBUMS, - R.drawable.ic_album - ) else null - } - - override suspend fun topAlbums(): Home? { - val albums = TopAndRecentlyPlayedTracksLoader.getTopAlbums(context) - return if (albums.isNotEmpty()) Home( - albums, - HomeAdapter.TOP_ALBUMS, - R.drawable.ic_album - ) else null - } - - override suspend fun topArtists(): Home? { - - val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context) - return if (artists.isNotEmpty()) Home( - artists, - HomeAdapter.TOP_ARTISTS, - R.drawable.ic_artist - ) else null - - } - - override suspend fun favoritePlaylist(): Home? { - val playlists = PlaylistLoader.getFavoritePlaylist(context) - return if (playlists.isNotEmpty()) Home( - playlists, - HomeAdapter.FAVOURITES, - R.drawable.ic_favorite - ) else null - } override suspend fun artistInfo( name: String, @@ -148,4 +88,149 @@ class RepositoryImpl( album: String ): LastFmAlbum = lastFMService.albumInfo(artist, album) + @ExperimentalCoroutinesApi + override suspend fun homeSectionsFlow(): Flow>> { + val homes = MutableStateFlow>>(value = Result.Loading) + println("homeSections:Loading") + val homeSections = mutableListOf() + val sections = listOf( + topArtistsHome(), + topAlbumsHome(), + recentArtistsHome(), + recentAlbumsHome(), + suggestionsHome(), + favoritePlaylistHome(), + genresHome() + ) + for (section in sections) { + if (section.arrayList.isNotEmpty()) { + println("${section.homeSection} -> ${section.arrayList.size}") + homeSections.add(section) + } + } + if (homeSections.isEmpty()) { + homes.value = Result.Error + } else { + homes.value = Result.Success(homeSections) + } + return homes + } + + override suspend fun homeSections(): List { + val homeSections = mutableListOf() + val sections = listOf( + topArtistsHome(), + topAlbumsHome(), + recentArtistsHome(), + recentAlbumsHome(), + suggestionsHome(), + favoritePlaylistHome(), + genresHome() + ) + for (section in sections) { + if (section.arrayList.isNotEmpty()) { + println("${section.homeSection} -> ${section.arrayList.size}") + homeSections.add(section) + } + } + return homeSections + } + + suspend fun playlists(): Home { + val playlist = PlaylistLoader.getAllPlaylists(context) + return Home(playlist, TOP_ALBUMS) + } + + override suspend fun suggestionsHome(): Home { + val songs = + NotRecentlyPlayedPlaylist(context).getSongs(context).shuffled().takeUnless { + it.size > 9 + }?.take(9) ?: emptyList() + return Home(songs, SUGGESTIONS) + } + + override suspend fun genresHome(): Home { + val genres = GenreLoader.getAllGenres(context).shuffled() + return Home(genres, GENRES) + } + + + override suspend fun recentArtistsHome(): Home { + val artists = LastAddedSongsLoader.getLastAddedArtists(context).take(5) + return Home(artists, RECENT_ARTISTS) + } + + override suspend fun recentAlbumsHome(): Home { + val albums = LastAddedSongsLoader.getLastAddedAlbums(context).take(5) + return Home(albums, RECENT_ALBUMS) + } + + override suspend fun topAlbumsHome(): Home { + val albums = TopAndRecentlyPlayedTracksLoader.getTopAlbums(context).take(5) + return Home(albums, TOP_ALBUMS) + } + + override suspend fun topArtistsHome(): Home { + val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context).take(5) + return Home(artists, TOP_ARTISTS) + } + + override suspend fun favoritePlaylistHome(): Home { + val playlists = PlaylistLoader.getFavoritePlaylist(context).take(5) + val songs = if (playlists.isNotEmpty()) + PlaylistSongsLoader.getPlaylistSongList(context, playlists[0]) + else emptyList() + + return Home(songs, FAVOURITES) + } + + override fun songsFlow(): Flow>> = flow { + emit(Result.Loading) + val data = SongLoader.getAllSongs(context) + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } + + override fun albumsFlow(): Flow>> = flow { + emit(Result.Loading) + val data = AlbumLoader.getAllAlbums(context) + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } + + override fun artistsFlow(): Flow>> = flow { + emit(Result.Loading) + val data = ArtistLoader.getAllArtists(context) + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } + + override fun playlistsFlow(): Flow>> = flow { + emit(Result.Loading) + val data = PlaylistLoader.getAllPlaylists(context) + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } + + override fun genresFlow(): Flow>> = flow { + emit(Result.Loading) + val data = GenreLoader.getAllGenres(context) + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt b/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt index 984960df3..2f5fc2a19 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/providers/interfaces/Repository.kt @@ -14,9 +14,11 @@ package code.name.monkey.retromusic.providers.interfaces +import code.name.monkey.retromusic.Result import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.network.model.LastFmArtist +import kotlinx.coroutines.flow.Flow /** * Created by hemanths on 11/08/17. @@ -47,17 +49,41 @@ interface Repository { suspend fun albumInfo(artist: String, album: String): LastFmAlbum suspend fun artistById(artistId: Int): Artist - suspend fun recentArtists(): Home? - suspend fun topArtists(): Home? + suspend fun recentArtists(): List - suspend fun topAlbums(): Home? + suspend fun topArtists(): List - suspend fun recentAlbums(): Home? + suspend fun topAlbums(): List - suspend fun favoritePlaylist(): Home? + suspend fun recentAlbums(): List - suspend fun suggestions(): Home? + suspend fun recentArtistsHome(): Home + + suspend fun topArtistsHome(): Home + + suspend fun topAlbumsHome(): Home + + suspend fun recentAlbumsHome(): Home + + suspend fun favoritePlaylistHome(): Home + + suspend fun suggestionsHome(): Home + + suspend fun genresHome(): Home + + suspend fun homeSections(): List + + suspend fun homeSectionsFlow(): Flow>> + + fun songsFlow(): Flow>> + + fun albumsFlow(): Flow>> + + fun artistsFlow(): Flow>> + + fun playlistsFlow(): Flow>> + + fun genresFlow(): Flow>> - suspend fun homeGenres(): Home? } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationBarTinted.kt b/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationBarTinted.kt index 91cb5d53f..c09801b85 100644 --- a/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationBarTinted.kt +++ b/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationBarTinted.kt @@ -66,5 +66,5 @@ class BottomNavigationBarTinted @JvmOverloads constructor( } fun Int.addAlpha(): Int { - return ColorUtil.withAlpha(this, 0.12f) + return ColorUtil.withAlpha(this, 0.38f) } diff --git a/app/src/main/res/font/pacifico.xml b/app/src/main/res/font/pacifico.xml new file mode 100644 index 000000000..6cff80c84 --- /dev/null +++ b/app/src/main/res/font/pacifico.xml @@ -0,0 +1,7 @@ + + + diff --git a/app/src/main/res/layout/activity_main_content.xml b/app/src/main/res/layout/activity_main_content.xml index ffd74617b..aee8d3b0d 100644 --- a/app/src/main/res/layout/activity_main_content.xml +++ b/app/src/main/res/layout/activity_main_content.xml @@ -1,11 +1,28 @@ - + android:background="?colorSurface" + android:orientation="vertical"> + + + + + + + + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 000000000..72796a351 --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_album_details.xml b/app/src/main/res/layout/fragment_album_details.xml index f84cb563a..b81d1c654 100644 --- a/app/src/main/res/layout/fragment_album_details.xml +++ b/app/src/main/res/layout/fragment_album_details.xml @@ -1,140 +1,125 @@ - + android:layout_height="match_parent"> - + android:layout_height="wrap_content" + app:liftOnScroll="true"> - - - - - - + android:layout_height="wrap_content"> - + + + android:layout_height="48dp" /> + - + - - + - - - + android:orientation="vertical"> - + + + + + + + + + + + + + android:layout_marginTop="12dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/albumText" /> - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_artist_details.xml b/app/src/main/res/layout/fragment_artist_details.xml index 88b51eddf..817c00030 100644 --- a/app/src/main/res/layout/fragment_artist_details.xml +++ b/app/src/main/res/layout/fragment_artist_details.xml @@ -1,125 +1,110 @@ - + android:layout_height="match_parent"> - + android:layout_height="wrap_content" + app:liftOnScroll="true"> - - - - - - + android:layout_height="wrap_content"> - + + + android:layout_height="48dp" /> + + - + - - - - - + android:orientation="vertical"> - + + + + + + + + + + - - - - - - - - - - - - + android:layout_marginTop="16dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/text" /> - - - - \ No newline at end of file + + + \ 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 844a8c1d0..bc0ec2a81 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -1,63 +1,54 @@ - + android:layout_height="match_parent"> - + app:liftOnScroll="true"> - - - - - - - + app:layout_scrollFlags="scroll|enterAlways"> - + android:background="?attr/colorSurface" + app:navigationIcon="@drawable/ic_search" + app:popupTheme="?attr/toolbarPopupTheme" + app:title="@null" + tools:ignore="UnusedAttribute"> - + android:layout_gravity="center" + android:text="@string/app_name" + android:textStyle="bold" + android:textAppearance="@style/TextViewHeadline6" /> + - - - + + + - - - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main_settings.xml b/app/src/main/res/layout/fragment_main_settings.xml index cd2f3c295..421dc3dac 100644 --- a/app/src/main/res/layout/fragment_main_settings.xml +++ b/app/src/main/res/layout/fragment_main_settings.xml @@ -1,6 +1,7 @@ + app:tint="?colorOnSecondary" + tools:tint="?attr/colorAccent" /> - + android:layout_height="match_parent"> - + android:layout_height="wrap_content" + app:liftOnScroll="true"> - - - - - - + app:layout_scrollFlags="scroll|enterAlways"> - + android:background="?attr/colorSurface" + app:navigationIcon="@drawable/ic_keyboard_backspace_black" + app:titleTextAppearance="@style/ToolbarTextAppearanceNormal" /> - + - + + - - + - + + + android:layout_marginBottom="16dp" + android:text="@string/empty_text_emoji" + android:textAppearance="@style/TextViewHeadline3" /> - - - - - - - - \ No newline at end of file + android:text="@string/no_songs" + android:textAppearance="@style/TextViewHeadline5" + android:textColor="?android:attr/textColorSecondary" + tools:visibility="visible" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_album_card.xml b/app/src/main/res/layout/item_album_card.xml index 403d512b8..21ebef3d9 100644 --- a/app/src/main/res/layout/item_album_card.xml +++ b/app/src/main/res/layout/item_album_card.xml @@ -9,10 +9,11 @@ @@ -24,21 +25,23 @@ android:scaleType="centerCrop" app:srcCompat="@drawable/default_album_art" tools:src="@tools:sample/avatars" /> + + - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_grid_genre.xml b/app/src/main/res/layout/item_grid_genre.xml index 408c88694..41771f35a 100644 --- a/app/src/main/res/layout/item_grid_genre.xml +++ b/app/src/main/res/layout/item_grid_genre.xml @@ -12,7 +12,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" - android:textAppearance="@style/TextViewHeadline6" + android:textAppearance="@style/TextViewSubtitle1" tools:text="@tools:sample/full_names" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_image.xml b/app/src/main/res/layout/item_image.xml index e6fdad1e1..abd2797e9 100644 --- a/app/src/main/res/layout/item_image.xml +++ b/app/src/main/res/layout/item_image.xml @@ -35,7 +35,9 @@ android:ellipsize="end" android:maxLines="1" android:padding="8dp" - android:textAppearance="@style/TextViewNormal" + android:textAppearance="@style/TextViewBody1" + android:textColor="?android:attr/textColorPrimary" + android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/imageContainer" diff --git a/app/src/main/res/layout/item_suggestions.xml b/app/src/main/res/layout/item_suggestions.xml index 34042ea3b..e506fd467 100644 --- a/app/src/main/res/layout/item_suggestions.xml +++ b/app/src/main/res/layout/item_suggestions.xml @@ -10,8 +10,7 @@ android:id="@+id/title" android:layout_width="0dp" android:layout_height="wrap_content" - android:paddingHorizontal="16dp" - android:paddingTop="16dp" + android:padding="16dp" android:text="@string/suggestion_songs" android:textAppearance="@style/TextViewHeadline6" android:textStyle="bold" @@ -20,18 +19,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - + app:layout_constraintTop_toBottomOf="@id/title"> + app:layout_constraintTop_toTopOf="@id/card6"> + app:layout_constraintTop_toTopOf="@id/card7"> - - \ No newline at end of file diff --git a/app/src/main/res/layout/section_recycler_view.xml b/app/src/main/res/layout/section_recycler_view.xml index 9107539bc..852c6c480 100644 --- a/app/src/main/res/layout/section_recycler_view.xml +++ b/app/src/main/res/layout/section_recycler_view.xml @@ -5,47 +5,49 @@ android:id="@+id/recentArtistContainer" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical"> + android:orientation="vertical" + android:paddingBottom="12dp"> - - + app:layout_constraintTop_toTopOf="parent"> - + + + + \ 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 d0d7c7bfc..24566b4f9 100644 --- a/app/src/main/res/layout/sliding_music_panel_layout.xml +++ b/app/src/main/res/layout/sliding_music_panel_layout.xml @@ -49,7 +49,6 @@ android:layout_height="wrap_content" android:layout_gravity="bottom" android:elevation="0dp" - android:visibility="gone" app:itemHorizontalTranslationEnabled="false" app:itemIconTint="@drawable/bottom_navigation_item_colors" app:itemTextAppearanceActive="@style/BottomSheetItemTextAppearance" diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index 3ee85b086..1458c4158 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -16,11 +16,6 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:context=".DrawerActivity"> - - - + + \ 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 eca70cfe5..036d09bc5 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,5 @@ - - + + \ 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 2cbb88429cc06889b27ff25a4e24018e74b4e49b..2e9430316812a75f58c45e950c76561953d7e121 100644 GIT binary patch delta 3511 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 diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index 912c27f24999a112ebb61d1bb0b59e0099560dc7..7c57cfbacec138a131d4b1bc30513e38760aa5f4 100644 GIT binary patch delta 1929 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 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 2cbb88429cc06889b27ff25a4e24018e74b4e49b..2e9430316812a75f58c45e950c76561953d7e121 100644 GIT binary patch delta 3511 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 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index 7f7b6d052f2fa9a1bd6cc68213192e52d8bc3e87..7fd5e167d0a5675e737babe7b069f89c92938267 100644 GIT binary patch delta 2173 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 z000000000000000000000000000000000000000`3H}CL3k`SdW#&i#015yANkvXX Hu0mjf&~Ijb delta 1639 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 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index c18aa6c84a7d3ff3d962814f2e009768959e1ae8..26e875c971176febc58b5df27ffcde5705b45e30 100644 GIT binary patch delta 2560 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 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 5b423cb79c7dd22e81f196ab9ca8a15cb2271aa9..a20a78c215cb30f3af974363c6234401070a1dd8 100644 GIT binary patch literal 8041 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 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 4678e7020d4e78f5c6312bd0f28f7a78db3c78ed..5c34c133989242df49467443f11e9f8182fd53f8 100644 GIT binary patch literal 4727 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 literal 8242 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 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 544a56bfab4ec430cf1b6c0a4c627b4675772620..7b1f6de21410f99aa0fb6b29986539bc6dcb2f62 100644 GIT binary patch literal 11693 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 literal 11445 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�$JH~E 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 GU;ck(0`43D 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 544a56bfab4ec430cf1b6c0a4c627b4675772620..7b1f6de21410f99aa0fb6b29986539bc6dcb2f62 100644 GIT binary patch literal 11693 zcmYLPbyQP-8{c5S5Tv_11f;vW8zmGFBu0vKGrFW(xYFN|2?6oz^l%RJsJqa;i@brt>?M)*BH$p zS${6}WjiWiiiE4491a0n9+ErX9ADuGT`Hf){MC>>9)Bi3_kryZlXe3czPjGbj~^!B zx?uCTPI>tQF20F?@uAKeZmck#?aWR82^T5_3#+cPuIsMS6&F)aX^P)C;X*%Xu|7x0 z{{Lek9U4eax+3arjNv5@Spc=40^w)Hfqd6^e3+dRf^4Jx=6WU`S1w+bf{A>xBK5a8 z`3%bVz=WP zxvcCqEd5msO+$l2(?A8JW1!d{&(w~g$`iY_me=1R(1>x7` zyGO=-{E->b6!P>R`K#7vr4N5?4LUZWG16p%GKSz9L-h+{W;XIJJ#+ymDyX#Xn1jQ?+zH(WB%~Lusx-h7cZTI$7 z!bcga?7AlY$$L~8Y7f(^M(n_Be4H___nh8y5MBhxqdH_ahOZWi7;Wlt)2CO=^v=IT z4CZG~aWCy=mUm2+s{NejWrSwfg%5Pa$BvD9CWw1D1#!=s ztRQX8z2*X1s5jd^%^w#hC>Hw*a7{sm*n?IO|E*ddhtfmCi2mV1iJ#fHH^&u5J;d}~ zn?<;i)l;3s4S@+wT}Ms4OL0cp*+^i$!Vs-(ZF+Ci@wS`~8X z^LM$q6N0Xiu+nG43o?DCz38%(i(KG^P|Q@Xb^0`SDt#4a{*{rIHjixjJkpcP#t=7f zgVOp160p`C&|L~mB0>vzle!Z1V?G1vAypkISvEr{NV=XzG3BSkneOeGY9(!Q#D!iE z6n7l5$Pykl*-+ES3!Z*}JUt;jq{KmNn?&w+#xjFR(ZA`doD}XHGif@ZpXk5Fv-th_ z@?bV6B??0EU}$LQaC#{Ye)_43beEBu>MkiP+$vj46Zkgkxjvs_ZDr;CS96l4)MAhnrLC$jHbwqHN}k{PCF?J-d#R!aG#S)#2slPyKh7hnxM_k|AlP z{-kttbPVJ?7ErYe9+bICL*)5tW8`ELB=`&Z-e5Fr4GJNroWBo z1&`;d-f;yy`Jx|H88sncV`Dpccmxa@eYZznZF39uI^O}?YJ#OgNzA}+z$=|Tf~ed& z>KME`)c>|NhN2&3=GN{bC3*r5W>`u1U^t#$Ui*;F*XI`(A;6^fr*gal`Qd_PEG@oZ z>>0lA_V<%8lgAJx!u{u~yXK)wy}=CfI9jNi>6w>@B&zsa9VPfez;a1xx=Fg2X|IjR zKH+NAvHzaNGA%v(E#L=#9!{Xg!Nb#WJaM6KB_JVzPM4?$^IDHX>C)$@hfA&RPd5Ac zvKHIZ`O#bGgY6yR#P08N+awto<;hxAr|!0!Ha0#Oba-;hGeT(8NvMjYp^t6Thx0X` zel!g180=_;D(5oZxqItZ85Mf~r!WnAK#pRXbY+*aA;YtJw4uY`QA7M!%ZE#y1KahA3TM;FIX(Btua1)?V*-pg#b%eNP=#go^(-u zwl_(S))7JOdoe#hw%8pY+32u5w*lGMIX>pxCagB?evN0Nkb}aK56t`6a=ZKA0(G%( zN2~44ZNH1LeGCbE>j!Zxu|`ulk_rR|=|FKr+D{*D&$gX*$0@;D^gC!uFN`}pKL@i* zYRR8!QzuLGM>}Me4Q2B9-MJP9nL2V9HNx{{!&S_56C}Boo=}n*OME9Xqheot-VY}0 zBsG5u>MF)=9!X-(`~o`ra<)+Saq~wcenDlQl6Jlwu$R_4yc!D4i*f7%vCQcXP0K4J;K}_=@D- zVgs}NLap>L9kN0w4BqN@?>+}yR2%MGET!mMS>@PIggZBbBr10-DIjNZF;A|CyTh8s zElv2GR4X_O#b2@V&F(F@820>4=nh*T`H$Ue2u&dlM~VqE6H`?E3uK`m9>;5;EiEkx zA;L-45r!kSgfkXRpD*J0FZMoRK{n=!Rf)@wzS5B(b=+m5C|Tu520W4c&XdSQQPUR~ zb2XSfIy!3p!hM&C58is}U23wE_e*$Ms_XCcxu%hjc=Fr9d6(gOvEJ_SNnETB|L)sG zBLk9?-D(?u$L&t$1)^v>18JBr8am1DwAmNR`+PeQP?>>LZdE@?tvLuQ>~6)AGI`7G zcA(edydA|-QMSP@)zt%xp9u#wh6?w0;WKltuMP&aMHY8sHzN*wlfVwFa>t@KA{7<=bUSYJTZK^! zr@sNuPmkliA8kK6YW_su*cluf`|;M3ijboMHeXHYd$(Uo@EiY0nzfi#c$^#z?$eqENgk_^9hGu*tM_JSo9bjU$5FoKV} z?y*=7S*4!R8l;{I72{xjH@1C6y^o;k$E3tWic?5+!{qufB%LAf3w$@ICs}QqF5{=t zV0X>ly>SknOeW?wBhoF}yJkm1v*~VVq|PGx_}At%PSbzN3-7<%8@`mq&m)V~n5=h; zRaot{och5E<lYUTAT<7(!M@-eHuirz@?noY13VTP3neqBCC@8ZgvwB8OSy}bI;ABAutV8SZ1HGyJt`@DQ$PB^W ztK%o-x}{fi0(K2#`QK2} zHZMc|wzz{ zC@`K}aZUg!AzjNvpAAbR<#}!Qyjv4if>SVnj4CKd_LG7k|9#ygjdx=c4P$zo+F(n&QB5^IQZl|0TZU zM6vFNZ>T|SuGt#p=oXgC=l4%qZNkjV*3qoy&sM5% zszn5LmHxG4Z_VY95PnO(u!=UlUlLsOC%s~9QT&Huwu-zW68T^}JUsVvSiFe^PMV}* z9F^MuXB#b4%N^XIqA<)L@qGNNOoltyY1iYP55^HvZ;3V7!1`QF;Ei$^d9A!I8=SP}>n zJG!SZ7%^Pp7Zass3Vw<;pQNdXV(Kp2$+ZHpbwoC(kv=s-kS_iehefRIZ~VCr_+VY} ziUE9%zBNhfr=NATKaD2fxB@$U0-Gf{Gn5&n;#i@#6Mn}e2 z-baeh$YNrqsPEpPjc}Lj#j6x2oUs9Gv>orFyBLS2poXl?H5x^+R4NOdqNlf4TP3}9_yH_iC7I{w4)Bx4 zVW#d7b?2vvEMXUGfR;Hx+X>H*7c%V-j%)TGMY-PGOiw{^);RHzM11LQk=^TutFdhBI)N+)}dqEGM1jEbvZ<9>fRZ~CmhU`bM~Q+*QvgRv;* z8F{*NX$L(u^C5-2)RO&><|c&fnI}w;iL!zzhA})gG=#>amM%4lG1gWYbPsL}U+MC@ zhiL{Hg={)+1Ecun0W7bCNDmqc7*iG=@88@r(3v}81#V@xUGEbAmBFic;O-DBw*kqE z#r^#G^N>?mq59QNbr9L3UFEyxaa>6QT&!9~yu6_vb{PHzA)g|WlA*ccBKyXUl^&iG z-NoN!V*k5+=Dc6uT1EPlm6bi1G;@yRNn>tF=wg77hUQnSCP5tCkIFut0L%Pk~&; z`hT#qpq{M=EASN-;iOY8qXsAJLIHdbmB$h$iOzH+BZLbPuVheBWA6^i=8f zXE0{7#OTSu5Q@XIA<@!ILOYUI>II(u53QwFt3S)}r#a5+Fhub5Nj^WElk}aew7S@( z&EoDr%G{3EI=jgmPTyEENHwSN^puKoCa_eJyb&I?3sGC|hGr|59~tdW(h;G1%OvEb zr;~icta|erspI}%JAe7Nf2V(jT+|GoOwyrb1iX(EJCNk*VKzECnaRwE zb1jw>Is5glN4q|*e&>etIbhTvq_gm!bWb0J`)b@@31P!q@aTMMZr|btpclGD+=iV9q>O845Kzan$*8 zcj7IJgoTHzlBcnK&KFZ|T3A@l>$rV6d8!=Gl9I~_{h)+|If7(8 z@5nTPmONWF?It4=RB(nW2YPiA9UZM|3v6}WKIaUX{4XGW$)}r-?JFxQ7SfLUs18ZchKCectk5W={(Ry)js6+?gs?6+a2C#y}zo z2RW|wkHvzrAbJL+d4^I(3;}l+`^)%DYA5vUX#@W)Md+Hp-9z4e5NPl#m~Jc)Wbt@^ z9ZoLj$Pxy+;1w3W-Ryq4tceY=T>4c!12L%m5J-Tu+GwK-@<)7>27SB*iMulN{|W{* zVlM$$pMr;;Sl_!lo~@cG>|(`Y*7_cD^LV`;d=kvF-wy#}Qlk8X_=F2s5u!|eC*`$b zi2@OGn&?gBh>KSKf_w;gkA^PgCbbv@3S*XXdbS%l2Wk8qLr%8P6D=Y@inncb-A~+! z=Iq*&H+Z^Ve>OR=cOpy`scryxb~W_ry)>vFpN7RAJ7_(WBSFuR>86Hgoh=e1guFGD zDOkg4Mv8VRQp_mzMgzX?e;)&lr-y!kAz1`{LiobgR_dJ0Fl$hpwKX`jY$^dZtU@pK zT7^a!tkU#4?^TkNTeIWp5=w6k5roT?e-HTtMp)cpi>P0!sOkE~hhz}|5Dh=*s2WmA zEa)ii&~c@uj^01dDd=SgAX+7N`=@6s&PG4vjYZ%iAe0H^;agi zT(&bT%19i>ZH+Je7mO3oxG2Cf^wjXod9H?-=h4w3PUE(sGPwod1XLZ*Pxr>q(Z*)^ z40QN(xW0zQ_#0rdaP$0zi6C7b=!c+R;UJgY@i#g)je0LWpKXo210W5Pg%^y-ER{1b zcD2poCmS;a{(_UI_{`^YPVv44Pt62;U~W<<$x+blc9>cawF$em^7VZvuC1*l3fV>= z5JtsJAXCg>tE7|^H#wXh9LR7`UNtw=pIP}{GUhQe?Oe4=&;8#@d08m89+vt&8?}%V z4KBC6rhx+Wz_&^WO=-Tek8mPkZtf@}j2>@f zLan5g)pn2RAQJLEo_^PIsMKQp!7#}|_43wcf4JR16xSmjP31K*Z1TXkw#kisNkG{$ zDAsAa2~gU78@<6c11Y~nsUhnr> zMHA$UuTDM}jz#GmgDb^1_zeX1RxFYY&!g@c;m#Z0-}XUkeuczu=Yc*XvpU9@bH}_la!nsL_W~u;cEGlZ2TLW0)F#bbObZ|%b=yILEQCH zp_^=D0?PB!tvP_Te{y`srfj(JalSi|CNRO(|9WyN=mvPa^SisqzwCL5 zjD+M#Dp&4<{APe$JLyudPF-Dnbred|CYzQBdX$~c^C>krai+$^x~iUVOsuL+$R+x& z+g|}NM3wu93}tEr-ZlDi-NzyAAJzC~2E{rlBIs}BKx@R%00G&dUAsC-seN=r!?Yo6 z`QW3IGmH`3t#w8)chk;-*eupS%q&@4|68>}_jN$hicVF}6nK;au>AWgY_RRdjPxhR zugS@L%0%dRJ|X)wJ#YZm&&%KH$6R2538GKmSh^kk{+HkqOCXiV^*=j3rzFVK5Tz&EHwBMn6={ z7v2y*^Q|%yndle+i^8g6#njJ0Tk^bbf7yf&S z_B@x0M5C|MFio=DqVZ|m?v^=ynMmX&PEtvKu|T=cF%FjrU?y~8a&jM+;dt6?XuG9) z5FtjWh>(yL!=C&r78PaXTXpo5Da3NCjQ$P>$9Fy2oVxA4ak+abowqrnqE+NXYvw)Q3 z?sD${>ZgKknj8xw=F+?*`U+(~hBa8UiXko13LN!M%jMMzyQ!e~pCI2>WVK~F2f!90 z-Vf+wpMIVlr4NXrFgvJ>RU)it#5o32ivcAH3T$-D;Z8*`q|$#K+Bp z4!xnJrLFb7b&ihM)z1UP`cg(cFW=tZPs^kmZGVP0QJS$yN=imS_)9g~g~lFiR7i^& z^{FPE;o)%Ggf=u+Rmty#yK0c~ruyVD_)CEa8jCxx~9dd(;GZF6Ds9ya5RfWuN_ z+DLB6+}zwtHoRv}Wz^Zz(^DG-{<-Dv4nAqFrK~aqJ6c$jYnwuzEtqAs6b1h9 zOrD;eM%hzU^3N9cA+nmtdF1S4dX=(MS*+>i_vLAzFCPmFD-rVr7S@$w>7NePuRC8y zgVJj?n0rztAZ=R+Q@zlsZ^L|v~6g) zNcw3fba*EM5gm>!JUluFRi+s~i7#jZ&dO+y0q@-dj|e>}MBPszz?EqLaZs-J%9&43 zHI}g&f0yZlO;AE3) z8o0Wg^aq6+0t4d4(Obibvcr8T!ZLQ%l&e?9p5{)yTn7DZP-^N3abw9yW8P~zDD8Dj5 zk9Ssm?Z?@n{o#LpwcQ8dDFl4;`zq|R!_Y@_r-fxSULy_F`bDZRs2y5H&y8apXmBNU zjsnhMlzPrFA#$V9!I_Vf=1uzqVxyxof;A1c-1JI0jeJX zax@Kiyu3*!SW=xhv7FkGgDXLvD}s4G>=*F0c*!C+`jmp42M+urAj}$Bo`6Yx!5Sj2 zudi?GKZ}ST|D-(bWIQM)5=aDyJ|CF8#^#mUZAuPE*4wJDa^J>j-#pdH=~C!!y~{6? zPV%$;g@-vwrJd4l^N(L#Rc+19&CHM6PXolJ9f7*KvH5l($r^Cl)!CC?pM58%PfQk8 zP&EDArQ9G6K$8kTKkP~l;q3s&<635ix%TiubZO@NXaHD-F-d&0@HQ6au{VreTnc@mgOZCvsjZwHg3VSD8!5 zz9}e0LP%ys@w<%UUts@>#;gv!e((m0@riT@f7N7n8+UIFC0@!Eo{O`y^FXc%a9?IB zMpB+dl7s*o53TApYOerhe<*LQpUek3Kb@%8gEFz+_=zVy4*1%o(fIRsPy7VT-vnRaZN_vTZ|3zwyi+M&T#?z zg?NEZce|Og1mx%k665>BuP8S;eKEJ}aIDiZKss&JW7e4zBceR0fl#0Z)pjHMiL~~% zolnUtoxbm@Lc9clp%@T<{3}vUDqBn;?l<^YH5{i5JyA1(zx_k6YU6EtdVkzqQy{^y zg|X*Cv>+ymF(^u!3(}^wH8@j7eJ@fG)**&F$Cr4)s-&S2M#%QIl39%(hUm%WvN~!B zu%VYpj1`!dVR>*cGWV>{;1|-ur<*@<_OFr< z`g(h7O%&;*)dE6bh2O)H4xXq-t65n4~6^v{1&Y_4Z`CoA;#xTw4j=96VdQGwRynp_|N^ zu5zbUw_G3}-9?MZ&A!NEKPw==(C`b3JF|N4hu>tRaL)s{Qz<{Z*?FTdUab?KrY)N+ zRYN`ekhlQ_N1eq_i@&GM?XYa7^`#lKu1F{G&pWd4bfYr9hPt|hA}!!v=-0@V@T2G8 zQ0JuFK0ALwBf*5vbZ2!K3MyQ-=&uyyr7`m$D&q}Z;l4!m0QOlsB^A|Lcx^w|%onq7 zB~wLlufq*qha!l-pBLC1oli(o`)u~1@Y{&`-5@iB`Wt7VANyGAM}VH8WAMLhytEwh zz>titb^pNtlVMS}RO6A^EX-_3L-kJ2Pr7%AS101_A)n()`rE%Zm4*#FE??IcmI7ZS z(GV-+5GR43=cSFUkUHjRG5o)#fw7Mx&rlfQxy}mc=;&P5<5?&ngkhv;F7Xl*NisTm z5iimzD=YVcj6N;aTmHQ~oVQ`kGx3hrQF}Uzv^Nltpk(?kPuH@QtOSQ)zwDqc3mZ5t zmOaDe$I}ksC)CYtO#>pHKq78)T6(|MT|p!(axBPRyDP6OV&e9E zxBTZhj(NYPVqZlnUZI3#f=qH_p2=T#^{;#h4`2_EYpk-%Slt5Ib%!|{1w`37EgL0A zjG_1D@$e^{Jl&bST+Ns&fQ%0@=u!46H(CkJt1#C)LqpWjvd+B1jO!1_ zHZ8i$E?M(_lWx-!@MrMl&xP64NEZE!8v2EAozV77{*?g|C@n? z*XkD=OZY#n)#iBFR1Tko;edddGHrT`4gHZU6R(c$pBO~W!`bFJ422l(HglC3azSNf zWt)vdY%y3~dm}{YPJ-TMsF7D&x;LUsOgmWAw~GxPW(FH0)nuutPrLGAWj!OIF-Tp2 zG9_;TSZlOvp|gt%1_Yg5=6ITyJ&!{z;OTak4H*@aX6!h6VkIqbeGZRKg78ILL2Etv z@8x3Ev|6A?fV%X=0I&@3b?y5Z)B;FN$bP`IM*+zg(DrdCwsVhkd3jkkA+zFb%k5xt)8k$Zx&YUBLJ2uxvTO_*GjQqhbQYL(1d^6palHhiVhwZKg}Q zcuoY(Ny_*(=9?lEG=k`I^Auvp9R7m{KXY?BEy8k|Kri9;nZXc&dxLt55zPzsjGa$o z7I6YM6>peMB;z27njah-#*{ajf%uRUu-|9uLo!Y<2t*ECtkVj-fQ)=3hu>~a%scFY zeUW$jYxh)eHv^v2P3&lq&I=5=Wh7w}iK><(tWmh0B*d2@^$F^0b<0faZm?<@S0;Z++ zV7B68aB%QFf)^+=ryt++jMr;1$6Y~BB*P(MhD>hhA7Ya@*YutTJbw%hTAkYJaFeII z&y@OUv$Tfty3PT%cPek^M7A;Z6=r6gwGpdK_MJ*9zl9s^4Du8uzlEJf5Fm)Vnt%|I zcjk56Qni-1@h?;b>dW)``g-`m&3uh%?w1J*K5=qyg(Jh(25W2j(UTV^Y5DdAL~Kz7 zRPj^pYGsV?v=8N{Qk(4N9lOm3qGpB2%gVpSza`+Nd0dw?pzxd2?AxRJ3O7OAk6xdI|?ywpcoTx5Uk>1>qqS^_YZ(p{BldKG3oQ?&mWia+E`raU(jMSe+h7|tAv}CNvHSa6L7b}+L~SC_D!b6 ztqZ&GeTe3c7NMP=Aj&^vi_aE&rk9qM0AsPuKMRTv@VQ#Ee$*{#Un++hSvPrSx`?sE z925x@e3mjCV&b%B_rFW@0Gi&Qp-HU1Ya3EK994V_s}B;O_4-2aDrEw7MbN1~p&Vsj zM*;Gc5Geu*xQU|hDVBS{W)yS1lY1Ta3ZJym5=*2-lch+RaTE=k>etiy^OHwBSQ0); z*;eZMX`gDevCDUcm{tRS9JNT4ykRexH%Xp@m8bU+nQQn6GMj3Wg~anV7D&MYB6KAt0vA?oqZw-MM)RfVS?=q2L*#jn_mY{l}n55+8s6Bl-& zE9X(Z-cFz+7vR?w8V|lj8;C#v3OcRp?x4aFF!gqUG}K56i#L;S3vWLXlP?v?n>9zd zQ2bWG^Y3k@fI}M>U=0v%ZfE6L^A zP>ki`gD>rO0Y6L4q{(*n3P7us{M41A07H@{mMe$XAqf7T)BK3g_-ENsbyicE`6%8^ z_vA3$Ju0V-=}t49aOB*>hZ2#kSdvRZGS+%?vz9xa=bpd`C@e7!0g$)vzYJ4{amdJ) zJ^@BeZo$*)*oai@`JbUU#tb<$YWXH~vll>md5wSDI@vmPT+WnBCk*jU7V_u2 zz7lFWpW#3g#ip2oRf=B}CqDztQmr`F;l_|}KwngeuAzXuIxHXS7Q68wZiFe4T+N$6 znI$9H`(OU!^@GcTwF(+;e2<2P*qDwU0SP&IW*1PZR9#~MMQcJpNXS3FxY$bGjfLgy z9`k<3v)*Up$G^xJP}eqoK2`KPK`V5M*KtLLfpBRMts#wS^=BCfFjXP3B;4i%G2Z~G zcuN)cs1${9fA!bLb88rvKQMtPG(JgrOG#Nd0V)$)s!*7)m0Z0De^T#ZqDwGgzLhzP z26|>P!iNF?@4?9-bnTr`@QNs)UV?!xn{bvIs;NWSaIDq-_4R_3Pe4mrcSw;+ z!8L#@R=a20qk;(Q-_`*t>(b7s-Mj{Q=ka+)r9?0g&kkTrnBN=9kxIMVV2fYI!+xXbo9agWct%-}e?jfkb zVnSkKzOZip$FQVHyF1E9>c^Ll9Bcv@$@^E|mp+spm$-bGB>y>cHzhpPTJPeX6m#+Y zXo)O^Ye^r!r^Z*C{XP^)p+*L9M<17#{f6MZfaLb}>l%X$L zjoC$Jftz*nm-)7%nz<(rmpAW+sXl*dg?NEqlRG;#mJSB!isU8p?;0jM81LCXT`lGu zwXZCAh&H&?C4sxRMR!q;$Qq?N6?{G?f5m*ulN*R1G=Za@UI`m#2opyx#78cP3Y>YP zfageM#PlpFctu5gMLK@OOS$N=P=9Ryb38EagyQ7=^gDD!bYX!7LLMq1J7ZQ~Ac4FQ z{j1LY7u=>_YUv9&6+bkSlK1xbW}W}zag$l@Y`Wi7^ilgk!*Mk|`KBA#1a3m%O&csg z>zYtNg#6KA@r$;mLLzECsa)FWa}B4DsWIuWUBfW~#oZWAoJSl!sg$pdsS!)YlV~t9 zTrj#7Kp*hw5)b~m=QY*o6wv;Y7A literal 11445 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 + tools:layout="@layout/fragment_main_activity_recycler_view" /> + android:label="" + tools:layout="@layout/fragment_banner_home"/> \ No newline at end of file diff --git a/app/src/main/res/navigation/main_graph.xml b/app/src/main/res/navigation/main_graph.xml index 999edefa2..beead4b8e 100644 --- a/app/src/main/res/navigation/main_graph.xml +++ b/app/src/main/res/navigation/main_graph.xml @@ -52,7 +52,7 @@ + + + + - + + \ No newline at end of file diff --git a/app/src/main/res/navigation/settings_graph.xml b/app/src/main/res/navigation/settings_graph.xml index 513b5670f..9e8ddbcc1 100644 --- a/app/src/main/res/navigation/settings_graph.xml +++ b/app/src/main/res/navigation/settings_graph.xml @@ -69,7 +69,12 @@ + app:destination="@id/aboutActivity" + app:enterAnim="@anim/retro_fragment_open_enter" + app:exitAnim="@anim/retro_fragment_open_exit" + app:launchSingleTop="true" + app:popEnterAnim="@anim/retro_fragment_close_enter" + app:popExitAnim="@anim/retro_fragment_close_exit" /> - - - + - + + \ No newline at end of file diff --git a/app/src/main/res/transition/grid_exit_transition.xml b/app/src/main/res/transition/grid_exit_transition.xml new file mode 100644 index 000000000..6ff14dad3 --- /dev/null +++ b/app/src/main/res/transition/grid_exit_transition.xml @@ -0,0 +1,27 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night-v27/styles.xml b/app/src/main/res/values-night-v27/styles.xml index 701328992..87c77add9 100644 --- a/app/src/main/res/values-night-v27/styles.xml +++ b/app/src/main/res/values-night-v27/styles.xml @@ -15,11 +15,9 @@ \ No newline at end of file diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 5313087d4..99e61bb24 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -15,11 +15,9 @@ diff --git a/app/src/main/res/values-v23/styles.xml b/app/src/main/res/values-v23/styles.xml index dee5c9fdd..c70057acf 100644 --- a/app/src/main/res/values-v23/styles.xml +++ b/app/src/main/res/values-v23/styles.xml @@ -21,7 +21,6 @@ \ No newline at end of file diff --git a/app/src/main/res/values-v27/styles_parents.xml b/app/src/main/res/values-v27/styles_parents.xml index ca42d0971..d346f69c9 100644 --- a/app/src/main/res/values-v27/styles_parents.xml +++ b/app/src/main/res/values-v27/styles_parents.xml @@ -15,20 +15,18 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values/styles_parents.xml b/app/src/main/res/values/styles_parents.xml index efd3e55cc..ef8e30992 100644 --- a/app/src/main/res/values/styles_parents.xml +++ b/app/src/main/res/values/styles_parents.xml @@ -1,5 +1,5 @@ - + @@ -87,19 +88,4 @@ wrap_content - - - - \ No newline at end of file